blob: 5a1a62f4f8d8b219a4d46d5521ff20b106cac14e [file] [log] [blame]
Harald Weltec997ceb2018-05-30 01:39:43 +02001/* (C) 2018 by Harald Welte <laforge@gnumonks.org>
2 * All Rights Reserved
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Affero General Public License for more details.
13 *
14 * You should have received a copy of the GNU Affero General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19#include <osmocom/core/utils.h>
20#include <osmocom/core/logging.h>
21#include <osmocom/core/fsm.h>
22#include <osmocom/core/linuxlist.h>
23#include <osmocom/gsm/gsm0808.h>
24#include <osmocom/core/msgb.h>
Maxb407a8a2018-11-07 12:56:54 +010025#include <osmocom/bsc/abis_rsl.h>
Harald Weltec997ceb2018-05-30 01:39:43 +020026#include <osmocom/bsc/bsc_msc_data.h>
27#include <osmocom/bsc/debug.h>
28#include <osmocom/bsc/osmo_bsc.h>
29#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
30#include <osmocom/bsc/gsm_data.h>
31#include <osmocom/bsc/osmo_bsc_lcls.h>
Neels Hofmeyrf14aaa42019-04-23 18:37:37 +020032#include <osmocom/bsc/lchan_rtp_fsm.h>
Pau Espin Pedrol3d0fbe32022-08-08 15:03:41 +020033#include <osmocom/bsc/lchan.h>
Neels Hofmeyrf14aaa42019-04-23 18:37:37 +020034#include <osmocom/mgcp_client/mgcp_client_endpoint_fsm.h>
Harald Weltec997ceb2018-05-30 01:39:43 +020035
36struct value_string lcls_event_names[] = {
37 { LCLS_EV_UPDATE_CFG_CSC, "UPDATE_CFG_CSC" },
38 { LCLS_EV_APPLY_CFG_CSC, "APPLY_CFG_CSC" },
39 { LCLS_EV_CORRELATED, "CORRELATED" },
40 { LCLS_EV_OTHER_ENABLED, "OTHER_ENABLED" },
41 { LCLS_EV_OTHER_BREAK, "OTHER_BREAK" },
42 { LCLS_EV_OTHER_DEAD, "OTHER_DEAD" },
43 { 0, NULL }
44};
45
Pau Espin Pedrol2cfd0002019-03-12 18:53:26 +010046const struct value_string bsc_lcls_mode_names[] = {
47 { BSC_LCLS_MODE_DISABLED, "disabled" },
48 { BSC_LCLS_MODE_MGW_LOOP, "mgw-loop" },
49 { BSC_LCLS_MODE_BTS_LOOP, "bts-loop" },
50 { 0, NULL }
51};
Harald Weltec997ceb2018-05-30 01:39:43 +020052
53/***********************************************************************
54 * Utility functions
55 ***********************************************************************/
56
Max09273b72019-01-08 15:08:59 +010057enum gsm0808_lcls_status lcls_get_status(const struct gsm_subscriber_connection *conn)
Harald Weltec997ceb2018-05-30 01:39:43 +020058{
59 if (!conn->lcls.fi)
Maxf3bd8382018-12-19 19:47:26 +010060 return GSM0808_LCLS_STS_NA;
Harald Weltec997ceb2018-05-30 01:39:43 +020061
62 switch (conn->lcls.fi->state) {
63 case ST_NO_LCLS:
Maxf3bd8382018-12-19 19:47:26 +010064 return GSM0808_LCLS_STS_NA;
Harald Weltec997ceb2018-05-30 01:39:43 +020065 case ST_NOT_YET_LS:
66 return GSM0808_LCLS_STS_NOT_YET_LS;
67 case ST_NOT_POSSIBLE_LS:
68 return GSM0808_LCLS_STS_NOT_POSSIBLE_LS;
69 case ST_NO_LONGER_LS:
70 return GSM0808_LCLS_STS_NO_LONGER_LS;
71 case ST_REQ_LCLS_NOT_SUPP:
72 return GSM0808_LCLS_STS_REQ_LCLS_NOT_SUPP;
73 case ST_LOCALLY_SWITCHED:
74 case ST_LOCALLY_SWITCHED_WAIT_BREAK:
75 case ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK:
76 return GSM0808_LCLS_STS_LOCALLY_SWITCHED;
77 }
78 OSMO_ASSERT(0);
79}
80
81static void lcls_send_notify(struct gsm_subscriber_connection *conn)
82{
83 enum gsm0808_lcls_status status = lcls_get_status(conn);
84 struct msgb *msg;
85
Maxf3bd8382018-12-19 19:47:26 +010086 if (status == GSM0808_LCLS_STS_NA)
Harald Weltec997ceb2018-05-30 01:39:43 +020087 return;
88
89 LOGPFSM(conn->lcls.fi, "Sending BSSMAP LCLS NOTIFICATION (%s)\n",
90 gsm0808_lcls_status_name(status));
91 msg = gsm0808_create_lcls_notification(status, false);
92 osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, msg);
93}
94
95static struct gsm_subscriber_connection *
Max09273b72019-01-08 15:08:59 +010096find_conn_with_same_gcr(const struct gsm_subscriber_connection *conn_local)
Harald Weltec997ceb2018-05-30 01:39:43 +020097{
98 struct gsm_network *net = conn_local->network;
99 struct gsm_subscriber_connection *conn_other;
100
101 llist_for_each_entry(conn_other, &net->subscr_conns, entry) {
102 /* don't report back the same connection */
103 if (conn_other == conn_local)
104 continue;
105 /* don't consider any conn where GCR length is not the same as before */
106 if (conn_other->lcls.global_call_ref_len != conn_local->lcls.global_call_ref_len)
107 continue;
108 if (!memcmp(conn_other->lcls.global_call_ref, conn_local->lcls.global_call_ref,
109 conn_local->lcls.global_call_ref_len))
110 return conn_other;
111 }
112 return NULL;
113}
114
115static bool lcls_is_supported_config(enum gsm0808_lcls_config cfg)
116{
117 /* this is the only configuration that we support for now */
118 if (cfg == GSM0808_LCLS_CFG_BOTH_WAY)
119 return true;
120 else
121 return false;
122}
123
124/* LCLS Call Leg Correlation as per 23.284 4.3 / 48.008 3.1.33.2.1 */
125static int lcls_perform_correlation(struct gsm_subscriber_connection *conn_local)
126{
127 struct gsm_subscriber_connection *conn_other;
128
129 /* We can only correlate if a GCR is present */
130 OSMO_ASSERT(conn_local->lcls.global_call_ref_len);
131 /* We can only correlate if we're not in active LS */
132 OSMO_ASSERT(conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED &&
133 conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK &&
134 conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK);
135
136 conn_other = conn_local->lcls.other;
137 if (conn_other) {
138 LOGPFSM(conn_local->lcls.fi, "Breaking previous correlation with %s\n",
139 osmo_fsm_inst_name(conn_other->lcls.fi));
140 OSMO_ASSERT(conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED &&
141 conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK &&
142 conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK);
143 conn_local->lcls.other->lcls.other = NULL;
144 conn_local->lcls.other = NULL;
145 }
146
147 conn_other = find_conn_with_same_gcr(conn_local);
148 if (!conn_other) {
149 /* we found no other call with same GCR: not possible */
150 LOGPFSM(conn_local->lcls.fi, "Unsuccessful correlation\n");
151 return -ENODEV;
152 }
153
154 /* store pointer to "other" in "local" */
155 conn_local->lcls.other = conn_other;
156
157 LOGPFSM(conn_local->lcls.fi, "Successfully correlated with %s\n",
158 osmo_fsm_inst_name(conn_other->lcls.fi));
159
160 /* notify other conn about our correlation */
161 osmo_fsm_inst_dispatch(conn_other->lcls.fi, LCLS_EV_CORRELATED, conn_local);
162
163 return 0;
164}
165
Harald Weltec997ceb2018-05-30 01:39:43 +0200166/* Update the connections LCLS configuration and return old/previous configuration.
167 * \returns (staticallly allocated) old configuration; NULL if new config not supported */
Maxfe65ece2018-11-09 13:36:11 +0100168static struct osmo_lcls *update_lcls_cfg_csc(struct gsm_subscriber_connection *conn,
Max09273b72019-01-08 15:08:59 +0100169 const struct osmo_lcls *new_cfg_csc)
Harald Weltec997ceb2018-05-30 01:39:43 +0200170{
Maxfe65ece2018-11-09 13:36:11 +0100171 static struct osmo_lcls old_cfg_csc = { 0 };
Harald Weltec997ceb2018-05-30 01:39:43 +0200172 old_cfg_csc.config = conn->lcls.config;
173 old_cfg_csc.control = conn->lcls.control;
174
Maxfe65ece2018-11-09 13:36:11 +0100175 if (new_cfg_csc->config != GSM0808_LCLS_CFG_NA) {
Harald Weltec997ceb2018-05-30 01:39:43 +0200176 if (!lcls_is_supported_config(new_cfg_csc->config))
177 return NULL;
178 if (conn->lcls.config != new_cfg_csc->config) {
Max43c403a2018-12-19 16:53:05 +0100179 LOGPFSM(conn->lcls.fi, "LCLS update Config %s -> %s\n",
180 gsm0808_lcls_config_name(conn->lcls.config),
181 gsm0808_lcls_config_name(new_cfg_csc->config));
Harald Weltec997ceb2018-05-30 01:39:43 +0200182 conn->lcls.config = new_cfg_csc->config;
183 }
184 }
Maxfe65ece2018-11-09 13:36:11 +0100185 if (new_cfg_csc->control != GSM0808_LCLS_CSC_NA) {
Harald Weltec997ceb2018-05-30 01:39:43 +0200186 if (conn->lcls.control != new_cfg_csc->control) {
Max43c403a2018-12-19 16:53:05 +0100187 LOGPFSM(conn->lcls.fi, "LCLS update Control %s -> %s\n",
188 gsm0808_lcls_control_name(conn->lcls.control),
189 gsm0808_lcls_control_name(new_cfg_csc->control));
Harald Weltec997ceb2018-05-30 01:39:43 +0200190 conn->lcls.control = new_cfg_csc->control;
191 }
192 }
193
194 return &old_cfg_csc;
195}
196
197/* Attempt to update conn->lcls with the new config/csc provided. If new config is
198 * unsupported, change into LCLS NOT SUPPORTED state and return -EINVAL. */
199static int lcls_handle_cfg_update(struct gsm_subscriber_connection *conn, void *data)
200{
Maxfe65ece2018-11-09 13:36:11 +0100201 struct osmo_lcls *new_cfg_csc, *old_cfg_csc;
Harald Weltec997ceb2018-05-30 01:39:43 +0200202
Maxfe65ece2018-11-09 13:36:11 +0100203 new_cfg_csc = (struct osmo_lcls *) data;
Harald Weltec997ceb2018-05-30 01:39:43 +0200204 old_cfg_csc = update_lcls_cfg_csc(conn, new_cfg_csc);
205 if (!old_cfg_csc) {
206 osmo_fsm_inst_state_chg(conn->lcls.fi, ST_REQ_LCLS_NOT_SUPP, 0, 0);
207 return -EINVAL;
208 }
209 return 0;
210}
211
212/* notify the LCLS FSM about new LCLS Config and/or CSC */
213void lcls_update_config(struct gsm_subscriber_connection *conn,
214 const uint8_t *config, const uint8_t *control)
215{
Maxfe65ece2018-11-09 13:36:11 +0100216 struct osmo_lcls new_cfg = {
217 .config = GSM0808_LCLS_CFG_NA,
218 .control = GSM0808_LCLS_CSC_NA,
Harald Weltec997ceb2018-05-30 01:39:43 +0200219 };
220 /* nothing to update, skip it */
221 if (!config && !control)
222 return;
223 if (config)
224 new_cfg.config = *config;
225 if (control)
226 new_cfg.control = *control;
227 osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_UPDATE_CFG_CSC, &new_cfg);
228}
229
230/* apply the configuration, may be changed before by lcls_update_config */
231void lcls_apply_config(struct gsm_subscriber_connection *conn)
232{
233 osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_APPLY_CFG_CSC, NULL);
234}
235
Maxb407a8a2018-11-07 12:56:54 +0100236/* Redirect BTS's RTP traffic via RSL MDCX command:
237 * when enable == true, redirect to remote BTS's IP:port
238 * when enable == false, redirect back to the MGW's IP:port
239 */
240static inline void lcls_rsl(const struct gsm_subscriber_connection *conn, bool enable)
241{
242 const struct gsm_lchan *lchan = conn->lchan;
243 /* RSL_IE_IPAC_REMOTE_IP */
244 uint32_t ip = enable ? conn->lcls.other->lchan->abis_ip.bound_ip : lchan->abis_ip.connect_ip;
245 /* RSL_IE_IPAC_REMOTE_PORT */
246 uint16_t port = enable ? conn->lcls.other->lchan->abis_ip.bound_port : lchan->abis_ip.connect_port;
Neels Hofmeyr43aeeaf2021-06-02 20:13:39 +0000247 struct msgb *msg;
Maxb407a8a2018-11-07 12:56:54 +0100248
249 if (!conn->lcls.other) {
250 LOGPFSM(conn->lcls.fi, "%s LCLS: other conn is not available!\n", enable ? "enable" : "disable");
251 return;
252 }
253
Neels Hofmeyr43aeeaf2021-06-02 20:13:39 +0000254 msg = rsl_make_ipacc_mdcx(lchan, ip, port);
255 if (!msg) {
256 LOGPFSML(conn->lcls.fi, LOGL_ERROR, "Error encoding IPACC MDCX\n");
257 return;
258 }
259 abis_rsl_sendmsg(msg);
Maxb407a8a2018-11-07 12:56:54 +0100260}
261
Max49c7d222018-11-07 12:48:04 +0100262static inline bool lcls_check_toggle_allowed(const struct gsm_subscriber_connection *conn, bool enable)
263{
264 if (conn->lcls.other &&
265 conn->sccp.msc->lcls_mode != conn->lcls.other->sccp.msc->lcls_mode) {
266 LOGPFSM(conn->lcls.fi, "FIXME: LCLS connection mode mismatch: %s != %s\n",
267 get_value_string(bsc_lcls_mode_names, conn->sccp.msc->lcls_mode),
268 get_value_string(bsc_lcls_mode_names, conn->lcls.other->sccp.msc->lcls_mode));
269 return false;
270 }
271
272 if (conn->sccp.msc->lcls_mode == BSC_LCLS_MODE_MGW_LOOP && !conn->user_plane.mgw_endpoint_ci_msc) {
273 /* the MGCP FSM has died, e.g. due to some MGCP/SDP parsing error */
274 LOGPFSML(conn->lcls.fi, LOGL_NOTICE, "Cannot %s LCLS without MSC-side MGCP FSM\n",
275 enable ? "enable" : "disable");
276 return false;
277 }
278
279 return true;
280}
281
Max5962f232018-11-01 11:55:15 +0100282/* Close the loop for LCLS using MGCP */
283static inline void lcls_mdcx(const struct gsm_subscriber_connection *conn, struct mgcp_conn_peer *mdcx_info)
284{
285 mgcp_pick_codec(mdcx_info, conn->lchan, false);
286
Neels Hofmeyrf14aaa42019-04-23 18:37:37 +0200287 osmo_mgcpc_ep_ci_request(conn->user_plane.mgw_endpoint_ci_msc, MGCP_VERB_MDCX, mdcx_info,
Max5962f232018-11-01 11:55:15 +0100288 NULL, 0, 0, NULL);
289}
290
Harald Weltec997ceb2018-05-30 01:39:43 +0200291static void lcls_break_local_switching(struct gsm_subscriber_connection *conn)
292{
Pau Espin Pedrol46b1c812019-05-23 16:32:19 +0200293 struct mgcp_conn_peer mdcx_info = {};
Harald Weltec997ceb2018-05-30 01:39:43 +0200294
Max6fb500f2018-11-07 12:34:07 +0100295 LOGPFSM(conn->lcls.fi, "=== HERE IS WHERE WE DISABLE LCLS(%s)\n",
296 bsc_lcls_mode_name(conn->sccp.msc->lcls_mode));
297
Max49c7d222018-11-07 12:48:04 +0100298 if (!lcls_check_toggle_allowed(conn, false))
Harald Weltec997ceb2018-05-30 01:39:43 +0200299 return;
Harald Weltec997ceb2018-05-30 01:39:43 +0200300
Maxb407a8a2018-11-07 12:56:54 +0100301 switch(conn->sccp.msc->lcls_mode) {
302 case BSC_LCLS_MODE_MGW_LOOP:
303 mdcx_info.port = conn->user_plane.msc_assigned_rtp_port;
Max654bf622018-11-07 12:42:02 +0100304 osmo_strlcpy(mdcx_info.addr, conn->user_plane.msc_assigned_rtp_addr, sizeof(mdcx_info.addr));
305 lcls_mdcx(conn, &mdcx_info);
Maxb407a8a2018-11-07 12:56:54 +0100306 break;
307 case BSC_LCLS_MODE_BTS_LOOP:
308 lcls_rsl(conn, false);
309 break;
310 case BSC_LCLS_MODE_DISABLED:
311 LOGPFSM(conn->lcls.fi, "FIXME: attempt to break LCLS loop while LCLS is disabled?!\n");
312 break;
313 default:
314 LOGPFSM(conn->lcls.fi, "FIXME: unknown LCLS mode %s\n",
315 bsc_lcls_mode_name(conn->sccp.msc->lcls_mode));
Max654bf622018-11-07 12:42:02 +0100316 }
Harald Weltec997ceb2018-05-30 01:39:43 +0200317}
318
Max09273b72019-01-08 15:08:59 +0100319static bool lcls_enable_possible(const struct gsm_subscriber_connection *conn)
Harald Weltec997ceb2018-05-30 01:39:43 +0200320{
321 struct gsm_subscriber_connection *other_conn = conn->lcls.other;
322 OSMO_ASSERT(other_conn);
323
324 if (!lcls_is_supported_config(conn->lcls.config)) {
325 LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported local config\n");
326 return false;
327 }
328
329 if (!lcls_is_supported_config(other_conn->lcls.config)) {
330 LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported other config\n");
331 return false;
332 }
333
334 if (conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) {
335 LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient local control\n");
336 return false;
337 }
338
339 if (other_conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) {
340 LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient other control\n");
341 return false;
342 }
343
Philipp Maierbf399962018-09-12 16:17:41 +0200344 if (conn->lchan->type != conn->lcls.other->lchan->type
Philipp Maier48338572018-07-10 09:32:27 +0200345 && conn->sccp.msc->lcls_codec_mismatch_allow == false) {
Philipp Maier0d4a98a2018-09-25 13:18:08 +0200346 LOGPFSM(conn->lcls.fi,
347 "Not enabling LS due to channel type mismatch: %s:%s != %s:%s\n",
348 gsm_lchan_name(conn->lchan),
349 gsm_chan_t_name(conn->lchan->type),
350 gsm_lchan_name(conn->lcls.other->lchan),
351 gsm_chan_t_name(conn->lcls.other->lchan->type));
Philipp Maier48338572018-07-10 09:32:27 +0200352 return false;
353 }
354
Neels Hofmeyr0951d752021-04-27 23:17:14 +0000355 if (conn->lchan->current_ch_mode_rate.chan_mode != conn->lcls.other->lchan->current_ch_mode_rate.chan_mode
Philipp Maier48338572018-07-10 09:32:27 +0200356 && conn->sccp.msc->lcls_codec_mismatch_allow == false) {
Philipp Maier0d4a98a2018-09-25 13:18:08 +0200357 LOGPFSM(conn->lcls.fi,
358 "Not enabling LS due to TCH-mode mismatch: %s:%s != %s:%s\n",
359 gsm_lchan_name(conn->lchan),
Neels Hofmeyr0951d752021-04-27 23:17:14 +0000360 gsm48_chan_mode_name(conn->lchan->current_ch_mode_rate.chan_mode),
Philipp Maier0d4a98a2018-09-25 13:18:08 +0200361 gsm_lchan_name(conn->lcls.other->lchan),
Neels Hofmeyr0951d752021-04-27 23:17:14 +0000362 gsm48_chan_mode_name(conn->lcls.other->lchan->current_ch_mode_rate.chan_mode));
Philipp Maier48338572018-07-10 09:32:27 +0200363 return false;
364 }
365
Harald Weltec997ceb2018-05-30 01:39:43 +0200366 return true;
367}
368
369/***********************************************************************
370 * State callback functions
371 ***********************************************************************/
372
373static void lcls_no_lcls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
374{
375 struct gsm_subscriber_connection *conn = fi->priv;
376
377 /* we're just starting and cannot yet have a correlated call */
378 OSMO_ASSERT(conn->lcls.other == NULL);
379
Harald Welte1f1c56c2018-06-03 12:24:27 +0200380 if (conn->sccp.msc->lcls_mode == BSC_LCLS_MODE_DISABLED) {
381 LOGPFSML(fi, LOGL_DEBUG, "LCLS disabled for this MSC, ignoring %s\n",
382 osmo_fsm_event_name(fi->fsm, event));
383 return;
384 }
385
Harald Weltec997ceb2018-05-30 01:39:43 +0200386 /* If there's no GCR set, we can never leave this state */
387 if (conn->lcls.global_call_ref_len == 0) {
388 LOGPFSML(fi, LOGL_NOTICE, "No GCR set, ignoring %s\n",
389 osmo_fsm_event_name(fi->fsm, event));
390 return;
391 }
392
393 switch (event) {
394 case LCLS_EV_UPDATE_CFG_CSC:
395 if (lcls_handle_cfg_update(conn, data) != 0)
396 return;
Vadim Yanitskiyc53ca912019-04-06 18:54:39 +0700397 break;
Harald Weltec997ceb2018-05-30 01:39:43 +0200398 case LCLS_EV_APPLY_CFG_CSC:
Maxf3bd8382018-12-19 19:47:26 +0100399 if (conn->lcls.config == GSM0808_LCLS_CFG_NA)
Harald Weltec997ceb2018-05-30 01:39:43 +0200400 return;
401 if (lcls_perform_correlation(conn) != 0) {
402 /* Correlation leads to no result: Not Possible to LS */
403 osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0);
404 return;
405 }
406 /* we now have two correlated calls */
407 OSMO_ASSERT(conn->lcls.other);
408 if (lcls_enable_possible(conn)) {
409 /* Local Switching now active */
410 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
411 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn);
412 } else {
413 /* Couldn't be enabled: Not yet LS */
414 osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0);
415 }
416 break;
417 default:
418 OSMO_ASSERT(0);
419 break;
420 }
421}
422
423static void lcls_not_yet_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
424{
425 struct gsm_subscriber_connection *conn = fi->priv;
426
427 /* not yet locally switched means that we have correlation but no instruction
428 * to actually connect them yet */
429 OSMO_ASSERT(conn->lcls.other);
430
431 switch (event) {
432 case LCLS_EV_UPDATE_CFG_CSC:
433 if (lcls_handle_cfg_update(conn, data) != 0)
434 return;
Vadim Yanitskiyc53ca912019-04-06 18:54:39 +0700435 break;
Harald Weltec997ceb2018-05-30 01:39:43 +0200436 case LCLS_EV_APPLY_CFG_CSC:
437 if (lcls_enable_possible(conn)) {
438 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
439 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn);
440 }
441 break;
442 case LCLS_EV_OTHER_ENABLED:
443 OSMO_ASSERT(conn->lcls.other == data);
444 if (lcls_enable_possible(conn)) {
445 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
446 /* Send LCLS-NOTIFY to inform MSC */
447 lcls_send_notify(conn);
448 } else {
449 /* we couldn't enable our side, so ask other side to break */
450 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn);
451 }
452 break;
453 case LCLS_EV_CORRELATED:
454 /* other call informs us that he correlated with us */
455 conn->lcls.other = data;
456 break;
457 case LCLS_EV_OTHER_DEAD:
458 OSMO_ASSERT(conn->lcls.other == data);
459 conn->lcls.other = NULL;
460 osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0);
461 /* Send LCLS-NOTIFY to inform MSC */
462 lcls_send_notify(conn);
463 break;
464 default:
465 OSMO_ASSERT(0);
466 break;
467 }
468}
469
470static void lcls_not_possible_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
471{
472 struct gsm_subscriber_connection *conn = fi->priv;
473
474 OSMO_ASSERT(conn->lcls.other == NULL);
475
476 switch (event) {
477 case LCLS_EV_UPDATE_CFG_CSC:
478 if (lcls_handle_cfg_update(conn, data) != 0)
479 return;
Vadim Yanitskiyc53ca912019-04-06 18:54:39 +0700480 break;
Harald Weltec997ceb2018-05-30 01:39:43 +0200481 case LCLS_EV_APPLY_CFG_CSC:
482 if (lcls_perform_correlation(conn) != 0) {
483 /* no correlation result: Remain in NOT_POSSIBLE_LS */
484 return;
485 }
486 /* we now have two correlated calls */
487 OSMO_ASSERT(conn->lcls.other);
488 if (lcls_enable_possible(conn)) {
489 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
490 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn);
491 } else {
492 osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0);
493 }
494 break;
495 case LCLS_EV_CORRELATED:
496 /* other call informs us that he correlated with us */
497 conn->lcls.other = data;
498 osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0);
499 /* Send NOTIFY about the fact that correlation happened */
500 lcls_send_notify(conn);
501 break;
502 case LCLS_EV_OTHER_DEAD:
503 OSMO_ASSERT(conn->lcls.other == data);
504 conn->lcls.other = NULL;
505 break;
506 default:
507 OSMO_ASSERT(0);
508 break;
509 }
510}
511
512static void lcls_no_longer_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
513{
514 struct gsm_subscriber_connection *conn = fi->priv;
515
516 OSMO_ASSERT(conn->lcls.other);
517
518 switch (event) {
519 case LCLS_EV_UPDATE_CFG_CSC:
520 if (lcls_handle_cfg_update(conn, data) != 0)
521 return;
522 if (lcls_enable_possible(conn)) {
523 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
524 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn);
Keith Whytedcd03032022-10-15 01:40:41 +0100525 } else {
526 osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0);
Harald Weltec997ceb2018-05-30 01:39:43 +0200527 }
528 break;
529 case LCLS_EV_OTHER_ENABLED:
530 OSMO_ASSERT(conn->lcls.other == data);
531 if (lcls_enable_possible(conn)) {
532 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
533 /* Send LCLS-NOTIFY to inform MSC */
534 lcls_send_notify(conn);
535 } else {
536 /* we couldn't enable our side, so ask other side to break */
537 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn);
538 }
539 break;
540 case LCLS_EV_CORRELATED:
541 /* other call informs us that he correlated with us */
542 conn->lcls.other = data;
543 break;
544 case LCLS_EV_OTHER_DEAD:
545 OSMO_ASSERT(conn->lcls.other == data);
546 conn->lcls.other = NULL;
547 osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0);
548 /* Send LCLS-NOTIFY to inform MSC */
549 lcls_send_notify(conn);
550 break;
Keith Whytedcd03032022-10-15 01:40:41 +0100551 case LCLS_EV_APPLY_CFG_CSC:
552 break;
Harald Weltec997ceb2018-05-30 01:39:43 +0200553 default:
554 OSMO_ASSERT(0);
555 break;
556 }
557}
558
559static void lcls_req_lcls_not_supp_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
560{
561 struct gsm_subscriber_connection *conn = fi->priv;
562
563 /* we could have a correlated other call or not */
564
565 switch (event) {
566 case LCLS_EV_UPDATE_CFG_CSC:
567 if (lcls_handle_cfg_update(conn, data) != 0)
568 return;
Pau Espin Pedrol3d0fbe32022-08-08 15:03:41 +0200569 //FIXME osmo_fsm_inst_state_chg(fi,
Harald Weltec997ceb2018-05-30 01:39:43 +0200570 return;
571 case LCLS_EV_APPLY_CFG_CSC:
572 if (lcls_perform_correlation(conn) != 0) {
573 osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0);
574 return;
575 }
576 /* we now have two correlated calls */
577 OSMO_ASSERT(conn->lcls.other);
578 if (!lcls_is_supported_config(conn->lcls.config))
579 return;
580 if (lcls_enable_possible(conn))
581 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0);
582 else
583 osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0);
584 break;
585 case LCLS_EV_CORRELATED:
586 /* other call informs us that he correlated with us */
587 conn->lcls.other = data;
588 break;
589 case LCLS_EV_OTHER_DEAD:
590 OSMO_ASSERT(conn->lcls.other == data);
591 conn->lcls.other = NULL;
592 break;
593 default:
594 OSMO_ASSERT(0);
595 break;
596 }
597
598}
599
600static void lcls_locally_switched_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
601{
602 struct gsm_subscriber_connection *conn = fi->priv;
603
604 OSMO_ASSERT(conn->lcls.other);
605
606 switch (event) {
607 case LCLS_EV_UPDATE_CFG_CSC:
608 if (lcls_handle_cfg_update(conn, data) != 0) {
609 lcls_break_local_switching(conn);
610 return;
611 }
612 break;
613 case LCLS_EV_APPLY_CFG_CSC:
614 if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) {
615 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK, 0, 0);
616 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn);
617 /* FIXME: what if there's a new config included? */
618 return;
619 }
620 /* TODO: Handle any changes of "config" once we support bi-casting etc. */
621 break;
622 case LCLS_EV_OTHER_BREAK:
623 OSMO_ASSERT(conn->lcls.other == data);
624 osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_BREAK, 0, 0);
625 break;
626 case LCLS_EV_OTHER_DEAD:
627 OSMO_ASSERT(conn->lcls.other == data);
628 conn->lcls.other = NULL;
629 osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0);
630 /* Send LCLS-NOTIFY to inform MSC */
631 lcls_send_notify(conn);
632 break;
633 default:
634 OSMO_ASSERT(0);
635 break;
636 }
637}
638
639
640static void lcls_locally_switched_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
641{
642 struct gsm_subscriber_connection *conn = fi->priv;
643 struct gsm_subscriber_connection *conn_other = conn->lcls.other;
Neels Hofmeyr31f525e2018-05-14 18:14:15 +0200644 const struct mgcp_conn_peer *other_mgw_info;
Maxb407a8a2018-11-07 12:56:54 +0100645 struct mgcp_conn_peer mdcx_info;
Harald Weltec997ceb2018-05-30 01:39:43 +0200646
647 OSMO_ASSERT(conn_other);
648
Max49c7d222018-11-07 12:48:04 +0100649 if (!lcls_check_toggle_allowed(conn, true))
650 return;
651
Max6fb500f2018-11-07 12:34:07 +0100652 LOGPFSM(fi, "=== HERE IS WHERE WE ENABLE LCLS(%s)\n",
653 bsc_lcls_mode_name(conn->sccp.msc->lcls_mode));
Max49c7d222018-11-07 12:48:04 +0100654
Neels Hofmeyr31f525e2018-05-14 18:14:15 +0200655 if (!conn_other->user_plane.mgw_endpoint_ci_msc) {
Harald Weltec997ceb2018-05-30 01:39:43 +0200656 LOGPFSML(fi, LOGL_ERROR, "Cannot enable LCLS without MSC-side MGCP FSM. FIXME\n");
657 return;
658 }
659
Neels Hofmeyrf14aaa42019-04-23 18:37:37 +0200660 other_mgw_info = osmo_mgcpc_ep_ci_get_rtp_info(conn_other->user_plane.mgw_endpoint_ci_msc);
Neels Hofmeyr31f525e2018-05-14 18:14:15 +0200661 if (!other_mgw_info) {
662 LOGPFSML(fi, LOGL_ERROR, "Cannot enable LCLS without RTP port info of MSC-side"
663 " -- missing CRCX?\n");
664 return;
665 }
Harald Weltec997ceb2018-05-30 01:39:43 +0200666
Maxb407a8a2018-11-07 12:56:54 +0100667 switch(conn->sccp.msc->lcls_mode) {
668 case BSC_LCLS_MODE_MGW_LOOP:
669 mdcx_info = *other_mgw_info;
Max654bf622018-11-07 12:42:02 +0100670 /* Make sure the request doesn't want to use the other side's endpoint string. */
671 mdcx_info.endpoint[0] = 0;
672 lcls_mdcx(conn, &mdcx_info);
Maxb407a8a2018-11-07 12:56:54 +0100673 break;
674 case BSC_LCLS_MODE_BTS_LOOP:
675 lcls_rsl(conn, true);
676 break;
677 case BSC_LCLS_MODE_DISABLED:
678 LOGPFSM(conn->lcls.fi, "FIXME: attempt to close LCLS loop while LCLS is disabled?!\n");
679 break;
680 default:
681 LOGPFSM(conn->lcls.fi, "FIXME: unknown LCLS mode %s\n",
682 bsc_lcls_mode_name(conn->sccp.msc->lcls_mode));
Max654bf622018-11-07 12:42:02 +0100683 }
Harald Weltec997ceb2018-05-30 01:39:43 +0200684}
685
686static void lcls_locally_switched_wait_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
687{
688 struct gsm_subscriber_connection *conn = fi->priv;
689
690 OSMO_ASSERT(conn->lcls.other);
691
692 switch (event) {
693 case LCLS_EV_UPDATE_CFG_CSC:
694 if (lcls_handle_cfg_update(conn, data) != 0) {
695 lcls_break_local_switching(conn);
696 return;
697 }
698 break;
699 case LCLS_EV_APPLY_CFG_CSC:
700 if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) {
701 lcls_break_local_switching(conn);
702 osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0);
703 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn);
704 /* no NOTIFY here, as the caller will be returning status in LCLS-CTRL-ACK */
705 /* FIXME: what if there's a new config included? */
706 return;
707 }
708 /* TODO: Handle any changes of "config" once we support bi-casting etc. */
709 break;
710 case LCLS_EV_OTHER_BREAK:
711 /* we simply ignore it, must be a re-transmission */
712 break;
713 case LCLS_EV_OTHER_DEAD:
714 OSMO_ASSERT(conn->lcls.other == data);
715 conn->lcls.other = NULL;
716 break;
717 default:
718 lcls_locally_switched_fn(fi, event, data);
719 break;
720 }
721}
722
723static void lcls_locally_switched_wait_other_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data)
724{
725 struct gsm_subscriber_connection *conn = fi->priv;
726
727 OSMO_ASSERT(conn->lcls.other);
728
729 switch (event) {
730 case LCLS_EV_UPDATE_CFG_CSC:
731 if (lcls_handle_cfg_update(conn, data) != 0) {
732 lcls_break_local_switching(conn);
733 return;
734 }
735 /* TODO: Handle any changes of "config" once we support bi-casting etc. */
736 break;
737 case LCLS_EV_OTHER_BREAK:
738 case LCLS_EV_OTHER_DEAD:
739 OSMO_ASSERT(conn->lcls.other == data);
740 lcls_break_local_switching(conn);
741 osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0);
742 /* Send LCLS-NOTIFY to inform MSC */
743 lcls_send_notify(conn);
744 break;
745 default:
746 lcls_locally_switched_fn(fi, event, data);
747 break;
748 }
749}
750
751static void lcls_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
752{
753 struct gsm_subscriber_connection *conn = fi->priv;
754
755 if (conn->lcls.other) {
Martin Haukea29affd2019-11-13 22:10:41 +0100756 /* inform the "other" side that we're dead, so it can disable LS and send NOTIFY */
Harald Weltec997ceb2018-05-30 01:39:43 +0200757 if (conn->lcls.other->fi)
758 osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_DEAD, conn);
759 conn->lcls.other = NULL;
760 }
761}
762
763
764/***********************************************************************
765 * FSM Definition
766 ***********************************************************************/
767
768#define S(x) (1 << (x))
769
770static const struct osmo_fsm_state lcls_fsm_states[] = {
771 [ST_NO_LCLS] = {
772 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
773 S(LCLS_EV_APPLY_CFG_CSC),
774 .out_state_mask = S(ST_NO_LCLS) |
775 S(ST_NOT_YET_LS) |
776 S(ST_NOT_POSSIBLE_LS) |
777 S(ST_REQ_LCLS_NOT_SUPP) |
778 S(ST_LOCALLY_SWITCHED),
779 .name = "NO_LCLS",
780 .action = lcls_no_lcls_fn,
781 },
782 [ST_NOT_YET_LS] = {
783 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
784 S(LCLS_EV_APPLY_CFG_CSC) |
785 S(LCLS_EV_CORRELATED) |
786 S(LCLS_EV_OTHER_ENABLED) |
787 S(LCLS_EV_OTHER_DEAD),
788 .out_state_mask = S(ST_NOT_YET_LS) |
789 S(ST_REQ_LCLS_NOT_SUPP) |
790 S(ST_LOCALLY_SWITCHED),
791 .name = "NOT_YET_LS",
792 .action = lcls_not_yet_ls_fn,
793 },
794 [ST_NOT_POSSIBLE_LS] = {
795 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
796 S(LCLS_EV_APPLY_CFG_CSC) |
797 S(LCLS_EV_CORRELATED),
798 .out_state_mask = S(ST_NOT_YET_LS) |
799 S(ST_NOT_POSSIBLE_LS) |
800 S(ST_REQ_LCLS_NOT_SUPP) |
801 S(ST_LOCALLY_SWITCHED),
802 .name = "NOT_POSSIBLE_LS",
803 .action = lcls_not_possible_ls_fn,
804 },
805 [ST_NO_LONGER_LS] = {
806 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
807 S(LCLS_EV_APPLY_CFG_CSC) |
808 S(LCLS_EV_CORRELATED) |
809 S(LCLS_EV_OTHER_ENABLED) |
810 S(LCLS_EV_OTHER_DEAD),
811 .out_state_mask = S(ST_NO_LONGER_LS) |
812 S(ST_REQ_LCLS_NOT_SUPP) |
813 S(ST_LOCALLY_SWITCHED),
814 .name = "NO_LONGER_LS",
815 .action = lcls_no_longer_ls_fn,
816 },
817 [ST_REQ_LCLS_NOT_SUPP] = {
818 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
819 S(LCLS_EV_APPLY_CFG_CSC) |
820 S(LCLS_EV_CORRELATED) |
821 S(LCLS_EV_OTHER_DEAD),
822 .out_state_mask = S(ST_NOT_YET_LS) |
823 S(ST_REQ_LCLS_NOT_SUPP) |
824 S(ST_LOCALLY_SWITCHED),
825 .name = "REQ_LCLS_NOT_SUPP",
826 .action = lcls_req_lcls_not_supp_fn,
827 },
828 [ST_LOCALLY_SWITCHED] = {
829 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
830 S(LCLS_EV_APPLY_CFG_CSC) |
831 S(LCLS_EV_OTHER_BREAK) |
832 S(LCLS_EV_OTHER_DEAD),
833 .out_state_mask = S(ST_NO_LONGER_LS) |
834 S(ST_NOT_POSSIBLE_LS) |
835 S(ST_REQ_LCLS_NOT_SUPP) |
836 S(ST_LOCALLY_SWITCHED_WAIT_BREAK) |
837 S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK) |
838 S(ST_LOCALLY_SWITCHED),
839 .name = "LOCALLY_SWITCHED",
840 .action = lcls_locally_switched_fn,
841 .onenter = lcls_locally_switched_onenter,
842 },
843 /* received an "other" break, waiting for the local break */
844 [ST_LOCALLY_SWITCHED_WAIT_BREAK] = {
845 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
846 S(LCLS_EV_APPLY_CFG_CSC) |
847 S(LCLS_EV_OTHER_BREAK) |
848 S(LCLS_EV_OTHER_DEAD),
849 .out_state_mask = S(ST_NO_LONGER_LS) |
850 S(ST_REQ_LCLS_NOT_SUPP) |
851 S(ST_LOCALLY_SWITCHED) |
852 S(ST_LOCALLY_SWITCHED_WAIT_BREAK),
853 .name = "LOCALLY_SWITCHED_WAIT_BREAK",
854 .action = lcls_locally_switched_wait_break_fn,
855 },
856 /* received a local break, waiting for the "other" break */
857 [ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK] = {
858 .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) |
859 S(LCLS_EV_OTHER_BREAK) |
860 S(LCLS_EV_OTHER_DEAD),
861 .out_state_mask = S(ST_NO_LONGER_LS) |
862 S(ST_REQ_LCLS_NOT_SUPP) |
863 S(ST_LOCALLY_SWITCHED) |
864 S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK),
865 .name = "LOCALLY_SWITCHED_WAIT_OTHER_BREAK",
866 .action = lcls_locally_switched_wait_other_break_fn,
867 },
868
869
870};
871
872struct osmo_fsm lcls_fsm = {
873 .name = "LCLS",
874 .states = lcls_fsm_states,
875 .num_states = ARRAY_SIZE(lcls_fsm_states),
876 .allstate_event_mask = 0,
877 .allstate_action = NULL,
878 .cleanup = lcls_fsm_cleanup,
879 .timer_cb = NULL,
880 .log_subsys = DLCLS,
881 .event_names = lcls_event_names,
882};