aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/bsc/Makefile.am2
-rw-r--r--include/osmocom/bsc/bsc_api.h3
-rw-r--r--include/osmocom/bsc/bsc_subscr_conn_fsm.h74
-rw-r--r--include/osmocom/bsc/gsm_data.h33
-rw-r--r--include/osmocom/bsc/handover.h20
-rw-r--r--include/osmocom/bsc/osmo_bsc.h2
-rw-r--r--include/osmocom/bsc/osmo_bsc_mgcp.h62
-rw-r--r--include/osmocom/bsc/osmo_bsc_sigtran.h5
-rw-r--r--src/ipaccess/Makefile.am1
-rw-r--r--src/libbsc/Makefile.am2
-rw-r--r--src/libbsc/abis_rsl.c19
-rw-r--r--src/libbsc/bsc_api.c110
-rw-r--r--src/libbsc/bsc_subscr_conn_fsm.c1056
-rw-r--r--src/libbsc/handover_decision_2.c6
-rw-r--r--src/libbsc/handover_logic.c170
-rw-r--r--src/osmo-bsc/Makefile.am1
-rw-r--r--src/osmo-bsc/osmo_bsc_api.c115
-rw-r--r--src/osmo-bsc/osmo_bsc_audio.c18
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c280
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c5
-rw-r--r--src/osmo-bsc/osmo_bsc_mgcp.c1149
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c107
-rw-r--r--src/utils/Makefile.am1
-rw-r--r--tests/abis/abis_test.c4
-rw-r--r--tests/bsc-nat/bsc_nat_test.c2
-rw-r--r--tests/bsc/bsc_test.c4
-rw-r--r--tests/bssap/Makefile.am1
-rw-r--r--tests/bssap/bssap_test.c4
-rw-r--r--tests/channel/channel_test.c1
-rw-r--r--tests/gsm0408/gsm0408_test.c4
-rw-r--r--tests/handover/Makefile.am6
-rw-r--r--tests/handover/handover_test.c78
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c4
33 files changed, 1626 insertions, 1723 deletions
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 17e8bd313..b25dfd833 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -12,6 +12,7 @@ noinst_HEADERS = \
bsc_nat_sccp.h \
bsc_rll.h \
bsc_subscriber.h \
+ bsc_subscr_conn_fsm.h \
bss.h \
bts_ipaccess_nanobts_omlattr.h \
chan_alloc.h \
@@ -36,7 +37,6 @@ noinst_HEADERS = \
openbscdefines.h \
osmo_bsc.h \
osmo_bsc_grace.h \
- osmo_bsc_mgcp.h \
osmo_bsc_rf.h \
osmo_bsc_sigtran.h \
bsc_msc_data.h \
diff --git a/include/osmocom/bsc/bsc_api.h b/include/osmocom/bsc/bsc_api.h
index 6ee05629f..01f90b158 100644
--- a/include/osmocom/bsc/bsc_api.h
+++ b/include/osmocom/bsc/bsc_api.h
@@ -46,6 +46,9 @@ struct bsc_api {
void (*conn_cleanup)(struct gsm_subscriber_connection *conn);
};
+uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan);
+uint8_t chan_mode_to_speech(struct gsm_lchan *lchan);
+
int bsc_api_init(struct gsm_network *network, struct bsc_api *api);
int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch);
int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate);
diff --git a/include/osmocom/bsc/bsc_subscr_conn_fsm.h b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
new file mode 100644
index 000000000..234161fa0
--- /dev/null
+++ b/include/osmocom/bsc/bsc_subscr_conn_fsm.h
@@ -0,0 +1,74 @@
+#pragma once
+#include <osmocom/core/fsm.h>
+
+enum gscon_fsm_event {
+ /* local SCCP stack tells us incoming conn from MSC */
+ GSCON_EV_A_CONN_IND,
+ /* RSL side requests CONNECT to MSC */
+ GSCON_EV_A_CONN_REQ,
+ /* MSC confirms the SCCP connection */
+ GSCON_EV_A_CONN_CFM,
+ /* MSC requests assignment */
+ GSCON_EV_A_ASSIGNMENT_CMD,
+ /* MSC has sent BSSMAP CLEAR CMD */
+ GSCON_EV_A_CLEAR_CMD,
+ /* MSC SCCP disconnect indication */
+ GSCON_EV_A_DISC_IND,
+ /* MSC sends Handover Request (in CR) */
+ GSCON_EV_A_HO_REQ,
+
+ /* RR ASSIGNMENT COMPLETE received */
+ GSCON_EV_RR_ASS_COMPL,
+ /* RR ASSIGNMENT FAIL received */
+ GSCON_EV_RR_ASS_FAIL,
+ /* RR MODE MODIFY ACK received */
+ GSCON_EV_RR_MODE_MODIFY_ACK,
+
+ /* RR HO ACC (access burst on ext HO) */
+ GSCON_EV_RR_HO_ACC, /* NOT USED YET! */
+ /* RR HANDOVER COMPLETE received */
+ GSCON_EV_RR_HO_COMPL, /* NOT USED YET! */
+ GSCON_EV_RR_HO_FAIL, /* NOT USED YET! */
+
+ /* RSL RLL Release Indication */
+ GSCON_EV_RLL_REL_IND,
+ /* RSL CONNection FAILure Indication */
+ GSCON_EV_RSL_CONN_FAIL,
+
+ /* RSL/lchan tells us clearing is complete */
+ GSCON_EV_RSL_CLEAR_COMPL,
+
+ /* Mobile-originated DTAP (from MS) */
+ GSCON_EV_MO_DTAP,
+ /* Mobile-terminated DTAP (from MSC) */
+ GSCON_EV_MT_DTAP,
+
+ /* Transmit custom SCCP message */
+ GSCON_EV_TX_SCCP,
+
+ /* MGW is indicating failure (BTS) */
+ GSCON_EV_MGW_FAIL_BTS,
+ /* MGW is indicating failure (MSC) */
+ GSCON_EV_MGW_FAIL_MSC,
+ /* CRCX response received (BTS) */
+ GSCON_EV_MGW_CRCX_RESP_BTS,
+ /* MDCX response received (BTS) */
+ GSCON_EV_MGW_MDCX_RESP_BTS,
+ /* CRCX response received (MSC) */
+ GSCON_EV_MGW_CRCX_RESP_MSC,
+
+ /* Internal handover request (intra-BSC handover) */
+ GSCON_EV_HO_START,
+ /* Handover timed out (T3103 in handover_logic.c) */
+ GSCON_EV_HO_TIMEOUT,
+ /* Handover failed (handover_logic.c) */
+ GSCON_EV_HO_FAIL,
+ /* Handover completed successfully (handover_logic.c) */
+ GSCON_EV_HO_COMPL,
+};
+
+struct gsm_subscriber_connection;
+struct gsm_network;
+
+/* Allocate a subscriber connection and its associated FSM */
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net);
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 74009a9c2..82a5a5129 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -15,10 +15,12 @@
#include <osmocom/gsm/bts_features.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/crypt/auth.h>
#include <osmocom/bsc/rest_octets.h>
+#include <osmocom/bsc/handover.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/gsm/gsm_utils.h>
@@ -95,6 +97,9 @@ struct gsm_subscriber_connection {
/* global linked list of subscriber_connections */
struct llist_head entry;
+ /* FSM instance to control the subscriber connection state (RTP, A) */
+ struct osmo_fsm_inst *fi;
+
/* libbsc subscriber information (if available) */
struct bsc_subscr *bsub;
@@ -103,8 +108,9 @@ struct gsm_subscriber_connection {
/* the primary / currently active lchan to the BTS/subscriber */
struct gsm_lchan *lchan;
- /* the future/allocated but not yet used lchan during HANDOVER */
- struct gsm_lchan *ho_lchan;
+
+ /* handover information, if a handover is pending for this conn. */
+ struct bsc_handover *ho;
/* timer for assignment handling */
struct osmo_timer_list T10;
@@ -166,11 +172,21 @@ struct gsm_subscriber_connection {
* assignment complete message) */
struct sockaddr_storage aoip_rtp_addr_local;
- /* storage to keep states of the MGCP connection handler, the
- * handler is created when an assignment request is received
- * and is terminated when the assignment complete message is
- * sent */
- struct mgcp_ctx *mgcp_ctx;
+ /* FSM instance to control the BTS sided RTP connection */
+ struct osmo_fsm_inst *fi_bts;
+
+ /* FSM instance to control the MSC sided RTP connection */
+ struct osmo_fsm_inst *fi_msc;
+
+ /* Endpoint identifier of the MGCP endpoint the connection uses */
+ char *mgw_endpoint;
+
+ /* Channel rate flag, FR=1, HR=0, Invalid=-1 */
+ int full_rate;
+
+ /* Channel mode flage (signaling or voice channel) */
+ enum gsm48_chan_mode chan_mode;
+
} user_plane;
};
@@ -1340,8 +1356,7 @@ void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts);
int gsm_bts_model_register(struct gsm_bts_model *model);
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan);
-void bsc_subscr_con_free(struct gsm_subscriber_connection *conn);
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network);
struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network);
void msc_subscr_con_free(struct gsm_subscriber_connection *conn);
diff --git a/include/osmocom/bsc/handover.h b/include/osmocom/bsc/handover.h
index 0fbfaf671..eb03f6a7b 100644
--- a/include/osmocom/bsc/handover.h
+++ b/include/osmocom/bsc/handover.h
@@ -4,10 +4,12 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
+#include <osmocom/gsm/gsm_utils.h>
struct gsm_lchan;
struct gsm_bts;
struct gsm_subscriber_connection;
+struct gsm_meas_rep mr;
#define LOGPHOLCHANTOLCHAN(old_lchan, new_lchan, level, fmt, args...) \
LOGP(DHODEC, level, "(BTS %u trx %u arfcn %u ts %u lchan %u %s)->(BTS %u trx %u arfcn %u ts %u lchan %u %s) (subscr %s) " fmt, \
@@ -38,21 +40,23 @@ enum hodec_id {
struct bsc_handover {
struct llist_head list;
- enum hodec_id from_hodec_id;
-
+ /* Initial details of what is requested */
struct gsm_lchan *old_lchan;
- struct gsm_lchan *new_lchan;
-
- struct osmo_timer_list T3103;
-
- uint8_t ho_ref;
+ struct gsm_bts *new_bts;
+ enum gsm_chan_t new_lchan_type;
+ bool async;
+ /* Derived and resulting state */
bool inter_cell;
- bool async;
+ uint8_t ho_ref;
+ enum hodec_id from_hodec_id;
+ struct gsm_lchan *new_lchan;
+ struct osmo_timer_list T3103;
};
int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
enum gsm_chan_t new_lchan_type);
+int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn);
void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan);
struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan);
diff --git a/include/osmocom/bsc/osmo_bsc.h b/include/osmocom/bsc/osmo_bsc.h
index 678ac41d2..ed5698db4 100644
--- a/include/osmocom/bsc/osmo_bsc.h
+++ b/include/osmocom/bsc/osmo_bsc.h
@@ -39,6 +39,4 @@ void bsc_gen_location_state_trap(struct gsm_bts *bts);
struct llist_head *bsc_access_lists(void);
-int bssmap_send_aoip_ass_compl(struct gsm_lchan *lchan);
-
#endif
diff --git a/include/osmocom/bsc/osmo_bsc_mgcp.h b/include/osmocom/bsc/osmo_bsc_mgcp.h
deleted file mode 100644
index 15039f747..000000000
--- a/include/osmocom/bsc/osmo_bsc_mgcp.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#pragma once
-
-#include <osmocom/mgcp_client/mgcp_common.h>
-#include <osmocom/mgcp_client/mgcp_client.h>
-
-/* MGCP state handler context. This context information stores all information
- * to handle the direction of the RTP streams via MGCP. There is one instance
- * of this context struct per subscriber connection.
- * (see also struct osmo_bsc_sccp_con) */
-struct mgcp_ctx {
- /* FSM instance, which handles the connection switching procedure */
- struct osmo_fsm_inst *fsm;
-
- /* RTP endpoint number. This number number identifies the endpoint
- * on the MGW on which the BTS and NET connection is created. This
- * endpoint number is assigned and released automatically. */
- uint16_t rtp_endpoint;
-
- /* RTP connection identifiers */
- char conn_id_bts[MGCP_CONN_ID_LENGTH];
- char conn_id_net[MGCP_CONN_ID_LENGTH];
-
- /* Copy of the pointer and the data with context information
- * needed to process the AoIP and MGCP requests (system data) */
- struct mgcp_client *mgcp;
- struct gsm_subscriber_connection *conn;
- enum gsm48_chan_mode chan_mode;
- bool full_rate;
- struct gsm_lchan *lchan;
- struct gsm_lchan *ho_lchan;
- struct msgb *resp;
- mgcp_trans_id_t mgw_pending_trans;
-};
-
-void mgcp_init(struct gsm_network *net);
-
-struct mgcp_ctx *mgcp_assignm_req(void *ctx, struct mgcp_client *mgcp,
- struct gsm_subscriber_connection *conn,
- enum gsm48_chan_mode chan_mode, bool full_rate);
-void mgcp_clear_complete(struct mgcp_ctx *mgcp_ctx, struct msgb *resp);
-void mgcp_ass_complete(struct mgcp_ctx *mgcp_ctx, struct gsm_lchan *lchan);
-void mgcp_free_ctx(struct mgcp_ctx *mgcp_ctx);
diff --git a/include/osmocom/bsc/osmo_bsc_sigtran.h b/include/osmocom/bsc/osmo_bsc_sigtran.h
index 5cb723066..80d4f5b8b 100644
--- a/include/osmocom/bsc/osmo_bsc_sigtran.h
+++ b/include/osmocom/bsc/osmo_bsc_sigtran.h
@@ -33,11 +33,6 @@ int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct ms
/* Send data to MSC */
int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg);
-/* Delete a connection from the list with open connections
- * (called by osmo_bsc_api.c on failing open connections and
- * locally, when a connection is closed by the MSC */
-int osmo_bsc_sigtran_del_conn(struct gsm_subscriber_connection *sccp);
-
/* Initalize osmo sigtran backhaul */
int osmo_bsc_sigtran_init(struct llist_head *mscs);
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index a6195b9c7..9b2a83e31 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -24,7 +24,6 @@ OSMO_LIBS = \
bin_PROGRAMS = \
abisip-find \
- ipaccess-config \
ipaccess-proxy \
$(NULL)
diff --git a/src/libbsc/Makefile.am b/src/libbsc/Makefile.am
index 805a7ee1f..895636387 100644
--- a/src/libbsc/Makefile.am
+++ b/src/libbsc/Makefile.am
@@ -13,6 +13,7 @@ AM_CFLAGS = \
$(LIBOSMOMGCP_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(COVERAGE_CFLAGS) \
+ $(LIBOSMOMGCPCLIENT_CFLAGS) \
$(NULL)
noinst_LIBRARIES = \
@@ -62,5 +63,6 @@ libbsc_a_SOURCES = \
handover_cfg.c \
penalty_timers.c \
handover_decision_2.c \
+ bsc_subscr_conn_fsm.c \
$(NULL)
diff --git a/src/libbsc/abis_rsl.c b/src/libbsc/abis_rsl.c
index 7400f896e..2017d2c0c 100644
--- a/src/libbsc/abis_rsl.c
+++ b/src/libbsc/abis_rsl.c
@@ -45,6 +45,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/bsc/pcu_if.h>
#include <osmocom/bsc/bsc_api.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
@@ -1357,21 +1358,28 @@ static int rsl_rx_chan_act_nack(struct msgb *msg)
static int rsl_rx_conn_fail(struct msgb *msg)
{
struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);
+ struct gsm_lchan *lchan = msg->lchan;
struct tlv_parsed tp;
+ uint8_t cause = 0;
- LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING state %s ",
+ LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL in state %s ",
gsm_lchan_name(msg->lchan),
gsm_lchans_name(msg->lchan->state));
rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh));
- if (TLVP_PRESENT(&tp, RSL_IE_CAUSE))
+ if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) {
print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE),
TLVP_LEN(&tp, RSL_IE_CAUSE));
+ cause = *TLVP_VAL(&tp, RSL_IE_CAUSE);
+ }
LOGPC(DRSL, LOGL_NOTICE, "\n");
- rate_ctr_inc(&msg->lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL]);
- return rsl_rf_chan_release_err(msg->lchan);
+ rate_ctr_inc(&lchan->ts->trx->bts->bts_ctrs->ctr[BTS_CTR_CHAN_RF_FAIL]);
+
+ osmo_fsm_inst_dispatch(lchan->conn->fi, GSCON_EV_RSL_CONN_FAIL, &cause);
+
+ return 0;
}
static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
@@ -2232,6 +2240,9 @@ static int abis_rsl_rx_rll(struct msgb *msg)
rll_indication(msg->lchan, rllh->link_id,
BSC_RLLR_IND_REL_IND);
rsl_handle_release(msg->lchan);
+ /* if it was the main signalling link, let the subscr_conn_fsm know */
+ if (msg->lchan->conn && sapi == 0 && (rllh->link_id >> 6) == 0)
+ osmo_fsm_inst_dispatch(msg->lchan->conn->fi, GSCON_EV_RLL_REL_IND, msg);
break;
case RSL_MT_REL_CONF:
/* BTS informs us of having received UA from MS,
diff --git a/src/libbsc/bsc_api.c b/src/libbsc/bsc_api.c
index d792b5899..c47654778 100644
--- a/src/libbsc/bsc_api.c
+++ b/src/libbsc/bsc_api.c
@@ -2,7 +2,7 @@
/* (C) 2010-2011 by Holger Hans Peter Freyther
* (C) 2010-2011 by On-Waves
- * (C) 2009 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009,2017 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
@@ -51,7 +51,7 @@ static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_a
static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan);
/* GSM 08.08 3.2.2.33 */
-static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
+uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
{
uint8_t channel_mode = 0, channel = 0;
@@ -100,7 +100,7 @@ static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan)
return channel_mode << 4 | channel;
}
-static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan)
+uint8_t chan_mode_to_speech(struct gsm_lchan *lchan)
{
int mode = 0;
@@ -133,27 +133,6 @@ static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan)
return mode;
}
-static void assignment_t10_timeout(void *_conn)
-{
- struct bsc_api *api;
- struct gsm_subscriber_connection *conn =
- (struct gsm_subscriber_connection *) _conn;
-
- LOGP(DMSC, LOGL_ERROR, "Assignment T10 timeout on %p\n", conn);
-
- /*
- * normal release on the secondary channel but only if the
- * secondary_channel has not been released by the handle_chan_nack.
- */
- if (conn->secondary_lchan)
- lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
- conn->secondary_lchan = NULL;
-
- /* inform them about the failure */
- api = conn->network->bsc_api;
- api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
-}
-
/*! \brief Determine and apply AMR multi-rate configuration to lchan
* Determine which AMR multi-rate configuration to use and apply it to
* the lchan (so it can be communicated to BTS and MS during channel
@@ -265,24 +244,6 @@ static int handle_new_assignment(struct gsm_subscriber_connection *conn, int cha
return 0;
}
-struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan)
-{
- struct gsm_subscriber_connection *conn;
- struct gsm_network *net = lchan->ts->trx->bts->network;
-
- conn = talloc_zero(net, struct gsm_subscriber_connection);
- if (!conn)
- return NULL;
-
- conn->network = net;
- conn->lchan = lchan;
- lchan->conn = conn;
- INIT_LLIST_HEAD(&conn->ho_dtap_cache);
- conn->sccp.conn_id = -1;
- llist_add_tail(&conn->entry, &net->subscr_conns);
- return conn;
-}
-
static void ho_dtap_cache_add(struct gsm_subscriber_connection *conn, struct msgb *msg,
int link_id, bool allow_sacch)
{
@@ -301,12 +262,12 @@ static void ho_dtap_cache_add(struct gsm_subscriber_connection *conn, struct msg
msgb_enqueue(&conn->ho_dtap_cache, msg);
}
-static void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send)
+void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send)
{
struct msgb *msg;
unsigned int flushed_count = 0;
- if (conn->secondary_lchan || conn->ho_lchan) {
+ if (conn->secondary_lchan || conn->ho) {
LOGP(DHO, LOGL_ERROR, "%s: Cannot send cached DTAP messages, handover/assignment is still ongoing\n",
bsc_subscr_name(conn->bsub));
send = 0;
@@ -326,38 +287,6 @@ static void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send
}
}
-void bsc_subscr_con_free(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return;
-
- if (conn->network->bsc_api->conn_cleanup)
- conn->network->bsc_api->conn_cleanup(conn);
-
- if (conn->ho_lchan) {
- LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n");
- conn->ho_lchan->conn = NULL;
- }
-
- if (conn->lchan) {
- LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n");
- conn->lchan->conn = NULL;
- }
-
- if (conn->secondary_lchan) {
- LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n");
- conn->secondary_lchan->conn = NULL;
- }
-
- /* drop pending messages */
- ho_dtap_cache_flush(conn, 0);
-
- penalty_timers_free(&conn->hodec2.penalty_timers);
-
- llist_del(&conn->entry);
- talloc_free(conn);
-}
-
int bsc_api_init(struct gsm_network *network, struct bsc_api *api)
{
network->bsc_api = api;
@@ -379,7 +308,7 @@ int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn,
}
/* buffer message during assignment / handover */
- if (conn->secondary_lchan || conn->ho_lchan) {
+ if (conn->secondary_lchan || conn->ho) {
ho_dtap_cache_add(conn, msg, link_id, !! allow_sacch);
return 0;
}
@@ -478,9 +407,7 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in
gsm48_lchan_modify(conn->lchan, chan_mode);
}
- /* we will now start the timer to complete the assignment */
- osmo_timer_setup(&conn->T10, assignment_t10_timeout, conn);
- osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE);
+ /* we expect the caller will manage T10 */
return 0;
error:
@@ -500,7 +427,7 @@ static void handle_ass_compl(struct gsm_subscriber_connection *conn,
struct gsm48_hdr *gh;
struct bsc_api *api = conn->network->bsc_api;
- if (conn->ho_lchan) {
+ if (conn->ho) {
struct lchan_signal_data sig;
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -556,7 +483,7 @@ static void handle_ass_fail(struct gsm_subscriber_connection *conn,
uint8_t *rr_failure;
struct gsm48_hdr *gh;
- if (conn->ho_lchan) {
+ if (conn->ho) {
struct lchan_signal_data sig;
struct gsm48_hdr *gh = msgb_l3(msg);
@@ -796,19 +723,18 @@ int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id)
} else {
/* allocate a new connection */
rc = BSC_API_CONN_POL_REJECT;
- lchan->conn = bsc_subscr_con_allocate(msg->lchan);
+ lchan->conn = bsc_subscr_con_allocate(msg->lchan->ts->trx->bts->network);
if (!lchan->conn) {
lchan_release(lchan, 1, RSL_REL_NORMAL);
return -1;
}
+ lchan->conn->lchan = lchan;
/* fwd via bsc_api to send COMPLETE L3 INFO to MSC */
rc = api->compl_l3(lchan->conn, msg, 0);
if (rc != BSC_API_CONN_POL_ACCEPT) {
- lchan->conn->lchan = NULL;
- bsc_subscr_con_free(lchan->conn);
- lchan_release(lchan, 1, RSL_REL_NORMAL);
+ //osmo_fsm_inst_dispatch(lchan->conn->fi, FIXME, NULL);
}
}
@@ -846,7 +772,7 @@ int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher,
*/
int gsm0808_clear(struct gsm_subscriber_connection *conn)
{
- if (conn->ho_lchan)
+ if (conn->ho)
bsc_clear_handover(conn, 1);
if (conn->secondary_lchan)
@@ -857,7 +783,6 @@ int gsm0808_clear(struct gsm_subscriber_connection *conn)
conn->lchan = NULL;
conn->secondary_lchan = NULL;
- conn->ho_lchan = NULL;
osmo_timer_del(&conn->T10);
@@ -959,16 +884,9 @@ static void handle_release(struct gsm_subscriber_connection *conn,
/* now give up all channels */
if (conn->lchan == lchan)
conn->lchan = NULL;
- if (conn->ho_lchan == lchan) {
+ if (conn->ho && conn->ho->new_lchan == lchan)
bsc_clear_handover(conn, 0);
- conn->ho_lchan = NULL;
- }
lchan->conn = NULL;
-
- gsm0808_clear(conn);
-
- if (destruct)
- bsc_subscr_con_free(conn);
}
static void handle_chan_ack(struct gsm_subscriber_connection *conn,
diff --git a/src/libbsc/bsc_subscr_conn_fsm.c b/src/libbsc/bsc_subscr_conn_fsm.c
new file mode 100644
index 000000000..652fdcdec
--- /dev/null
+++ b/src/libbsc/bsc_subscr_conn_fsm.c
@@ -0,0 +1,1056 @@
+/* (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+
+#include <osmocom/bsc/debug.h>
+#include <osmocom/bsc/bsc_api.h>
+#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/handover.h>
+#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/bsc_subscriber.h>
+#include <osmocom/bsc/osmo_bsc_sigtran.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/bsc/penalty_timers.h>
+#include <osmocom/mgcp_client/mgcp_client_fsm.h>
+#include <osmocom/core/byteswap.h>
+
+#define S(x) (1 << (x))
+
+#define MGCP_MGW_TIMEOUT 4 /* in seconds */
+#define MGCP_MGW_TIMEOUT_TIMER_NR 1
+
+#define MGCP_MGW_HO_TIMEOUT 4 /* in seconds */
+#define MGCP_MGW_HO_TIMEOUT_TIMER_NR 2
+
+#define GSM0808_T10_TIMER_NR 10
+#define GSM0808_T10_VALUE 6
+
+#define ENDPOINT_ID "rtpbridge/*@mgw"
+
+enum gscon_fsm_states {
+ ST_INIT,
+ /* waiting for CC from MSC */
+ ST_WAIT_CC,
+ /* active connection */
+ ST_ACTIVE,
+ /* during assignment; waiting for ASS_CMPL */
+ ST_WAIT_ASS_CMPL,
+ /* during assignment; waiting for MODE_MODIFY_ACK */
+ ST_WAIT_MODE_MODIFY_ACK,
+ /* BSSMAP CLEAR has been received */
+ ST_CLEARING,
+
+/* MGW handling */
+ /* during assignment; waiting for MGW response to CRCX for BTS */
+ ST_WAIT_CRCX_BTS,
+ /* during assignment; waiting for MGW response to MDCX for BTS */
+ ST_WAIT_MDCX_BTS,
+ /* during assignment; waiting for MGW response to CRCX for MSC */
+ ST_WAIT_CRCX_MSC,
+
+/* MT (inbound) handover */
+ /* Wait for Handover Access from MS/BTS */
+ ST_WAIT_MT_HO_ACC,
+ /* Wait for RR Handover Complete from MS/BTS */
+ ST_WAIT_MT_HO_COMPL,
+
+/* MO (outbound) handover */
+ /* Wait for Handover Command / Handover Required Reject from MSC */
+ ST_WAIT_MO_HO_CMD,
+ /* Wait for Clear Command from MSC */
+ ST_MO_HO_PROCEEDING,
+
+/* Internal HO handling */
+ /* Wait for the handover logic to complete the handover */
+ ST_WAIT_HO_COMPL,
+ /* during handover; waiting for MGW response to MDCX for BTS */
+ ST_WAIT_MDCX_BTS_HO,
+};
+
+static const struct value_string gscon_fsm_event_names[] = {
+ {GSCON_EV_A_CONN_IND, "MT-CONNECT.ind"},
+ {GSCON_EV_A_CONN_REQ, "MO-CONNECT.req"},
+ {GSCON_EV_A_CONN_CFM, "MO-CONNECT.cfm"},
+ {GSCON_EV_A_ASSIGNMENT_CMD, "ASSIGNMENT_CMD"},
+ {GSCON_EV_A_CLEAR_CMD, "CLEAR_CMD"},
+ {GSCON_EV_A_DISC_IND, "DISCONNET.ind"},
+ {GSCON_EV_A_HO_REQ, "HANDOVER_REQUEST"},
+
+ {GSCON_EV_RR_ASS_COMPL, "RR_ASSIGN_COMPL"},
+ {GSCON_EV_RR_ASS_FAIL, "RR_ASSIGN_FAIL"},
+ {GSCON_EV_RR_MODE_MODIFY_ACK, "RR_MODE_MODIFY_ACK"},
+ {GSCON_EV_RR_HO_ACC, "RR_HO_ACCESS"},
+ {GSCON_EV_RR_HO_COMPL, "RR_HO_COMPLETE"},
+ {GSCON_EV_RLL_REL_IND, "RLL_RELEASE.ind"},
+ {GSCON_EV_RSL_CONN_FAIL, "RSL_CONN_FAIL.ind"},
+ {GSCON_EV_RSL_CLEAR_COMPL, "RSL_CLEAR_COMPLETE"},
+
+ {GSCON_EV_MO_DTAP, "MO-DTAP"},
+ {GSCON_EV_MT_DTAP, "MT-DTAP"},
+ {GSCON_EV_TX_SCCP, "TX_SCCP"},
+
+ {GSCON_EV_MGW_FAIL_BTS, "MGW_FAILURE_BTS"},
+ {GSCON_EV_MGW_FAIL_MSC, "MGW_FAILURE_MSC"},
+ {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_HO_START, "HO_START"},
+ {GSCON_EV_HO_TIMEOUT, "HO_TIMEOUT"},
+ {GSCON_EV_HO_FAIL, "HO_FAIL"},
+ {GSCON_EV_HO_COMPL, "HO_COMPL"},
+
+ {0, NULL}
+};
+
+/* Send data SCCP message through SCCP connection. All sigtran messages
+ * that are send from this FSM must use this function. Never use
+ * osmo_bsc_sigtran_send() directly since this would defeat the checks
+ * provided by this function. */
+static void sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi)
+{
+ int rc;
+
+ /* Make sure that we only attempt to send SCCP messages if we have
+ * a life SCCP connection. Otherwise drop the message. */
+ if (fi->state == ST_INIT || fi->state == ST_WAIT_CC) {
+ LOGPFSML(fi, LOGL_ERROR, "No active SCCP connection, dropping message!\n");
+ msgb_free(msg);
+ return;
+ }
+
+ rc = osmo_bsc_sigtran_send(conn, msg);
+ if (rc < 0)
+ LOGPFSML(fi, LOGL_ERROR, "Unable to deliver SCCP message!\n");
+}
+
+/* Generate and send assignment complete message */
+static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi)
+{
+ struct msgb *resp;
+ struct gsm0808_speech_codec sc;
+ struct gsm_subscriber_connection *conn;
+
+ conn = lchan->conn;
+
+ OSMO_ASSERT(lchan->abis_ip.ass_compl.valid);
+ OSMO_ASSERT(conn);
+
+ LOGPFSML(fi, LOGL_DEBUG, "Sending assignment complete message... (id=%i)\n", conn->sccp.conn_id);
+
+ /* Extrapolate speech codec from speech mode */
+ gsm0808_speech_codec_from_chan_type(&sc, lchan->abis_ip.ass_compl.speech_mode);
+
+ /* Generate message */
+ resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause,
+ lchan->abis_ip.ass_compl.chosen_channel,
+ lchan->abis_ip.ass_compl.encr_alg_id,
+ lchan->abis_ip.ass_compl.speech_mode,
+ &conn->user_plane.aoip_rtp_addr_local, &sc, NULL);
+
+ if (!resp) {
+ LOGPFSML(fi, LOGL_ERROR, "Failed to generate assignment completed message! (id=%i)\n",
+ conn->sccp.conn_id);
+ }
+
+ sigtran_send(conn, resp, fi);
+}
+
+/* forward MT DTAP from BSSAP side to RSL side */
+static void submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi)
+{
+ int rc;
+ struct msgb *resp = NULL;
+
+ OSMO_ASSERT(fi);
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(conn);
+
+ rc = gsm0808_submit_dtap(conn, msg, OBSC_LINKID_CB(msg), 1);
+ if (rc != 0) {
+ LOGPFSML(fi, LOGL_ERROR, "Tx BSSMAP CLEAR REQUEST to MSC\n");
+ resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+}
+
+/* forward MO DTAP from RSL side to BSSAP side */
+/* FIXME: move fi parameter to the beginning */
+static void forward_dtap(struct msgb *msg, struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi)
+{
+ struct msgb *resp = NULL;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(conn);
+
+ resp = gsm0808_create_dtap(msg, OBSC_LINKID_CB(msg));
+ sigtran_send(conn, resp, fi);
+}
+
+/* In case there are open MGCP connections, toss
+ * those connections */
+static void toss_mgcp_conn(struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi)
+{
+ LOGPFSML(fi, LOGL_ERROR, "tossing all MGCP connections...\n");
+
+ if (conn->user_plane.fi_bts) {
+ mgcp_conn_delete(conn->user_plane.fi_bts);
+ conn->user_plane.fi_bts = NULL;
+ }
+
+ if (conn->user_plane.fi_msc) {
+ mgcp_conn_delete(conn->user_plane.fi_msc);
+ conn->user_plane.fi_msc = NULL;
+ }
+
+ if (conn->user_plane.mgw_endpoint) {
+ talloc_free(conn->user_plane.mgw_endpoint);
+ conn->user_plane.mgw_endpoint = NULL;
+ }
+}
+
+static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct osmo_scu_prim *scu_prim = NULL;
+ struct msgb *msg = NULL;
+ int rc;
+
+ switch (event) {
+ case GSCON_EV_A_CONN_REQ:
+ /* RLL ESTABLISH IND with initial L3 Message */
+ msg = data;
+ /* FIXME: Extract Mobile ID and update FSM using osmo_fsm_inst_set_id()
+ * i.e. we will probably extract the mobile identity earlier, where the
+ * imsi filter code is. Then we could just use it here.
+ * related: OS#2969 */
+
+ rc = osmo_bsc_sigtran_open_conn(conn, msg);
+ if (rc < 0) {
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ } else {
+ /* SCCP T(conn est) is 1-2 minutes, way too long. The MS will timeout
+ * using T3210 (20s), T3220 (5s) or T3230 (10s) */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_CC, 20, 993210);
+ }
+ break;
+ case GSCON_EV_A_CONN_IND:
+ scu_prim = data;
+ if (!conn->sccp.msc) {
+ LOGPFSML(fi, LOGL_NOTICE, "N-CONNECT.ind from unknown MSC %s\n",
+ osmo_sccp_addr_dump(&scu_prim->u.connect.calling_addr));
+ osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
+ &scu_prim->u.connect.called_addr, 0);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ }
+ /* FIXME: Extract optional IMSI and update FSM using osmo_fsm_inst_set_id()
+ * related: OS2969 (same as above) */
+
+ LOGPFSML(fi, LOGL_NOTICE, "No support for MSC-originated SCCP Connections yet\n");
+ osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
+ &scu_prim->u.connect.called_addr, 0);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* We've sent the CONNECTION.req to the SCCP provider and are waiting for CC from MSC */
+static void gscon_fsm_wait_cc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case GSCON_EV_A_CONN_CFM:
+ /* MSC has confirmed the connection, we now change into the
+ * active state and wait there for further operations */
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ /* if there's user payload, forward it just like EV_MT_DTAP */
+ /* FIXME: Question: if there's user payload attached to the CC, forward it like EV_MT_DTAP? */
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* We're on an active subscriber connection, passing DTAP back and forth */
+static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *resp = NULL;
+ struct mgcp_conn_peer conn_peer;
+ int rc;
+
+ switch (event) {
+ case GSCON_EV_A_ASSIGNMENT_CMD:
+ /* MSC requests us to perform assignment, this code section is
+ * triggered via signal GSCON_EV_A_ASSIGNMENT_CMD from
+ * bssmap_handle_assignm_req() in osmo_bsc_bssap.c, which does
+ * the parsing of incoming assignment requests. */
+
+ LOGPFSML(fi, LOGL_NOTICE, "Channel assignment: chan_mode=%s, full_rate=%i\n",
+ get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode),
+ conn->user_plane.full_rate);
+
+ /* FIXME: We need to check if current channel is sufficient. If
+ * yes, do MODIFY. If not, do assignment (see commented lines below) */
+
+ /* FIXME: At the moment, the FSM is constructed in an
+ * unfortunate way. In case of a voice channel assignment
+ * we first go through a couple of MGCP related states,
+ * then reach the state where the actual channel assignment
+ * happens and then again we perform some MGCP related
+ * actions and eventually end up in ST_ACTIVE again. This
+ * could be restructured */
+
+ switch (conn->user_plane.chan_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ /* A voice channel is requested, so we run down the
+ * mgcp-ass-mgcp state-chain (see FIXME above) */
+ memset(&conn_peer, 0, sizeof(conn_peer));
+ conn_peer.call_id = conn->sccp.conn_id;
+ osmo_strlcpy(conn_peer.endpoint, ENDPOINT_ID, sizeof(conn_peer.endpoint));
+
+ /* (Pre)Change state and create the connection */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+ conn->user_plane.fi_bts =
+ mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_BTS,
+ GSCON_EV_MGW_CRCX_RESP_BTS, &conn_peer);
+ if (!conn->user_plane.fi_bts) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+ break;
+ case GSM48_CMODE_SIGN:
+ /* A signalling channel is requested, so we perform the
+ * channel assignment directly without performing any
+ * MGCP actions. ST_WAIT_ASS_CMPL will see by the
+ * conn->user_plane.chan_mode parameter that this
+ * assignment is for a signalling channel and will then
+ * change back to ST_ACTIVE (here) immediately. */
+ rc = gsm0808_assign_req(conn, conn->user_plane.full_rate, conn->user_plane.chan_mode);
+ if (rc != 0) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ return;
+ }
+
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR);
+ break;
+ default:
+ /* An unsupported channel is requested, so we have to
+ * reject this request by sending an assignment failure
+ * message immediately */
+ LOGPFSML(fi, LOGL_ERROR, "Requested channel mode is not supported!\n",
+ get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode),
+ conn->user_plane.full_rate);
+
+ /* The requested channel mode is not supported */
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP, NULL);
+ sigtran_send(conn, resp, fi);
+ break;
+ }
+ break;
+ case GSCON_EV_HO_START:
+ rc = bsc_handover_start_gscon(conn);
+ if (rc) {
+ resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ return;
+ }
+
+ /* Note: No timeout is set here, T3103 in handover_logic.c
+ * will generate a GSCON_EV_HO_TIMEOUT event should the
+ * handover time out, so we do not need another timeout
+ * here (maybe its worth to think about giving GSCON
+ * more power over the actual handover process). */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_HO_COMPL, 0, 0);
+ break;
+ case GSCON_EV_A_HO_REQ:
+ /* FIXME: reject any handover requests with HO FAIL until implemented */
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* Before we may start the channel assignment we need to get an IP/Port for the
+ * RTP connection from the MGW */
+static void gscon_fsm_wait_crcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_conn_peer *conn_peer = NULL;
+ struct msgb *resp = NULL;
+ int rc;
+
+ switch (event) {
+ case GSCON_EV_MGW_CRCX_RESP_BTS:
+ conn_peer = data;
+
+ /* Check if the MGW has assigned an enpoint to us, we can not
+ * proceed */
+ if (strlen(conn_peer->endpoint) <= 0) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+
+ /* Memorize the endpoint name we got assigned from the MGW.
+ * When the BTS sided connection is done, we need to create
+ * a second connection on that same endpoint, so we need
+ * to know its ID */
+ if (!conn->user_plane.mgw_endpoint)
+ conn->user_plane.mgw_endpoint = talloc_zero_size(conn, MGCP_ENDPOINT_MAXLEN);
+ OSMO_ASSERT(conn->user_plane.mgw_endpoint);
+ osmo_strlcpy(conn->user_plane.mgw_endpoint, conn_peer->endpoint, MGCP_ENDPOINT_MAXLEN);
+
+ /* Store the IP-Address and the port the MGW assigned to us,
+ * then start the channel assignment. */
+ conn->user_plane.rtp_port = conn_peer->port;
+ conn->user_plane.rtp_ip = osmo_ntohl(inet_addr(conn_peer->addr));
+ rc = gsm0808_assign_req(conn, conn->user_plane.full_rate, conn->user_plane.chan_mode);
+ if (rc != 0) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, GSM0808_T10_VALUE, GSM0808_T10_TIMER_NR);
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* We're waiting for an ASSIGNMENT COMPLETE from MS */
+static void gscon_fsm_wait_ass_cmpl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_lchan *lchan = conn->lchan;
+ struct mgcp_conn_peer conn_peer;
+ struct in_addr addr;
+ struct msgb *resp = NULL;
+ int rc;
+
+ switch (event) {
+ case GSCON_EV_RR_ASS_COMPL:
+ switch (conn->user_plane.chan_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_AMR:
+ /* FIXME: What if we are using SCCP-Lite? */
+
+ /* We are dealing with a voice channel, so we can not
+ * confirm the assignment directly. We must first do
+ * some final steps on the MGCP side. */
+
+ /* Prepare parameters with the information we got during the assignment */
+ memset(&conn_peer, 0, sizeof(conn_peer));
+ addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
+ osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr));
+ conn_peer.port = lchan->abis_ip.bound_port;
+
+ /* (Pre)Change state and modify the connection */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+ rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
+ if (rc != 0) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+ break;
+ case GSM48_CMODE_SIGN:
+ /* Confirm the successful assignment on BSSMAP and
+ * change back into active state */
+ send_ass_compl(lchan, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ default:
+ /* Unsupported modes should have been already filtered
+ * by gscon_fsm_active(). If we reach the default
+ * section here anyway than some unsupported mode must
+ * have made it into the FSM, this would be a bug, so
+ * we fire an assertion here */
+ OSMO_ASSERT(false);
+ break;
+ }
+
+ break;
+ case GSCON_EV_RR_ASS_FAIL:
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* We are waiting for the MGW response to the MDCX */
+static void gscon_fsm_wait_mdcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_conn_peer conn_peer;
+ struct sockaddr_in *sin = NULL;
+ struct msgb *resp = NULL;
+
+ switch (event) {
+ case GSCON_EV_MGW_MDCX_RESP_BTS:
+
+ /* Prepare parameters with the connection information we got
+ * with the assignment command */
+ memset(&conn_peer, 0, sizeof(conn_peer));
+ conn_peer.call_id = conn->sccp.conn_id;
+ sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote;
+ conn_peer.port = osmo_ntohs(sin->sin_port);
+ osmo_strlcpy(conn_peer.addr, inet_ntoa(sin->sin_addr), sizeof(conn_peer.addr));
+
+ /* Make sure we use the same endpoint where we created the
+ * BTS connection. */
+ osmo_strlcpy(conn_peer.endpoint, conn->user_plane.mgw_endpoint, sizeof(conn_peer.endpoint));
+
+ /* (Pre)Change state and create the connection */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_MSC, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+ conn->user_plane.fi_msc =
+ mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_MSC, GSCON_EV_MGW_CRCX_RESP_MSC,
+ &conn_peer);
+ if (!conn->user_plane.fi_bts) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ return;
+ }
+
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+static void gscon_fsm_wait_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_conn_peer *conn_peer = NULL;
+ struct gsm_lchan *lchan = conn->lchan;
+ struct sockaddr_in *sin = NULL;
+
+ switch (event) {
+ case GSCON_EV_MGW_CRCX_RESP_MSC:
+ conn_peer = data;
+
+ /* Store address information we got in response from the CRCX command. */
+ sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_local;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = inet_addr(conn_peer->addr);
+ sin->sin_port = osmo_ntohs(conn_peer->port);
+
+ /* Send assignment complete message to the MSC */
+ send_ass_compl(lchan, fi);
+
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* We're waiting for a MODE MODIFY ACK from MS + BTS */
+static void gscon_fsm_wait_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct gsm_lchan *lchan = conn->lchan;
+
+ switch (event) {
+ case GSCON_EV_RR_MODE_MODIFY_ACK:
+ /* we assume that not only have we received the RR MODE_MODIFY_ACK, but
+ * actually that also the BTS side of the channel mode has been changed accordingly */
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+
+ /* FIXME: Check if this requires special handling. For now I assume that the send_ass_compl()
+ * can be used. But I am not sure. */
+ send_ass_compl(lchan, fi);
+
+ break;
+ /* FIXME: Do we need to handle DTAP traffic in this state? Maybe yes? Needs to be checked. */
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+static void gscon_fsm_clearing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *resp;
+
+ switch (event) {
+ case GSCON_EV_RSL_CLEAR_COMPL:
+ resp = gsm0808_create_clear_complete();
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* Wait for the handover logic to tell us whether the handover completed,
+ * failed or has timed out */
+static void gscon_fsm_wait_ho_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct mgcp_conn_peer conn_peer;
+ struct gsm_lchan *lchan = conn->lchan;
+ struct in_addr addr;
+ struct msgb *resp;
+ int rc;
+
+ switch (event) {
+ case GSCON_EV_HO_COMPL:
+ /* The handover logic informs us that the handover has been
+ * completet. Now we have to tell the MGW the IP/Port on the
+ * new BTS so that the uplink RTP traffic can be redirected
+ * there. */
+
+ /* Prepare parameters with the information we got during the
+ * handover procedure (via IPACC) */
+ memset(&conn_peer, 0, sizeof(conn_peer));
+ addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
+ osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr));
+ conn_peer.port = lchan->abis_ip.bound_port;
+
+ /* (Pre)Change state and modify the connection */
+ osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS_HO, MGCP_MGW_TIMEOUT, MGCP_MGW_HO_TIMEOUT_TIMER_NR);
+ rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
+ if (rc != 0) {
+ resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ return;
+ }
+ break;
+ case GSCON_EV_HO_TIMEOUT:
+ case GSCON_EV_HO_FAIL:
+ /* The handover logic informs us that the handover failed for
+ * some reason. This means the phone stays on the TS/BTS on
+ * which it currently is. We will change back to the active
+ * state again as there are no further operations needed */
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+/* Wait for the MGW to confirm handover related modification of the connection
+ * parameters */
+static void gscon_fsm_wait_mdcx_bts_ho(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ switch (event) {
+ case GSCON_EV_MGW_MDCX_RESP_BTS:
+ /* The MGW has confirmed the handover MDCX, and the handover
+ * is now also done on the RTP side. We may now change back
+ * to the active state. */
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ case GSCON_EV_MO_DTAP:
+ forward_dtap((struct msgb *)data, conn, fi);
+ break;
+ case GSCON_EV_MT_DTAP:
+ submit_dtap(conn, (struct msgb *)data, fi);
+ break;
+ case GSCON_EV_TX_SCCP:
+ sigtran_send(conn, (struct msgb *)data, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+#define EV_TRANSPARENT_SCCP S(GSCON_EV_TX_SCCP) | S(GSCON_EV_MO_DTAP) | S(GSCON_EV_MT_DTAP)
+
+static const struct osmo_fsm_state gscon_fsm_states[] = {
+ [ST_INIT] = {
+ .name = OSMO_STRINGIFY(INIT),
+ .in_event_mask = S(GSCON_EV_A_CONN_REQ) | S(GSCON_EV_A_CONN_IND),
+ .out_state_mask = S(ST_WAIT_CC),
+ .action = gscon_fsm_init,
+ },
+ [ST_WAIT_CC] = {
+ .name = OSMO_STRINGIFY(WAIT_CC),
+ .in_event_mask = S(GSCON_EV_A_CONN_CFM),
+ .out_state_mask = S(ST_ACTIVE),
+ .action = gscon_fsm_wait_cc,
+ },
+ [ST_ACTIVE] = {
+ .name = OSMO_STRINGIFY(ACTIVE),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_A_ASSIGNMENT_CMD) | S(GSCON_EV_A_HO_REQ) |
+ S(GSCON_EV_HO_START),
+ .out_state_mask = S(ST_CLEARING) | S(ST_WAIT_CRCX_BTS) | S(ST_WAIT_ASS_CMPL) |
+ S(ST_WAIT_MODE_MODIFY_ACK) | S(ST_WAIT_MO_HO_CMD) | S(ST_WAIT_HO_COMPL),
+ .action = gscon_fsm_active,
+ },
+ [ST_WAIT_CRCX_BTS] = {
+ .name = OSMO_STRINGIFY(WAIT_CRCX_BTS),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_BTS),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_ASS_CMPL),
+ .action = gscon_fsm_wait_crcx_bts,
+ },
+ [ST_WAIT_ASS_CMPL] = {
+ .name = OSMO_STRINGIFY(WAIT_ASS_CMPL),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_RR_ASS_COMPL) | S(GSCON_EV_RR_ASS_FAIL),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS),
+ .action = gscon_fsm_wait_ass_cmpl,
+ },
+ [ST_WAIT_MDCX_BTS] = {
+ .name = OSMO_STRINGIFY(WAIT_MDCX_BTS),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CRCX_MSC),
+ .action = gscon_fsm_wait_mdcx_bts,
+ },
+ [ST_WAIT_CRCX_MSC] = {
+ .name = OSMO_STRINGIFY(WAIT_CRCX_MSC),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_MSC),
+ .out_state_mask = S(ST_ACTIVE),
+ .action = gscon_fsm_wait_crcx_msc,
+ },
+ [ST_WAIT_MODE_MODIFY_ACK] = {
+ .name = OSMO_STRINGIFY(WAIT_MODE_MODIFY_ACK),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_RR_MODE_MODIFY_ACK),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .action = gscon_fsm_wait_mode_modify_ack,
+ },
+ [ST_CLEARING] = {
+ .name = OSMO_STRINGIFY(CLEARING),
+ .in_event_mask = S(GSCON_EV_RSL_CLEAR_COMPL),
+ .action = gscon_fsm_clearing,
+ },
+
+ /* TODO: external handover, probably it makes sense to break up the
+ * program flow in handover_logic.c a bit and handle some of the logic
+ * here? */
+ [ST_WAIT_MT_HO_ACC] = {
+ .name = OSMO_STRINGIFY(WAIT_MT_HO_ACC),
+ },
+ [ST_WAIT_MT_HO_COMPL] = {
+ .name = OSMO_STRINGIFY(WAIT_MT_HO_COMPL),
+ },
+ [ST_WAIT_MO_HO_CMD] = {
+ .name = OSMO_STRINGIFY(WAIT_MO_HO_CMD),
+ },
+ [ST_MO_HO_PROCEEDING] = {
+ .name = OSMO_STRINGIFY(MO_HO_PROCEEDING),
+ },
+
+ /* Internal handover */
+ [ST_WAIT_HO_COMPL] = {
+ .name = OSMO_STRINGIFY(WAIT_HO_COMPL),
+ .in_event_mask = S(GSCON_EV_HO_COMPL) | S(GSCON_EV_HO_FAIL) | S(GSCON_EV_HO_TIMEOUT),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS_HO),
+ .action = gscon_fsm_wait_ho_compl,
+ },
+ [ST_WAIT_MDCX_BTS_HO] = {
+ .name = OSMO_STRINGIFY(WAIT_MDCX_BTS_HO),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS),
+ .action = gscon_fsm_wait_mdcx_bts_ho,
+ .out_state_mask = S(ST_ACTIVE),
+ },
+};
+
+static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *resp = NULL;
+
+ /* When a connection on the MGW fails, make sure that the reference
+ * in our book-keeping is erased. */
+ switch (event) {
+ case GSCON_EV_MGW_FAIL_BTS:
+ conn->user_plane.fi_bts = NULL;
+ break;
+ case GSCON_EV_MGW_FAIL_MSC:
+ conn->user_plane.fi_msc = NULL;
+ break;
+ }
+
+ /* Regular allstate event processing */
+ switch (event) {
+ case GSCON_EV_MGW_FAIL_BTS:
+ case GSCON_EV_MGW_FAIL_MSC:
+ /* Note: An MGW connection die per definition at any time.
+ * However, if it dies during the assignment we must return
+ * with an assignment failure */
+ OSMO_ASSERT(fi->state != ST_INIT && fi->state != ST_WAIT_CC)
+ if (fi->state == ST_WAIT_CRCX_BTS || fi->state == ST_WAIT_ASS_CMPL || fi->state == ST_WAIT_MDCX_BTS
+ || fi->state == ST_WAIT_CRCX_MSC) {
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ }
+ break;
+ case GSCON_EV_A_CLEAR_CMD:
+ /* MSC tells us to cleanly shut down */
+ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ gsm0808_clear(conn);
+ /* FIXME: Release all terestrial resources in ST_CLEARING */
+ /* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
+ * release to be completed or for the guard timer to expire before returning the
+ * CLEAR COMPLETE message" */
+
+ /* Close MGCP connections */
+ toss_mgcp_conn(conn, fi);
+
+ /* FIXME: Question: Is this a hack to force a clear complete from internel?
+ * nobody seems to send the event from outside? */
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RSL_CLEAR_COMPL, NULL);
+ break;
+ case GSCON_EV_A_DISC_IND:
+ /* MSC or SIGTRAN network has hard-released SCCP connection,
+ * terminate the FSM now. */
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
+ break;
+ case GSCON_EV_RLL_REL_IND:
+ /* BTS reports that one of the LAPDm data links was released */
+ /* send proper clear request to MSC */
+ LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n");
+ resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE);
+ sigtran_send(conn, resp, fi);
+ break;
+ case GSCON_EV_RSL_CONN_FAIL:
+ LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n");
+ resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
+ sigtran_send(conn, resp, fi);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ break;
+ }
+}
+
+void ho_dtap_cache_flush(struct gsm_subscriber_connection *conn, int send);
+
+static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ if (conn->ho) {
+ LOGPFSML(fi, LOGL_DEBUG, "Releasing handover state\n");
+ bsc_clear_handover(conn, 1);
+ conn->ho = NULL;
+ }
+
+ if (conn->secondary_lchan) {
+ LOGPFSML(fi, LOGL_DEBUG, "Releasing secondary_lchan\n");
+ lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
+ conn->secondary_lchan = NULL;
+ }
+ if (conn->lchan) {
+ LOGPFSML(fi, LOGL_DEBUG, "Releasing lchan\n");
+ lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END);
+ conn->lchan = NULL;
+ }
+
+ if (conn->bsub) {
+ LOGPFSML(fi, LOGL_DEBUG, "Putting bsc_subscr\n");
+ bsc_subscr_put(conn->bsub);
+ conn->bsub = NULL;
+ }
+
+ if (conn->sccp.state != SUBSCR_SCCP_ST_NONE) {
+ LOGPFSML(fi, LOGL_DEBUG, "Disconnecting SCCP\n");
+ struct bsc_msc_data *msc = conn->sccp.msc;
+ /* FIXME: include a proper cause value / error message? */
+ osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn_id, &msc->a.bsc_addr, 0);
+ conn->sccp.state = SUBSCR_SCCP_ST_NONE;
+ }
+
+ /* drop pending messages */
+ ho_dtap_cache_flush(conn, 0);
+
+ penalty_timers_free(&conn->hodec2.penalty_timers);
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+ fi->priv = NULL;
+}
+
+static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+
+ /* Make sure all possibly still open MGCP connections get closed */
+ toss_mgcp_conn(conn, fi);
+}
+
+static int gscon_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *resp = NULL;
+
+ switch (fi->T) {
+ case 993210:
+ /* MSC has not responded/confirmed connection witH CC */
+ /* N-DISCONNET.req is sent in gscon_cleanup() above */
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ break;
+ case GSM0808_T10_TIMER_NR: /* Assignment Failed */
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ case MGCP_MGW_TIMEOUT_TIMER_NR: /* Assignment failed (no response from MGW) */
+ resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_EQUIPMENT_FAILURE, NULL);
+ sigtran_send(conn, resp, fi);
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ case MGCP_MGW_HO_TIMEOUT_TIMER_NR: /* Handover failed (no response from MGW) */
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+ return 0;
+}
+
+static struct osmo_fsm gscon_fsm = {
+ .name = "SUBSCR_CONN",
+ .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),
+ .allstate_action = gscon_fsm_allstate,
+ .cleanup = gscon_cleanup,
+ .pre_term = gscon_pre_term,
+ .timer_cb = gscon_timer_cb,
+ .log_subsys = DMSC,
+ .event_names = gscon_fsm_event_names,
+};
+
+/* Allocate a subscriber connection and its associated FSM */
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net)
+{
+ struct gsm_subscriber_connection *conn;
+ static bool g_initialized = false;
+
+ if (!g_initialized) {
+ osmo_fsm_register(&gscon_fsm);
+ g_initialized = true;
+ }
+
+ conn = talloc_zero(net, struct gsm_subscriber_connection);
+ if (!conn)
+ return NULL;
+
+ conn->network = net;
+ INIT_LLIST_HEAD(&conn->ho_dtap_cache);
+ /* BTW, penalty timers will be initialized on-demand. */
+ conn->sccp.conn_id = -1;
+
+ /* don't allocate from 'conn' context, as gscon_cleanup() will call talloc_free(conn) before
+ * libosmocore will call talloc_free(conn->fi), i.e. avoid use-after-free during cleanup */
+ conn->fi = osmo_fsm_inst_alloc(&gscon_fsm, net, conn, LOGL_NOTICE, NULL);
+ if (!conn->fi) {
+ talloc_free(conn);
+ return NULL;
+ }
+
+ llist_add_tail(&conn->entry, &net->subscr_conns);
+ return conn;
+}
diff --git a/src/libbsc/handover_decision_2.c b/src/libbsc/handover_decision_2.c
index 467a19d53..7ac54df95 100644
--- a/src/libbsc/handover_decision_2.c
+++ b/src/libbsc/handover_decision_2.c
@@ -1150,7 +1150,7 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Initial Assignment is still ongoing\n");
return;
}
- if (lchan->conn->ho_lchan) {
+ if (lchan->conn->ho) {
LOGPHOLCHAN(lchan, LOGL_INFO, "Skipping, Handover already triggered\n");
return;
}
@@ -1349,7 +1349,7 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
break;
/* omit if there is an ongoing ho/as */
if (!lc->conn || lc->conn->secondary_lchan
- || lc->conn->ho_lchan)
+ || lc->conn->ho)
break;
/* We desperately want to resolve congestion, ignore rxlev when
* collecting candidates by passing include_weaker_rxlev=true. */
@@ -1365,7 +1365,7 @@ static int bts_resolve_congestion(struct gsm_bts *bts, int tchf_congestion, int
/* omit of there is an ongoing ho/as */
if (!lc->conn
|| lc->conn->secondary_lchan
- || lc->conn->ho_lchan)
+ || lc->conn->ho)
continue;
/* We desperately want to resolve congestion, ignore rxlev when
* collecting candidates by passing include_weaker_rxlev=true. */
diff --git a/src/libbsc/handover_logic.c b/src/libbsc/handover_logic.c
index cdc21f56f..960bf6993 100644
--- a/src/libbsc/handover_logic.c
+++ b/src/libbsc/handover_logic.c
@@ -40,6 +40,7 @@
#include <osmocom/bsc/gsm_04_08_utils.h>
#include <osmocom/bsc/handover.h>
#include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
static LLIST_HEAD(bsc_handovers);
static LLIST_HEAD(handover_decision_callbacks);
@@ -81,70 +82,105 @@ static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan)
int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan, struct gsm_bts *new_bts,
enum gsm_chan_t new_lchan_type)
{
- struct gsm_network *network;
- struct gsm_lchan *new_lchan;
+ struct gsm_subscriber_connection *conn;
struct bsc_handover *ho;
static uint8_t ho_ref = 0;
- int rc;
- bool do_assignment = false;
+ bool do_assignment;
+
+ OSMO_ASSERT(old_lchan);
/* don't attempt multiple handovers for the same lchan at
* the same time */
if (bsc_ho_by_old_lchan(old_lchan))
return -EBUSY;
+ conn = old_lchan->conn;
+ if (!conn) {
+ LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n");
+ return -ENOSPC;
+ }
+
if (!new_bts)
new_bts = old_lchan->ts->trx->bts;
- do_assignment = (new_bts == old_lchan->ts->trx->bts);
-
- network = new_bts->network;
+ OSMO_ASSERT(new_bts);
- rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
+ do_assignment = (new_bts == old_lchan->ts->trx->bts);
- if (!old_lchan->conn) {
- LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n");
- return -ENOSPC;
+ ho = talloc_zero(conn, struct bsc_handover);
+ if (!ho) {
+ LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
+ return -ENOMEM;
}
+ ho->from_hodec_id = from_hodec_id;
+ ho->old_lchan = old_lchan;
+ ho->new_bts = new_bts;
+ ho->new_lchan_type = new_lchan_type;
+ ho->ho_ref = ho_ref++;
+ ho->inter_cell = !do_assignment;
+ ho->async = true;
+ llist_add(&ho->list, &bsc_handovers);
+
+ conn->ho = ho;
- DEBUGP(DHO, "(BTS %u trx %u ts %u lchan %u %s)->(BTS %u lchan %s) Beginning with handover operation...\n",
+ DEBUGP(DHO, "(BTS %u trx %u ts %u lchan %u %s)->(BTS %u lchan %s) Initiating %s...\n",
old_lchan->ts->trx->bts->nr,
old_lchan->ts->trx->nr,
old_lchan->ts->nr,
old_lchan->nr,
gsm_pchan_name(old_lchan->ts->pchan),
new_bts->nr,
- gsm_lchant_name(new_lchan_type));
+ gsm_lchant_name(new_lchan_type),
+ do_assignment ? "Assignment" : "Handover");
- new_lchan = lchan_alloc(new_bts, new_lchan_type, 0);
- if (!new_lchan) {
- LOGP(DHO, LOGL_NOTICE, "No free channel for %s\n", gsm_lchant_name(new_lchan_type));
- rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]);
- return -ENOSPC;
- }
+ return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_HO_START, NULL);
+}
+
+/*! Start actual handover. Call bsc_handover_start() instead; The only legal caller is the GSCON FSM in
+ * bsc_subscr_conn_fsm.c. */
+int bsc_handover_start_gscon(struct gsm_subscriber_connection *conn)
+{
+ int rc;
+ struct gsm_network *network = conn->network;
+ struct bsc_handover *ho = conn->ho;
+ struct gsm_lchan *old_lchan;
+ struct gsm_lchan *new_lchan;
- ho = talloc_zero(tall_bsc_ctx, struct bsc_handover);
if (!ho) {
- LOGP(DHO, LOGL_FATAL, "Out of Memory\n");
- lchan_free(new_lchan);
- return -ENOMEM;
+ LOGP(DHO, LOGL_ERROR, "%s: Requested to start handover, but conn->ho is NULL\n",
+ bsc_subscr_name(conn->bsub));
+ return -EINVAL;
+ }
+
+ OSMO_ASSERT(ho->old_lchan && ho->new_bts);
+
+ if (ho->old_lchan->conn != conn) {
+ LOGP(DHO, LOGL_ERROR,
+ "%s: Requested to start handover, but the lchan does not belong to this conn\n",
+ bsc_subscr_name(conn->bsub));
+ return -EINVAL;
+ }
+
+ rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
+
+ ho->new_lchan = lchan_alloc(ho->new_bts, ho->new_lchan_type, 0);
+ if (!ho->new_lchan) {
+ LOGP(DHO, LOGL_NOTICE, "No free channel for %s\n", gsm_lchant_name(ho->new_lchan_type));
+ rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]);
+ return -ENOSPC;
}
- ho->from_hodec_id = from_hodec_id;
- ho->old_lchan = old_lchan;
- ho->new_lchan = new_lchan;
- ho->ho_ref = ho_ref++;
- ho->inter_cell = !do_assignment;
- ho->async = true;
- LOGPHO(ho, LOGL_INFO, "Triggering %s\n", do_assignment? "Assignment" : "Handover");
+ LOGPHO(ho, LOGL_INFO, "Triggering %s\n", ho->inter_cell? "Handover" : "Assignment");
/* copy some parameters from old lchan */
+ old_lchan = ho->old_lchan;
+ new_lchan = ho->new_lchan;
memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr));
- if (do_assignment) {
+ if (!ho->inter_cell) {
new_lchan->ms_power = old_lchan->ms_power;
new_lchan->rqd_ta = old_lchan->rqd_ta;
} else {
new_lchan->ms_power =
- ms_pwr_ctl_lvl(new_bts->band, new_bts->ms_max_power);
+ ms_pwr_ctl_lvl(ho->new_bts->band, ho->new_bts->ms_max_power);
/* FIXME: do we have a better idea of the timing advance? */
//new_lchan->rqd_ta = old_lchan->rqd_ta;
}
@@ -154,24 +190,21 @@ int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan,
memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, sizeof(new_lchan->mr_ms_lv));
memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, sizeof(new_lchan->mr_bts_lv));
- new_lchan->conn = old_lchan->conn;
- new_lchan->conn->ho_lchan = new_lchan;
+ new_lchan->conn = conn;
rc = rsl_chan_activate_lchan(new_lchan,
ho->async ? RSL_ACT_INTER_ASYNC : RSL_ACT_INTER_SYNC,
ho->ho_ref);
if (rc < 0) {
LOGPHO(ho, LOGL_INFO, "%s Failure: activate lchan rc = %d\n",
- do_assignment? "Assignment" : "Handover", rc);
- new_lchan->conn->ho_lchan = NULL;
- new_lchan->conn = NULL;
- talloc_free(ho);
+ ho->inter_cell? "Handover" : "Assignment", rc);
lchan_free(new_lchan);
+ ho->new_lchan = NULL;
+ bsc_clear_handover(conn, 0);
return rc;
}
rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ);
- llist_add(&ho->list, &bsc_handovers);
/* we continue in the SS_LCHAN handler / ho_chan_activ_ack */
return 0;
@@ -180,26 +213,20 @@ int bsc_handover_start(enum hodec_id from_hodec_id, struct gsm_lchan *old_lchan,
/* clear any operation for this connection */
void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan)
{
- struct bsc_handover *ho;
-
- ho = bsc_ho_by_new_lchan(conn->ho_lchan);
-
-
- if (!ho && conn->ho_lchan)
- LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n");
+ struct bsc_handover *ho = conn->ho;
- if (!ho) {
- LOGP(DHO, LOGL_ERROR, "unable to find HO record\n");
+ if (!ho)
return;
- }
-
- conn->ho_lchan->conn = NULL;
- conn->ho_lchan = NULL;
- if (free_lchan)
- lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
+ if (ho->new_lchan) {
+ ho->new_lchan->conn = NULL;
+ if (free_lchan)
+ lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
+ ho->new_lchan = NULL;
+ }
handover_free(ho);
+ conn->ho = NULL;
}
/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */
@@ -211,10 +238,10 @@ static void ho_T3103_cb(void *_ho)
DEBUGP(DHO, "HO T3103 expired\n");
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]);
- ho->new_lchan->conn->ho_lchan = NULL;
- ho->new_lchan->conn = NULL;
- lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END);
- handover_free(ho);
+ /* Inform the GSCON FSM about the timed out handover */
+ osmo_fsm_inst_dispatch(ho->old_lchan->conn->fi, GSCON_EV_HO_TIMEOUT, NULL);
+
+ bsc_clear_handover(ho->old_lchan->conn, 1);
}
/* RSL has acknowledged activation of the new lchan */
@@ -263,12 +290,7 @@ static int ho_chan_activ_nack(struct gsm_lchan *new_lchan)
if (hdc && hdc->on_ho_chan_activ_nack)
hdc->on_ho_chan_activ_nack(ho);
- new_lchan->conn->ho_lchan = NULL;
- new_lchan->conn = NULL;
- handover_free(ho);
-
- /* FIXME: maybe we should try to allocate a new LCHAN here? */
-
+ bsc_clear_handover(new_lchan->conn, 0);
return 0;
}
@@ -296,16 +318,19 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
if (ho->old_lchan != new_lchan->conn->lchan)
LOGPHO(ho, LOGL_ERROR, "Primary lchan changed during handover.\n");
- if (new_lchan != new_lchan->conn->ho_lchan)
+ if (new_lchan->conn->ho != ho)
LOGPHO(ho, LOGL_ERROR, "Handover channel changed during this handover.\n");
- new_lchan->conn->ho_lchan = NULL;
new_lchan->conn->lchan = new_lchan;
ho->old_lchan->conn = NULL;
lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END);
handover_free(ho);
+ new_lchan->conn->ho = NULL;
+
+ /* Inform the GSCON FSM that the handover is complete */
+ osmo_fsm_inst_dispatch(new_lchan->conn->fi, GSCON_EV_HO_COMPL, NULL);
return 0;
}
@@ -314,7 +339,6 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
{
struct gsm_network *net = old_lchan->ts->trx->bts->network;
struct bsc_handover *ho;
- struct gsm_lchan *new_lchan;
struct handover_decision_callbacks *hdc;
ho = bsc_ho_by_old_lchan(old_lchan);
@@ -329,16 +353,10 @@ static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan)
rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]);
- new_lchan = ho->new_lchan;
-
- /* release the channel and forget about it */
- ho->new_lchan->conn->ho_lchan = NULL;
- ho->new_lchan->conn = NULL;
- handover_free(ho);
-
- lchan_release(new_lchan, 0, RSL_REL_LOCAL_END);
-
+ bsc_clear_handover(ho->new_lchan->conn, 1);
+ /* Inform the GSCON FSM that the handover failed */
+ osmo_fsm_inst_dispatch(old_lchan->conn->fi, GSCON_EV_HO_FAIL, NULL);
return 0;
}
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index 3019470d3..cc9674396 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -30,7 +30,6 @@ osmo_bsc_SOURCES = \
osmo_bsc_vty.c \
osmo_bsc_api.c \
osmo_bsc_grace.c \
- osmo_bsc_mgcp.c \
osmo_bsc_msc.c \
osmo_bsc_sigtran.c \
osmo_bsc_filter.c \
diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c
index 75dae1293..40c06dd50 100644
--- a/src/osmo-bsc/osmo_bsc_api.c
+++ b/src/osmo-bsc/osmo_bsc_api.c
@@ -17,6 +17,7 @@
*
*/
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/osmo_bsc.h>
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/debug.h>
@@ -31,24 +32,23 @@
#include <osmocom/bsc/osmo_bsc_sigtran.h>
-#define return_when_not_connected(conn) \
- if (conn->sccp.state != SUBSCR_SCCP_ST_CONNECTED) {\
- LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
- return; \
- }
+/* Check if we have a proper connection to the MSC */
+static bool msc_connected(struct gsm_subscriber_connection *conn)
+{
+ /* No subscriber conn at all */
+ if (!conn)
+ return false;
-#define return_when_not_connected_val(conn, ret) \
- if (conn->sccp.state != SUBSCR_SCCP_ST_CONNECTED) {\
- LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \
- return ret; \
- }
+ /* Connection to MSC not established */
+ if (!conn->sccp.msc)
+ return false;
-#define queue_msg_or_return(resp) \
- if (!resp) { \
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \
- return; \
- } \
- osmo_bsc_sigtran_send(conn, resp);
+ /* Reset procedure not (yet) executed */
+ if (a_reset_conn_ready(conn->sccp.msc->a.reset) == false)
+ return false;
+
+ return true;
+}
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
static int complete_layer3(struct gsm_subscriber_connection *conn,
@@ -136,24 +136,33 @@ static int bsc_filter_data(struct gsm_subscriber_connection *conn,
static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci)
{
+ int rc;
struct msgb *resp;
- return_when_not_connected(conn);
- LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci);
+ if (!msc_connected(conn))
+ return;
+ LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci);
resp = gsm0808_create_sapi_reject(dlci);
- queue_msg_or_return(resp);
+ rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+ if (rc != 0)
+ msgb_free(resp);
}
static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn,
struct msgb *msg, uint8_t chosen_encr)
{
+ int rc;
struct msgb *resp;
- return_when_not_connected(conn);
+
+ if (!msc_connected(conn))
+ return;
LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n");
resp = gsm0808_create_cipher_complete(msg, chosen_encr);
- queue_msg_or_return(resp);
+ rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+ if (rc != 0)
+ msgb_free(resp);
}
static void bsc_send_ussd_no_srv(struct gsm_subscriber_connection *conn,
@@ -274,15 +283,11 @@ static int complete_layer3(struct gsm_subscriber_connection *conn,
resp = gsm0808_create_layer3_2(msg, cgi_for_msc(conn->sccp.msc, conn_get_bts(conn)), NULL);
if (!resp) {
LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n");
- osmo_bsc_sigtran_del_conn(conn);
+ //osmo_bsc_sigtran_del_conn(conn);
return BSC_API_CONN_POL_REJECT;
}
- if (osmo_bsc_sigtran_open_conn(conn, resp) != 0) {
- osmo_bsc_sigtran_del_conn(conn);
- msgb_free(resp);
- return BSC_API_CONN_POL_REJECT;
- }
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, resp);
return BSC_API_CONN_POL_ACCEPT;
}
@@ -307,7 +312,7 @@ static int move_to_msc(struct gsm_subscriber_connection *_conn,
*/
if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) {
gsm0808_clear(_conn);
- bsc_subscr_con_free(_conn);
+ //bsc_subscr_con_free(_conn);
return 1;
}
@@ -376,8 +381,9 @@ static int handle_cc_setup(struct gsm_subscriber_connection *conn,
static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg)
{
int lu_cause;
- struct msgb *resp;
- return_when_not_connected(conn);
+
+ if (!msc_connected(conn))
+ return;
LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id);
@@ -399,16 +405,22 @@ static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, st
bsc_scan_bts_msg(conn, msg);
- resp = gsm0808_create_dtap(msg, link_id);
- queue_msg_or_return(resp);
+ /* Store link_id in msg->cb */
+ OBSC_LINKID_CB(msg) = link_id;
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MO_DTAP, msg);
}
static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause,
uint8_t chosen_channel, uint8_t encr_alg_id,
uint8_t speech_model)
{
- struct msgb *resp;
- return_when_not_connected(conn);
+ if (!msc_connected(conn))
+ return;
+
+ conn->lchan->abis_ip.ass_compl.rr_cause = rr_cause;
+ conn->lchan->abis_ip.ass_compl.chosen_channel = chosen_channel;
+ conn->lchan->abis_ip.ass_compl.encr_alg_id = encr_alg_id;
+ conn->lchan->abis_ip.ass_compl.speech_mode = speech_model;
if (is_ipaccess_bts(conn_get_bts(conn)) && conn->user_plane.rtp_ip) {
/* NOTE: In a network that makes use of an IPA base station
@@ -419,37 +431,29 @@ static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_
* postpone the AoIP assignment completed message until we
* know the RTP IP/Port combination. */
LOGP(DMSC, LOGL_INFO, "POSTPONE MSC ASSIGN COMPL\n");
- conn->lchan->abis_ip.ass_compl.rr_cause = rr_cause;
- conn->lchan->abis_ip.ass_compl.chosen_channel = chosen_channel;
- conn->lchan->abis_ip.ass_compl.encr_alg_id = encr_alg_id;
- conn->lchan->abis_ip.ass_compl.speech_mode = speech_model;
conn->lchan->abis_ip.ass_compl.valid = true;
} else {
/* NOTE: Send the A assignment complete message immediately. */
LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n");
- resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel,
- encr_alg_id, speech_model);
- queue_msg_or_return(resp);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RR_ASS_COMPL, NULL);
}
}
static void bsc_assign_fail(struct gsm_subscriber_connection *conn,
uint8_t cause, uint8_t *rr_cause)
{
- struct msgb *resp;
- return_when_not_connected(conn);
-
LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN FAIL\n");
-
- resp = gsm0808_create_assignment_failure(cause, rr_cause);
- queue_msg_or_return(resp);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RR_ASS_FAIL, NULL);
}
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause)
{
+ int rc;
struct msgb *resp;
- return_when_not_connected_val(conn, 1);
+
+ if (!msc_connected(conn))
+ return 1;
LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n");
@@ -459,7 +463,10 @@ static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t ca
return 1;
}
- osmo_bsc_sigtran_send(conn, resp);
+ rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+ if (rc != 0)
+ msgb_free(resp);
+
return 1;
}
@@ -467,12 +474,16 @@ static void bsc_cm_update(struct gsm_subscriber_connection *conn,
const uint8_t *cm2, uint8_t cm2_len,
const uint8_t *cm3, uint8_t cm3_len)
{
+ int rc;
struct msgb *resp;
- return_when_not_connected(conn);
- resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len);
+ if (!msc_connected(conn))
+ return;
- queue_msg_or_return(resp);
+ resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len);
+ rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
+ if (rc != 0)
+ msgb_free(resp);
}
static void bsc_mr_config(struct gsm_subscriber_connection *conn,
diff --git a/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c
index 295d8543a..99a3ebdf4 100644
--- a/src/osmo-bsc/osmo_bsc_audio.c
+++ b/src/osmo-bsc/osmo_bsc_audio.c
@@ -29,7 +29,8 @@
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
-#include <osmocom/bsc/osmo_bsc_mgcp.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
+#include <osmocom/bsc/bsc_subscriber.h>
#include <arpa/inet.h>
@@ -71,13 +72,17 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
break;
case S_ABISIP_MDCX_ACK:
- if (con->ho_lchan) {
- LOGP(DHO, LOGL_DEBUG, "%s -> %s BTS sent MDCX ACK\n", gsm_lchan_name(lchan),
- gsm_lchan_name(con->ho_lchan));
+ if (con->ho) {
+ LOGPHO(con->ho, LOGL_DEBUG, "BTS sent MDCX ACK\n");
/* No need to do anything for handover here. As soon as a HANDOVER DETECT
* happens, osmo_bsc_mgcp.c will trigger the MGCP MDCX towards MGW by
- * receiving an S_LCHAN_HANDOVER_DETECT signal. */
+ * receiving an S_LCHAN_HANDOVER_DETECT signal.
+ *
+ * FIXME: This will not work, osmo_bsc_mgcp.c is now removed. The
+ * switchover must be handled by the GSCON FSM because there we
+ * we instantiate the child FSMs which handle the MGCP traffic. */
#if 0
+/* FIXME: This does not work anymore, we will have to implement this in the GSCON FSM */
/* NOTE: When an ho_lchan exists, the MDCX is part of an
* handover operation (intra-bsc). This means we will not
* inform the MSC about the event, which means that no
@@ -92,7 +97,8 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
* IPA based base stations. See also osmo_bsc_api.c,
* function bsc_assign_compl() */
LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL (POSTPONED)\n");
- mgcp_ass_complete(con->user_plane.mgcp_ctx, lchan);
+ osmo_fsm_inst_dispatch(con->fi, GSCON_EV_RR_ASS_COMPL, NULL);
+
}
break;
}
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 176a41374..c489300ac 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -1,6 +1,7 @@
/* GSM 08.08 BSSMAP handling */
/* (C) 2009-2012 by Holger Hans Peter Freyther <zecke@selfish.org>
* (C) 2009-2012 by On-Waves
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
@@ -24,9 +25,9 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/bsc_subscriber.h>
-#include <osmocom/bsc/osmo_bsc_mgcp.h>
#include <osmocom/bsc/paging.h>
#include <osmocom/bsc/gsm_04_08_utils.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
@@ -536,45 +537,6 @@ static int select_best_cipher(uint8_t msc_mask, uint8_t bsc_mask)
}
/*
- * GSM 08.08 § 3.1.9.1 and 3.2.1.21...
- * release our gsm_subscriber_connection and send message
- */
-static int bssmap_handle_clear_command(struct gsm_subscriber_connection *conn,
- struct msgb *msg, unsigned int payload_length)
-{
- struct msgb *resp;
-
- /* TODO: handle the cause of this package */
-
- LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn);
- gsm0808_clear(conn);
-
- /* generate the clear complete message */
- resp = gsm0808_create_clear_complete();
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
- return -1;
- }
-
- if (conn->user_plane.mgcp_ctx) {
- /* NOTE: This is the AoIP case, osmo-bsc has to negotiate with
- * the MGCP-GW. For this an mgcp_ctx should be created that
- * contains the FSM and some system data. When the connection
- * is removed from the MGCP-GW, then osmo_bsc_sigtran_send()
- * calls osmo_bsc_sigtran_send(). */
- mgcp_clear_complete(conn->user_plane.mgcp_ctx, resp);
- } else {
- /* NOTE: This is the SCCP-Lite case, since we do not handle
- * the MGCP-GW switching ourselves, we may skip everything
- * that is MGCP-GW related and sent the clear complete message
- * directly */
- osmo_bsc_sigtran_send(conn, resp);
- }
-
- return 0;
-}
-
-/*
* GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick
* the cipher to be used for this. In case we are already using
* a cipher we will have to send cipher mode reject to the MSC,
@@ -669,7 +631,7 @@ reject:
return -1;
}
- osmo_bsc_sigtran_send(conn, resp);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
return -1;
}
@@ -735,118 +697,129 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn,
}
/* Currently we only support a limited subset of all
- * possible channel types. The limitation ends by not using
- * multi-slot, limiting the channel coding to speech */
- if (ct.ch_indctr != GSM0808_CHAN_SPEECH) {
+ * possible channel types, such as multi-slot or CSD */
+ switch (ct.ch_indctr) {
+ case GSM0808_CHAN_DATA:
LOGP(DMSC, LOGL_ERROR, "Unsupported channel type, currently only speech is supported!\n");
cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP;
goto reject;
- }
-
- /* Detect if a CIC code is present, if so, we use the classic ip.access
- * method to calculate the RTP port */
- if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
- conn->user_plane.cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- timeslot = conn->user_plane.cic & 0x1f;
- multiplex = (conn->user_plane.cic & ~0x1f) >> 5;
- } else if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
- /* Decode AoIP transport address element */
- rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR),
- TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode AoIP transport address.\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ case GSM0808_CHAN_SPEECH:
+ /* Detect if a CIC code is present, if so, we use the classic ip.access method to
+ * calculate the RTP port */
+ if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
+ conn->user_plane.cic =
+ osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
+ timeslot = conn->user_plane.cic & 0x1f;
+ multiplex = (conn->user_plane.cic & ~0x1f) >> 5;
+ } else if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
+ /* Decode AoIP transport address element */
+ rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr,
+ TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR),
+ TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode AoIP transport address.\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ aoip = true;
+ } else {
+ LOGP(DMSC, LOGL_ERROR, "AoIP transport address and CIC missing. "
+ "Audio will not work.\n");
+ cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
goto reject;
}
- aoip = true;
- } else {
- LOGP(DMSC, LOGL_ERROR, "AoIP transport address and CIC missing. Audio will not work.\n");
- cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
- goto reject;
- }
- /* Decode speech codec list (AoIP) */
- conn->codec_list_present = false;
- if (aoip) {
- /* Check for speech codec list element */
- if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) {
- LOGP(DMSC, LOGL_ERROR, "Mandatory speech codec list not present.\n");
- cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ /* FIXME: At the moment osmo-bsc does not support any other
+ * A-Interface other than AoIP. So we must reject all
+ * assignment requests that are not AoIP compliant. However,
+ * might support other A-Interface dialects lateron again,
+ * thats why we preserve the logic around the AoIP detection
+ * here. */
+ if (!aoip) {
+ LOGP(DMSC, LOGL_ERROR, "Requested A-Interface type is not supported! (AoIP only!)\n");
+ cause = GSM0808_CAUSE_REQ_A_IF_TYPE_NOT_SUPP;
goto reject;
}
- /* Decode Speech Codec list */
- rc = gsm0808_dec_speech_codec_list(&conn->codec_list,
- TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST),
- TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST));
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "Unable to decode speech codec list\n");
- cause = GSM0808_CAUSE_INCORRECT_VALUE;
- goto reject;
+ /* Decode speech codec list (AoIP) */
+ conn->codec_list_present = false;
+ if (aoip) {
+
+ /* Check for speech codec list element */
+ if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) {
+ LOGP(DMSC, LOGL_ERROR, "Mandatory speech codec list not present.\n");
+ cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING;
+ goto reject;
+ }
+
+ /* Decode Speech Codec list */
+ rc = gsm0808_dec_speech_codec_list(&conn->codec_list,
+ TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST),
+ TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST));
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "Unable to decode speech codec list\n");
+ cause = GSM0808_CAUSE_INCORRECT_VALUE;
+ goto reject;
+ }
+ conn->codec_list_present = true;
+ scl_ptr = &conn->codec_list;
}
- conn->codec_list_present = true;
- scl_ptr = &conn->codec_list;
- }
- /* Match codec information from the assignment command against the
- * local preferences of the BSC */
- rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc);
- if (rc < 0) {
- LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="
- " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
- ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
- /* TODO: actually output codec names, e.g. implement gsm0808_permitted_speech_names[] and
- * iterate perm_spch. */
- cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
- goto reject;
- }
- DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="
- " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
- full_rate? "full rate" : "half rate",
- get_value_string(gsm48_chan_mode_names, chan_mode),
- ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
-
- /* Forward the assignment request to lower layers */
- if (aoip) {
- /* Store network side RTP connection information, we will
- * process this address later after we have established an RTP
- * connection to the BTS. This is just for organizational
- * reasons, functional wise it would not matter when exactly
- * the network side RTP connection is made, as long it is made
- * before we return with the assignment complete message. */
- memcpy(&conn->user_plane.aoip_rtp_addr_remote, &rtp_addr, sizeof(rtp_addr));
-
- /* Create an assignment request using the MGCP fsm. This FSM
- * is directly started when its created (now) and will also
- * take care about the further processing (creating RTP
- * endpoints, calling gsm0808_assign_req(), responding to
- * the assignment request etc... */
- conn->user_plane.mgcp_ctx = mgcp_assignm_req(msc->network, msc->network->mgw.client,
- conn, chan_mode, full_rate);
- if (!conn->user_plane.mgcp_ctx) {
- LOGP(DMSC, LOGL_ERROR, "MGCP / MGW failure, rejecting assignment... (id=%i)\n",
- conn->sccp.conn_id);
- cause = GSM0808_CAUSE_EQUIPMENT_FAILURE;
+ /* Match codec information from the assignment command against the
+ * local preferences of the BSC */
+ rc = match_codec_pref(&full_rate, &chan_mode, &ct, scl_ptr, msc);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="
+ " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+ ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
+ /* TODO: actually output codec names, e.g. implement
+ * gsm0808_permitted_speech_names[] and iterate perm_spch. */
+ cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
goto reject;
}
- /* We now may return here, the FSM will do all further work */
- return 0;
- } else {
- /* Note: In the sccp-lite case we to not perform any mgcp operation,
- * (the MSC does that for us). We set conn->rtp_ip to 0 and check
- * on this later. By this we know that we have to behave accordingly
- * to sccp-lite. */
- conn->user_plane.rtp_port = mgcp_timeslot_to_port(multiplex, timeslot, msc->rtp_base);
- conn->user_plane.rtp_ip = 0;
- return gsm0808_assign_req(conn, chan_mode, full_rate);
+ DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="
+ " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+ full_rate? "full rate" : "half rate",
+ get_value_string(gsm48_chan_mode_names, chan_mode),
+ ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
+
+ /* Forward the assignment request to lower layers */
+ if (aoip) {
+ /* Store network side RTP connection information, we will
+ * process this address later after we have established an RTP
+ * connection to the BTS. This is just for organizational
+ * reasons, functional wise it would not matter when exactly
+ * the network side RTP connection is made, as long it is made
+ * before we return with the assignment complete message. */
+ memcpy(&conn->user_plane.aoip_rtp_addr_remote, &rtp_addr, sizeof(rtp_addr));
+ } else {
+ /* Note: In the sccp-lite case we to not perform any mgcp operation,
+ * (the MSC does that for us). We set conn->rtp_ip to 0 and check
+ * on this later. By this we know that we have to behave accordingly
+ * to sccp-lite. */
+ conn->user_plane.rtp_port = mgcp_timeslot_to_port(multiplex, timeslot, msc->rtp_base);
+ conn->user_plane.rtp_ip = 0;
+ }
+ conn->user_plane.chan_mode = chan_mode;
+ conn->user_plane.full_rate = full_rate;
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL);
+ break;
+ case GSM0808_CHAN_SIGN:
+ conn->user_plane.chan_mode = GSM48_CMODE_SIGN;
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL);
+ break;
+ default:
+ cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS;
+ goto reject;
}
+ return 0;
reject:
resp = gsm0808_create_assignment_failure(cause, NULL);
OSMO_ASSERT(resp);
- osmo_bsc_sigtran_send(conn, resp);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_TX_SCCP, resp);
return -1;
}
@@ -897,7 +870,7 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn,
switch (msg->l4h[0]) {
case BSS_MAP_MSG_CLEAR_CMD:
- ret = bssmap_handle_clear_command(conn, msg, length);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CLEAR_CMD, msg);
break;
case BSS_MAP_MSG_CIPHER_MODE_CMD:
ret = bssmap_handle_cipher_mode(conn, msg, length);
@@ -958,7 +931,9 @@ static int dtap_rcvmsg(struct gsm_subscriber_connection *conn,
/* pass it to the filter for extra actions */
rc = bsc_scan_msc_msg(conn, gsm48);
- dtap_rc = gsm0808_submit_dtap(conn, gsm48, header->link_id, 1);
+ /* Store link_id in msgb->cb */
+ OBSC_LINKID_CB(msg) = header->link_id;
+ dtap_rc = osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_MT_DTAP, gsm48);
if (rc == BSS_SEND_USSD)
bsc_send_welcome_ussd(conn);
return dtap_rc;
@@ -1016,38 +991,3 @@ int bsc_handle_dt(struct gsm_subscriber_connection *conn,
return -1;
}
-
-/* Generate and send assignment complete message */
-int bssmap_send_aoip_ass_compl(struct gsm_lchan *lchan)
-{
- struct msgb *resp;
- struct gsm0808_speech_codec sc;
- struct gsm_subscriber_connection *conn;
-
- conn = lchan->conn;
-
- OSMO_ASSERT(lchan->abis_ip.ass_compl.valid);
- OSMO_ASSERT(conn);
-
- LOGP(DMSC, LOGL_DEBUG, "Sending assignment complete message... (id=%i)\n", conn->sccp.conn_id);
-
- /* Extrapolate speech codec from speech mode */
- gsm0808_speech_codec_from_chan_type(&sc, lchan->abis_ip.ass_compl.speech_mode);
-
- /* Generate message */
- resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause,
- lchan->abis_ip.ass_compl.chosen_channel,
- lchan->abis_ip.ass_compl.encr_alg_id,
- lchan->abis_ip.ass_compl.speech_mode,
- &conn->user_plane.aoip_rtp_addr_local,
- &sc,
- NULL);
-
- if (!resp) {
- LOGP(DMSC, LOGL_ERROR, "Failed to generate assignment completed message! (id=%i)\n",
- conn->sccp.conn_id);
- return -EINVAL;
- }
-
- return osmo_bsc_sigtran_send(conn, resp);
-}
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index af8f83dae..27c5640ea 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -14,7 +14,7 @@
* 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 <http://www.gnu.org/licenses/>.
+ * along with this program. If not, see <http://www.gnu.org/lienses/>.
*
*/
@@ -29,7 +29,6 @@
#include <osmocom/bsc/ipaccess.h>
#include <osmocom/bsc/ctrl.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
-#include <osmocom/bsc/osmo_bsc_mgcp.h>
#include <osmocom/bsc/handover_decision.h>
#include <osmocom/bsc/handover_decision_2.h>
@@ -511,8 +510,6 @@ int main(int argc, char **argv)
exit(1);
}
- mgcp_init(bsc_gsmnet);
-
handover_decision_1_init();
hodec2_init(bsc_gsmnet);
diff --git a/src/osmo-bsc/osmo_bsc_mgcp.c b/src/osmo-bsc/osmo_bsc_mgcp.c
deleted file mode 100644
index 4b6420e9b..000000000
--- a/src/osmo-bsc/osmo_bsc_mgcp.c
+++ /dev/null
@@ -1,1149 +0,0 @@
-/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
- * All Rights Reserved
- *
- * Author: Philipp Maier
- *
- * 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 <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <osmocom/mgcp_client/mgcp_client.h>
-#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/osmo_bsc_mgcp.h>
-#include <osmocom/bsc/debug.h>
-#include <osmocom/bsc/osmo_bsc.h>
-#include <osmocom/bsc/signal.h>
-#include <osmocom/core/logging.h>
-#include <osmocom/core/utils.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/core/fsm.h>
-#include <osmocom/bsc/osmo_bsc_sigtran.h>
-#include <osmocom/core/byteswap.h>
-#include <arpa/inet.h>
-
-#define S(x) (1 << (x))
-
-#define MGCP_MGW_TIMEOUT 4 /* in seconds */
-#define MGCP_MGW_TIMEOUT_TIMER_NR 1
-#define MGCP_BSS_TIMEOUT 4 /* in seconds */
-#define MGCP_BSS_TIMEOUT_TIMER_NR 2
-
-#define MGCP_ENDPOINT_FORMAT "%x@mgw"
-
-/* Some internal cause codes to indicate fault
- * condition inside the FSM */
-enum bsc_mgcp_cause_code {
- MGCP_ERR_MGW_FAIL,
- MGCP_ERR_MGW_INVAL_RESP,
- MGCP_ERR_MGW_TX_FAIL,
- MGCP_ERR_UNEXP_TEARDOWN,
- MGCP_ERR_ASSGMNT_FAIL,
- MGCP_ERR_UNSUPP_ADDR_FMT,
- MGCP_ERR_BSS_TIMEOUT,
- MGCP_ERR_NOMEM
-};
-
-/* Human readable respresentation of the faul codes,
- * will be displayed by handle_error() */
-static const struct value_string bsc_mgcp_cause_codes_names[] = {
- {MGCP_ERR_MGW_FAIL, "operation failed on MGW"},
- {MGCP_ERR_MGW_INVAL_RESP, "invalid / unparseable response from MGW"},
- {MGCP_ERR_MGW_TX_FAIL, "failed to transmit MGCP message to MGW"},
- {MGCP_ERR_UNEXP_TEARDOWN, "unexpected connection teardown (BSS)"},
- {MGCP_ERR_ASSGMNT_FAIL, "assignment failure (BSS)"},
- {MGCP_ERR_UNSUPP_ADDR_FMT, "unsupported network address format used (MSC)"},
- {MGCP_ERR_BSS_TIMEOUT, "assignment could not be completed in time (BSS)"},
- {MGCP_ERR_NOMEM, "out of memory"},
- {0, NULL}
-};
-
-enum fsm_bsc_mgcp_states {
- ST_CRCX_BTS,
- ST_ASSIGN_PROC,
- ST_MDCX_BTS,
- ST_CRCX_NET,
- ST_ASSIGN_COMPL,
- ST_CALL,
- ST_MDCX_BTS_HO,
- ST_HALT
-};
-
-enum bsc_mgcp_fsm_evt {
- /* Initial event: start off the state machine */
- EV_INIT,
-
- /* External event: Assignment complete, event is issued shortly before
- * the assignment complete message is sent via the A-Interface */
- EV_ASS_COMPLETE,
-
- /* External event: Teardown event, this event is used to notify the end
- * of a call. It is also issued in case of errors to teardown a half
- * open connection. */
- EV_TEARDOWN,
-
- /* External event: Handover event, this event notifies the FSM that a
- * handover is required. The FSM will then perform an extra MDCX to
- * configure the new connection data at the MGW. The only valid state
- * where a Handover event can be received is ST_CALL. */
- EV_HANDOVER,
-
- /* Internal event: The mgcp_gw has sent its CRCX response for
- * the BTS side */
- EV_CRCX_BTS_RESP,
-
- /* Internal event: The mgcp_gw has sent its MDCX response for
- * the BTS side */
- EV_MDCX_BTS_RESP,
-
- /* Internal event: The mgcp_gw has sent its CRCX response for
- * the NET side */
- EV_CRCX_NET_RESP,
-
- /* Internal event: The mgcp_gw has sent its DLCX response for
- * the NET and BTS side */
- EV_DLCX_ALL_RESP,
-
- /* Internal event: The mgcp_gw has responded to the (Handover-)
- MDCX that has been send to update the BTS connection. */
- EV_MDCX_BTS_HO_RESP,
-};
-
-/* A general error handler function. On error we still have an interest to
- * remove a half open connection (if possible). This function will execute
- * a controlled jump to the DLCX phase. From there, the FSM will then just
- * continue like the call were ended normally */
-#define handle_error(mgcp_ctx, cause) \
- _handle_error(mgcp_ctx, cause, __FILE__, __LINE__)
-static void _handle_error(struct mgcp_ctx *mgcp_ctx, enum bsc_mgcp_cause_code cause,
- const char *file, int line)
-{
- struct osmo_fsm_inst *fi;
-
- OSMO_ASSERT(mgcp_ctx);
- fi = mgcp_ctx->fsm;
- OSMO_ASSERT(fi);
-
- LOGPFSMLSRC(mgcp_ctx->fsm, LOGL_ERROR, file, line, "%s -- graceful shutdown...\n",
- get_value_string(bsc_mgcp_cause_codes_names, cause));
-
- /* Set the VM into the state where it waits for the call end */
- osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
-
- /* Simulate the call end by sending a teardown event, so that
- * the FSM proceeds directly with the DLCX */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
-}
-
-static void crcx_for_bts_resp_cb(struct mgcp_response *r, void *priv);
-
-/* Callback for ST_CRCX_BTS: startup state machine send out CRCX for BTS side */
-static void fsm_crcx_bts_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
- struct gsm_subscriber_connection *conn;
- struct msgb *msg;
- struct mgcp_msg mgcp_msg;
- struct mgcp_client *mgcp;
- uint16_t rtp_endpoint;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
-
- rtp_endpoint = mgcp_client_next_endpoint(mgcp);
- mgcp_ctx->rtp_endpoint = rtp_endpoint;
-
- LOGPFSML(fi, LOGL_DEBUG,
- "CRCX/BTS: creating connection for the BTS side on MGW endpoint:0x%x...\n", rtp_endpoint);
-
- /* Generate MGCP message string */
- mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_CRCX,
- .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE),
- .call_id = conn->sccp.conn_id,
- .conn_mode = MGCP_CONN_LOOPBACK
- };
- if (snprintf(mgcp_msg.endpoint, MGCP_ENDPOINT_MAXLEN, MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
- MGCP_ENDPOINT_MAXLEN) {
- handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
- return;
- }
- msg = mgcp_msg_gen(mgcp, &mgcp_msg);
- OSMO_ASSERT(msg);
-
- /* Transmit MGCP message to MGW */
- mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
- rc = mgcp_client_tx(mgcp, msg, crcx_for_bts_resp_cb, mgcp_ctx);
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, ST_ASSIGN_PROC, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
-}
-
-/* Callback for MGCP-Client: handle response for BTS associated CRCX */
-static void crcx_for_bts_resp_cb(struct mgcp_response *r, void *priv)
-{
- struct mgcp_ctx *mgcp_ctx = priv;
- int rc;
- struct gsm_subscriber_connection *conn;
- uint32_t addr;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR,
- "CRCX/BTS: late MGW response, FSM already terminated -- ignoring...\n");
- return;
- }
-
- if (r->head.response_code != 200) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
- "CRCX/BTS: response yields error: %d %s\n", r->head.response_code, r->head.comment);
- handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
- return;
- }
-
- /* memorize connection identifier */
- osmo_strlcpy(mgcp_ctx->conn_id_bts, r->head.conn_id, sizeof(mgcp_ctx->conn_id_bts));
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with CI: %s\n", mgcp_ctx->conn_id_bts);
-
- rc = mgcp_response_parse_params(r);
- if (rc) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/BTS: Cannot parse response\n");
- handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
- return;
- }
-
- addr = inet_addr(r->audio_ip);
- if (addr == INADDR_NONE) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/BTS: Cannot parse response (invalid IP-address)\n");
- handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
-
- /* Set the connection details in the conn struct. The code that
- * controls the BTS via RSL will take these values and signal them
- * to the BTS via RSL/IPACC */
- conn->user_plane.rtp_port = r->audio_port;
- conn->user_plane.rtp_ip = osmo_ntohl(addr);
-
- /* Notify the FSM that we got the response. */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_BTS_RESP, mgcp_ctx);
-}
-
-/* Callback for ST_ASSIGN_PROC: An mgcp response has been received, proceed
- * with the assignment request */
-static void fsm_proc_assignmnent_req_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
- struct gsm_subscriber_connection *conn;
- enum gsm48_chan_mode chan_mode;
- bool full_rate;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
-
- switch (event) {
- case EV_CRCX_BTS_RESP:
- break;
- default:
- handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
- return;
- }
-
- OSMO_ASSERT(conn);
- chan_mode = mgcp_ctx->chan_mode;
- full_rate = mgcp_ctx->full_rate;
-
- LOGPFSML(fi, LOGL_DEBUG, "MGW proceeding assignment request...\n");
- rc = gsm0808_assign_req(conn, chan_mode, full_rate);
-
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_ASSGMNT_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, ST_MDCX_BTS, MGCP_BSS_TIMEOUT, MGCP_BSS_TIMEOUT_TIMER_NR);
-}
-
-static void mdcx_for_bts_resp_cb(struct mgcp_response *r, void *priv);
-
-/* Callback for ST_MDCX_BTS: When the BSS has completed the assignment,
- * proceed with updating the connection for the BTS side */
-static void fsm_mdcx_bts_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
- struct gsm_subscriber_connection *conn;
- struct gsm_lchan *lchan;
- struct msgb *msg;
- struct mgcp_msg mgcp_msg;
- struct mgcp_client *mgcp;
- uint16_t rtp_endpoint;
- struct in_addr addr;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
-
- switch (event) {
- case EV_ASS_COMPLETE:
- break;
- default:
- handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
- return;
- }
-
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
- lchan = mgcp_ctx->lchan;
- OSMO_ASSERT(lchan);
-
- rtp_endpoint = mgcp_ctx->rtp_endpoint;
-
- addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
- LOGPFSML(fi, LOGL_DEBUG,
- "MDCX/BTS: completing connection for the BTS side on MGW endpoint:0x%x, BTS expects RTP input on address %s:%u\n",
- rtp_endpoint, inet_ntoa(addr), lchan->abis_ip.bound_port);
-
- /* Generate MGCP message string */
- mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_MDCX,
- .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
- MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT),
- .call_id = conn->sccp.conn_id,
- .conn_id = mgcp_ctx->conn_id_bts,
- .conn_mode = MGCP_CONN_RECV_SEND,
- .audio_ip = inet_ntoa(addr),
- .audio_port = lchan->abis_ip.bound_port
- };
- if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
- sizeof(mgcp_msg.endpoint)) {
- handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
- return;
- }
- msg = mgcp_msg_gen(mgcp, &mgcp_msg);
- OSMO_ASSERT(msg);
-
- /* Transmit MGCP message to MGW */
- mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
- rc = mgcp_client_tx(mgcp, msg, mdcx_for_bts_resp_cb, mgcp_ctx);
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, ST_CRCX_NET, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
-}
-
-/* Callback for MGCP-Client: handle response for BTS associated MDCX */
-static void mdcx_for_bts_resp_cb(struct mgcp_response *r, void *priv)
-{
- struct mgcp_ctx *mgcp_ctx = priv;
- int rc;
- struct in_addr addr;
- struct gsm_lchan *lchan;
-
- OSMO_ASSERT(mgcp_ctx);
- lchan = mgcp_ctx->lchan;
- OSMO_ASSERT(lchan);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR,
- "MDCX/BTS: late MGW response, FSM already terminated -- ignoring...\n");
- return;
- }
-
- if (r->head.response_code != 200) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
- "MDCX/BTS: response yields error: %d %s\n", r->head.response_code, r->head.comment);
- handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
- return;
- }
-
- rc = mgcp_response_parse_params(r);
- if (rc) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "MDCX/BTS: Cannot parse MDCX response\n");
- handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "MDCX/BTS: MGW responded with address %s:%u\n", r->audio_ip, r->audio_port);
-
- addr.s_addr = lchan->abis_ip.bound_ip;
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
- "MDCX/BTS: corresponding lchan has been bound to address %s:%u\n",
- inet_ntoa(addr), lchan->abis_ip.bound_port);
-
- /* Notify the FSM that we got the response. */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_BTS_RESP, mgcp_ctx);
-}
-
-static void crcx_for_net_resp_cb(struct mgcp_response *r, void *priv);
-
-/* Callback for ST_CRCX_NET: An mgcp response has been received, proceed... */
-static void fsm_crcx_net_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
- struct gsm_subscriber_connection *conn;
- struct msgb *msg;
- struct mgcp_msg mgcp_msg;
- struct mgcp_client *mgcp;
- uint16_t rtp_endpoint;
- struct sockaddr_in *sin;
- char *addr;
- uint16_t port;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
-
- switch (event) {
- case EV_MDCX_BTS_RESP:
- break;
- default:
- handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
- return;
- }
-
- rtp_endpoint = mgcp_ctx->rtp_endpoint;
-
- LOGPFSML(fi, LOGL_DEBUG,
- "CRCX/NET: creating connection for the NET side on MGW endpoint:0x%x...\n", rtp_endpoint);
-
- /* Currently we only have support for IPv4 in our MGCP software, the
- * AoIP part is ready to support IPv6 in theory, because the IE
- * parser/generator uses sockaddr_storage for the AoIP transport
- * identifier. However, the MGW does not support IPv6 yet. This is
- * why we stop here in case some MSC tries to signal IPv6 AoIP
- * transport identifiers */
- if (conn->user_plane.aoip_rtp_addr_remote.ss_family != AF_INET) {
- LOGPFSML(fi, LOGL_ERROR,
- "CRCX/NET: endpoint:%x MSC uses unsupported address format in AoIP transport identifier -- aborting...\n",
- rtp_endpoint);
- handle_error(mgcp_ctx, MGCP_ERR_UNSUPP_ADDR_FMT);
- return;
- }
-
- sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote;
- addr = inet_ntoa(sin->sin_addr);
- port = osmo_ntohs(sin->sin_port);
- LOGPFSML(fi, LOGL_DEBUG, "CRCX/NET: MSC expects RTP input on address %s:%u\n", addr, port);
-
- /* Generate MGCP message string */
- mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_CRCX,
- .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_MODE |
- MGCP_MSG_PRESENCE_AUDIO_IP | MGCP_MSG_PRESENCE_AUDIO_PORT),
- .call_id = conn->sccp.conn_id,
- .conn_mode = MGCP_CONN_RECV_SEND,
- .audio_ip = addr,
- .audio_port = port
- };
- if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
- sizeof(mgcp_msg.endpoint)) {
- handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
- return;
- }
- msg = mgcp_msg_gen(mgcp, &mgcp_msg);
- OSMO_ASSERT(msg);
-
- /* Transmit MGCP message to MGW */
- mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
- rc = mgcp_client_tx(mgcp, msg, crcx_for_net_resp_cb, mgcp_ctx);
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(fi, ST_ASSIGN_COMPL, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
-}
-
-/* Callback for MGCP-Client: handle response for NET associated CRCX */
-static void crcx_for_net_resp_cb(struct mgcp_response *r, void *priv)
-{
- struct mgcp_ctx *mgcp_ctx = priv;
- int rc;
- struct gsm_subscriber_connection *conn;
- struct gsm_lchan *lchan;
- struct sockaddr_in *sin;
- uint32_t addr;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- lchan = mgcp_ctx->lchan;
- OSMO_ASSERT(lchan);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR,
- "CRCX/NET: late MGW response, FSM already terminated -- ignoring...\n");
- return;
- }
-
- if (r->head.response_code != 200) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
- "CRCX/NET: response yields error: %d %s\n", r->head.response_code, r->head.comment);
- handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
- return;
- }
-
- /* memorize connection identifier */
- osmo_strlcpy(mgcp_ctx->conn_id_net, r->head.conn_id, sizeof(mgcp_ctx->conn_id_net));
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/NET: MGW responded with CI: %s\n", mgcp_ctx->conn_id_net);
-
- rc = mgcp_response_parse_params(r);
- if (rc) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/NET: Cannot parse CRCX response\n");
- handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
- return;
- }
-
- addr = inet_addr(r->audio_ip);
- if (addr == INADDR_NONE) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "CRCX/NET: Cannot parse response (invalid IP-address)\n");
- handle_error(mgcp_ctx, MGCP_ERR_MGW_INVAL_RESP);
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "CRCX/NET: MGW responded with address %s:%u\n",
- r->audio_ip, r->audio_port);
-
- /* Store address */
- sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_local;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = addr;
- sin->sin_port = osmo_ntohs(r->audio_port);
-
- /* Notify the FSM that we got the response. */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_CRCX_NET_RESP, mgcp_ctx);
-}
-
-/* Callback for ST_ASSIGN_COMPL: Send back assignment complete and wait until the call ends */
-static void fsm_send_assignment_complete(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
- struct gsm_lchan *lchan;
-
- OSMO_ASSERT(mgcp_ctx);
-
- switch (event) {
- case EV_CRCX_NET_RESP:
- break;
- default:
- handle_error(mgcp_ctx, MGCP_ERR_UNEXP_TEARDOWN);
- return;
- }
-
- lchan = mgcp_ctx->lchan;
- OSMO_ASSERT(lchan);
-
- /* Send assignment completion message via AoIP, this will complete
- * the circuit. The message will also contain the port and IP-Address
- * where the MGW expects the RTP input from the MSC side */
- bssmap_send_aoip_ass_compl(lchan);
-
- LOGPFSML(fi, LOGL_DEBUG, "call in progress, waiting for call end...\n");
-
- osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
-}
-
-static void dlcx_for_all_resp_cb(struct mgcp_response *r, void *priv);
-static void mdcx_for_bts_ho_resp_cb(struct mgcp_response *r, void *priv);
-
-/* Helper function to perform a connection teardown. This function may be
- * called from ST_CALL and ST_MDCX_BTS_HO only. It will perform a state
- * change to ST_HALT when teardown is done. */
-static void handle_teardown(struct mgcp_ctx *mgcp_ctx)
-{
- struct gsm_subscriber_connection *conn;
- struct msgb *msg;
- struct mgcp_msg mgcp_msg;
- struct mgcp_client *mgcp;
- uint16_t rtp_endpoint;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
-
- rtp_endpoint = mgcp_ctx->rtp_endpoint;
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
- "DLCX: removing connection for the BTS and NET side on MGW endpoint:0x%x...\n", rtp_endpoint);
-
- /* We now relase the endpoint back to the pool in order to allow
- * other connections to use this endpoint */
- mgcp_client_release_endpoint(rtp_endpoint, mgcp);
-
- /* Generate MGCP message string */
- mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_DLCX,
- .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID),
- .call_id = conn->sccp.conn_id
- };
- if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
- sizeof(mgcp_msg.endpoint)) {
- handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
- return;
- }
- msg = mgcp_msg_gen(mgcp, &mgcp_msg);
- OSMO_ASSERT(msg);
-
- /* Transmit MGCP message to MGW */
- mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
- rc = mgcp_client_tx(mgcp, msg, dlcx_for_all_resp_cb, mgcp_ctx);
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_HALT, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
-}
-
-/* Helper function to perform a handover (MDCX). This function may be
- * called from ST_CALL and ST_MDCX_BTS_HO only. It will perform a state
- * change to ST_CALL when teardown is done. */
-static void handle_handover(struct mgcp_ctx *mgcp_ctx)
-{
- struct gsm_subscriber_connection *conn;
- struct msgb *msg;
- struct mgcp_msg mgcp_msg;
- struct mgcp_client *mgcp;
- struct gsm_lchan *ho_lchan;
- uint16_t rtp_endpoint;
- struct in_addr addr;
- int rc;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
- ho_lchan = mgcp_ctx->ho_lchan;
- OSMO_ASSERT(ho_lchan);
-
- rtp_endpoint = mgcp_ctx->rtp_endpoint;
-
- addr.s_addr = osmo_ntohl(ho_lchan->abis_ip.bound_ip);
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG,
- "MDCX/BTS/HO: handover connection from old BTS to new BTS side on MGW endpoint:0x%x, new BTS expects RTP input on address %s:%u\n\n",
- rtp_endpoint, inet_ntoa(addr), ho_lchan->abis_ip.bound_port);
-
- /* Generate MGCP message string */
- mgcp_msg = (struct mgcp_msg) {
- .verb = MGCP_VERB_MDCX,
- .presence = (MGCP_MSG_PRESENCE_ENDPOINT | MGCP_MSG_PRESENCE_CALL_ID | MGCP_MSG_PRESENCE_CONN_ID |
- MGCP_MSG_PRESENCE_CONN_MODE | MGCP_MSG_PRESENCE_AUDIO_IP |
- MGCP_MSG_PRESENCE_AUDIO_PORT),
- .call_id = conn->sccp.conn_id,
- .conn_id = mgcp_ctx->conn_id_bts,
- .conn_mode = MGCP_CONN_RECV_SEND,
- .audio_ip = inet_ntoa(addr),
- .audio_port = ho_lchan->abis_ip.bound_port};
- if (snprintf(mgcp_msg.endpoint, sizeof(mgcp_msg.endpoint), MGCP_ENDPOINT_FORMAT, rtp_endpoint) >=
- sizeof(mgcp_msg.endpoint)) {
- handle_error(mgcp_ctx, MGCP_ERR_NOMEM);
- return;
- }
- msg = mgcp_msg_gen(mgcp, &mgcp_msg);
- OSMO_ASSERT(msg);
-
- /* Transmit MGCP message to MGW */
- mgcp_ctx->mgw_pending_trans = mgcp_msg_trans_id(msg);
- rc = mgcp_client_tx(mgcp, msg, mdcx_for_bts_ho_resp_cb, mgcp_ctx);
- if (rc < 0) {
- handle_error(mgcp_ctx, MGCP_ERR_MGW_TX_FAIL);
- return;
- }
-
- osmo_fsm_inst_state_chg(mgcp_ctx->fsm, ST_MDCX_BTS_HO, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
-}
-
-/* Callback for ST_CALL: Handle call teardown and Handover */
-static void fsm_active_call_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = data;
-
- OSMO_ASSERT(mgcp_ctx);
-
- switch (event) {
- case EV_TEARDOWN:
- handle_teardown(mgcp_ctx);
- break;
- case EV_HANDOVER:
- handle_handover(mgcp_ctx);
- break;
- }
-
-}
-
-/* Callback for MGCP-Client: handle response for BTS/Handover associated MDCX */
-static void mdcx_for_bts_ho_resp_cb(struct mgcp_response *r, void *priv)
-{
- struct mgcp_ctx *mgcp_ctx = priv;
-
- OSMO_ASSERT(mgcp_ctx);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR, "MDCX/BTS/HO: late MGW response, FSM already terminated -- ignoring...\n");
- return;
- }
-
- if (r->head.response_code != 200) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
- "MDCX/BTS/HO: response yields error: %d %s\n", r->head.response_code, r->head.comment);
- handle_error(mgcp_ctx, MGCP_ERR_MGW_FAIL);
- return;
- }
-
- /* Notify the FSM that we got the response. */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_MDCX_BTS_HO_RESP, mgcp_ctx);
-}
-
-/* Callback for ST_MDCX_BTS_HO: Complete updating the connection data after
- * handoverin the call to another BTS */
-static void fsm_complete_handover(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
-
- OSMO_ASSERT(mgcp_ctx);
-
- switch (event) {
- case EV_MDCX_BTS_HO_RESP:
- /* The response from the MGW arrived, the connection pointing
- * towards the BTS is now updated, so we now change back to
- * ST_CALL, where we will wait for the call-end (or another
- * handover) */
- LOGPFSML(fi, LOGL_DEBUG, "MDCX/BTS/HO: handover done, waiting for call end...\n");
- osmo_fsm_inst_state_chg(fi, ST_CALL, 0, 0);
- break;
- case EV_HANDOVER:
- /* This handles the rare, but possible situation where another
- * handover is happening while we still wait for the the MGW to
- * complete the current one. In this case we will stop waiting
- * for the response and directly move on with that second
- * handover */
- handle_handover(mgcp_ctx);
- break;
- case EV_TEARDOWN:
- /* It may happen that the BSS wants to teardown all connections
- * while we are still waiting for the MGW to respond. In this
- * case we start to teard down the connection immediately */
- handle_teardown(mgcp_ctx);
- break;
- }
-}
-
-/* Callback for MGCP-Client: handle response for NET associated CRCX */
-static void dlcx_for_all_resp_cb(struct mgcp_response *r, void *priv)
-{
- struct mgcp_ctx *mgcp_ctx = priv;
- struct gsm_subscriber_connection *conn;
- struct mgcp_client *mgcp;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR,
- "DLCX: late MGW response, FSM already terminated -- ignoring...\n");
- return;
- }
-
- /* Note: We check the return code, but in case of an error there is
- * not much that can be done to recover. However, at least we tryed
- * to remove the connection (if there was even any) */
- if (r->head.response_code != 200) {
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR,
- "DLCX: response yields error: %d %s\n", r->head.response_code, r->head.comment);
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_ERROR, "DLCX: MGW has acknowledged the removal of the connections\n");
-
- /* Notify the FSM that we got the response. */
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_DLCX_ALL_RESP, mgcp_ctx);
-}
-
-/* Callback for ST_HALT: Terminate the state machine */
-static void fsm_halt_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct mgcp_ctx *mgcp_ctx = (struct mgcp_ctx *)data;
- struct gsm_subscriber_connection *conn;
-
- OSMO_ASSERT(mgcp_ctx);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
-
- /* Send pending sigtran message */
- if (mgcp_ctx->resp) {
- LOGPFSML(fi, LOGL_DEBUG, "sending pending sigtran response message...\n");
- osmo_bsc_sigtran_send(conn, mgcp_ctx->resp);
- mgcp_ctx->resp = NULL;
- }
-
- /* Destroy the state machine and all context information */
- osmo_fsm_inst_free(mgcp_ctx->fsm);
- mgcp_ctx->fsm = NULL;
-}
-
-/* Timer callback to shut down in case of connectivity problems */
-static int fsm_timeout_cb(struct osmo_fsm_inst *fi)
-{
- struct mgcp_ctx *mgcp_ctx = fi->priv;
- struct mgcp_client *mgcp;
-
- OSMO_ASSERT(mgcp_ctx);
- mgcp = mgcp_ctx->mgcp;
- OSMO_ASSERT(mgcp);
-
- /* Ensure that no sigtran response, is present. Otherwiese we might try
- * to send a sigtran response when the sccp connection is already freed. */
- mgcp_ctx->resp = NULL;
-
- if (fi->T == MGCP_MGW_TIMEOUT_TIMER_NR) {
- /* Note: We were unable to communicate with the MGW,
- * unfortunately there is no meaningful action we can take
- * now other than giving up. */
-
- /* At least release the occupied endpoint ID */
- mgcp_client_release_endpoint(mgcp_ctx->rtp_endpoint, mgcp);
-
- /* Cancel the transaction that timed out */
- mgcp_client_cancel(mgcp, mgcp_ctx->mgw_pending_trans);
-
- /* Initiate self destruction of the FSM */
- osmo_fsm_inst_state_chg(fi, ST_HALT, 0, 0);
- osmo_fsm_inst_dispatch(fi, EV_TEARDOWN, mgcp_ctx);
- } else if (fi->T == MGCP_BSS_TIMEOUT_TIMER_NR)
- /* Note: If the logic that controls the BSS is unable to
- * negotiate a connection, we presumably still have a
- * working connection to the MGW, we will try to
- * shut down gracefully. */
- handle_error(mgcp_ctx, MGCP_ERR_BSS_TIMEOUT);
- else {
- /* Note: Ther must not be any unsolicited timers
- * in this FSM. If so, we have serious problem. */
- OSMO_ASSERT(false);
- }
-
- return 0;
-}
-
-static struct osmo_fsm_state fsm_bsc_mgcp_states[] = {
-
- /* Startup state machine, send CRCX to BTS. */
- [ST_CRCX_BTS] = {
- .in_event_mask = S(EV_INIT),
- .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_ASSIGN_PROC),
- .name = OSMO_STRINGIFY(ST_CRCX_BTS),
- .action = fsm_crcx_bts_cb,
- },
-
- /* When the CRCX response for the BTS side is received, then
- * proceed the assignment on the BSS side. */
- [ST_ASSIGN_PROC] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_CRCX_BTS_RESP),
- .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_MDCX_BTS),
- .name = OSMO_STRINGIFY(ST_ASSIGN_PROC),
- .action = fsm_proc_assignmnent_req_cb,
- },
-
- /* When the BSS has processed the assignment request,
- * then send the MDCX command for the BTS side in order to
- * update the connections with the actual PORT/IP where the
- * BTS expects the RTP input. */
- [ST_MDCX_BTS] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_ASS_COMPLETE),
- .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_CRCX_NET),
- .name = OSMO_STRINGIFY(ST_MDCX_BTS),
- .action = fsm_mdcx_bts_cb,
- },
-
- /* When the MDCX response for the BTS siede is received, then
- * directly proceed with sending the CRCX command to connect the
- * network side. This is done in one phase (no MDCX needed). */
- [ST_CRCX_NET] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_MDCX_BTS_RESP),
- .out_state_mask = S(ST_HALT) | S(ST_CALL) | S(ST_ASSIGN_COMPL),
- .name = OSMO_STRINGIFY(ST_CRCX_NET),
- .action = fsm_crcx_net_cb,
- },
-
- /* When the CRCX response for the NET side is received. Then
- * send the assignment complete message via the A-Interface and
- * enter wait state in order to wait for the end of the call. */
- [ST_ASSIGN_COMPL] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_CRCX_NET_RESP),
- .out_state_mask = S(ST_HALT) | S(ST_CALL),
- .name = OSMO_STRINGIFY(ST_ASSIGN_COMPL),
- .action = fsm_send_assignment_complete,
- },
-
- /* When the call ends, remove all RTP connections from the
- * MGW by sending a wildcarded DLCX. In case of a handover,
- * go for an extra MDCX to update the connection and land in
- * this state again when done. */
- [ST_CALL] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_HANDOVER),
- .out_state_mask = S(ST_HALT) | S(ST_MDCX_BTS_HO),
- .name = OSMO_STRINGIFY(ST_CALL),
- .action = fsm_active_call_cb,
- },
-
- /* A handover is in progress. When the response to the respective
- * MDCX is received, then go back to ST_CALL and wait for the
- * call end */
- [ST_MDCX_BTS_HO] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_HANDOVER) | S(EV_MDCX_BTS_HO_RESP),
- .out_state_mask = S(ST_HALT) | S(ST_CALL),
- .name = OSMO_STRINGIFY(ST_MDCX_BTS_HO),
- .action = fsm_complete_handover,
- },
-
- /* When the MGW confirms that the connections are terminated,
- * then halt the state machine. */
- [ST_HALT] = {
- .in_event_mask = S(EV_TEARDOWN) | S(EV_DLCX_ALL_RESP),
- .out_state_mask = 0,
- .name = OSMO_STRINGIFY(ST_HALT),
- .action = fsm_halt_cb,
- },
-};
-
-/* State machine definition */
-static struct osmo_fsm fsm_bsc_mgcp = {
- .name = "MGW",
- .states = fsm_bsc_mgcp_states,
- .num_states = ARRAY_SIZE(fsm_bsc_mgcp_states),
- .log_subsys = DMGCP,
- .timer_cb = fsm_timeout_cb,
-};
-
-/* Notify that the a new call begins. This will create a connection for the
- * BTS on the MGW and set up the port numbers in struct osmo_bsc_sccp_con.
- * After that gsm0808_assign_req() to proceed.
- * Parameter:
- * ctx: talloc context
- * network: associated gsm network
- * conn: associated sccp connection
- * chan_mode: channel mode (system data, passed through)
- * full_rate: full rate flag (system data, passed through)
- * Returns an mgcp_context that contains system data and the OSMO-FSM */
-struct mgcp_ctx *mgcp_assignm_req(void *ctx, struct mgcp_client *mgcp,
- struct gsm_subscriber_connection *conn,
- enum gsm48_chan_mode chan_mode, bool full_rate)
-{
- struct mgcp_ctx *mgcp_ctx;
- char name[32];
-
- OSMO_ASSERT(mgcp);
- OSMO_ASSERT(conn);
-
- OSMO_ASSERT(snprintf(name, sizeof(name), "MGW_%i", conn->sccp.conn_id) < sizeof(name));
-
- /* Allocate and configure a new fsm instance */
- mgcp_ctx = talloc_zero(ctx, struct mgcp_ctx);
- OSMO_ASSERT(mgcp_ctx);
-
- mgcp_ctx->fsm = osmo_fsm_inst_alloc(&fsm_bsc_mgcp, NULL, ctx, LOGL_DEBUG, name);
- OSMO_ASSERT(mgcp_ctx->fsm);
- mgcp_ctx->fsm->priv = mgcp_ctx;
- mgcp_ctx->mgcp = mgcp;
- mgcp_ctx->conn = conn;
- mgcp_ctx->chan_mode = chan_mode;
- mgcp_ctx->full_rate = full_rate;
-
- /* start state machine */
- OSMO_ASSERT(mgcp_ctx->fsm->state == ST_CRCX_BTS);
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_INIT, mgcp_ctx);
-
- return mgcp_ctx;
-}
-
-/* Notify that the call has ended, remove all connections from the MGW,
- * then send the clear complete message and destroy the FSM instance
- * Parameter:
- * mgcp_ctx: context information (FSM, and pointer to external system data)
- * respmgcp_ctx: pending clear complete message to send via A-Interface */
-void mgcp_clear_complete(struct mgcp_ctx *mgcp_ctx, struct msgb *resp)
-{
- struct gsm_subscriber_connection *conn;
-
- OSMO_ASSERT(mgcp_ctx);
- OSMO_ASSERT(resp);
- conn = mgcp_ctx->conn;
- OSMO_ASSERT(conn);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR,
- "clear completion attemted on already terminated FSM -- forwarding directly...\n");
- osmo_bsc_sigtran_send(conn, resp);
- mgcp_ctx->resp = NULL;
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "bss is indicating call end...\n");
-
- mgcp_ctx->resp = resp;
-
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_TEARDOWN, mgcp_ctx);
-}
-
-/* Notify that the BSS ready, send the assingnment complete message when the
- * mgcp connection is completed
- * Parameter:
- * mgcp_ctx: context information (FSM, and pointer to external system data)
- * lchan: needed for sending the assignment complete message via A-Interface */
-void mgcp_ass_complete(struct mgcp_ctx *mgcp_ctx, struct gsm_lchan *lchan)
-{
- OSMO_ASSERT(mgcp_ctx);
- OSMO_ASSERT(lchan);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR, "assignment completion attemted on already terminated FSM -- ignored\n");
- mgcp_ctx->lchan = NULL;
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "bss is indicating assignment completion...\n");
-
- mgcp_ctx->lchan = lchan;
-
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_ASS_COMPLETE, mgcp_ctx);
-
- return;
-}
-
-/* Notify that the call got handovered to another BTS, update the connection
- * that is pointing to the BTS side with the connection data for the new bts.
- * Parameter:
- * mgcp_ctx: context information (FSM, and pointer to external system data)
- * ho_lchan: the lchan on the new BTS */
-static void mgcp_handover(struct mgcp_ctx *mgcp_ctx, struct gsm_lchan *ho_lchan)
-{
- OSMO_ASSERT(mgcp_ctx);
- OSMO_ASSERT(ho_lchan);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_ERROR, "handover attemted on already terminated FSM -- ignored\n");
- mgcp_ctx->ho_lchan = NULL;
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "bss is indicating handover...\n");
-
- mgcp_ctx->ho_lchan = ho_lchan;
-
- osmo_fsm_inst_dispatch(mgcp_ctx->fsm, EV_HANDOVER, mgcp_ctx);
-
- return;
-}
-
-/* GSM 08.58 HANDOVER DETECT has been received */
-static int mgcp_sig_ho_detect(struct gsm_lchan *new_lchan)
-{
- struct gsm_subscriber_connection *conn;
- if (!new_lchan) {
- LOGP(DHO, LOGL_ERROR, "HO Detect signal for NULL lchan\n");
- return -EINVAL;
- }
-
- conn = new_lchan->conn;
-
- if (!conn) {
- LOGP(DHO, LOGL_ERROR, "%s HO Detect for lchan without conn\n",
- gsm_lchan_name(new_lchan));
- return -EINVAL;
- }
-
- if (!conn->sccp.conn_id) {
- LOGP(DHO, LOGL_ERROR, "%s HO Detect for conn without sccp_conn_id\n",
- gsm_lchan_name(new_lchan));
- return -EINVAL;
- }
-
- if (!conn->user_plane.mgcp_ctx) {
- LOGP(DHO, LOGL_ERROR, "%s HO Detect for conn without MGCP ctx\n",
- gsm_lchan_name(new_lchan));
- return -EINVAL;
- }
-
- mgcp_handover(conn->user_plane.mgcp_ctx, new_lchan);
- return 0;
-}
-
-static int mgcp_sig_cb(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct lchan_signal_data *lchan_data;
-
- switch (subsys) {
- case SS_LCHAN:
- lchan_data = signal_data;
- switch (signal) {
- case S_LCHAN_HANDOVER_DETECT:
- return mgcp_sig_ho_detect(lchan_data->lchan);
- }
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-/* Free an existing mgcp context gracefully
- * Parameter:
- * mgcp_ctx: context information (FSM, and pointer to external system data) */
-void mgcp_free_ctx(struct mgcp_ctx *mgcp_ctx)
-{
- OSMO_ASSERT(mgcp_ctx);
-
- if (mgcp_ctx->fsm == NULL) {
- LOGP(DMGCP, LOGL_DEBUG, "fsm already terminated, freeing only related context information...\n");
- talloc_free(mgcp_ctx);
- return;
- }
-
- LOGPFSML(mgcp_ctx->fsm, LOGL_DEBUG, "terminating fsm and freeing related context information...\n");
-
- osmo_fsm_inst_free(mgcp_ctx->fsm);
- talloc_free(mgcp_ctx);
-}
-
-void mgcp_init(struct gsm_network *net)
-{
- osmo_fsm_register(&fsm_bsc_mgcp);
- osmo_signal_register_handler(SS_LCHAN, mgcp_sig_cb, NULL);
-}
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
index ab903b622..1a31a7c52 100644
--- a/src/osmo-bsc/osmo_bsc_sigtran.c
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -1,8 +1,7 @@
-/* (C) 2017 by sysmocom s.f.m.c. GmbH
+/* (C) 2017 by sysmocom s.f.m.c. GmbH, Author: Philipp Maier
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
* All Rights Reserved
*
- * Author: Philipp Maier
- *
* 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
@@ -20,6 +19,7 @@
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/core/linuxlist.h>
@@ -32,7 +32,7 @@
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/gsm_04_80.h>
-#include <osmocom/bsc/osmo_bsc_mgcp.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
/* A pointer to a list with all involved MSCs
* (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */
@@ -127,18 +127,10 @@ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_add
}
/* Send data to MSC, use the connection id which MSC it is */
-static int handle_data_from_msc(int conn_id, struct msgb *msg)
+static int handle_data_from_msc(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- struct gsm_subscriber_connection *conn = get_bsc_conn_by_conn_id(conn_id);
- int rc = -EINVAL;
-
- if (conn) {
- msg->l3h = msgb_l2(msg);
- rc = bsc_handle_dt(conn, msg, msgb_l2len(msg));
- } else
- LOGP(DMSC, LOGL_NOTICE, "incoming data from unknown connection id: %i\n", conn_id);
-
- return rc;
+ msg->l3h = msgb_l2(msg);
+ return bsc_handle_dt(conn, msg, msgb_l2len(msg));
}
/* Sent unitdata to MSC, use the point code to determine which MSC it is */
@@ -177,23 +169,32 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
- /* Handle (Reject) inbound connections */
+ /* Handle inbound connections */
DEBUGP(DMSC, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id);
- LOGP(DMSC, LOGL_DEBUG, "Rejecting inbound SCCP connection...\n");
- rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0);
+ conn = bsc_subscr_con_allocate(bsc_gsmnet);
+ if (conn) {
+ conn->sccp.msc = get_msc_by_addr(&scu_prim->u.connect.calling_addr);
+ /* MSC may be NULL, let the FSM deal with it */
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_IND, scu_prim);
+ } else
+ LOGP(DMSC, LOGL_ERROR, "Unable to alloc subscr_conn for inbound N-CONNECT.ind\n");
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
/* Handle outbound connection confirmation */
+ DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id,
+ osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
conn = get_bsc_conn_by_conn_id(scu_prim->u.connect.conn_id);
- if (conn)
+ if (conn) {
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, scu_prim);
conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
- if (msgb_l2len(oph->msg) > 0) {
- DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id,
- osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
- rc = handle_data_from_msc(scu_prim->u.connect.conn_id, oph->msg);
- } else
- DEBUGP(DMSC, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id);
+ if (msgb_l2len(oph->msg) > 0)
+ handle_data_from_msc(conn, oph->msg);
+ } else {
+ LOGP(DMSC, LOGL_ERROR, "N-CONNET.cfm(%u, %s) for unknown conn?!?\n",
+ scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg),
+ msgb_l2len(oph->msg)));
+ }
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
@@ -202,28 +203,25 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
/* Incoming data is a sign of a vital connection */
- conn = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
- if (conn)
+ conn = get_bsc_conn_by_conn_id(scu_prim->u.data.conn_id);
+ if (conn) {
a_reset_conn_success(conn->sccp.msc->a.reset);
-
- rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg);
+ handle_data_from_msc(conn, oph->msg);
+ }
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id,
+ osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)),
+ scu_prim->u.disconnect.cause);
/* indication of disconnect */
conn = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
- if (conn)
+ if (conn) {
conn->sccp.state = SUBSCR_SCCP_ST_NONE;
- if (msgb_l2len(oph->msg) > 0) {
- DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s, cause=%i)\n", scu_prim->u.disconnect.conn_id,
- osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)), scu_prim->u.disconnect.cause);
- handle_data_from_msc(scu_prim->u.disconnect.conn_id, oph->msg);
- } else {
- DEBUGP(DMSC, "N-DISCONNECT.ind(%u, cause=%i)\n", scu_prim->u.disconnect.conn_id,
- scu_prim->u.disconnect.cause);
+ if (msgb_l2len(oph->msg) > 0)
+ handle_data_from_msc(conn, oph->msg);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_DISC_IND, scu_prim);
}
- if (conn)
- rc = osmo_bsc_sigtran_del_conn(conn);
break;
default:
@@ -352,31 +350,6 @@ int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *m
return rc;
}
-/* Delete a connection from the list with open connections
- * (called by osmo_bsc_api.c on failing open connections and
- * locally, when a connection is closed by the MSC */
-int osmo_bsc_sigtran_del_conn(struct gsm_subscriber_connection *conn)
-{
- if (!conn)
- return 0;
-
- LOGP(DMSC, LOGL_ERROR,
- "sccp connection (id=%i) not cleared (gsm subscriber connection still active) -- forcefully clearing it now!\n", conn->sccp.conn_id);
-
- /* This bahaviour might be caused by a bad connection. Maybe we
- * will have to go through the reset procedure again */
- a_reset_conn_fail(conn->sccp.msc->a.reset);
-
- /* Remove mgcp context if existant */
- if (conn->user_plane.mgcp_ctx)
- mgcp_free_ctx(conn->user_plane.mgcp_ctx);
-
- /* free the "conn" and make sure any pending lchans are also free'd */
- bsc_subscr_con_free(conn);
-
- return 0;
-}
-
/* Send an USSD notification in case we loose the connection to the MSC */
static void bsc_notify_msc_lost(struct gsm_subscriber_connection *conn)
{
@@ -411,13 +384,9 @@ void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc)
bsc_notify_msc_lost(conn);
/* Take down all occopied RF channels */
- gsm0808_clear(conn);
-
/* Disconnect all Sigtran connections */
- osmo_sccp_tx_disconn(msc->a.sccp_user, conn->sccp.conn_id, &msc->a.bsc_addr, 0);
-
/* Delete subscriber connection */
- osmo_bsc_sigtran_del_conn(conn);
+ osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REQUEST, NULL);
}
}
}
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index 983a3bb04..0c681a560 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -22,7 +22,6 @@ noinst_HEADERS = \
$(NULL)
bin_PROGRAMS = \
- bs11_config \
isdnsync \
meas_json \
$(NULL)
diff --git a/tests/abis/abis_test.c b/tests/abis/abis_test.c
index bb70a1505..7deb0f00f 100644
--- a/tests/abis/abis_test.c
+++ b/tests/abis/abis_test.c
@@ -182,3 +182,7 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}
diff --git a/tests/bsc-nat/bsc_nat_test.c b/tests/bsc-nat/bsc_nat_test.c
index 09378ae6a..bb287ec4c 100644
--- a/tests/bsc-nat/bsc_nat_test.c
+++ b/tests/bsc-nat/bsc_nat_test.c
@@ -1590,3 +1590,5 @@ void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg)
{
abort();
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network) { return NULL; }
diff --git a/tests/bsc/bsc_test.c b/tests/bsc/bsc_test.c
index 541a44caf..744b9e1cb 100644
--- a/tests/bsc/bsc_test.c
+++ b/tests/bsc/bsc_test.c
@@ -235,3 +235,7 @@ int main(int argc, char **argv)
printf("Testing execution completed.\n");
return 0;
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}
diff --git a/tests/bssap/Makefile.am b/tests/bssap/Makefile.am
index e4fe38b77..30a9246c8 100644
--- a/tests/bssap/Makefile.am
+++ b/tests/bssap/Makefile.am
@@ -29,7 +29,6 @@ bssap_test_SOURCES = \
$(top_srcdir)/src/osmo-bsc/osmo_bsc_sigtran.c \
$(top_srcdir)/src/osmo-bsc/osmo_bsc_filter.c \
$(top_srcdir)/src/osmo-bsc/osmo_bsc_grace.c \
- $(top_srcdir)/src/osmo-bsc/osmo_bsc_mgcp.c \
$(NULL)
bssap_test_LDADD = \
diff --git a/tests/bssap/bssap_test.c b/tests/bssap/bssap_test.c
index cf60e3871..5601e29a1 100644
--- a/tests/bssap/bssap_test.c
+++ b/tests/bssap/bssap_test.c
@@ -150,3 +150,7 @@ int main(int argc, char **argv)
return 0;
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}
diff --git a/tests/channel/channel_test.c b/tests/channel/channel_test.c
index b41e3d6a7..78db1d4e3 100644
--- a/tests/channel/channel_test.c
+++ b/tests/channel/channel_test.c
@@ -119,5 +119,6 @@ void ipa_client_conn_open() {}
void ipa_client_conn_send() {}
void ipa_msg_push_header() {}
void ipaccess_bts_handle_ccm() {}
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *network) { return NULL; }
struct tlv_definition nm_att_tlvdef;
diff --git a/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c
index e8d5501bb..5a4c168dc 100644
--- a/tests/gsm0408/gsm0408_test.c
+++ b/tests/gsm0408/gsm0408_test.c
@@ -861,3 +861,7 @@ int main(int argc, char **argv)
return EXIT_SUCCESS;
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}
diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am
index 7133fcddc..957bbeeb9 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -9,6 +9,8 @@ AM_CFLAGS = \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOGSM_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
+ $(LIBOSMOMGCPCLIENT_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
@@ -28,7 +30,7 @@ handover_test_SOURCES = \
$(NULL)
handover_test_LDFLAGS =\
- -Wl,--wrap=abis_rsl_sendmsg \
+ -Wl,--wrap=abis_rsl_sendmsg,--wrap=mgcp_conn_modify,--wrap=mgcp_conn_delete\
$(NULL)
handover_test_LDADD = \
@@ -36,4 +38,6 @@ handover_test_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOSIGTRAN_LIBS) \
+ $(LIBOSMOMGCPCLIENT_LIBS) \
$(NULL)
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index bf7350c62..b03b65a6b 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -39,9 +39,59 @@
#include <osmocom/bsc/bss.h>
#include <osmocom/bsc/bsc_api.h>
#include <osmocom/bsc/osmo_bsc.h>
+#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
struct gsm_network *bsc_gsmnet;
+/* override, requires '-Wl,--wrap=mgcp_conn_modify'.
+ * Catch modification of an MGCP connection. */
+int __real_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer);
+int __wrap_mgcp_conn_modify(struct osmo_fsm_inst *fi, uint32_t parent_evt, struct mgcp_conn_peer *conn_peer)
+{
+ /* CAUTION HACK:
+ *
+ * The pointer fi is misused to pass a reference to GSCON FSM !
+ *
+ * This function is called from gscon_fsm_wait_ho_compl() from
+ * bsc_subscr_conn_fsm.c when GSCON_EV_HO_COMPL is dispatched to the
+ * GSCON FSM. By then, the GSCON FSM has already changed to the state
+ * ST_WAIT_MDCX_BTS_HO (see gscon_fsm_wait_mdcx_bts_ho()) and waits for
+ * GSCON_EV_MGW_MDCX_RESP_BTS. The signal GSCON_EV_MGW_MDCX_RESP_BTS
+ * is sent to this function using the parameter parent_evt. So we
+ * implicitly know the event that is needed to simulate a successful
+ * MGW negotiation to the GSCON FSM. All we need to do is to dispatch
+ * parent_evt back to the GSCON FSM in order to make it think that the
+ * MGW negotiation is done.
+ *
+ * Unfortunately, there is a problem with this test implementation.
+ * in order to simplfy the test we do not allocate any MGCP Client
+ * FSM but the GSCON FSM will call this function with the fi pointer
+ * pointing to the MGCP Client FSM. This means we get a nullpointer
+ * here and there is no way to distinguish which GSCON FSM called
+ * the function at all (normally we would know through the parent
+ * pointer).
+ *
+ * To get around this problem we populate the fi pointer with the
+ * reference to the GSCON FSM itsself, so we can know who called the
+ * function. This is a misuse of the pointer since it normally would
+ * hold an MGCP Client FSM instead of a GSCON FSM.
+ *
+ * See also note in function create_conn() */
+
+ osmo_fsm_inst_dispatch(fi, parent_evt, NULL);
+ return 0;
+}
+
+/* override, requires '-Wl,--wrap=mgcp_conn_delete'.
+ * Catch deletion of an MGCP connection. */
+int __real_mgcp_conn_delete(struct osmo_fsm_inst *fi);
+int __wrap_mgcp_conn_delete(struct osmo_fsm_inst *fi)
+{
+ /* Just do nothing and pretend that everything went well.
+ * We never have allocatec any MGCP connections. */
+ return 0;
+}
+
/* measurement report */
uint8_t meas_rep_ba = 0, meas_rep_valid = 1, meas_valid = 1, meas_multi_rep = 0;
@@ -186,7 +236,24 @@ static struct gsm_bts *create_bts(int arfcn)
void create_conn(struct gsm_lchan *lchan)
{
- lchan->conn = bsc_subscr_con_allocate(lchan);
+ struct gsm_subscriber_connection *conn;
+ conn = bsc_subscr_con_allocate(lchan->ts->trx->bts->network);
+
+ /* CAUTION HACK: When __real_mgcp_conn_modify() is called by the GSCON
+ * FSM, then we need to know the reference to caller FSM (GSCON FSM).
+ * Unfortunately the function __real_mgcp_conn_modify() is called with
+ * fi_bts, which is unpopulated in this setup. The real function would
+ * perform the communication with the MGW and then dispatch a signal
+ * back to the parent FSM. Since we do not have all that in this setup
+ * we populate the fi_bts pointer with a reference to the GSCON FSM in
+ * order to have it available later in __real_mgcp_conn_modify(). */
+ conn->user_plane.fi_bts = conn->fi;
+
+ lchan->conn = conn;
+ conn->lchan = lchan;
+ /* kick the FSM from INIT through to the ACTIVE state */
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_REQ, NULL);
+ osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_CONN_CFM, NULL);
}
/* create lchan */
@@ -1256,6 +1323,11 @@ static const struct log_info_cat log_categories[] = {
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
+ [DMSC] = {
+ .name = "DMSC",
+ .description = "Mobile Switching Center",
+ .enabled = 1, .loglevel = LOGL_DEBUG,
+ },
};
const struct log_info log_info = {
@@ -1592,7 +1664,7 @@ int main(int argc, char **argv)
struct gsm_subscriber_connection *conn = lchan[i]->conn;
lchan[i]->conn = NULL;
conn->lchan = NULL;
- bsc_subscr_con_free(conn);
+ osmo_fsm_inst_term(conn->fi, OSMO_FSM_TERM_REGULAR, NULL);
lchan_free(lchan[i]);
}
@@ -1615,3 +1687,5 @@ void trau_mux_unmap() {}
void trau_mux_map_lchan() {}
void trau_recv_lchan() {}
void trau_send_frame() {}
+int osmo_bsc_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
+int osmo_bsc_sigtran_open_conn(struct gsm_subscriber_connection *conn, struct msgb *msg) { return 0; }
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index 6fa221c9d..663594cda 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -292,3 +292,7 @@ int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
abort();
}
+
+struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net) {
+ OSMO_ASSERT(0);
+}