blob: 1ddec08af522bddf5364dbd0e37974f736c437a2 [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;
Philipp Maier782ccec2018-10-08 17:12:49 +0200161 struct gsm_mncc mncc;
Philipp Maier621ba032017-11-07 17:19:25 +0100162
163 OSMO_ASSERT(mgcp_ctx);
164 fi = mgcp_ctx->fsm;
165 OSMO_ASSERT(fi);
166
Philipp Maier79beccd2018-04-11 17:36:45 +0200167 /* Check if the endpoint identifier is a specific endpoint identifier,
168 * since in order to perform a DLCX we must know the specific
169 * identifier of the endpoint we want to release. If we do not have
170 * this information because of errornous communication we can not
171 * perform a DLCX. */
172 if (strstr(mgcp_ctx->rtp_endpoint, "*"))
173 dlcx_possible = false;
174
Philipp Maier621ba032017-11-07 17:19:25 +0100175 LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
176 get_value_string(msc_mgcp_cause_codes_names, cause));
177
Stefan Sperling722f2b42018-09-28 14:26:35 +0200178 /* Request the higher layers (gsm_04_08.c) to release the call. If the
179 * problem occured after msc_mgcp_call_release() was calls, remain
180 * silent because we already got informed and the higher layers might
181 * already freed their context information (trans). */
182 if (!mgcp_ctx->free_ctx) {
Philipp Maier782ccec2018-10-08 17:12:49 +0200183 mncc = (struct gsm_mncc) {
184 .msg_type = MNCC_REL_REQ,
185 .callref = mgcp_ctx->trans->callref,
186 .cause = {
187 .location = GSM48_CAUSE_LOC_PRN_S_LU,
188 .coding = 0, /* FIXME */
189 .value = GSM48_CC_CAUSE_RESOURCE_UNAVAIL
190 }
191 };
192
Stefan Sperling722f2b42018-09-28 14:26:35 +0200193 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
194 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
195 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
196 }
197
Philipp Maier4eef20b2018-03-13 13:07:45 +0100198 /* For the shutdown we have two options. Whenever it makes sense to
199 * send a DLCX to the MGW in order to be sure that the connection is
200 * properly cleaned up, the dlcx flag should be set. In other cases
201 * where a DLCX does not make sense (e.g. the MGW times out), halting
202 * directly is the better options. In those cases, the dlcx flag
203 * should not be set */
Philipp Maier79beccd2018-04-11 17:36:45 +0200204 if (dlcx && dlcx_possible) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100205 /* Fast-forward the FSM into call state. In this state the FSM
206 * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When
207 * one of the two events is received a DLCX will be send to
208 * the MGW. After that. The FSM automatically halts but will
209 * still expect a call msc_mgcp_call_release() to be freed
210 * completely */
211 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
212 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
213 } else {
214 /* Halt the state machine immediately. The FSM will not be
215 * freed yet, we stil require the higher layers to call
216 * msc_mgcp_call_release() */
217 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
218 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
219 }
Philipp Maier621ba032017-11-07 17:19:25 +0100220}
221
222/* Timer callback to shut down in case of connectivity problems */
223static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
224{
225 struct mgcp_ctx *mgcp_ctx = fi->priv;
226 struct mgcp_client *mgcp;
227
228 OSMO_ASSERT(mgcp_ctx);
229 mgcp = mgcp_ctx->mgcp;
230 OSMO_ASSERT(mgcp);
231
232 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
233 /* We were unable to communicate with the MGW, unfortunately
234 * there is no meaningful action we can take now other than
235 * giving up. */
236
Philipp Maier621ba032017-11-07 17:19:25 +0100237 /* Cancel the transaction that timed out */
238 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
239
Philipp Maier4eef20b2018-03-13 13:07:45 +0100240 /* halt of the FSM */
241 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100242 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
243 /* If the logic that controls the RAN is unable to negotiate a
244 * connection, we presumably still have a working connection to
245 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100246 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100247 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
248 /* Under normal conditions, the MSC logic should always command
249 * to release the call at some point. However, the release may
250 * be missing due to errors in the MSC logic and we may have
251 * reached ST_HALT because of cascading errors and timeouts. In
252 * this and only in this case we will allow ST_HALT to free all
253 * context information on its own authority. */
254 mgcp_ctx->free_ctx = true;
255
256 /* Initiate self destruction of the FSM */
257 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
258 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
259 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
260 /* There may be rare cases in which the MSC is unable to
261 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100262 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100263 } else {
264 /* Ther must not be any unsolicited timers in this FSM. If so,
265 * we have serious problem. */
266 OSMO_ASSERT(false);
267 }
268
269 return 0;
270}
271
272static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
273
274/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
275static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
276{
277 struct mgcp_ctx *mgcp_ctx = data;
278 struct mgcp_client *mgcp;
279 struct mgcp_msg mgcp_msg;
280 struct msgb *msg;
281 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200282 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100283 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200284
Philipp Maier621ba032017-11-07 17:19:25 +0100285 OSMO_ASSERT(mgcp_ctx);
286 mgcp = mgcp_ctx->mgcp;
287 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200288 trans = mgcp_ctx->trans;
289 OSMO_ASSERT(trans);
290 conn = trans->conn;
291 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200292
Philipp Maier79beccd2018-04-11 17:36:45 +0200293 /* NOTE: In case of error, we will not be able to perform any DLCX
294 * operation because until this point we do not have requested any
295 * endpoint yet. */
296
Philipp Maier621ba032017-11-07 17:19:25 +0100297 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200298 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100299
300 /* Generate MGCP message string */
301 mgcp_msg = (struct mgcp_msg) {
302 .verb = MGCP_VERB_CRCX,
303 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100304 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200305 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100306 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100307 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100308 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200309 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100310 return;
311 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100312
Philipp Maier5046db92018-05-29 12:02:38 +0200313 /* HACK: We put the connection in loopback mode at the beginnig to
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +0200314 * trick the hNodeB into doing the IuUP negotiation with itself.
315 * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
Philipp Maier5046db92018-05-29 12:02:38 +0200316#ifdef BUILD_IU
317 if (conn->via_ran == RAN_UTRAN_IU)
318 mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK;
319#endif
320
Philipp Maier621ba032017-11-07 17:19:25 +0100321 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
322 OSMO_ASSERT(msg);
323
324 /* Transmit MGCP message to MGW */
325 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
326 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
327 if (rc < 0) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200328 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100329 return;
330 }
331
332 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
333}
334
335/* Callback for MGCP-Client: handle response for RAN associated CRCX */
336static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
337{
338 struct mgcp_ctx *mgcp_ctx = priv;
339 int rc;
340 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100341 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100342
Philipp Maier79beccd2018-04-11 17:36:45 +0200343 /* NOTE: In case of error, we will not be able to perform any DLCX
344 * operation because until we either get a parseable message that
345 * contains an error code (no endpoint is seized in those cases)
346 * or we get an unparseable message. In this case we can not be
347 * sure, but we also can not draw any assumptions from unparseable
348 * messages. */
349
Philipp Maier621ba032017-11-07 17:19:25 +0100350 OSMO_ASSERT(mgcp_ctx);
351 trans = mgcp_ctx->trans;
352 OSMO_ASSERT(trans);
353 conn = trans->conn;
354 OSMO_ASSERT(conn);
355
356 if (r->head.response_code != 200) {
357 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
358 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier79beccd2018-04-11 17:36:45 +0200359 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100360 return;
361 }
362
Philipp Maiera2353c62018-02-08 14:15:59 +0100363 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100364 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
365 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 +0100366 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
367 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100368
369 rc = mgcp_response_parse_params(r);
370 if (rc) {
371 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier79beccd2018-04-11 17:36:45 +0200372 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100373 return;
374 }
375
376 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
377
378 conn->rtp.local_port_ran = r->audio_port;
379 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
380
381 /* Notify the FSM that we got the response. */
382 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
383}
384
385static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
386
387/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
388static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
389{
390 struct mgcp_ctx *mgcp_ctx = data;
391 struct mgcp_client *mgcp;
392 struct mgcp_msg mgcp_msg;
393 struct msgb *msg;
394 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200395 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100396 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200397
Philipp Maier621ba032017-11-07 17:19:25 +0100398 OSMO_ASSERT(mgcp_ctx);
399 mgcp = mgcp_ctx->mgcp;
400 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200401 trans = mgcp_ctx->trans;
402 OSMO_ASSERT(trans);
403 conn = trans->conn;
404 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200405
Philipp Maier621ba032017-11-07 17:19:25 +0100406 switch (event) {
407 case EV_CRCX_RAN_RESP:
408 break;
409 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100410 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100411 return;
412 }
413
414 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200415 "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100416
417 /* Generate MGCP message string */
418 mgcp_msg = (struct mgcp_msg) {
419 .verb = MGCP_VERB_CRCX,
420 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100421 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200422 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100423 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100424 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100425 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100426 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100427 return;
428 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100429
Philipp Maier621ba032017-11-07 17:19:25 +0100430 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
431 OSMO_ASSERT(msg);
432
433 /* Transmit MGCP message to MGW */
434 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
435 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
436 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100437 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100438 return;
439 }
440
441 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
442}
443
444/* Callback for MGCP-Client: handle response for CN associated CRCX */
445static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
446{
447 struct mgcp_ctx *mgcp_ctx = priv;
448 int rc;
449 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100450 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100451
452 OSMO_ASSERT(mgcp_ctx);
453 trans = mgcp_ctx->trans;
454 OSMO_ASSERT(trans);
455 conn = trans->conn;
456 OSMO_ASSERT(conn);
457
458 if (r->head.response_code != 200) {
459 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
460 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100461 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100462 return;
463 }
464
465 /* memorize connection identifier */
466 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
467 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
468
469 rc = mgcp_response_parse_params(r);
470 if (rc) {
471 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100472 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100473 return;
474 }
475
476 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
477
478 conn->rtp.local_port_cn = r->audio_port;
479 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
480
481 /* Notify the FSM that we got the response. */
482 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
483}
484
485/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
486static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
487{
488 struct mgcp_ctx *mgcp_ctx = data;
489 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100490 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100491
492 OSMO_ASSERT(mgcp_ctx);
493 trans = mgcp_ctx->trans;
494 OSMO_ASSERT(trans);
495 conn = trans->conn;
496 OSMO_ASSERT(conn);
497
498 switch (event) {
499 case EV_CRCX_CN_RESP:
500 break;
501 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100502 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100503 return;
504 }
505
506 /* Forward assignment request to A/RANAP */
507 if (conn->via_ran == RAN_UTRAN_IU) {
508#ifdef BUILD_IU
509 /* Assign a voice channel via RANAP on 3G */
510 if (iu_rab_act_cs(trans))
511 goto error;
512#else
513 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
514 goto error;
515#endif
516 } else if (conn->via_ran == RAN_GERAN_A) {
517 /* Assign a voice channel via A on 2G */
518 if (a_iface_tx_assignment(trans))
519 goto error;
520 } else {
521 /* Unset or unimplemented new RAN type */
522 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
523 return;
524 }
525
526 /* Respond back to MNCC (if requested) */
527 if (trans->tch_rtp_create) {
528 if (gsm48_tch_rtp_create(trans))
529 goto error;
530 }
531
532 /* Note: When we reach this point then the situation is basically that
533 * we have two sides connected, both are in loopback. The local ports
534 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100535 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100536 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
537 return;
538
539error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100540 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100541}
542
543static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
544
545/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
546static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
547{
548 struct mgcp_ctx *mgcp_ctx = data;
549 struct mgcp_client *mgcp;
550 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100551 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100552 struct mgcp_msg mgcp_msg;
553 struct msgb *msg;
554 int rc;
555
556 OSMO_ASSERT(mgcp_ctx);
557 mgcp = mgcp_ctx->mgcp;
558 OSMO_ASSERT(mgcp);
559 trans = mgcp_ctx->trans;
560 OSMO_ASSERT(trans);
561 conn = trans->conn;
562 OSMO_ASSERT(conn);
563
564 switch (event) {
565 case EV_CONNECT:
566 break;
567 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100568 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100569 return;
570 }
571
572 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100573 "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 +0100574 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
575
576 /* Generate MGCP message string */
577 mgcp_msg = (struct mgcp_msg) {
578 .verb = MGCP_VERB_MDCX,
579 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
580 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
581 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100582 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100583 .conn_id = mgcp_ctx->conn_id_cn,
584 .conn_mode = MGCP_CONN_RECV_SEND,
585 .audio_ip = conn->rtp.remote_addr_cn,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200586 .audio_port = conn->rtp.remote_port_cn,
587 .codecs[0] = conn->rtp.codec_cn,
588 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100589 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100590 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100591 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100592 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100593 return;
594 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100595
Philipp Maier621ba032017-11-07 17:19:25 +0100596 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
597 OSMO_ASSERT(msg);
598
599 /* Transmit MGCP message to MGW */
600 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
601 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
602 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100603 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100604 return;
605 }
606
607 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
608}
609
610/* Callback for MGCP-Client: handle response for CN associated CRCX */
611static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
612{
613 struct mgcp_ctx *mgcp_ctx = priv;
614
615 OSMO_ASSERT(mgcp_ctx);
616
617 if (r->head.response_code != 200) {
618 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
619 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100620 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100621 return;
622 }
623
624 /* Notify the FSM that we got the response. */
625 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
626}
627
628/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
629 * for the RAN side if we already have valid IP/Port data for the RAN sided
630 * RTP stream. */
631static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
632{
633 struct mgcp_ctx *mgcp_ctx = data;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100634 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100635 struct gsm_trans *trans;
636
637 OSMO_ASSERT(mgcp_ctx);
638 trans = mgcp_ctx->trans;
639 OSMO_ASSERT(trans);
640 conn = trans->conn;
641 OSMO_ASSERT(conn);
642
643 switch (event) {
644 case EV_MDCX_CN_RESP:
645 break;
646 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100647 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100648 return;
649 }
650
651 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
652 * IuCS interface is complete (IP-Address and Port are valid) */
653 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
654
655 /* If we already have a valid remote port and IP-Address from the RAN side
656 * call leg, the assignment has been completed before we got here, so we
657 * may move on immediately */
658 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
659 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
660}
661
662static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
663
664/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
665static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
666{
667 struct mgcp_ctx *mgcp_ctx = data;
668 struct mgcp_client *mgcp;
669 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100670 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100671 struct mgcp_msg mgcp_msg;
672 struct msgb *msg;
673 int rc;
674
675 OSMO_ASSERT(mgcp_ctx);
676 mgcp = mgcp_ctx->mgcp;
677 OSMO_ASSERT(mgcp);
678 trans = mgcp_ctx->trans;
679 OSMO_ASSERT(trans);
680 conn = trans->conn;
681 OSMO_ASSERT(conn);
682
683 switch (event) {
684 case EV_ASSIGN:
685 break;
686 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100687 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100688 return;
689 }
690
691 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100692 "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 +0100693 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
694
695 /* Generate MGCP message string */
696 mgcp_msg = (struct mgcp_msg) {
697 .verb = MGCP_VERB_MDCX,
698 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
699 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
700 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100701 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100702 .conn_id = mgcp_ctx->conn_id_ran,
703 .conn_mode = MGCP_CONN_RECV_SEND,
704 .audio_ip = conn->rtp.remote_addr_ran,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200705 .audio_port = conn->rtp.remote_port_ran,
706 .codecs[0] = conn->rtp.codec_ran,
707 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100708 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100709 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100710 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100711 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100712 return;
713 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100714
Philipp Maier621ba032017-11-07 17:19:25 +0100715 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
716 OSMO_ASSERT(msg);
717
718 /* Transmit MGCP message to MGW */
719 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
720 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
721 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100722 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100723 return;
724 }
725
726 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
727}
728
729/* Callback for MGCP-Client: handle response for CN associated CRCX */
730static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
731{
732 struct mgcp_ctx *mgcp_ctx = priv;
733
734 OSMO_ASSERT(mgcp_ctx);
735
736 if (r->head.response_code != 200) {
737 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
738 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100739 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100740 return;
741 }
742
743 /* Notify the FSM that we got the response. */
744 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
745}
746
747/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
748static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
749{
750 struct mgcp_ctx *mgcp_ctx = data;
751 OSMO_ASSERT(mgcp_ctx);
752
753 switch (event) {
754 case EV_MDCX_RAN_RESP:
755 break;
756 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100757 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100758 return;
759 }
760
761 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
762 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
763}
764
765static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
766
767/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
768static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
769{
770
771 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
772 struct mgcp_client *mgcp;
773 struct mgcp_msg mgcp_msg;
774 struct msgb *msg;
775 int rc;
776
777 OSMO_ASSERT(mgcp_ctx);
778 mgcp = mgcp_ctx->mgcp;
779 OSMO_ASSERT(mgcp);
780
781 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200782 "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 +0100783
Philipp Maier621ba032017-11-07 17:19:25 +0100784 /* Generate MGCP message string */
785 mgcp_msg = (struct mgcp_msg) {
786 .verb = MGCP_VERB_DLCX,
787 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100788 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100789 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100790 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100791 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100792 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100793 return;
794 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100795
Philipp Maier621ba032017-11-07 17:19:25 +0100796 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
797 OSMO_ASSERT(msg);
798
799 /* Transmit MGCP message to MGW */
800 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
801 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
802 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100803 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100804 return;
805 }
806
807 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
808}
809
810/* Callback for MGCP-Client: handle response for CN associated CRCX */
811static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
812{
813 struct mgcp_ctx *mgcp_ctx = priv;
814
815 OSMO_ASSERT(mgcp_ctx);
816
Harald Welte33d61e72018-02-10 10:43:38 +0100817 /* DLCX is the only command where 250 is permitted as positive result */
818 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100819 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
820 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100821 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100822 return;
823 }
824
825 /* Notify the FSM that we got the response. */
826 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
827}
828
829/* Callback for ST_HALT: Terminate the state machine */
830static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
831{
832 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100833 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100834
835 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100836 mgcp = mgcp_ctx->mgcp;
837 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100838
839 /* NOTE: We must not free the context information now, we have to
840 * wait until msc_mgcp_call_release() is called. Then we are sure
841 * that the logic controlling us is fully aware that the context
842 * information is freed. If we would free early now the controlling
843 * logic might mistakenly think that the context info is still alive,
844 * so lets keep the context info until we are explicitly asked for
845 * throwing it away. */
846 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100847 /* Be sure that there is no pending MGW transaction */
848 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
849
850 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100851 osmo_fsm_inst_free(mgcp_ctx->fsm);
852 talloc_free(mgcp_ctx);
853 return;
854 }
855
856 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
857}
858
859static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
860
861 /* Startup state machine, send CRCX for RAN side. */
862 [ST_CRCX_RAN] = {
863 .in_event_mask = S(EV_INIT),
864 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
865 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
866 .action = fsm_crcx_ran_cb,
867 },
868 /* When the response to the RAN CRCX is received, then proceed with
869 sending the CRCX for CN side */
870 [ST_CRCX_CN] = {
871 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
872 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
873 .name = OSMO_STRINGIFY(ST_CRCX_CN),
874 .action = fsm_crcx_cn_cb,
875 },
876 /* Complete the CRCX phase by starting the assignment. Depending on the
877 * RAT (Radio Access Technology), this will either trigger an
878 * Assignment Request on the A-Interface or an RAB-Assignment on the
879 * IU-interface */
880 [ST_CRCX_COMPL] = {
881 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
882 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
883 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
884 .action = fsm_crcx_compl,
885 },
886 /* Wait for MSC to complete the assignment request, when complete, we
887 * will enter the MDCX phase by sending an MDCX for the CN side to the
888 * MGW */
889 [ST_MDCX_CN] = {
890 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
891 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
892 .name = OSMO_STRINGIFY(ST_MDCX_CN),
893 .action = fsm_mdcx_cn_cb,
894 },
895 /* We arrive in this state when the MDCX phase for the CN side has
896 * completed we will check the IP/Port of the RAN connection. If this
897 * data is valid we may continue with the MDCX phase for the RAN side.
898 * If not we wait until the assinment completes on the A or on the IuCS
899 * interface. The completion of the assignment will fill in the port and
900 * IP-Address of the RAN side and way may continue then. */
901 [ST_MDCX_CN_COMPL] = {
902 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
903 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
904 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
905 .action = fsm_mdcx_cn_compl_cb,
906 },
907 /* When the response for the CN MDCX is received, send the MDCX for the
908 * RAN side to the MGW */
909 [ST_MDCX_RAN] = {
910 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
911 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
912 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
913 .action = fsm_mdcx_ran_cb,
914 },
915 /* The RAN side MDCX phase is complete when the response is received
916 * from the MGW. The call is then active, we change to ST_CALL and wait
917 * there until the call ends. */
918 [ST_MDCX_RAN_COMPL] = {
919 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
920 .out_state_mask = S(ST_HALT) | S(ST_CALL),
921 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
922 .action = fsm_mdcx_ran_compl_cb,
923 },
924 /* We are now in the active call phase, wait until the call is done
925 * and send a DLCX then to remove all connections from the MGW */
926 [ST_CALL] = {
927 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
928 .out_state_mask = S(ST_HALT),
929 .name = OSMO_STRINGIFY(ST_CALL),
930 .action = fsm_call_cb,
931 },
932 /* When the MGW confirms that the connections are terminated, then halt
933 * the state machine. */
934 [ST_HALT] = {
935 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
936 .out_state_mask = S(ST_HALT),
937 .name = OSMO_STRINGIFY(ST_HALT),
938 .action = fsm_halt_cb,
939 },
940};
941
942/* State machine definition */
943static struct osmo_fsm fsm_msc_mgcp = {
944 .name = "MGW",
945 .states = fsm_msc_mgcp_states,
946 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
947 .log_subsys = DMGCP,
948 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100949 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100950};
951
952/* Notify that a new call begins. This will create a connection for the
953 * RAN and the CN on the MGW.
954 * Parameter:
955 * trans: transaction context.
956 * Returns -EINVAL on error, 0 on success. */
957int msc_mgcp_call_assignment(struct gsm_trans *trans)
958{
959 struct mgcp_ctx *mgcp_ctx;
960 char name[32];
961 static bool fsm_registered = false;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100962 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100963 struct mgcp_client *mgcp;
964
965 OSMO_ASSERT(trans);
966
967 if (!trans->conn) {
968 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
969 vlr_subscr_name(trans->vsub));
970 return -EINVAL;
971 }
972
973 conn = trans->conn;
974 mgcp = conn->network->mgw.client;
975 OSMO_ASSERT(mgcp);
976
977 if (conn->rtp.mgcp_ctx) {
978 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
979 vlr_subscr_name(trans->vsub));
980 return -EINVAL;
981 }
982
983#ifdef BUILD_IU
984 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
985 static uint8_t next_iu_rab_id = 1;
986 if (conn->via_ran == RAN_UTRAN_IU)
987 conn->iu.rab_id = next_iu_rab_id++;
988#endif
989
990 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
991 return -EINVAL;
992
993 /* Register the fsm description (if not already done) */
994 if (fsm_registered == false) {
995 osmo_fsm_register(&fsm_msc_mgcp);
996 fsm_registered = true;
997 }
998
999 /* Allocate and configure a new fsm instance */
1000 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
1001 OSMO_ASSERT(mgcp_ctx);
Philipp Maiera2353c62018-02-08 14:15:59 +01001002 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, ENDPOINT_ID, sizeof(mgcp_ctx->rtp_endpoint)) >=
1003 MGCP_ENDPOINT_MAXLEN) {
1004 talloc_free(mgcp_ctx);
1005 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier (%s) exceeds maximum length...\n",
1006 vlr_subscr_name(trans->vsub), ENDPOINT_ID);
1007 return -EINVAL;
1008 }
Philipp Maier621ba032017-11-07 17:19:25 +01001009 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
1010 OSMO_ASSERT(mgcp_ctx->fsm);
1011 mgcp_ctx->fsm->priv = mgcp_ctx;
1012 mgcp_ctx->mgcp = mgcp;
1013 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +01001014 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +01001015
1016 /* start state machine */
1017 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
1018 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
1019
1020 conn->rtp.mgcp_ctx = mgcp_ctx;
1021
1022 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
1023 vlr_subscr_name(conn->vsub));
1024
1025 return 0;
1026}
1027
1028/* Inform the FSM that the assignment (RAN connection) is now complete.
1029 * Parameter:
Neels Hofmeyrc036b792018-11-29 22:37:51 +01001030 * conn: RAN connection context.
Philipp Maier621ba032017-11-07 17:19:25 +01001031 * port: port number of the remote leg.
1032 * addr: IP-address of the remote leg.
1033 * Returns -EINVAL on error, 0 on success. */
Neels Hofmeyrc036b792018-11-29 22:37:51 +01001034int msc_mgcp_ass_complete(struct ran_conn *conn, uint16_t port, char *addr)
Philipp Maier621ba032017-11-07 17:19:25 +01001035{
1036 struct mgcp_ctx *mgcp_ctx;
1037
1038 OSMO_ASSERT(conn);
1039
1040 if (port == 0) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001041 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignment completion failed\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001042 vlr_subscr_name(conn->vsub));
1043 return -EINVAL;
1044 }
1045 if (!addr || strlen(addr) <= 0) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001046 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignment completion failed\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001047 vlr_subscr_name(conn->vsub));
1048 return -EINVAL;
1049 }
1050
1051 mgcp_ctx = conn->rtp.mgcp_ctx;
1052 if (!mgcp_ctx) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001053 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignment completion failed.\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001054 vlr_subscr_name(conn->vsub));
1055 return -EINVAL;
1056 }
1057
1058 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1059 * information at latest when we enter the MDCX phase for the RAN side. */
1060 conn->rtp.remote_port_ran = port;
1061 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
1062
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001063 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignment completed, rtp %s:%d.\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001064 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
1065
1066 /* Note: We only dispatch the event if we are really waiting for the
1067 * assignment, if we are not yet waiting, there is no need to loudly
1068 * broadcast an event that the all other states do not understand anyway */
1069 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1070 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1071
1072 return 0;
1073}
1074
1075/* Make the connection of a previously assigned call complete
1076 * Parameter:
1077 * trans: transaction context.
1078 * port: port number of the remote leg.
1079 * addr: IP-address of the remote leg.
1080 * Returns -EINVAL on error, 0 on success. */
1081int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1082{
1083 struct mgcp_ctx *mgcp_ctx;
Neels Hofmeyrc036b792018-11-29 22:37:51 +01001084 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +01001085
1086 OSMO_ASSERT(trans);
1087 OSMO_ASSERT(addr);
1088
1089 if (port == 0) {
1090 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1091 vlr_subscr_name(trans->vsub));
1092 return -EINVAL;
1093 }
1094 if (!addr || strlen(addr) <= 0) {
1095 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1096 vlr_subscr_name(trans->vsub));
1097 return -EINVAL;
1098 }
1099 if (!trans->conn) {
1100 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1101 vlr_subscr_name(trans->vsub));
1102 return -EINVAL;
1103 }
1104 if (!trans->conn->rtp.mgcp_ctx) {
1105 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1106 vlr_subscr_name(trans->vsub));
1107 return -EINVAL;
1108 }
1109 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1110 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1111 vlr_subscr_name(trans->vsub));
1112 return -EINVAL;
1113 }
1114
1115 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1116
1117 /* The FSM should already have passed all CRCX phases and be ready to move
1118 * on with the MDCX phases. */
1119 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1120 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1121 vlr_subscr_name(trans->vsub));
1122 return -EINVAL;
1123 }
1124
1125 conn = trans->conn;
1126 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1127 conn->rtp.remote_port_cn = port;
1128
1129 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1130
1131 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1132 vlr_subscr_name(conn->vsub));
1133
1134 return 0;
1135}
1136
1137/* Release ongoing call.
1138 * Parameter:
1139 * trans: connection context.
1140 * Returns -EINVAL on error, 0 on success. */
1141int msc_mgcp_call_release(struct gsm_trans *trans)
1142{
1143 struct mgcp_ctx *mgcp_ctx;
1144
1145 OSMO_ASSERT(trans);
1146
1147 if (!trans->conn) {
1148 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1149 vlr_subscr_name(trans->vsub));
1150 return -EINVAL;
1151 }
1152 if (!trans->conn->rtp.mgcp_ctx) {
1153 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1154 vlr_subscr_name(trans->vsub));
1155 return -EINVAL;
1156 }
1157 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1158 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1159 vlr_subscr_name(trans->vsub));
1160 return -EINVAL;
1161 }
1162
1163 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1164
1165 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1166 * all context information immediately */
1167 mgcp_ctx->free_ctx = true;
1168
1169 /* Initaite teardown, regardless of which state we are currently
1170 * in */
1171 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1172
1173 /* Prevent any further operation that is triggered from outside by
1174 * overwriting the context pointer with NULL. The FSM will now
1175 * take care for a graceful shutdown and when done it will free
1176 * all related context information */
1177 trans->conn->rtp.mgcp_ctx = NULL;
1178
1179 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1180 vlr_subscr_name(trans->vsub));
1181
1182 return 0;
1183}