blob: f5bdeb7aa9613acd7f7f3eb51f1faf56fdfd1b7f [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
Philipp Maier5046db92018-05-29 12:02:38 +0200281#ifdef BUILD_IU
282 struct gsm_trans *trans;
283 struct gsm_subscriber_connection *conn;
284#endif
285
Philipp Maier621ba032017-11-07 17:19:25 +0100286 OSMO_ASSERT(mgcp_ctx);
287 mgcp = mgcp_ctx->mgcp;
288 OSMO_ASSERT(mgcp);
289
Philipp Maier5046db92018-05-29 12:02:38 +0200290#ifdef BUILD_IU
291 trans = mgcp_ctx->trans;
292 OSMO_ASSERT(trans);
293 conn = trans->conn;
294 OSMO_ASSERT(conn);
295#endif
296
Philipp Maier79beccd2018-04-11 17:36:45 +0200297 /* NOTE: In case of error, we will not be able to perform any DLCX
298 * operation because until this point we do not have requested any
299 * endpoint yet. */
300
Philipp Maier621ba032017-11-07 17:19:25 +0100301 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200302 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100303
304 /* Generate MGCP message string */
305 mgcp_msg = (struct mgcp_msg) {
306 .verb = MGCP_VERB_CRCX,
307 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100308 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200309 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100310 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100311 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100312 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200313 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100314 return;
315 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100316
Philipp Maier5046db92018-05-29 12:02:38 +0200317 /* HACK: We put the connection in loopback mode at the beginnig to
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +0200318 * trick the hNodeB into doing the IuUP negotiation with itself.
319 * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
Philipp Maier5046db92018-05-29 12:02:38 +0200320#ifdef BUILD_IU
321 if (conn->via_ran == RAN_UTRAN_IU)
322 mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK;
323#endif
324
Philipp Maier621ba032017-11-07 17:19:25 +0100325 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
326 OSMO_ASSERT(msg);
327
328 /* Transmit MGCP message to MGW */
329 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
330 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
331 if (rc < 0) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200332 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100333 return;
334 }
335
336 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
337}
338
339/* Callback for MGCP-Client: handle response for RAN associated CRCX */
340static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
341{
342 struct mgcp_ctx *mgcp_ctx = priv;
343 int rc;
344 struct gsm_trans *trans;
345 struct gsm_subscriber_connection *conn;
346
Philipp Maier79beccd2018-04-11 17:36:45 +0200347 /* NOTE: In case of error, we will not be able to perform any DLCX
348 * operation because until we either get a parseable message that
349 * contains an error code (no endpoint is seized in those cases)
350 * or we get an unparseable message. In this case we can not be
351 * sure, but we also can not draw any assumptions from unparseable
352 * messages. */
353
Philipp Maier621ba032017-11-07 17:19:25 +0100354 OSMO_ASSERT(mgcp_ctx);
355 trans = mgcp_ctx->trans;
356 OSMO_ASSERT(trans);
357 conn = trans->conn;
358 OSMO_ASSERT(conn);
359
360 if (r->head.response_code != 200) {
361 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
362 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier79beccd2018-04-11 17:36:45 +0200363 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100364 return;
365 }
366
Philipp Maiera2353c62018-02-08 14:15:59 +0100367 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100368 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
369 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 +0100370 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
371 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100372
373 rc = mgcp_response_parse_params(r);
374 if (rc) {
375 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier79beccd2018-04-11 17:36:45 +0200376 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100377 return;
378 }
379
380 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
381
382 conn->rtp.local_port_ran = r->audio_port;
383 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
384
385 /* Notify the FSM that we got the response. */
386 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
387}
388
389static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
390
391/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
392static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
393{
394 struct mgcp_ctx *mgcp_ctx = data;
395 struct mgcp_client *mgcp;
396 struct mgcp_msg mgcp_msg;
397 struct msgb *msg;
398 int rc;
399
Philipp Maier5046db92018-05-29 12:02:38 +0200400#ifdef BUILD_IU
401 struct gsm_trans *trans;
402 struct gsm_subscriber_connection *conn;
403#endif
404
Philipp Maier621ba032017-11-07 17:19:25 +0100405 OSMO_ASSERT(mgcp_ctx);
406 mgcp = mgcp_ctx->mgcp;
407 OSMO_ASSERT(mgcp);
408
Philipp Maier5046db92018-05-29 12:02:38 +0200409#ifdef BUILD_IU
410 trans = mgcp_ctx->trans;
411 OSMO_ASSERT(trans);
412 conn = trans->conn;
413 OSMO_ASSERT(conn);
414#endif
415
Philipp Maier621ba032017-11-07 17:19:25 +0100416 switch (event) {
417 case EV_CRCX_RAN_RESP:
418 break;
419 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100420 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100421 return;
422 }
423
424 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200425 "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100426
427 /* Generate MGCP message string */
428 mgcp_msg = (struct mgcp_msg) {
429 .verb = MGCP_VERB_CRCX,
430 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100431 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200432 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100433 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100434 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100435 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100436 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100437 return;
438 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100439
Philipp Maier621ba032017-11-07 17:19:25 +0100440 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
441 OSMO_ASSERT(msg);
442
443 /* Transmit MGCP message to MGW */
444 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
445 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
446 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100447 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100448 return;
449 }
450
451 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
452}
453
454/* Callback for MGCP-Client: handle response for CN associated CRCX */
455static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
456{
457 struct mgcp_ctx *mgcp_ctx = priv;
458 int rc;
459 struct gsm_trans *trans;
460 struct gsm_subscriber_connection *conn;
461
462 OSMO_ASSERT(mgcp_ctx);
463 trans = mgcp_ctx->trans;
464 OSMO_ASSERT(trans);
465 conn = trans->conn;
466 OSMO_ASSERT(conn);
467
468 if (r->head.response_code != 200) {
469 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
470 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100471 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100472 return;
473 }
474
475 /* memorize connection identifier */
476 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
477 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
478
479 rc = mgcp_response_parse_params(r);
480 if (rc) {
481 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100482 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100483 return;
484 }
485
486 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
487
488 conn->rtp.local_port_cn = r->audio_port;
489 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
490
491 /* Notify the FSM that we got the response. */
492 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
493}
494
495/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
496static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
497{
498 struct mgcp_ctx *mgcp_ctx = data;
499 struct gsm_trans *trans;
500 struct gsm_subscriber_connection *conn;
501
502 OSMO_ASSERT(mgcp_ctx);
503 trans = mgcp_ctx->trans;
504 OSMO_ASSERT(trans);
505 conn = trans->conn;
506 OSMO_ASSERT(conn);
507
508 switch (event) {
509 case EV_CRCX_CN_RESP:
510 break;
511 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100512 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100513 return;
514 }
515
516 /* Forward assignment request to A/RANAP */
517 if (conn->via_ran == RAN_UTRAN_IU) {
518#ifdef BUILD_IU
519 /* Assign a voice channel via RANAP on 3G */
520 if (iu_rab_act_cs(trans))
521 goto error;
522#else
523 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
524 goto error;
525#endif
526 } else if (conn->via_ran == RAN_GERAN_A) {
527 /* Assign a voice channel via A on 2G */
528 if (a_iface_tx_assignment(trans))
529 goto error;
530 } else {
531 /* Unset or unimplemented new RAN type */
532 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
533 return;
534 }
535
536 /* Respond back to MNCC (if requested) */
537 if (trans->tch_rtp_create) {
538 if (gsm48_tch_rtp_create(trans))
539 goto error;
540 }
541
542 /* Note: When we reach this point then the situation is basically that
543 * we have two sides connected, both are in loopback. The local ports
544 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100545 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100546 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
547 return;
548
549error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100550 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100551}
552
553static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
554
555/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
556static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
557{
558 struct mgcp_ctx *mgcp_ctx = data;
559 struct mgcp_client *mgcp;
560 struct gsm_trans *trans;
561 struct gsm_subscriber_connection *conn;
562 struct mgcp_msg mgcp_msg;
563 struct msgb *msg;
564 int rc;
565
566 OSMO_ASSERT(mgcp_ctx);
567 mgcp = mgcp_ctx->mgcp;
568 OSMO_ASSERT(mgcp);
569 trans = mgcp_ctx->trans;
570 OSMO_ASSERT(trans);
571 conn = trans->conn;
572 OSMO_ASSERT(conn);
573
574 switch (event) {
575 case EV_CONNECT:
576 break;
577 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100578 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100579 return;
580 }
581
582 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100583 "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 +0100584 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
585
586 /* Generate MGCP message string */
587 mgcp_msg = (struct mgcp_msg) {
588 .verb = MGCP_VERB_MDCX,
589 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
590 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
591 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100592 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100593 .conn_id = mgcp_ctx->conn_id_cn,
594 .conn_mode = MGCP_CONN_RECV_SEND,
595 .audio_ip = conn->rtp.remote_addr_cn,
596 .audio_port = conn->rtp.remote_port_cn
597 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100598 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100599 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100600 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100601 return;
602 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100603
Philipp Maier621ba032017-11-07 17:19:25 +0100604 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
605 OSMO_ASSERT(msg);
606
607 /* Transmit MGCP message to MGW */
608 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
609 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
610 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100611 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100612 return;
613 }
614
615 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
616}
617
618/* Callback for MGCP-Client: handle response for CN associated CRCX */
619static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
620{
621 struct mgcp_ctx *mgcp_ctx = priv;
622
623 OSMO_ASSERT(mgcp_ctx);
624
625 if (r->head.response_code != 200) {
626 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
627 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100628 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100629 return;
630 }
631
632 /* Notify the FSM that we got the response. */
633 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
634}
635
636/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
637 * for the RAN side if we already have valid IP/Port data for the RAN sided
638 * RTP stream. */
639static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
640{
641 struct mgcp_ctx *mgcp_ctx = data;
642 struct gsm_subscriber_connection *conn;
643 struct gsm_trans *trans;
644
645 OSMO_ASSERT(mgcp_ctx);
646 trans = mgcp_ctx->trans;
647 OSMO_ASSERT(trans);
648 conn = trans->conn;
649 OSMO_ASSERT(conn);
650
651 switch (event) {
652 case EV_MDCX_CN_RESP:
653 break;
654 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100655 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100656 return;
657 }
658
659 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
660 * IuCS interface is complete (IP-Address and Port are valid) */
661 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
662
663 /* If we already have a valid remote port and IP-Address from the RAN side
664 * call leg, the assignment has been completed before we got here, so we
665 * may move on immediately */
666 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
667 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
668}
669
670static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
671
672/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
673static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
674{
675 struct mgcp_ctx *mgcp_ctx = data;
676 struct mgcp_client *mgcp;
677 struct gsm_trans *trans;
678 struct gsm_subscriber_connection *conn;
679 struct mgcp_msg mgcp_msg;
680 struct msgb *msg;
681 int rc;
682
683 OSMO_ASSERT(mgcp_ctx);
684 mgcp = mgcp_ctx->mgcp;
685 OSMO_ASSERT(mgcp);
686 trans = mgcp_ctx->trans;
687 OSMO_ASSERT(trans);
688 conn = trans->conn;
689 OSMO_ASSERT(conn);
690
691 switch (event) {
692 case EV_ASSIGN:
693 break;
694 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100695 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100696 return;
697 }
698
699 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100700 "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 +0100701 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
702
703 /* Generate MGCP message string */
704 mgcp_msg = (struct mgcp_msg) {
705 .verb = MGCP_VERB_MDCX,
706 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
707 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
708 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100709 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100710 .conn_id = mgcp_ctx->conn_id_ran,
711 .conn_mode = MGCP_CONN_RECV_SEND,
712 .audio_ip = conn->rtp.remote_addr_ran,
713 .audio_port = conn->rtp.remote_port_ran
714 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100715 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100716 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100717 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100718 return;
719 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100720
Philipp Maier621ba032017-11-07 17:19:25 +0100721 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
722 OSMO_ASSERT(msg);
723
724 /* Transmit MGCP message to MGW */
725 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
726 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
727 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100728 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100729 return;
730 }
731
732 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
733}
734
735/* Callback for MGCP-Client: handle response for CN associated CRCX */
736static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
737{
738 struct mgcp_ctx *mgcp_ctx = priv;
739
740 OSMO_ASSERT(mgcp_ctx);
741
742 if (r->head.response_code != 200) {
743 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
744 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100745 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100746 return;
747 }
748
749 /* Notify the FSM that we got the response. */
750 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
751}
752
753/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
754static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
755{
756 struct mgcp_ctx *mgcp_ctx = data;
757 OSMO_ASSERT(mgcp_ctx);
758
759 switch (event) {
760 case EV_MDCX_RAN_RESP:
761 break;
762 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100763 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100764 return;
765 }
766
767 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
768 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
769}
770
771static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
772
773/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
774static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
775{
776
777 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
778 struct mgcp_client *mgcp;
779 struct mgcp_msg mgcp_msg;
780 struct msgb *msg;
781 int rc;
782
783 OSMO_ASSERT(mgcp_ctx);
784 mgcp = mgcp_ctx->mgcp;
785 OSMO_ASSERT(mgcp);
786
787 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200788 "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 +0100789
Philipp Maier621ba032017-11-07 17:19:25 +0100790 /* Generate MGCP message string */
791 mgcp_msg = (struct mgcp_msg) {
792 .verb = MGCP_VERB_DLCX,
793 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100794 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100795 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100796 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100797 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100798 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100799 return;
800 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100801
Philipp Maier621ba032017-11-07 17:19:25 +0100802 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
803 OSMO_ASSERT(msg);
804
805 /* Transmit MGCP message to MGW */
806 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
807 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
808 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100809 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100810 return;
811 }
812
813 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
814}
815
816/* Callback for MGCP-Client: handle response for CN associated CRCX */
817static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
818{
819 struct mgcp_ctx *mgcp_ctx = priv;
820
821 OSMO_ASSERT(mgcp_ctx);
822
Harald Welte33d61e72018-02-10 10:43:38 +0100823 /* DLCX is the only command where 250 is permitted as positive result */
824 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100825 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
826 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100827 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100828 return;
829 }
830
831 /* Notify the FSM that we got the response. */
832 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
833}
834
835/* Callback for ST_HALT: Terminate the state machine */
836static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
837{
838 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100839 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100840
841 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100842 mgcp = mgcp_ctx->mgcp;
843 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100844
845 /* NOTE: We must not free the context information now, we have to
846 * wait until msc_mgcp_call_release() is called. Then we are sure
847 * that the logic controlling us is fully aware that the context
848 * information is freed. If we would free early now the controlling
849 * logic might mistakenly think that the context info is still alive,
850 * so lets keep the context info until we are explicitly asked for
851 * throwing it away. */
852 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100853 /* Be sure that there is no pending MGW transaction */
854 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
855
856 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100857 osmo_fsm_inst_free(mgcp_ctx->fsm);
858 talloc_free(mgcp_ctx);
859 return;
860 }
861
862 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
863}
864
865static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
866
867 /* Startup state machine, send CRCX for RAN side. */
868 [ST_CRCX_RAN] = {
869 .in_event_mask = S(EV_INIT),
870 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
871 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
872 .action = fsm_crcx_ran_cb,
873 },
874 /* When the response to the RAN CRCX is received, then proceed with
875 sending the CRCX for CN side */
876 [ST_CRCX_CN] = {
877 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
878 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
879 .name = OSMO_STRINGIFY(ST_CRCX_CN),
880 .action = fsm_crcx_cn_cb,
881 },
882 /* Complete the CRCX phase by starting the assignment. Depending on the
883 * RAT (Radio Access Technology), this will either trigger an
884 * Assignment Request on the A-Interface or an RAB-Assignment on the
885 * IU-interface */
886 [ST_CRCX_COMPL] = {
887 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
888 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
889 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
890 .action = fsm_crcx_compl,
891 },
892 /* Wait for MSC to complete the assignment request, when complete, we
893 * will enter the MDCX phase by sending an MDCX for the CN side to the
894 * MGW */
895 [ST_MDCX_CN] = {
896 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
897 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
898 .name = OSMO_STRINGIFY(ST_MDCX_CN),
899 .action = fsm_mdcx_cn_cb,
900 },
901 /* We arrive in this state when the MDCX phase for the CN side has
902 * completed we will check the IP/Port of the RAN connection. If this
903 * data is valid we may continue with the MDCX phase for the RAN side.
904 * If not we wait until the assinment completes on the A or on the IuCS
905 * interface. The completion of the assignment will fill in the port and
906 * IP-Address of the RAN side and way may continue then. */
907 [ST_MDCX_CN_COMPL] = {
908 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
909 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
910 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
911 .action = fsm_mdcx_cn_compl_cb,
912 },
913 /* When the response for the CN MDCX is received, send the MDCX for the
914 * RAN side to the MGW */
915 [ST_MDCX_RAN] = {
916 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
917 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
918 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
919 .action = fsm_mdcx_ran_cb,
920 },
921 /* The RAN side MDCX phase is complete when the response is received
922 * from the MGW. The call is then active, we change to ST_CALL and wait
923 * there until the call ends. */
924 [ST_MDCX_RAN_COMPL] = {
925 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
926 .out_state_mask = S(ST_HALT) | S(ST_CALL),
927 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
928 .action = fsm_mdcx_ran_compl_cb,
929 },
930 /* We are now in the active call phase, wait until the call is done
931 * and send a DLCX then to remove all connections from the MGW */
932 [ST_CALL] = {
933 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
934 .out_state_mask = S(ST_HALT),
935 .name = OSMO_STRINGIFY(ST_CALL),
936 .action = fsm_call_cb,
937 },
938 /* When the MGW confirms that the connections are terminated, then halt
939 * the state machine. */
940 [ST_HALT] = {
941 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
942 .out_state_mask = S(ST_HALT),
943 .name = OSMO_STRINGIFY(ST_HALT),
944 .action = fsm_halt_cb,
945 },
946};
947
948/* State machine definition */
949static struct osmo_fsm fsm_msc_mgcp = {
950 .name = "MGW",
951 .states = fsm_msc_mgcp_states,
952 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
953 .log_subsys = DMGCP,
954 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100955 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100956};
957
958/* Notify that a new call begins. This will create a connection for the
959 * RAN and the CN on the MGW.
960 * Parameter:
961 * trans: transaction context.
962 * Returns -EINVAL on error, 0 on success. */
963int msc_mgcp_call_assignment(struct gsm_trans *trans)
964{
965 struct mgcp_ctx *mgcp_ctx;
966 char name[32];
967 static bool fsm_registered = false;
968 struct gsm_subscriber_connection *conn;
969 struct mgcp_client *mgcp;
970
971 OSMO_ASSERT(trans);
972
973 if (!trans->conn) {
974 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
975 vlr_subscr_name(trans->vsub));
976 return -EINVAL;
977 }
978
979 conn = trans->conn;
980 mgcp = conn->network->mgw.client;
981 OSMO_ASSERT(mgcp);
982
983 if (conn->rtp.mgcp_ctx) {
984 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
985 vlr_subscr_name(trans->vsub));
986 return -EINVAL;
987 }
988
989#ifdef BUILD_IU
990 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
991 static uint8_t next_iu_rab_id = 1;
992 if (conn->via_ran == RAN_UTRAN_IU)
993 conn->iu.rab_id = next_iu_rab_id++;
994#endif
995
996 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
997 return -EINVAL;
998
999 /* Register the fsm description (if not already done) */
1000 if (fsm_registered == false) {
1001 osmo_fsm_register(&fsm_msc_mgcp);
1002 fsm_registered = true;
1003 }
1004
1005 /* Allocate and configure a new fsm instance */
1006 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
1007 OSMO_ASSERT(mgcp_ctx);
Philipp Maiera2353c62018-02-08 14:15:59 +01001008 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, ENDPOINT_ID, sizeof(mgcp_ctx->rtp_endpoint)) >=
1009 MGCP_ENDPOINT_MAXLEN) {
1010 talloc_free(mgcp_ctx);
1011 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier (%s) exceeds maximum length...\n",
1012 vlr_subscr_name(trans->vsub), ENDPOINT_ID);
1013 return -EINVAL;
1014 }
Philipp Maier621ba032017-11-07 17:19:25 +01001015 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
1016 OSMO_ASSERT(mgcp_ctx->fsm);
1017 mgcp_ctx->fsm->priv = mgcp_ctx;
1018 mgcp_ctx->mgcp = mgcp;
1019 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +01001020 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +01001021
1022 /* start state machine */
1023 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
1024 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
1025
1026 conn->rtp.mgcp_ctx = mgcp_ctx;
1027
1028 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
1029 vlr_subscr_name(conn->vsub));
1030
1031 return 0;
1032}
1033
1034/* Inform the FSM that the assignment (RAN connection) is now complete.
1035 * Parameter:
1036 * conn: subscriber connection context.
1037 * port: port number of the remote leg.
1038 * addr: IP-address of the remote leg.
1039 * Returns -EINVAL on error, 0 on success. */
1040int msc_mgcp_ass_complete(struct gsm_subscriber_connection *conn, uint16_t port, char *addr)
1041{
1042 struct mgcp_ctx *mgcp_ctx;
1043
1044 OSMO_ASSERT(conn);
1045
1046 if (port == 0) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001047 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, assignment completion failed\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001048 vlr_subscr_name(conn->vsub));
1049 return -EINVAL;
1050 }
1051 if (!addr || strlen(addr) <= 0) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001052 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, assignment completion failed\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001053 vlr_subscr_name(conn->vsub));
1054 return -EINVAL;
1055 }
1056
1057 mgcp_ctx = conn->rtp.mgcp_ctx;
1058 if (!mgcp_ctx) {
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001059 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, assignment completion failed.\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001060 vlr_subscr_name(conn->vsub));
1061 return -EINVAL;
1062 }
1063
1064 /* Memorize port and IP-Address of the remote RAN call leg. We need this
1065 * information at latest when we enter the MDCX phase for the RAN side. */
1066 conn->rtp.remote_port_ran = port;
1067 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
1068
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +02001069 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) assignment completed, rtp %s:%d.\n",
Philipp Maier621ba032017-11-07 17:19:25 +01001070 vlr_subscr_name(conn->vsub), conn->rtp.remote_addr_ran, port);
1071
1072 /* Note: We only dispatch the event if we are really waiting for the
1073 * assignment, if we are not yet waiting, there is no need to loudly
1074 * broadcast an event that the all other states do not understand anyway */
1075 if (mgcp_ctx->fsm->state == ST_MDCX_RAN)
1076 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
1077
1078 return 0;
1079}
1080
1081/* Make the connection of a previously assigned call complete
1082 * Parameter:
1083 * trans: transaction context.
1084 * port: port number of the remote leg.
1085 * addr: IP-address of the remote leg.
1086 * Returns -EINVAL on error, 0 on success. */
1087int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1088{
1089 struct mgcp_ctx *mgcp_ctx;
1090 struct gsm_subscriber_connection *conn;
1091
1092 OSMO_ASSERT(trans);
1093 OSMO_ASSERT(addr);
1094
1095 if (port == 0) {
1096 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1097 vlr_subscr_name(trans->vsub));
1098 return -EINVAL;
1099 }
1100 if (!addr || strlen(addr) <= 0) {
1101 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1102 vlr_subscr_name(trans->vsub));
1103 return -EINVAL;
1104 }
1105 if (!trans->conn) {
1106 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1107 vlr_subscr_name(trans->vsub));
1108 return -EINVAL;
1109 }
1110 if (!trans->conn->rtp.mgcp_ctx) {
1111 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1112 vlr_subscr_name(trans->vsub));
1113 return -EINVAL;
1114 }
1115 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1116 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1117 vlr_subscr_name(trans->vsub));
1118 return -EINVAL;
1119 }
1120
1121 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1122
1123 /* The FSM should already have passed all CRCX phases and be ready to move
1124 * on with the MDCX phases. */
1125 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1126 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1127 vlr_subscr_name(trans->vsub));
1128 return -EINVAL;
1129 }
1130
1131 conn = trans->conn;
1132 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1133 conn->rtp.remote_port_cn = port;
1134
1135 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1136
1137 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1138 vlr_subscr_name(conn->vsub));
1139
1140 return 0;
1141}
1142
1143/* Release ongoing call.
1144 * Parameter:
1145 * trans: connection context.
1146 * Returns -EINVAL on error, 0 on success. */
1147int msc_mgcp_call_release(struct gsm_trans *trans)
1148{
1149 struct mgcp_ctx *mgcp_ctx;
1150
1151 OSMO_ASSERT(trans);
1152
1153 if (!trans->conn) {
1154 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1155 vlr_subscr_name(trans->vsub));
1156 return -EINVAL;
1157 }
1158 if (!trans->conn->rtp.mgcp_ctx) {
1159 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1160 vlr_subscr_name(trans->vsub));
1161 return -EINVAL;
1162 }
1163 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1164 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1165 vlr_subscr_name(trans->vsub));
1166 return -EINVAL;
1167 }
1168
1169 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1170
1171 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1172 * all context information immediately */
1173 mgcp_ctx->free_ctx = true;
1174
1175 /* Initaite teardown, regardless of which state we are currently
1176 * in */
1177 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1178
1179 /* Prevent any further operation that is triggered from outside by
1180 * overwriting the context pointer with NULL. The FSM will now
1181 * take care for a graceful shutdown and when done it will free
1182 * all related context information */
1183 trans->conn->rtp.mgcp_ctx = NULL;
1184
1185 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1186 vlr_subscr_name(trans->vsub));
1187
1188 return 0;
1189}