From c997ceb750a67baef1a05590febe1c678b287d8f Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Wed, 30 May 2018 01:39:43 +0200 Subject: [PATCH] Add initial 3GPP LCLS support to OsmoBSC This code contains the following code: * receive/parse/interpret LCLS specific BSSMAP IEs and PDUs * osmo_fsm handling the various states and their transitions * call leg correlation (finding the other subscr_conn with same GCR) * communication between the two call-leg LCLS FSMs * detection of supported / unsupported LCLS configurations * display of GCR / LCLS information in "show conns" * switch the media streams locally using MDCX to the MGW Closes: OS#1602 Change-Id: I614fade62834def5cafc94c4d2578cd747a3f9f7 --- include/osmocom/bsc/Makefile.am | 1 + include/osmocom/bsc/bsc_subscr_conn_fsm.h | 5 + include/osmocom/bsc/debug.h | 1 + include/osmocom/bsc/gsm_data.h | 12 + include/osmocom/bsc/osmo_bsc_lcls.h | 40 ++ src/libbsc/Makefile.am | 1 + src/libbsc/bsc_subscr_conn_fsm.c | 64 +- src/libbsc/bsc_vty.c | 8 + src/libbsc/osmo_bsc_lcls.c | 760 ++++++++++++++++++++++ src/osmo-bsc/osmo_bsc_bssap.c | 84 +++ src/osmo-bsc/osmo_bsc_main.c | 6 + 11 files changed, 981 insertions(+), 1 deletion(-) create mode 100644 include/osmocom/bsc/osmo_bsc_lcls.h create mode 100644 src/libbsc/osmo_bsc_lcls.c diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am index bae13f0cb..0987be9dd 100644 --- a/include/osmocom/bsc/Makefile.am +++ b/include/osmocom/bsc/Makefile.am @@ -47,4 +47,5 @@ noinst_HEADERS = \ vty.h \ bsc_api.h \ penalty_timers.h \ + osmo_bsc_lcls.h \ $(NULL) diff --git a/include/osmocom/bsc/bsc_subscr_conn_fsm.h b/include/osmocom/bsc/bsc_subscr_conn_fsm.h index 9498d9f2e..e8226f443 100644 --- a/include/osmocom/bsc/bsc_subscr_conn_fsm.h +++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h @@ -48,6 +48,8 @@ enum gscon_fsm_event { GSCON_EV_MGW_MDCX_RESP_BTS, /* CRCX response received (MSC) */ GSCON_EV_MGW_CRCX_RESP_MSC, + /* MDCX response received (MSC) - triggered by LCLS */ + GSCON_EV_MGW_MDCX_RESP_MSC, /* Internal handover request (intra-BSC handover) */ GSCON_EV_HO_START, @@ -57,6 +59,9 @@ enum gscon_fsm_event { GSCON_EV_HO_FAIL, /* Handover completed successfully (handover_logic.c) */ GSCON_EV_HO_COMPL, + + /* LCLS child FSM has terminated due to hard failure */ + GSCON_EV_LCLS_FAIL, }; struct gsm_subscriber_connection; diff --git a/include/osmocom/bsc/debug.h b/include/osmocom/bsc/debug.h index 37f102c7a..006b91873 100644 --- a/include/osmocom/bsc/debug.h +++ b/include/osmocom/bsc/debug.h @@ -25,5 +25,6 @@ enum { DCTRL, DFILTER, DPCU, + DLCLS, Debug_LastEntry, }; diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index 1cf79a53d..b1fceb3ea 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -188,6 +188,18 @@ struct gsm_subscriber_connection { enum gsm48_chan_mode chan_mode; } user_plane; + + /* LCLS (local call, local switch) related state */ + struct { + uint8_t global_call_ref[15]; + uint8_t global_call_ref_len; /* length of global_call_ref */ + uint8_t config; /* TS 48.008 3.2.2.116 */ + uint8_t control;/* TS 48.008 3.2.2.117 */ + /* LCLS FSM */ + struct osmo_fsm_inst *fi; + /* pointer to "other" connection, if Call Leg Relocation was successful */ + struct gsm_subscriber_connection *other; + } lcls; }; diff --git a/include/osmocom/bsc/osmo_bsc_lcls.h b/include/osmocom/bsc/osmo_bsc_lcls.h new file mode 100644 index 000000000..2e6023404 --- /dev/null +++ b/include/osmocom/bsc/osmo_bsc_lcls.h @@ -0,0 +1,40 @@ +#pragma once +#include + +enum lcls_fsm_state { + ST_NO_LCLS, + ST_NOT_YET_LS, + ST_NOT_POSSIBLE_LS, + ST_NO_LONGER_LS, + ST_REQ_LCLS_NOT_SUPP, + ST_LOCALLY_SWITCHED, + /* locally switched; received remote break; wait for "local" break */ + ST_LOCALLY_SWITCHED_WAIT_BREAK, + /* locally switched; received break; wait for "other" break */ + ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK, +}; + +enum lcls_event { + /* update LCLS config/control based on some BSSMAP signaling */ + LCLS_EV_UPDATE_CFG_CSC, + /* apply LCLS config/control */ + LCLS_EV_APPLY_CFG_CSC, + /* we have been identified as the correlation peer of another conn */ + LCLS_EV_CORRELATED, + /* "other" LCLS connection has enabled local switching */ + LCLS_EV_OTHER_ENABLED, + /* "other" LCLS connection is breaking local switch */ + LCLS_EV_OTHER_BREAK, + /* "other" LCLS connection is dying */ + LCLS_EV_OTHER_DEAD, +}; + +enum gsm0808_lcls_status lcls_get_status(struct gsm_subscriber_connection *conn); + +void lcls_update_config(struct gsm_subscriber_connection *conn, + const uint8_t *config, const uint8_t *control); + +void lcls_apply_config(struct gsm_subscriber_connection *conn); + +extern struct osmo_fsm lcls_fsm; + diff --git a/src/libbsc/Makefile.am b/src/libbsc/Makefile.am index d215e14f9..2e447292f 100644 --- a/src/libbsc/Makefile.am +++ b/src/libbsc/Makefile.am @@ -65,5 +65,6 @@ libbsc_a_SOURCES = \ handover_decision_2.c \ bsc_subscr_conn_fsm.c \ meas_feed.c \ + osmo_bsc_lcls.c \ $(NULL) diff --git a/src/libbsc/bsc_subscr_conn_fsm.c b/src/libbsc/bsc_subscr_conn_fsm.c index 89ac48234..bafe14589 100644 --- a/src/libbsc/bsc_subscr_conn_fsm.c +++ b/src/libbsc/bsc_subscr_conn_fsm.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,7 @@ static const struct value_string gscon_fsm_event_names[] = { {GSCON_EV_MGW_CRCX_RESP_BTS, "MGW_CRCX_RESPONSE_BTS"}, {GSCON_EV_MGW_MDCX_RESP_BTS, "MGW_MDCX_RESPONSE_BTS"}, {GSCON_EV_MGW_CRCX_RESP_MSC, "MGW_CRCX_RESPONSE_MSC"}, + {GSCON_EV_MGW_MDCX_RESP_MSC, "MGW_MDCX_RESPONSE_MSC"}, {GSCON_EV_HO_START, "HO_START"}, {GSCON_EV_HO_TIMEOUT, "HO_TIMEOUT"}, @@ -223,6 +225,37 @@ static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) return channel_mode << 4 | channel; } +/* Add the LCLS BSS Status IE to a BSSMAP message. We assume this is + * called on a msgb that was returned by gsm0808_create_ass_compl() */ +static void bssmap_add_lcls_status(struct msgb *msg, enum gsm0808_lcls_status status) +{ + OSMO_ASSERT(msg->l3h[0] == BSSAP_MSG_BSS_MANAGEMENT); + OSMO_ASSERT(msg->l3h[2] == BSS_MAP_MSG_ASSIGMENT_COMPLETE || + msg->l3h[2] == BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE || + msg->l3h[2] == BSS_MAP_MSG_HANDOVER_COMPLETE || + msg->l3h[2] == BSS_MAP_MSG_HANDOVER_PERFORMED); + OSMO_ASSERT(msgb_tailroom(msg) >= 2); + + /* append IE to end of message */ + msgb_tv_put(msg, GSM0808_IE_LCLS_BSS_STATUS, status); + /* increment the "length" byte in the BSSAP header */ + msg->l3h[1] += 2; +} + +/* Add (append) the LCLS BSS Status IE to a BSSMAP message, if there is any LCLS + * active on the given \a conn */ +static void bssmap_add_lcls_status_if_needed(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + enum gsm0808_lcls_status status = lcls_get_status(conn); + if (status != 0xff) { + LOGPFSM(conn->fi, "Adding LCLS BSS-Status (%s) to %s\n", + gsm0808_lcls_status_name(status), + gsm0808_bssmap_name(msg->l3h[2])); + bssmap_add_lcls_status(msg, status); + } +} + /* Generate and send assignment complete message */ static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi, bool voice) { @@ -236,6 +269,9 @@ static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi, bo conn = lchan->conn; OSMO_ASSERT(conn); + /* apply LCLS configuration (if any) */ + lcls_apply_config(conn); + LOGPFSML(fi, LOGL_DEBUG, "Sending assignment complete message... (id=%i)\n", conn->sccp.conn_id); /* Generate voice related fields */ @@ -268,6 +304,9 @@ static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi, bo conn->sccp.conn_id); } + /* Add LCLS BSS-Status IE in case there is any LCLS status for this connection */ + bssmap_add_lcls_status_if_needed(conn, resp); + sigtran_send(conn, resp, fi); } @@ -997,6 +1036,11 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); sigtran_send(conn, resp, fi); break; + case GSCON_EV_MGW_MDCX_RESP_MSC: + LOGPFSML(fi, LOGL_DEBUG, "Rx MDCX of MSC side (LCLS?)\n"); + break; + case GSCON_EV_LCLS_FAIL: + break; default: OSMO_ASSERT(false); break; @@ -1056,6 +1100,12 @@ static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause ca /* Make sure all possibly still open MGCP connections get closed */ toss_mgcp_conn(conn, fi); + + if (conn->lcls.fi) { + /* request termination of LCLS FSM */ + osmo_fsm_inst_term(conn->lcls.fi, cause, NULL); + conn->lcls.fi = NULL; + } } static int gscon_timer_cb(struct osmo_fsm_inst *fi) @@ -1100,7 +1150,8 @@ static struct osmo_fsm gscon_fsm = { .states = gscon_fsm_states, .num_states = ARRAY_SIZE(gscon_fsm_states), .allstate_event_mask = S(GSCON_EV_A_DISC_IND) | S(GSCON_EV_A_CLEAR_CMD) | S(GSCON_EV_RSL_CONN_FAIL) | - S(GSCON_EV_RLL_REL_IND) | S(GSCON_EV_MGW_FAIL_BTS) | S(GSCON_EV_MGW_FAIL_MSC), + S(GSCON_EV_RLL_REL_IND) | S(GSCON_EV_MGW_FAIL_BTS) | S(GSCON_EV_MGW_FAIL_MSC) | + S(GSCON_EV_MGW_MDCX_RESP_MSC) | S(GSCON_EV_LCLS_FAIL), .allstate_action = gscon_fsm_allstate, .cleanup = gscon_cleanup, .pre_term = gscon_pre_term, @@ -1117,6 +1168,7 @@ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *ne if (!g_initialized) { osmo_fsm_register(&gscon_fsm); + osmo_fsm_register(&lcls_fsm); g_initialized = true; } @@ -1137,6 +1189,16 @@ struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *ne return NULL; } + /* initialize to some magic values that indicate "IE not [yet] received" */ + conn->lcls.config = 0xff; + conn->lcls.control = 0xff; + conn->lcls.fi = osmo_fsm_inst_alloc_child(&lcls_fsm, conn->fi, GSCON_EV_LCLS_FAIL); + if (!conn->lcls.fi) { + osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_ERROR, NULL); + return NULL; + } + conn->lcls.fi->priv = conn; + llist_add_tail(&conn->entry, &net->subscr_conns); return conn; } diff --git a/src/libbsc/bsc_vty.c b/src/libbsc/bsc_vty.c index 6c2257dec..757a8a1cf 100644 --- a/src/libbsc/bsc_vty.c +++ b/src/libbsc/bsc_vty.c @@ -1521,6 +1521,14 @@ static void dump_one_subscr_conn(struct vty *vty, const struct gsm_subscriber_co conn->sccp.conn_id, conn->sccp.msc->nr, conn->hodec2.failures, get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode), conn->user_plane.mgw_endpoint, VTY_NEWLINE); + if (conn->lcls.global_call_ref_len) { + vty_out(vty, " LCLS GCR: %s%s", + osmo_hexdump_nospc(conn->lcls.global_call_ref, conn->lcls.global_call_ref_len), + VTY_NEWLINE); + vty_out(vty, " LCLS Config: 0x%02x, LCLS Control: 0x%02x, LCLS BSS Status: %s%s", + conn->lcls.config, conn->lcls.control, osmo_fsm_inst_state_name(conn->lcls.fi), + VTY_NEWLINE); + } if (conn->lchan) lchan_dump_full_vty(vty, conn->lchan); if (conn->secondary_lchan) diff --git a/src/libbsc/osmo_bsc_lcls.c b/src/libbsc/osmo_bsc_lcls.c new file mode 100644 index 000000000..e32376d43 --- /dev/null +++ b/src/libbsc/osmo_bsc_lcls.c @@ -0,0 +1,760 @@ +/* (C) 2018 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct value_string lcls_event_names[] = { + { LCLS_EV_UPDATE_CFG_CSC, "UPDATE_CFG_CSC" }, + { LCLS_EV_APPLY_CFG_CSC, "APPLY_CFG_CSC" }, + { LCLS_EV_CORRELATED, "CORRELATED" }, + { LCLS_EV_OTHER_ENABLED, "OTHER_ENABLED" }, + { LCLS_EV_OTHER_BREAK, "OTHER_BREAK" }, + { LCLS_EV_OTHER_DEAD, "OTHER_DEAD" }, + { 0, NULL } +}; + + +/*********************************************************************** + * Utility functions + ***********************************************************************/ + +enum gsm0808_lcls_status lcls_get_status(struct gsm_subscriber_connection *conn) +{ + if (!conn->lcls.fi) + return 0xff; + + switch (conn->lcls.fi->state) { + case ST_NO_LCLS: + return 0xff; + case ST_NOT_YET_LS: + return GSM0808_LCLS_STS_NOT_YET_LS; + case ST_NOT_POSSIBLE_LS: + return GSM0808_LCLS_STS_NOT_POSSIBLE_LS; + case ST_NO_LONGER_LS: + return GSM0808_LCLS_STS_NO_LONGER_LS; + case ST_REQ_LCLS_NOT_SUPP: + return GSM0808_LCLS_STS_REQ_LCLS_NOT_SUPP; + case ST_LOCALLY_SWITCHED: + case ST_LOCALLY_SWITCHED_WAIT_BREAK: + case ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK: + return GSM0808_LCLS_STS_LOCALLY_SWITCHED; + } + OSMO_ASSERT(0); +} + +static void lcls_send_notify(struct gsm_subscriber_connection *conn) +{ + enum gsm0808_lcls_status status = lcls_get_status(conn); + struct msgb *msg; + + if (status == 0xff) + return; + + LOGPFSM(conn->lcls.fi, "Sending BSSMAP LCLS NOTIFICATION (%s)\n", + gsm0808_lcls_status_name(status)); + msg = gsm0808_create_lcls_notification(status, false); + osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, msg); +} + +static struct gsm_subscriber_connection * +find_conn_with_same_gcr(struct gsm_subscriber_connection *conn_local) +{ + struct gsm_network *net = conn_local->network; + struct gsm_subscriber_connection *conn_other; + + llist_for_each_entry(conn_other, &net->subscr_conns, entry) { + /* don't report back the same connection */ + if (conn_other == conn_local) + continue; + /* don't consider any conn where GCR length is not the same as before */ + if (conn_other->lcls.global_call_ref_len != conn_local->lcls.global_call_ref_len) + continue; + if (!memcmp(conn_other->lcls.global_call_ref, conn_local->lcls.global_call_ref, + conn_local->lcls.global_call_ref_len)) + return conn_other; + } + return NULL; +} + +static bool lcls_is_supported_config(enum gsm0808_lcls_config cfg) +{ + /* this is the only configuration that we support for now */ + if (cfg == GSM0808_LCLS_CFG_BOTH_WAY) + return true; + else + return false; +} + +/* LCLS Call Leg Correlation as per 23.284 4.3 / 48.008 3.1.33.2.1 */ +static int lcls_perform_correlation(struct gsm_subscriber_connection *conn_local) +{ + struct gsm_subscriber_connection *conn_other; + + /* We can only correlate if a GCR is present */ + OSMO_ASSERT(conn_local->lcls.global_call_ref_len); + /* We can only correlate if we're not in active LS */ + OSMO_ASSERT(conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED && + conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK && + conn_local->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK); + + conn_other = conn_local->lcls.other; + if (conn_other) { + LOGPFSM(conn_local->lcls.fi, "Breaking previous correlation with %s\n", + osmo_fsm_inst_name(conn_other->lcls.fi)); + OSMO_ASSERT(conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED && + conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_BREAK && + conn_other->lcls.fi->state != ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK); + conn_local->lcls.other->lcls.other = NULL; + conn_local->lcls.other = NULL; + } + + conn_other = find_conn_with_same_gcr(conn_local); + if (!conn_other) { + /* we found no other call with same GCR: not possible */ + LOGPFSM(conn_local->lcls.fi, "Unsuccessful correlation\n"); + return -ENODEV; + } + + /* store pointer to "other" in "local" */ + conn_local->lcls.other = conn_other; + + LOGPFSM(conn_local->lcls.fi, "Successfully correlated with %s\n", + osmo_fsm_inst_name(conn_other->lcls.fi)); + + /* notify other conn about our correlation */ + osmo_fsm_inst_dispatch(conn_other->lcls.fi, LCLS_EV_CORRELATED, conn_local); + + return 0; +} + + +struct lcls_cfg_csc { + enum gsm0808_lcls_config config; + enum gsm0808_lcls_control control; +}; + +/* Update the connections LCLS configuration and return old/previous configuration. + * \returns (staticallly allocated) old configuration; NULL if new config not supported */ +static struct lcls_cfg_csc *update_lcls_cfg_csc(struct gsm_subscriber_connection *conn, + struct lcls_cfg_csc *new_cfg_csc) +{ + static struct lcls_cfg_csc old_cfg_csc; + old_cfg_csc.config = conn->lcls.config; + old_cfg_csc.control = conn->lcls.control; + + if (new_cfg_csc->config != 0xff) { + if (!lcls_is_supported_config(new_cfg_csc->config)) + return NULL; + if (conn->lcls.config != new_cfg_csc->config) { + /* TODO: logging */ + conn->lcls.config = new_cfg_csc->config; + } + } + if (new_cfg_csc->control != 0xff) { + if (conn->lcls.control != new_cfg_csc->control) { + /* TODO: logging */ + conn->lcls.control = new_cfg_csc->control; + } + } + + return &old_cfg_csc; +} + +/* Attempt to update conn->lcls with the new config/csc provided. If new config is + * unsupported, change into LCLS NOT SUPPORTED state and return -EINVAL. */ +static int lcls_handle_cfg_update(struct gsm_subscriber_connection *conn, void *data) +{ + struct lcls_cfg_csc *new_cfg_csc, *old_cfg_csc; + + new_cfg_csc = (struct lcls_cfg_csc *) data; + old_cfg_csc = update_lcls_cfg_csc(conn, new_cfg_csc); + if (!old_cfg_csc) { + osmo_fsm_inst_state_chg(conn->lcls.fi, ST_REQ_LCLS_NOT_SUPP, 0, 0); + return -EINVAL; + } + return 0; +} + +/* notify the LCLS FSM about new LCLS Config and/or CSC */ +void lcls_update_config(struct gsm_subscriber_connection *conn, + const uint8_t *config, const uint8_t *control) +{ + struct lcls_cfg_csc new_cfg = { + .config = 0xff, + .control = 0xff, + }; + /* nothing to update, skip it */ + if (!config && !control) + return; + if (config) + new_cfg.config = *config; + if (control) + new_cfg.control = *control; + osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_UPDATE_CFG_CSC, &new_cfg); +} + +/* apply the configuration, may be changed before by lcls_update_config */ +void lcls_apply_config(struct gsm_subscriber_connection *conn) +{ + osmo_fsm_inst_dispatch(conn->lcls.fi, LCLS_EV_APPLY_CFG_CSC, NULL); +} + +static void lcls_break_local_switching(struct gsm_subscriber_connection *conn) +{ + struct mgcp_conn_peer peer; + struct sockaddr_in *sin; + + LOGPFSM(conn->lcls.fi, "=== HERE IS WHERE WE DISABLE LCLS\n"); + if (!conn->user_plane.fi_msc) { + /* the MGCP FSM has died, e.g. due to some MGCP/SDP parsing error */ + LOGPFSML(conn->lcls.fi, LOGL_NOTICE, "Cannot disable LCLS without MSC-side MGCP FSM\n"); + return; + } + + sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote; + OSMO_ASSERT(sin->sin_family == AF_INET); + + memset(&peer, 0, sizeof(peer)); + peer.port = htons(sin->sin_port); + osmo_strlcpy(peer.addr, inet_ntoa(sin->sin_addr), sizeof(peer.addr)); + mgcp_conn_modify(conn->user_plane.fi_msc, 0, &peer); +} + +static bool lcls_enable_possible(struct gsm_subscriber_connection *conn) +{ + struct gsm_subscriber_connection *other_conn = conn->lcls.other; + OSMO_ASSERT(other_conn); + + if (!lcls_is_supported_config(conn->lcls.config)) { + LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported local config\n"); + return false; + } + + if (!lcls_is_supported_config(other_conn->lcls.config)) { + LOGPFSM(conn->lcls.fi, "Not enabling LS due to unsupported other config\n"); + return false; + } + + if (conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) { + LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient local control\n"); + return false; + } + + if (other_conn->lcls.control != GSM0808_LCLS_CSC_CONNECT) { + LOGPFSM(conn->lcls.fi, "Not enabling LS due to insufficient other control\n"); + return false; + } + + return true; +} + +/*********************************************************************** + * State callback functions + ***********************************************************************/ + +static void lcls_no_lcls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + /* we're just starting and cannot yet have a correlated call */ + OSMO_ASSERT(conn->lcls.other == NULL); + + /* If there's no GCR set, we can never leave this state */ + if (conn->lcls.global_call_ref_len == 0) { + LOGPFSML(fi, LOGL_NOTICE, "No GCR set, ignoring %s\n", + osmo_fsm_event_name(fi->fsm, event)); + return; + } + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) + return; + return; + case LCLS_EV_APPLY_CFG_CSC: + if (conn->lcls.config == 0xff) + return; + if (lcls_perform_correlation(conn) != 0) { + /* Correlation leads to no result: Not Possible to LS */ + osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); + return; + } + /* we now have two correlated calls */ + OSMO_ASSERT(conn->lcls.other); + if (lcls_enable_possible(conn)) { + /* Local Switching now active */ + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); + } else { + /* Couldn't be enabled: Not yet LS */ + osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); + } + break; + default: + OSMO_ASSERT(0); + break; + } +} + +static void lcls_not_yet_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + /* not yet locally switched means that we have correlation but no instruction + * to actually connect them yet */ + OSMO_ASSERT(conn->lcls.other); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) + return; + return; + case LCLS_EV_APPLY_CFG_CSC: + if (lcls_enable_possible(conn)) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); + } + break; + case LCLS_EV_OTHER_ENABLED: + OSMO_ASSERT(conn->lcls.other == data); + if (lcls_enable_possible(conn)) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + } else { + /* we couldn't enable our side, so ask other side to break */ + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); + } + break; + case LCLS_EV_CORRELATED: + /* other call informs us that he correlated with us */ + conn->lcls.other = data; + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + break; + default: + OSMO_ASSERT(0); + break; + } +} + +static void lcls_not_possible_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + OSMO_ASSERT(conn->lcls.other == NULL); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) + return; + return; + case LCLS_EV_APPLY_CFG_CSC: + if (lcls_perform_correlation(conn) != 0) { + /* no correlation result: Remain in NOT_POSSIBLE_LS */ + return; + } + /* we now have two correlated calls */ + OSMO_ASSERT(conn->lcls.other); + if (lcls_enable_possible(conn)) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); + } else { + osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); + } + break; + case LCLS_EV_CORRELATED: + /* other call informs us that he correlated with us */ + conn->lcls.other = data; + osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); + /* Send NOTIFY about the fact that correlation happened */ + lcls_send_notify(conn); + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + break; + default: + OSMO_ASSERT(0); + break; + } +} + +static void lcls_no_longer_ls_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + OSMO_ASSERT(conn->lcls.other); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) + return; + if (lcls_enable_possible(conn)) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_ENABLED, conn); + } + break; + case LCLS_EV_OTHER_ENABLED: + OSMO_ASSERT(conn->lcls.other == data); + if (lcls_enable_possible(conn)) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + } else { + /* we couldn't enable our side, so ask other side to break */ + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); + } + break; + case LCLS_EV_CORRELATED: + /* other call informs us that he correlated with us */ + conn->lcls.other = data; + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + break; + default: + OSMO_ASSERT(0); + break; + } +} + +static void lcls_req_lcls_not_supp_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + /* we could have a correlated other call or not */ + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) + return; + //FIXME osmo_fsm_inst_state_chg(fi, + return; + case LCLS_EV_APPLY_CFG_CSC: + if (lcls_perform_correlation(conn) != 0) { + osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); + return; + } + /* we now have two correlated calls */ + OSMO_ASSERT(conn->lcls.other); + if (!lcls_is_supported_config(conn->lcls.config)) + return; + if (lcls_enable_possible(conn)) + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED, 0, 0); + else + osmo_fsm_inst_state_chg(fi, ST_NOT_YET_LS, 0, 0); + break; + case LCLS_EV_CORRELATED: + /* other call informs us that he correlated with us */ + conn->lcls.other = data; + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + break; + default: + OSMO_ASSERT(0); + break; + } + +} + +static void lcls_locally_switched_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + OSMO_ASSERT(conn->lcls.other); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) { + lcls_break_local_switching(conn); + return; + } + break; + case LCLS_EV_APPLY_CFG_CSC: + if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) { + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); + /* FIXME: what if there's a new config included? */ + return; + } + /* TODO: Handle any changes of "config" once we support bi-casting etc. */ + break; + case LCLS_EV_OTHER_BREAK: + OSMO_ASSERT(conn->lcls.other == data); + osmo_fsm_inst_state_chg(fi, ST_LOCALLY_SWITCHED_WAIT_BREAK, 0, 0); + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + osmo_fsm_inst_state_chg(fi, ST_NOT_POSSIBLE_LS, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + break; + default: + OSMO_ASSERT(0); + break; + } +} + + +static void lcls_locally_switched_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct gsm_subscriber_connection *conn = fi->priv; + struct gsm_subscriber_connection *conn_other = conn->lcls.other; + struct mgcp_conn_peer peer; + struct sockaddr_in *sin; + + OSMO_ASSERT(conn_other); + + LOGPFSM(fi, "=== HERE IS WHERE WE ENABLE LCLS\n"); + if (!conn->user_plane.fi_msc) { + LOGPFSML(fi, LOGL_ERROR, "Cannot enable LCLS without MSC-side MGCP FSM. FIXME\n"); + return; + } + + sin = (struct sockaddr_in *)&conn_other->user_plane.aoip_rtp_addr_local; + OSMO_ASSERT(sin->sin_family == AF_INET); + + memset(&peer, 0, sizeof(peer)); + peer.port = htons(sin->sin_port); + osmo_strlcpy(peer.addr, inet_ntoa(sin->sin_addr), sizeof(peer.addr)); + mgcp_conn_modify(conn->user_plane.fi_msc, 0, &peer); + +} + +static void lcls_locally_switched_wait_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + OSMO_ASSERT(conn->lcls.other); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) { + lcls_break_local_switching(conn); + return; + } + break; + case LCLS_EV_APPLY_CFG_CSC: + if (conn->lcls.control == GSM0808_LCLS_CSC_RELEASE_LCLS) { + lcls_break_local_switching(conn); + osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0); + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_BREAK, conn); + /* no NOTIFY here, as the caller will be returning status in LCLS-CTRL-ACK */ + /* FIXME: what if there's a new config included? */ + return; + } + /* TODO: Handle any changes of "config" once we support bi-casting etc. */ + break; + case LCLS_EV_OTHER_BREAK: + /* we simply ignore it, must be a re-transmission */ + break; + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + conn->lcls.other = NULL; + break; + default: + lcls_locally_switched_fn(fi, event, data); + break; + } +} + +static void lcls_locally_switched_wait_other_break_fn(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + OSMO_ASSERT(conn->lcls.other); + + switch (event) { + case LCLS_EV_UPDATE_CFG_CSC: + if (lcls_handle_cfg_update(conn, data) != 0) { + lcls_break_local_switching(conn); + return; + } + /* TODO: Handle any changes of "config" once we support bi-casting etc. */ + break; + case LCLS_EV_OTHER_BREAK: + case LCLS_EV_OTHER_DEAD: + OSMO_ASSERT(conn->lcls.other == data); + lcls_break_local_switching(conn); + osmo_fsm_inst_state_chg(fi, ST_NO_LONGER_LS, 0, 0); + /* Send LCLS-NOTIFY to inform MSC */ + lcls_send_notify(conn); + break; + default: + lcls_locally_switched_fn(fi, event, data); + break; + } +} + +static void lcls_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause) +{ + struct gsm_subscriber_connection *conn = fi->priv; + + if (conn->lcls.other) { + /* inform the "other" side that we're dead, so it can disabe LS and send NOTIFY */ + if (conn->lcls.other->fi) + osmo_fsm_inst_dispatch(conn->lcls.other->lcls.fi, LCLS_EV_OTHER_DEAD, conn); + conn->lcls.other = NULL; + } +} + + +/*********************************************************************** + * FSM Definition + ***********************************************************************/ + +#define S(x) (1 << (x)) + +static const struct osmo_fsm_state lcls_fsm_states[] = { + [ST_NO_LCLS] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC), + .out_state_mask = S(ST_NO_LCLS) | + S(ST_NOT_YET_LS) | + S(ST_NOT_POSSIBLE_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED), + .name = "NO_LCLS", + .action = lcls_no_lcls_fn, + }, + [ST_NOT_YET_LS] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_CORRELATED) | + S(LCLS_EV_OTHER_ENABLED) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NOT_YET_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED), + .name = "NOT_YET_LS", + .action = lcls_not_yet_ls_fn, + }, + [ST_NOT_POSSIBLE_LS] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_CORRELATED), + .out_state_mask = S(ST_NOT_YET_LS) | + S(ST_NOT_POSSIBLE_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED), + .name = "NOT_POSSIBLE_LS", + .action = lcls_not_possible_ls_fn, + }, + [ST_NO_LONGER_LS] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_CORRELATED) | + S(LCLS_EV_OTHER_ENABLED) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NO_LONGER_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED), + .name = "NO_LONGER_LS", + .action = lcls_no_longer_ls_fn, + }, + [ST_REQ_LCLS_NOT_SUPP] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_CORRELATED) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NOT_YET_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED), + .name = "REQ_LCLS_NOT_SUPP", + .action = lcls_req_lcls_not_supp_fn, + }, + [ST_LOCALLY_SWITCHED] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_OTHER_BREAK) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NO_LONGER_LS) | + S(ST_NOT_POSSIBLE_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED_WAIT_BREAK) | + S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK) | + S(ST_LOCALLY_SWITCHED), + .name = "LOCALLY_SWITCHED", + .action = lcls_locally_switched_fn, + .onenter = lcls_locally_switched_onenter, + }, + /* received an "other" break, waiting for the local break */ + [ST_LOCALLY_SWITCHED_WAIT_BREAK] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_APPLY_CFG_CSC) | + S(LCLS_EV_OTHER_BREAK) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NO_LONGER_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED) | + S(ST_LOCALLY_SWITCHED_WAIT_BREAK), + .name = "LOCALLY_SWITCHED_WAIT_BREAK", + .action = lcls_locally_switched_wait_break_fn, + }, + /* received a local break, waiting for the "other" break */ + [ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK] = { + .in_event_mask = S(LCLS_EV_UPDATE_CFG_CSC) | + S(LCLS_EV_OTHER_BREAK) | + S(LCLS_EV_OTHER_DEAD), + .out_state_mask = S(ST_NO_LONGER_LS) | + S(ST_REQ_LCLS_NOT_SUPP) | + S(ST_LOCALLY_SWITCHED) | + S(ST_LOCALLY_SWITCHED_WAIT_OTHER_BREAK), + .name = "LOCALLY_SWITCHED_WAIT_OTHER_BREAK", + .action = lcls_locally_switched_wait_other_break_fn, + }, + + +}; + +struct osmo_fsm lcls_fsm = { + .name = "LCLS", + .states = lcls_fsm_states, + .num_states = ARRAY_SIZE(lcls_fsm_states), + .allstate_event_mask = 0, + .allstate_action = NULL, + .cleanup = lcls_fsm_cleanup, + .timer_cb = NULL, + .log_subsys = DLCLS, + .event_names = lcls_event_names, +}; diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index f7f99fa30..24a5e3a3e 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -34,8 +34,10 @@ #include #include #include +#include #include #include +#include #define IP_V4_ADDR_LEN 4 @@ -634,6 +636,83 @@ reject: return -1; } +/* handle LCLS specific IES in BSSMAP ASS REQ */ +static void bssmap_handle_ass_req_lcls(struct gsm_subscriber_connection *conn, + const struct tlv_parsed *tp) +{ + const struct tlv_p_entry *tlv; + const uint8_t *config, *control; + + tlv = TLVP_GET(tp, GSM0808_IE_GLOBAL_CALL_REF); + if (tlv) { + if (tlv->len > sizeof(conn->lcls.global_call_ref)) + LOGPFSML(conn->fi, LOGL_ERROR, "Global Call Ref IE of %u bytes is too long\n", + tlv->len); + else { + LOGPFSM(conn->fi, "Setting GCR to %s\n", osmo_hexdump_nospc(tlv->val, tlv->len)); + memcpy(&conn->lcls.global_call_ref, tlv->val, tlv->len); + conn->lcls.global_call_ref_len = tlv->len; + } + } + + config = TLVP_VAL_MINLEN(tp, GSM0808_IE_LCLS_CONFIG, 1); + control = TLVP_VAL_MINLEN(tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1); + + if (config || control) { + LOGPFSM(conn->fi, "BSSMAP ASS REQ contains LCLS (%s / %s)\n", + config ? gsm0808_lcls_config_name(*config) : "NULL", + control ? gsm0808_lcls_control_name(*control) : "NULL"); + } + + /* Update the LCLS state with Config + CSC (if any) */ + lcls_update_config(conn, config, control); + + /* Do not attempt to perform correlation yet, as during processing of the ASS REQ + * we don't have the MGCP/MGW connections yet, and hence couldn't enable LS. */ +} + +/* TS 48.008 3.2.1.91 */ +static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *conn, + struct msgb *msg, unsigned int length) +{ + struct msgb *resp; + struct tlv_parsed tp; + const uint8_t *config, *control; + int rc; + + OSMO_ASSERT(conn); + + rc = tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); + if (rc < 0) { + LOGPFSML(conn->fi, LOGL_ERROR, "Error parsing TLVs of LCLS CONNT CTRL: %s\n", + msgb_hexdump(msg)); + return rc; + } + config = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONFIG, 1); + control = TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1); + + LOGPFSM(conn->fi, "Rx LCLS CONNECT CTRL (%s / %s)\n", + config ? gsm0808_lcls_config_name(*config) : "NULL", + control ? gsm0808_lcls_control_name(*control) : "NULL"); + + if (conn->lcls.global_call_ref_len == 0) { + LOGPFSML(conn->fi, LOGL_ERROR, "Ignoring LCLS as no GCR was set before\n"); + return 0; + } + /* Update the LCLS state with Config + CSC (if any) */ + lcls_update_config(conn, TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONFIG, 1), + TLVP_VAL_MINLEN(&tp, GSM0808_IE_LCLS_CONN_STATUS_CTRL, 1)); + lcls_apply_config(conn); + + LOGPFSM(conn->fi, "Tx LCLS CONNECT CTRL ACK (%s)\n", + gsm0808_lcls_status_name(lcls_get_status(conn))); + resp = gsm0808_create_lcls_conn_ctrl_ack(lcls_get_status(conn)); + osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp); + + return 0; +} + + /* * Handle the assignment request message. * @@ -682,6 +761,8 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, goto reject; } + bssmap_handle_ass_req_lcls(conn, &tp); + /* Currently we only support a limited subset of all * possible channel types, such as multi-slot or CSD */ switch (ct.ch_indctr) { @@ -852,6 +933,9 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, case BSS_MAP_MSG_ASSIGMENT_RQST: ret = bssmap_handle_assignm_req(conn, msg, length); break; + case BSS_MAP_MSG_LCLS_CONNECT_CTRL: + ret = bssmap_handle_lcls_connect_ctrl(conn, msg, length); + break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l4h[0])); diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c index 095a07a16..fefc04180 100644 --- a/src/osmo-bsc/osmo_bsc_main.c +++ b/src/osmo-bsc/osmo_bsc_main.c @@ -369,6 +369,12 @@ static const struct log_info_cat osmo_bsc_categories[] = { .description = "PCU Interface", .enabled = 1, .loglevel = LOGL_DEBUG, }, + [DLCLS] = { + .name = "DLCLS", + .description = "Local Call, Local Switch", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + }; static int filter_fn(const struct log_context *ctx, struct log_target *tar)