blob: 7aeab4ab8c5869156e919881fc6daa3d94456982 [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>
Neels Hofmeyr212c0c92018-12-07 14:47:34 +010029#include <osmocom/gsm/protocol/gsm_08_08.h>
30
Philipp Maier621ba032017-11-07 17:19:25 +010031#include <osmocom/msc/msc_mgcp.h>
32#include <osmocom/msc/debug.h>
33#include <osmocom/msc/transaction.h>
34#include <osmocom/msc/a_iface.h>
35#include <osmocom/msc/msc_ifaces.h>
36#include <osmocom/msc/gsm_04_08.h>
37#include <osmocom/msc/iucs.h>
38#include <osmocom/msc/vlr.h>
39
40#include "../../bscconfig.h"
41
42#define S(x) (1 << (x))
43
44#define CONN_ID_RAN 1
45#define CONN_ID_CN 2
46
47#define MGCP_MGW_TIMEOUT 4 /* in seconds */
48#define MGCP_MGW_TIMEOUT_TIMER_NR 1
Philipp Maier4c573772018-02-08 14:05:43 +010049#define MGCP_RAN_TIMEOUT 120 /* in seconds */
Philipp Maier621ba032017-11-07 17:19:25 +010050#define MGCP_RAN_TIMEOUT_TIMER_NR 2
51#define MGCP_REL_TIMEOUT 60 /* in seconds */
52#define MGCP_REL_TIMEOUT_TIMER_NR 3
53#define MGCP_ASS_TIMEOUT 10 /* in seconds */
54#define MGCP_ASS_TIMEOUT_TIMER_NR 4
55
Philipp Maiera2353c62018-02-08 14:15:59 +010056#define ENDPOINT_ID "rtpbridge/*@mgw"
Philipp Maier621ba032017-11-07 17:19:25 +010057
58/* Some internal cause codes to indicate fault condition inside the FSM */
59enum msc_mgcp_cause_code {
60 MGCP_ERR_MGW_FAIL,
61 MGCP_ERR_MGW_INVAL_RESP,
62 MGCP_ERR_MGW_TX_FAIL,
Philipp Maier4eef20b2018-03-13 13:07:45 +010063 MGCP_ERR_MGW_TIMEOUT,
Philipp Maier621ba032017-11-07 17:19:25 +010064 MGCP_ERR_UNEXP_TEARDOWN,
65 MGCP_ERR_UNSUPP_ADDR_FMT,
66 MGCP_ERR_RAN_TIMEOUT,
67 MGCP_ERR_ASS_TIMEOUT,
Philipp Maier3a776522018-02-22 12:00:00 +010068 MGCP_ERR_TOOLONG,
Philipp Maier621ba032017-11-07 17:19:25 +010069 MGCP_ERR_ASSGMNT_FAIL
70};
71
72/* Human readable respresentation of the faul codes, will be displayed by
73 * handle_error() */
74static const struct value_string msc_mgcp_cause_codes_names[] = {
75 {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
76 {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
77 {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
Philipp Maier4eef20b2018-03-13 13:07:45 +010078 {MGCP_ERR_MGW_TIMEOUT, "request to MGW timed out"},
Philipp Maier621ba032017-11-07 17:19:25 +010079 {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown"},
80 {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used (RAN)"},
81 {MGCP_ERR_RAN_TIMEOUT, "call could not be completed in time (RAN)"},
82 {MGCP_ERR_ASS_TIMEOUT, "assignment could not be completed in time (RAN)"},
Philipp Maier3a776522018-02-22 12:00:00 +010083 {MGCP_ERR_TOOLONG, "string value too long"},
Philipp Maier621ba032017-11-07 17:19:25 +010084 {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (RAN)"},
85 {0, NULL}
86};
87
88enum fsm_msc_mgcp_states {
89 ST_CRCX_RAN,
90 ST_CRCX_CN,
91 ST_CRCX_COMPL,
92 ST_MDCX_CN,
93 ST_MDCX_CN_COMPL,
94 ST_MDCX_RAN,
95 ST_MDCX_RAN_COMPL,
96 ST_CALL,
97 ST_HALT,
98};
99
100enum msc_mgcp_fsm_evt {
101 /* Initial event: start off the state machine */
102 EV_INIT,
103
104 /* External event: Notify that the Assignment is complete and we
105 * may now forward IP/Port of the remote call leg to the MGW */
106 EV_ASSIGN,
107
108 /* External event: Notify that the Call is complete and that the
109 * two half open connections on the MGW should now be connected */
110 EV_CONNECT,
111
112 /* External event: Notify that the call is over and the connections
113 * on the mgw shall be removed */
114 EV_TEARDOWN,
115
116 /* Internal event: An error occurred that requires a controlled
117 * teardown of the RTP connections */
118 EV_TEARDOWN_ERROR,
119
120 /* Internal event: The mgcp_gw has sent its CRCX response for
121 * the RAN side */
122 EV_CRCX_RAN_RESP,
123
124 /* Internal event: The mgcp_gw has sent its CRCX response for
125 * the CN side */
126 EV_CRCX_CN_RESP,
127
128 /* Internal event: The mgcp_gw has sent its MDCX response for
129 * the RAN side */
130 EV_MDCX_RAN_RESP,
131
132 /* Internal event: The mgcp_gw has sent its MDCX response for
133 * the CN side */
134 EV_MDCX_CN_RESP,
135
136 /* Internal event: The mgcp_gw has sent its DLCX response for
137 * the RAN and CN side */
138 EV_DLCX_ALL_RESP,
139};
140
Philipp Maiere4f91722018-02-26 15:20:49 +0100141static const struct value_string msc_mgcp_fsm_evt_names[] = {
142 OSMO_VALUE_STRING(EV_INIT),
143 OSMO_VALUE_STRING(EV_ASSIGN),
144 OSMO_VALUE_STRING(EV_CONNECT),
145 OSMO_VALUE_STRING(EV_TEARDOWN),
146 OSMO_VALUE_STRING(EV_TEARDOWN_ERROR),
147 OSMO_VALUE_STRING(EV_CRCX_RAN_RESP),
148 OSMO_VALUE_STRING(EV_CRCX_CN_RESP),
149 OSMO_VALUE_STRING(EV_DLCX_ALL_RESP),
150 {0, NULL}
151};
152
Philipp Maier621ba032017-11-07 17:19:25 +0100153/* A general error handler function. On error we still have an interest to
154 * remove a half open connection (if possible). This function will execute
155 * a controlled jump to the DLCX phase. From there, the FSM will then just
156 * continue like the call were ended normally */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100157#define handle_error(mgcp_ctx, cause, dlcx) _handle_error(mgcp_ctx, cause, dlcx, __FILE__, __LINE__)
158static void _handle_error(struct mgcp_ctx *mgcp_ctx, enum msc_mgcp_cause_code cause, bool dlcx, const char *file,
159 int line)
Philipp Maier621ba032017-11-07 17:19:25 +0100160{
Philipp Maier79beccd2018-04-11 17:36:45 +0200161 bool dlcx_possible = true;
Philipp Maier621ba032017-11-07 17:19:25 +0100162 struct osmo_fsm_inst *fi;
Philipp Maier782ccec2018-10-08 17:12:49 +0200163 struct gsm_mncc mncc;
Philipp Maier621ba032017-11-07 17:19:25 +0100164
165 OSMO_ASSERT(mgcp_ctx);
166 fi = mgcp_ctx->fsm;
167 OSMO_ASSERT(fi);
168
Philipp Maier79beccd2018-04-11 17:36:45 +0200169 /* Check if the endpoint identifier is a specific endpoint identifier,
170 * since in order to perform a DLCX we must know the specific
171 * identifier of the endpoint we want to release. If we do not have
172 * this information because of errornous communication we can not
173 * perform a DLCX. */
174 if (strstr(mgcp_ctx->rtp_endpoint, "*"))
175 dlcx_possible = false;
176
Philipp Maier621ba032017-11-07 17:19:25 +0100177 LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
178 get_value_string(msc_mgcp_cause_codes_names, cause));
179
Stefan Sperling722f2b42018-09-28 14:26:35 +0200180 /* Request the higher layers (gsm_04_08.c) to release the call. If the
181 * problem occured after msc_mgcp_call_release() was calls, remain
182 * silent because we already got informed and the higher layers might
183 * already freed their context information (trans). */
184 if (!mgcp_ctx->free_ctx) {
Philipp Maier782ccec2018-10-08 17:12:49 +0200185 mncc = (struct gsm_mncc) {
186 .msg_type = MNCC_REL_REQ,
187 .callref = mgcp_ctx->trans->callref,
188 .cause = {
189 .location = GSM48_CAUSE_LOC_PRN_S_LU,
190 .coding = 0, /* FIXME */
191 .value = GSM48_CC_CAUSE_RESOURCE_UNAVAIL
192 }
193 };
194
Stefan Sperling722f2b42018-09-28 14:26:35 +0200195 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_TRANS_NET,
196 GSM48_CC_CAUSE_RESOURCE_UNAVAIL);
197 mncc_tx_to_cc(mgcp_ctx->trans->net, MNCC_REL_REQ, &mncc);
198 }
199
Philipp Maier4eef20b2018-03-13 13:07:45 +0100200 /* For the shutdown we have two options. Whenever it makes sense to
201 * send a DLCX to the MGW in order to be sure that the connection is
202 * properly cleaned up, the dlcx flag should be set. In other cases
203 * where a DLCX does not make sense (e.g. the MGW times out), halting
204 * directly is the better options. In those cases, the dlcx flag
205 * should not be set */
Philipp Maier79beccd2018-04-11 17:36:45 +0200206 if (dlcx && dlcx_possible) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100207 /* Fast-forward the FSM into call state. In this state the FSM
208 * expects either an EV_TEARDOWN or an EV_TEARDOWN_ERROR. When
209 * one of the two events is received a DLCX will be send to
210 * the MGW. After that. The FSM automatically halts but will
211 * still expect a call msc_mgcp_call_release() to be freed
212 * completely */
213 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
214 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN_ERROR, mgcp_ctx);
215 } else {
216 /* Halt the state machine immediately. The FSM will not be
217 * freed yet, we stil require the higher layers to call
218 * msc_mgcp_call_release() */
219 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
220 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN_ERROR, mgcp_ctx);
221 }
Philipp Maier621ba032017-11-07 17:19:25 +0100222}
223
224/* Timer callback to shut down in case of connectivity problems */
225static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
226{
227 struct mgcp_ctx *mgcp_ctx = fi->priv;
228 struct mgcp_client *mgcp;
229
230 OSMO_ASSERT(mgcp_ctx);
231 mgcp = mgcp_ctx->mgcp;
232 OSMO_ASSERT(mgcp);
233
234 if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
235 /* We were unable to communicate with the MGW, unfortunately
236 * there is no meaningful action we can take now other than
237 * giving up. */
238
Philipp Maier621ba032017-11-07 17:19:25 +0100239 /* Cancel the transaction that timed out */
240 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
241
Philipp Maier4eef20b2018-03-13 13:07:45 +0100242 /* halt of the FSM */
243 handle_error(mgcp_ctx, MGCP_ERR_MGW_TIMEOUT, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100244 } else if (fi->T == MGCP_RAN_TIMEOUT_TIMER_NR) {
245 /* If the logic that controls the RAN is unable to negotiate a
246 * connection, we presumably still have a working connection to
247 * the MGW, we will try to shut down gracefully. */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100248 handle_error(mgcp_ctx, MGCP_ERR_RAN_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100249 } else if (fi->T == MGCP_REL_TIMEOUT_TIMER_NR) {
250 /* Under normal conditions, the MSC logic should always command
251 * to release the call at some point. However, the release may
252 * be missing due to errors in the MSC logic and we may have
253 * reached ST_HALT because of cascading errors and timeouts. In
254 * this and only in this case we will allow ST_HALT to free all
255 * context information on its own authority. */
256 mgcp_ctx->free_ctx = true;
257
258 /* Initiate self destruction of the FSM */
259 osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
260 osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
261 } else if (fi->T == MGCP_ASS_TIMEOUT_TIMER_NR) {
262 /* There may be rare cases in which the MSC is unable to
263 * complete the call assignment */
Philipp Maier4eef20b2018-03-13 13:07:45 +0100264 handle_error(mgcp_ctx, MGCP_ERR_ASS_TIMEOUT, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100265 } else {
266 /* Ther must not be any unsolicited timers in this FSM. If so,
267 * we have serious problem. */
268 OSMO_ASSERT(false);
269 }
270
271 return 0;
272}
273
274static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv);
275
276/* Callback for ST_CRCX_RAN: Send CRCX for RAN side to MGW */
277static void fsm_crcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
278{
279 struct mgcp_ctx *mgcp_ctx = data;
280 struct mgcp_client *mgcp;
281 struct mgcp_msg mgcp_msg;
282 struct msgb *msg;
283 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200284 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100285 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200286
Philipp Maier621ba032017-11-07 17:19:25 +0100287 OSMO_ASSERT(mgcp_ctx);
288 mgcp = mgcp_ctx->mgcp;
289 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200290 trans = mgcp_ctx->trans;
291 OSMO_ASSERT(trans);
292 conn = trans->conn;
293 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200294
Philipp Maier79beccd2018-04-11 17:36:45 +0200295 /* NOTE: In case of error, we will not be able to perform any DLCX
296 * operation because until this point we do not have requested any
297 * endpoint yet. */
298
Philipp Maier621ba032017-11-07 17:19:25 +0100299 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200300 "CRCX/RAN: creating connection for the RAN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100301
302 /* Generate MGCP message string */
303 mgcp_msg = (struct mgcp_msg) {
304 .verb = MGCP_VERB_CRCX,
305 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100306 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200307 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100308 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100309 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100310 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200311 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100312 return;
313 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100314
Philipp Maier5046db92018-05-29 12:02:38 +0200315 /* HACK: We put the connection in loopback mode at the beginnig to
Neels Hofmeyra4efe3f2018-07-26 17:44:13 +0200316 * trick the hNodeB into doing the IuUP negotiation with itself.
317 * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */
Philipp Maier5046db92018-05-29 12:02:38 +0200318#ifdef BUILD_IU
319 if (conn->via_ran == RAN_UTRAN_IU)
320 mgcp_msg.conn_mode = MGCP_CONN_LOOPBACK;
321#endif
322
Philipp Maier621ba032017-11-07 17:19:25 +0100323 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
324 OSMO_ASSERT(msg);
325
326 /* Transmit MGCP message to MGW */
327 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
328 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_ran_resp_cb, mgcp_ctx);
329 if (rc < 0) {
Philipp Maier79beccd2018-04-11 17:36:45 +0200330 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100331 return;
332 }
333
334 osmo_fsm_inst_state_chg(fi, ST_CRCX_CN, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
335}
336
337/* Callback for MGCP-Client: handle response for RAN associated CRCX */
338static void mgw_crcx_ran_resp_cb(struct mgcp_response *r, void *priv)
339{
340 struct mgcp_ctx *mgcp_ctx = priv;
341 int rc;
342 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100343 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100344
Philipp Maier79beccd2018-04-11 17:36:45 +0200345 /* NOTE: In case of error, we will not be able to perform any DLCX
346 * operation because until we either get a parseable message that
347 * contains an error code (no endpoint is seized in those cases)
348 * or we get an unparseable message. In this case we can not be
349 * sure, but we also can not draw any assumptions from unparseable
350 * messages. */
351
Philipp Maier621ba032017-11-07 17:19:25 +0100352 OSMO_ASSERT(mgcp_ctx);
353 trans = mgcp_ctx->trans;
354 OSMO_ASSERT(trans);
355 conn = trans->conn;
356 OSMO_ASSERT(conn);
357
358 if (r->head.response_code != 200) {
359 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
360 "CRCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier79beccd2018-04-11 17:36:45 +0200361 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100362 return;
363 }
364
Philipp Maiera2353c62018-02-08 14:15:59 +0100365 /* memorize connection identifier and specific endpoint id */
Philipp Maier621ba032017-11-07 17:19:25 +0100366 osmo_strlcpy(mgcp_ctx->conn_id_ran, r->head.conn_id, sizeof(mgcp_ctx->conn_id_ran));
367 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 +0100368 osmo_strlcpy(mgcp_ctx->rtp_endpoint, r->head.endpoint, sizeof(mgcp_ctx->rtp_endpoint));
369 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/RAN: MGW assigned endpoint: %s\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100370
371 rc = mgcp_response_parse_params(r);
372 if (rc) {
373 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/RAN: Cannot parse response\n");
Philipp Maier79beccd2018-04-11 17:36:45 +0200374 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, false);
Philipp Maier621ba032017-11-07 17:19:25 +0100375 return;
376 }
377
378 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
379
380 conn->rtp.local_port_ran = r->audio_port;
381 osmo_strlcpy(conn->rtp.local_addr_ran, r->audio_ip, sizeof(conn->rtp.local_addr_ran));
382
383 /* Notify the FSM that we got the response. */
384 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_RAN_RESP, mgcp_ctx);
385}
386
387static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv);
388
389/* Callback for ST_CRCX_CN: check MGW response and send CRCX for CN side to MGW */
390static void fsm_crcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
391{
392 struct mgcp_ctx *mgcp_ctx = data;
393 struct mgcp_client *mgcp;
394 struct mgcp_msg mgcp_msg;
395 struct msgb *msg;
396 int rc;
Philipp Maier5046db92018-05-29 12:02:38 +0200397 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100398 struct ran_conn *conn;
Philipp Maier5046db92018-05-29 12:02:38 +0200399
Philipp Maier621ba032017-11-07 17:19:25 +0100400 OSMO_ASSERT(mgcp_ctx);
401 mgcp = mgcp_ctx->mgcp;
402 OSMO_ASSERT(mgcp);
Philipp Maier5046db92018-05-29 12:02:38 +0200403 trans = mgcp_ctx->trans;
404 OSMO_ASSERT(trans);
405 conn = trans->conn;
406 OSMO_ASSERT(conn);
Philipp Maier5046db92018-05-29 12:02:38 +0200407
Philipp Maier621ba032017-11-07 17:19:25 +0100408 switch (event) {
409 case EV_CRCX_RAN_RESP:
410 break;
411 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100412 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100413 return;
414 }
415
416 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200417 "CRCX/CN creating connection for the CN side on MGW endpoint:%s...\n", mgcp_ctx->rtp_endpoint);
Philipp Maier621ba032017-11-07 17:19:25 +0100418
419 /* Generate MGCP message string */
420 mgcp_msg = (struct mgcp_msg) {
421 .verb = MGCP_VERB_CRCX,
422 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
Philipp Maiera2353c62018-02-08 14:15:59 +0100423 .call_id = mgcp_ctx->call_id,
Philipp Maier65d8d0d2018-05-29 10:03:41 +0200424 .conn_mode = MGCP_CONN_RECV_ONLY
Philipp Maier621ba032017-11-07 17:19:25 +0100425 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100426 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100427 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100428 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100429 return;
430 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100431
Philipp Maier621ba032017-11-07 17:19:25 +0100432 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
433 OSMO_ASSERT(msg);
434
435 /* Transmit MGCP message to MGW */
436 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
437 rc = mgcp_client_tx(mgcp, msg, mgw_crcx_cn_resp_cb, mgcp_ctx);
438 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100439 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100440 return;
441 }
442
443 osmo_fsm_inst_state_chg(fi, ST_CRCX_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
444}
445
446/* Callback for MGCP-Client: handle response for CN associated CRCX */
447static void mgw_crcx_cn_resp_cb(struct mgcp_response *r, void *priv)
448{
449 struct mgcp_ctx *mgcp_ctx = priv;
450 int rc;
451 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100452 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100453
454 OSMO_ASSERT(mgcp_ctx);
455 trans = mgcp_ctx->trans;
456 OSMO_ASSERT(trans);
457 conn = trans->conn;
458 OSMO_ASSERT(conn);
459
460 if (r->head.response_code != 200) {
461 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
462 "CRCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100463 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100464 return;
465 }
466
467 /* memorize connection identifier */
468 osmo_strlcpy(mgcp_ctx->conn_id_cn, r->head.conn_id, sizeof(mgcp_ctx->conn_id_cn));
469 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with CI: %s\n", mgcp_ctx->conn_id_cn);
470
471 rc = mgcp_response_parse_params(r);
472 if (rc) {
473 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/CN: Cannot parse response\n");
Philipp Maier4eef20b2018-03-13 13:07:45 +0100474 handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100475 return;
476 }
477
478 LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/CN: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
479
480 conn->rtp.local_port_cn = r->audio_port;
481 osmo_strlcpy(conn->rtp.local_addr_cn, r->audio_ip, sizeof(conn->rtp.local_addr_cn));
482
483 /* Notify the FSM that we got the response. */
484 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_CN_RESP, mgcp_ctx);
485}
486
487/* Callback for ST_CRCX_COMPL: check MGW response, start assignment */
488static void fsm_crcx_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
489{
490 struct mgcp_ctx *mgcp_ctx = data;
491 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100492 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100493
494 OSMO_ASSERT(mgcp_ctx);
495 trans = mgcp_ctx->trans;
496 OSMO_ASSERT(trans);
497 conn = trans->conn;
498 OSMO_ASSERT(conn);
499
500 switch (event) {
501 case EV_CRCX_CN_RESP:
502 break;
503 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100504 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100505 return;
506 }
507
508 /* Forward assignment request to A/RANAP */
509 if (conn->via_ran == RAN_UTRAN_IU) {
510#ifdef BUILD_IU
511 /* Assign a voice channel via RANAP on 3G */
512 if (iu_rab_act_cs(trans))
513 goto error;
514#else
515 LOGPFSML(fi, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n");
516 goto error;
517#endif
518 } else if (conn->via_ran == RAN_GERAN_A) {
519 /* Assign a voice channel via A on 2G */
520 if (a_iface_tx_assignment(trans))
521 goto error;
522 } else {
523 /* Unset or unimplemented new RAN type */
524 LOGPFSML(fi, LOGL_ERROR, "Unknown RAN type: %d\n", conn->via_ran);
525 return;
526 }
527
528 /* Respond back to MNCC (if requested) */
529 if (trans->tch_rtp_create) {
530 if (gsm48_tch_rtp_create(trans))
531 goto error;
532 }
533
534 /* Note: When we reach this point then the situation is basically that
535 * we have two sides connected, both are in loopback. The local ports
536 * of the side pointing towards the BSS should be already communicated
Philipp Maier4c573772018-02-08 14:05:43 +0100537 * and we are waiting now the other end to pick up. */
Philipp Maier621ba032017-11-07 17:19:25 +0100538 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN, MGCP_RAN_TIMEOUT, MGCP_RAN_TIMEOUT_TIMER_NR);
539 return;
540
541error:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100542 handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100543}
544
545static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv);
546
547/* Callback for ST_MDCX_CN: send MDCX for RAN side to MGW */
548static void fsm_mdcx_cn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
549{
550 struct mgcp_ctx *mgcp_ctx = data;
551 struct mgcp_client *mgcp;
552 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100553 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100554 struct mgcp_msg mgcp_msg;
555 struct msgb *msg;
556 int rc;
557
558 OSMO_ASSERT(mgcp_ctx);
559 mgcp = mgcp_ctx->mgcp;
560 OSMO_ASSERT(mgcp);
561 trans = mgcp_ctx->trans;
562 OSMO_ASSERT(trans);
563 conn = trans->conn;
564 OSMO_ASSERT(conn);
565
566 switch (event) {
567 case EV_CONNECT:
568 break;
569 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100570 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100571 return;
572 }
573
574 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100575 "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 +0100576 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_cn, conn->rtp.remote_port_cn);
577
578 /* Generate MGCP message string */
579 mgcp_msg = (struct mgcp_msg) {
580 .verb = MGCP_VERB_MDCX,
581 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
582 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
583 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100584 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100585 .conn_id = mgcp_ctx->conn_id_cn,
586 .conn_mode = MGCP_CONN_RECV_SEND,
587 .audio_ip = conn->rtp.remote_addr_cn,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200588 .audio_port = conn->rtp.remote_port_cn,
589 .codecs[0] = conn->rtp.codec_cn,
590 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100591 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100592 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100593 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100594 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100595 return;
596 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100597
Philipp Maier621ba032017-11-07 17:19:25 +0100598 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
599 OSMO_ASSERT(msg);
600
601 /* Transmit MGCP message to MGW */
602 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
603 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_cn_resp_cb, mgcp_ctx);
604 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100605 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100606 return;
607 }
608
609 osmo_fsm_inst_state_chg(fi, ST_MDCX_CN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
610}
611
612/* Callback for MGCP-Client: handle response for CN associated CRCX */
613static void mgw_mdcx_cn_resp_cb(struct mgcp_response *r, void *priv)
614{
615 struct mgcp_ctx *mgcp_ctx = priv;
616
617 OSMO_ASSERT(mgcp_ctx);
618
619 if (r->head.response_code != 200) {
620 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
621 "MDCX/CN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100622 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100623 return;
624 }
625
626 /* Notify the FSM that we got the response. */
627 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_CN_RESP, mgcp_ctx);
628}
629
630/* Callback for ST_MDCX_CN_COMPL: wait for mgw response, move on with the MDCX
631 * for the RAN side if we already have valid IP/Port data for the RAN sided
632 * RTP stream. */
633static void fsm_mdcx_cn_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
634{
635 struct mgcp_ctx *mgcp_ctx = data;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100636 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100637 struct gsm_trans *trans;
638
639 OSMO_ASSERT(mgcp_ctx);
640 trans = mgcp_ctx->trans;
641 OSMO_ASSERT(trans);
642 conn = trans->conn;
643 OSMO_ASSERT(conn);
644
645 switch (event) {
646 case EV_MDCX_CN_RESP:
647 break;
648 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100649 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100650 return;
651 }
652
653 /* Enter MDCX phase, but we must be sure that the Assigmnet on the A or
654 * IuCS interface is complete (IP-Address and Port are valid) */
655 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN, MGCP_ASS_TIMEOUT, MGCP_ASS_TIMEOUT_TIMER_NR);
656
657 /* If we already have a valid remote port and IP-Address from the RAN side
658 * call leg, the assignment has been completed before we got here, so we
659 * may move on immediately */
660 if (conn->rtp.remote_port_ran != 0 || strlen(conn->rtp.remote_addr_ran) > 0)
661 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASSIGN, mgcp_ctx);
662}
663
664static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv);
665
666/* Callback for ST_MDCX_RAN: wait for assignment completion, send MDCX for CN side to MGW */
667static void fsm_mdcx_ran_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
668{
669 struct mgcp_ctx *mgcp_ctx = data;
670 struct mgcp_client *mgcp;
671 struct gsm_trans *trans;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100672 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100673 struct mgcp_msg mgcp_msg;
674 struct msgb *msg;
675 int rc;
676
677 OSMO_ASSERT(mgcp_ctx);
678 mgcp = mgcp_ctx->mgcp;
679 OSMO_ASSERT(mgcp);
680 trans = mgcp_ctx->trans;
681 OSMO_ASSERT(trans);
682 conn = trans->conn;
683 OSMO_ASSERT(conn);
684
685 switch (event) {
686 case EV_ASSIGN:
687 break;
688 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100689 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100690 return;
691 }
692
693 LOGPFSML(fi, LOGL_DEBUG,
Pau Espin Pedrol9fac9852018-03-17 01:54:32 +0100694 "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 +0100695 mgcp_ctx->rtp_endpoint, conn->rtp.remote_addr_ran, conn->rtp.remote_port_ran);
696
697 /* Generate MGCP message string */
698 mgcp_msg = (struct mgcp_msg) {
699 .verb = MGCP_VERB_MDCX,
700 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
701 MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
702 MGCP_MSG_PRESENCE_AUDIO_PORT),
Philipp Maiera2353c62018-02-08 14:15:59 +0100703 .call_id = mgcp_ctx->call_id,
Philipp Maier621ba032017-11-07 17:19:25 +0100704 .conn_id = mgcp_ctx->conn_id_ran,
705 .conn_mode = MGCP_CONN_RECV_SEND,
706 .audio_ip = conn->rtp.remote_addr_ran,
Philipp Maier8ad3dac2018-08-07 13:00:14 +0200707 .audio_port = conn->rtp.remote_port_ran,
708 .codecs[0] = conn->rtp.codec_ran,
709 .codecs_len = 1
Philipp Maier621ba032017-11-07 17:19:25 +0100710 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100711 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100712 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100713 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100714 return;
715 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100716
Philipp Maier621ba032017-11-07 17:19:25 +0100717 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
718 OSMO_ASSERT(msg);
719
720 /* Transmit MGCP message to MGW */
721 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
722 rc = mgcp_client_tx(mgcp, msg, mgw_mdcx_ran_resp_cb, mgcp_ctx);
723 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100724 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100725 return;
726 }
727
728 osmo_fsm_inst_state_chg(fi, ST_MDCX_RAN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
729}
730
731/* Callback for MGCP-Client: handle response for CN associated CRCX */
732static void mgw_mdcx_ran_resp_cb(struct mgcp_response *r, void *priv)
733{
734 struct mgcp_ctx *mgcp_ctx = priv;
735
736 OSMO_ASSERT(mgcp_ctx);
737
738 if (r->head.response_code != 200) {
739 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
740 "MDCX/RAN: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100741 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100742 return;
743 }
744
745 /* Notify the FSM that we got the response. */
746 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_RAN_RESP, mgcp_ctx);
747}
748
749/* Callback for ST_MDCX_RAN_COMPL: check MGW response */
750static void fsm_mdcx_ran_compl_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
751{
752 struct mgcp_ctx *mgcp_ctx = data;
753 OSMO_ASSERT(mgcp_ctx);
754
755 switch (event) {
756 case EV_MDCX_RAN_RESP:
757 break;
758 default:
Philipp Maier4eef20b2018-03-13 13:07:45 +0100759 handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100760 return;
761 }
762
763 LOGPFSML(fi, LOGL_DEBUG, "call active, waiting for teardown...\n");
764 osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
765}
766
767static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv);
768
769/* Callback for ST_CALL: call is active, send DLCX for both sides on teardown */
770static void fsm_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
771{
772
773 struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
774 struct mgcp_client *mgcp;
775 struct mgcp_msg mgcp_msg;
776 struct msgb *msg;
777 int rc;
778
779 OSMO_ASSERT(mgcp_ctx);
780 mgcp = mgcp_ctx->mgcp;
781 OSMO_ASSERT(mgcp);
782
783 LOGPFSML(fi, LOGL_DEBUG,
Philipp Maierd997fa12018-04-11 15:23:30 +0200784 "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 +0100785
Philipp Maier621ba032017-11-07 17:19:25 +0100786 /* Generate MGCP message string */
787 mgcp_msg = (struct mgcp_msg) {
788 .verb = MGCP_VERB_DLCX,
789 .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
Philipp Maiera2353c62018-02-08 14:15:59 +0100790 .call_id = mgcp_ctx->call_id
Philipp Maier621ba032017-11-07 17:19:25 +0100791 };
Philipp Maiera2353c62018-02-08 14:15:59 +0100792 if (osmo_strlcpy(mgcp_msg.endpoint, mgcp_ctx->rtp_endpoint, sizeof(mgcp_msg.endpoint)) >=
Philipp Maier621ba032017-11-07 17:19:25 +0100793 MGCP_ENDPOINT_MAXLEN) {
Philipp Maier3a776522018-02-22 12:00:00 +0100794 handle_error(mgcp_ctx, MGCP_ERR_TOOLONG, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100795 return;
796 }
Philipp Maiera2353c62018-02-08 14:15:59 +0100797
Philipp Maier621ba032017-11-07 17:19:25 +0100798 msg = mgcp_msg_gen(mgcp, &mgcp_msg);
799 OSMO_ASSERT(msg);
800
801 /* Transmit MGCP message to MGW */
802 mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
803 rc = mgcp_client_tx(mgcp, msg, mgw_dlcx_all_resp_cb, mgcp_ctx);
804 if (rc < 0) {
Philipp Maier4eef20b2018-03-13 13:07:45 +0100805 handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100806 return;
807 }
808
809 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
810}
811
812/* Callback for MGCP-Client: handle response for CN associated CRCX */
813static void mgw_dlcx_all_resp_cb(struct mgcp_response *r, void *priv)
814{
815 struct mgcp_ctx *mgcp_ctx = priv;
816
817 OSMO_ASSERT(mgcp_ctx);
818
Harald Welte33d61e72018-02-10 10:43:38 +0100819 /* DLCX is the only command where 250 is permitted as positive result */
820 if (r->head.response_code != 200 && r->head.response_code != 250) {
Philipp Maier621ba032017-11-07 17:19:25 +0100821 LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
822 "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
Philipp Maier4eef20b2018-03-13 13:07:45 +0100823 handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL, true);
Philipp Maier621ba032017-11-07 17:19:25 +0100824 return;
825 }
826
827 /* Notify the FSM that we got the response. */
828 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
829}
830
831/* Callback for ST_HALT: Terminate the state machine */
832static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
833{
834 struct mgcp_ctx *mgcp_ctx = data;
Philipp Maieraddf63b2018-03-14 13:37:44 +0100835 struct mgcp_client *mgcp;
Philipp Maier621ba032017-11-07 17:19:25 +0100836
837 OSMO_ASSERT(mgcp_ctx);
Philipp Maieraddf63b2018-03-14 13:37:44 +0100838 mgcp = mgcp_ctx->mgcp;
839 OSMO_ASSERT(mgcp);
Philipp Maier621ba032017-11-07 17:19:25 +0100840
841 /* NOTE: We must not free the context information now, we have to
842 * wait until msc_mgcp_call_release() is called. Then we are sure
843 * that the logic controlling us is fully aware that the context
844 * information is freed. If we would free early now the controlling
845 * logic might mistakenly think that the context info is still alive,
846 * so lets keep the context info until we are explicitly asked for
847 * throwing it away. */
848 if (mgcp_ctx->free_ctx) {
Philipp Maieraddf63b2018-03-14 13:37:44 +0100849 /* Be sure that there is no pending MGW transaction */
850 mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
851
852 /* Free FSM and its context information */
Philipp Maier621ba032017-11-07 17:19:25 +0100853 osmo_fsm_inst_free(mgcp_ctx->fsm);
854 talloc_free(mgcp_ctx);
855 return;
856 }
857
858 osmo_fsm_inst_state_chg(fi, ST_HALT, MGCP_REL_TIMEOUT, MGCP_REL_TIMEOUT_TIMER_NR);
859}
860
861static struct osmo_fsm_state fsm_msc_mgcp_states[] = {
862
863 /* Startup state machine, send CRCX for RAN side. */
864 [ST_CRCX_RAN] = {
865 .in_event_mask = S(EV_INIT),
866 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_CN),
867 .name = OSMO_STRINGIFY(ST_CRCX_RAN),
868 .action = fsm_crcx_ran_cb,
869 },
870 /* When the response to the RAN CRCX is received, then proceed with
871 sending the CRCX for CN side */
872 [ST_CRCX_CN] = {
873 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_RAN_RESP),
874 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_COMPL),
875 .name = OSMO_STRINGIFY(ST_CRCX_CN),
876 .action = fsm_crcx_cn_cb,
877 },
878 /* Complete the CRCX phase by starting the assignment. Depending on the
879 * RAT (Radio Access Technology), this will either trigger an
880 * Assignment Request on the A-Interface or an RAB-Assignment on the
881 * IU-interface */
882 [ST_CRCX_COMPL] = {
883 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CRCX_CN_RESP),
884 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN),
885 .name = OSMO_STRINGIFY(ST_CRCX_COMPL),
886 .action = fsm_crcx_compl,
887 },
888 /* Wait for MSC to complete the assignment request, when complete, we
889 * will enter the MDCX phase by sending an MDCX for the CN side to the
890 * MGW */
891 [ST_MDCX_CN] = {
892 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_CONNECT),
893 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_CN_COMPL),
894 .name = OSMO_STRINGIFY(ST_MDCX_CN),
895 .action = fsm_mdcx_cn_cb,
896 },
897 /* We arrive in this state when the MDCX phase for the CN side has
898 * completed we will check the IP/Port of the RAN connection. If this
899 * data is valid we may continue with the MDCX phase for the RAN side.
900 * If not we wait until the assinment completes on the A or on the IuCS
901 * interface. The completion of the assignment will fill in the port and
902 * IP-Address of the RAN side and way may continue then. */
903 [ST_MDCX_CN_COMPL] = {
904 .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_CN_RESP),
905 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN),
906 .name = OSMO_STRINGIFY(ST_MDCX_CN_COMPL),
907 .action = fsm_mdcx_cn_compl_cb,
908 },
909 /* When the response for the CN MDCX is received, send the MDCX for the
910 * RAN side to the MGW */
911 [ST_MDCX_RAN] = {
912 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_ASSIGN),
913 .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_RAN_COMPL),
914 .name = OSMO_STRINGIFY(ST_MDCX_RAN),
915 .action = fsm_mdcx_ran_cb,
916 },
917 /* The RAN side MDCX phase is complete when the response is received
918 * from the MGW. The call is then active, we change to ST_CALL and wait
919 * there until the call ends. */
920 [ST_MDCX_RAN_COMPL] = {
921 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_MDCX_RAN_RESP),
922 .out_state_mask = S(ST_HALT) | S(ST_CALL),
923 .name = OSMO_STRINGIFY(ST_MDCX_RAN_COMPL),
924 .action = fsm_mdcx_ran_compl_cb,
925 },
926 /* We are now in the active call phase, wait until the call is done
927 * and send a DLCX then to remove all connections from the MGW */
928 [ST_CALL] = {
929 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR),
930 .out_state_mask = S(ST_HALT),
931 .name = OSMO_STRINGIFY(ST_CALL),
932 .action = fsm_call_cb,
933 },
934 /* When the MGW confirms that the connections are terminated, then halt
935 * the state machine. */
936 [ST_HALT] = {
937 .in_event_mask = S(EV_TEARDOWN) | S(EV_TEARDOWN_ERROR) | S(EV_DLCX_ALL_RESP),
938 .out_state_mask = S(ST_HALT),
939 .name = OSMO_STRINGIFY(ST_HALT),
940 .action = fsm_halt_cb,
941 },
942};
943
944/* State machine definition */
945static struct osmo_fsm fsm_msc_mgcp = {
946 .name = "MGW",
947 .states = fsm_msc_mgcp_states,
948 .num_states = ARRAY_SIZE(fsm_msc_mgcp_states),
949 .log_subsys = DMGCP,
950 .timer_cb = fsm_timeout_cb,
Philipp Maiere4f91722018-02-26 15:20:49 +0100951 .event_names = msc_mgcp_fsm_evt_names,
Philipp Maier621ba032017-11-07 17:19:25 +0100952};
953
954/* Notify that a new call begins. This will create a connection for the
955 * RAN and the CN on the MGW.
956 * Parameter:
957 * trans: transaction context.
958 * Returns -EINVAL on error, 0 on success. */
959int msc_mgcp_call_assignment(struct gsm_trans *trans)
960{
961 struct mgcp_ctx *mgcp_ctx;
962 char name[32];
963 static bool fsm_registered = false;
Neels Hofmeyrc036b792018-11-29 22:37:51 +0100964 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +0100965 struct mgcp_client *mgcp;
966
967 OSMO_ASSERT(trans);
968
969 if (!trans->conn) {
970 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call assignment failed\n",
971 vlr_subscr_name(trans->vsub));
972 return -EINVAL;
973 }
974
975 conn = trans->conn;
976 mgcp = conn->network->mgw.client;
977 OSMO_ASSERT(mgcp);
978
979 if (conn->rtp.mgcp_ctx) {
980 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) double assignment detected, dropping...\n",
981 vlr_subscr_name(trans->vsub));
982 return -EINVAL;
983 }
984
985#ifdef BUILD_IU
986 /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */
987 static uint8_t next_iu_rab_id = 1;
988 if (conn->via_ran == RAN_UTRAN_IU)
989 conn->iu.rab_id = next_iu_rab_id++;
990#endif
991
992 if (snprintf(name, sizeof(name), "MGW_%i", trans->transaction_id) >= sizeof(name))
993 return -EINVAL;
994
995 /* Register the fsm description (if not already done) */
996 if (fsm_registered == false) {
997 osmo_fsm_register(&fsm_msc_mgcp);
998 fsm_registered = true;
999 }
1000
1001 /* Allocate and configure a new fsm instance */
1002 mgcp_ctx = talloc_zero(NULL, struct mgcp_ctx);
1003 OSMO_ASSERT(mgcp_ctx);
Philipp Maiera2353c62018-02-08 14:15:59 +01001004 if (osmo_strlcpy(mgcp_ctx->rtp_endpoint, ENDPOINT_ID, sizeof(mgcp_ctx->rtp_endpoint)) >=
1005 MGCP_ENDPOINT_MAXLEN) {
1006 talloc_free(mgcp_ctx);
1007 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) endpoint identifier (%s) exceeds maximum length...\n",
1008 vlr_subscr_name(trans->vsub), ENDPOINT_ID);
1009 return -EINVAL;
1010 }
Philipp Maier621ba032017-11-07 17:19:25 +01001011 mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_msc_mgcp, NULL, NULL, LOGL_DEBUG, name);
1012 OSMO_ASSERT(mgcp_ctx->fsm);
1013 mgcp_ctx->fsm->priv = mgcp_ctx;
1014 mgcp_ctx->mgcp = mgcp;
1015 mgcp_ctx->trans = trans;
Philipp Maiera2353c62018-02-08 14:15:59 +01001016 mgcp_ctx->call_id = trans->callref;
Philipp Maier621ba032017-11-07 17:19:25 +01001017
1018 /* start state machine */
1019 OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_RAN);
1020 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
1021
1022 conn->rtp.mgcp_ctx = mgcp_ctx;
1023
1024 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call assignment initiated\n",
1025 vlr_subscr_name(conn->vsub));
1026
1027 return 0;
1028}
1029
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001030static enum mgcp_codecs mgcp_codec_from_sc(const struct gsm0808_speech_codec *sc)
Philipp Maier621ba032017-11-07 17:19:25 +01001031{
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001032 switch (sc->type) {
1033 case GSM0808_SCT_FR1:
1034 return CODEC_GSM_8000_1;
1035 break;
1036 case GSM0808_SCT_FR2:
1037 return CODEC_GSMEFR_8000_1;
1038 break;
1039 case GSM0808_SCT_FR3:
1040 return CODEC_AMR_8000_1;
1041 break;
1042 case GSM0808_SCT_FR4:
1043 return CODEC_AMRWB_16000_1;
1044 break;
1045 case GSM0808_SCT_FR5:
1046 return CODEC_AMRWB_16000_1;
1047 break;
1048 case GSM0808_SCT_HR1:
1049 return CODEC_GSMHR_8000_1;
1050 break;
1051 case GSM0808_SCT_HR3:
1052 return CODEC_AMR_8000_1;
1053 break;
1054 case GSM0808_SCT_HR4:
1055 return CODEC_AMRWB_16000_1;
1056 break;
1057 case GSM0808_SCT_HR6:
1058 return CODEC_AMRWB_16000_1;
1059 break;
1060 default:
1061 return CODEC_PCMU_8000_1;
1062 break;
1063 }
1064}
Philipp Maier621ba032017-11-07 17:19:25 +01001065
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001066int msc_mgcp_ass_complete(struct ran_conn *conn, const struct gsm0808_speech_codec *speech_codec_chosen,
1067 const struct sockaddr_storage *aoip_transport_addr)
1068{
1069 struct sockaddr_in *rtp_addr_in;
1070 const char *addr;
1071 uint16_t port;
1072 struct mgcp_ctx *mgcp_ctx = conn->rtp.mgcp_ctx;
1073 struct osmo_fsm_inst *fi;
1074
1075 if (!mgcp_ctx || !mgcp_ctx->fsm) {
1076 LOGPCONN(conn, LOGL_ERROR, "Invalid MGCP context, Assignment Complete failed.\n");
1077 return -EINVAL;
1078 }
1079
1080 fi = mgcp_ctx->fsm;
1081
1082 if (fi->state != ST_MDCX_RAN) {
1083 LOGPFSML(fi, LOGL_ERROR, "Assignment Complete not allowed in this state\n");
1084 return -ENOTSUP;
1085 }
1086
1087 /* use address / port supplied with the AoIP transport address element */
1088 if (aoip_transport_addr->ss_family != AF_INET) {
1089 LOGPCONN(conn, LOGL_ERROR, "Assignment Complete: Unsupported addressing scheme (only IPV4 supported)\n");
1090 return -EINVAL;
1091 }
1092
1093 rtp_addr_in = (struct sockaddr_in *)&aoip_transport_addr;
1094 addr = inet_ntoa(rtp_addr_in->sin_addr);
1095 port = osmo_ntohs(rtp_addr_in->sin_port);
Philipp Maier621ba032017-11-07 17:19:25 +01001096
1097 if (port == 0) {
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001098 LOGPCONN(conn, LOGL_ERROR, "Assignment Complete: invalid remote call leg port (0)\n");
Philipp Maier621ba032017-11-07 17:19:25 +01001099 return -EINVAL;
1100 }
1101 if (!addr || strlen(addr) <= 0) {
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001102 LOGPCONN(conn, LOGL_ERROR, "Assignment Complete: invalid remote call leg address (empty)\n");
Philipp Maier621ba032017-11-07 17:19:25 +01001103 return -EINVAL;
1104 }
1105
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001106 conn->rtp.codec_ran = mgcp_codec_from_sc(speech_codec_chosen);
Philipp Maier621ba032017-11-07 17:19:25 +01001107 osmo_strlcpy(conn->rtp.remote_addr_ran, addr, sizeof(conn->rtp.remote_addr_ran));
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001108 conn->rtp.remote_port_ran = port;
Philipp Maier621ba032017-11-07 17:19:25 +01001109
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001110 LOGPCONN(conn, LOGL_DEBUG, "Assignment Complete: rtp %s:%u\n", addr, port);
Philipp Maier621ba032017-11-07 17:19:25 +01001111
Neels Hofmeyr212c0c92018-12-07 14:47:34 +01001112 return osmo_fsm_inst_dispatch(fi, EV_ASSIGN, mgcp_ctx);
Philipp Maier621ba032017-11-07 17:19:25 +01001113}
1114
1115/* Make the connection of a previously assigned call complete
1116 * Parameter:
1117 * trans: transaction context.
1118 * port: port number of the remote leg.
1119 * addr: IP-address of the remote leg.
1120 * Returns -EINVAL on error, 0 on success. */
1121int msc_mgcp_call_complete(struct gsm_trans *trans, uint16_t port, char *addr)
1122{
1123 struct mgcp_ctx *mgcp_ctx;
Neels Hofmeyrc036b792018-11-29 22:37:51 +01001124 struct ran_conn *conn;
Philipp Maier621ba032017-11-07 17:19:25 +01001125
1126 OSMO_ASSERT(trans);
1127 OSMO_ASSERT(addr);
1128
1129 if (port == 0) {
1130 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid remote call leg port, call completion failed\n",
1131 vlr_subscr_name(trans->vsub));
1132 return -EINVAL;
1133 }
1134 if (!addr || strlen(addr) <= 0) {
1135 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) missing remote call leg address, call completion failed\n",
1136 vlr_subscr_name(trans->vsub));
1137 return -EINVAL;
1138 }
1139 if (!trans->conn) {
1140 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call completion failed\n",
1141 vlr_subscr_name(trans->vsub));
1142 return -EINVAL;
1143 }
1144 if (!trans->conn->rtp.mgcp_ctx) {
1145 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call completion failed.\n",
1146 vlr_subscr_name(trans->vsub));
1147 return -EINVAL;
1148 }
1149 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1150 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call completion failed\n",
1151 vlr_subscr_name(trans->vsub));
1152 return -EINVAL;
1153 }
1154
1155 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1156
1157 /* The FSM should already have passed all CRCX phases and be ready to move
1158 * on with the MDCX phases. */
1159 if (mgcp_ctx->fsm->state != ST_MDCX_CN) {
1160 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid call state, call completion failed\n",
1161 vlr_subscr_name(trans->vsub));
1162 return -EINVAL;
1163 }
1164
1165 conn = trans->conn;
1166 osmo_strlcpy(conn->rtp.remote_addr_cn, addr, sizeof(conn->rtp.remote_addr_cn));
1167 conn->rtp.remote_port_cn = port;
1168
1169 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CONNECT, mgcp_ctx);
1170
1171 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call completion initiated\n",
1172 vlr_subscr_name(conn->vsub));
1173
1174 return 0;
1175}
1176
1177/* Release ongoing call.
1178 * Parameter:
1179 * trans: connection context.
1180 * Returns -EINVAL on error, 0 on success. */
1181int msc_mgcp_call_release(struct gsm_trans *trans)
1182{
1183 struct mgcp_ctx *mgcp_ctx;
1184
1185 OSMO_ASSERT(trans);
1186
1187 if (!trans->conn) {
1188 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid conn, call release failed\n",
1189 vlr_subscr_name(trans->vsub));
1190 return -EINVAL;
1191 }
1192 if (!trans->conn->rtp.mgcp_ctx) {
1193 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) invalid mgcp context, call release failed.\n",
1194 vlr_subscr_name(trans->vsub));
1195 return -EINVAL;
1196 }
1197 if (!trans->conn->rtp.mgcp_ctx->fsm) {
1198 LOGP(DMGCP, LOGL_ERROR, "(subscriber:%s) no FSM, call release failed\n",
1199 vlr_subscr_name(trans->vsub));
1200 return -EINVAL;
1201 }
1202
1203 mgcp_ctx = trans->conn->rtp.mgcp_ctx;
1204
1205 /* Inform the FSM that as soon as it reaches ST_HALT it may free
1206 * all context information immediately */
1207 mgcp_ctx->free_ctx = true;
1208
1209 /* Initaite teardown, regardless of which state we are currently
1210 * in */
1211 osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
1212
1213 /* Prevent any further operation that is triggered from outside by
1214 * overwriting the context pointer with NULL. The FSM will now
1215 * take care for a graceful shutdown and when done it will free
1216 * all related context information */
1217 trans->conn->rtp.mgcp_ctx = NULL;
1218
1219 LOGP(DMGCP, LOGL_DEBUG, "(subscriber:%s) call release initiated\n",
1220 vlr_subscr_name(trans->vsub));
1221
1222 return 0;
1223}