aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bsc')
-rw-r--r--src/osmo-bsc/Makefile.am4
-rw-r--r--src/osmo-bsc/osmo_bsc_api.c41
-rw-r--r--src/osmo-bsc/osmo_bsc_audio.c70
-rw-r--r--src/osmo-bsc/osmo_bsc_bssap.c307
-rw-r--r--src/osmo-bsc/osmo_bsc_main.c10
-rw-r--r--src/osmo-bsc/osmo_bsc_msc.c62
-rw-r--r--src/osmo-bsc/osmo_bsc_reset.c190
-rw-r--r--src/osmo-bsc/osmo_bsc_sccp.c328
-rw-r--r--src/osmo-bsc/osmo_bsc_sigtran.c561
-rw-r--r--src/osmo-bsc/osmo_bsc_vty.c94
10 files changed, 1232 insertions, 435 deletions
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index ae9410c9d..5642fb2ed 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -14,6 +14,7 @@ AM_CFLAGS = \
$(LIBOSMOSCCP_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
+ $(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
AM_LDFLAGS = \
@@ -30,7 +31,7 @@ osmo_bsc_SOURCES = \
osmo_bsc_api.c \
osmo_bsc_grace.c \
osmo_bsc_msc.c \
- osmo_bsc_sccp.c \
+ osmo_bsc_sigtran.c \
osmo_bsc_filter.c \
osmo_bsc_bssap.c \
osmo_bsc_audio.c \
@@ -52,4 +53,5 @@ osmo_bsc_LDADD = \
$(LIBOSMOCTRL_LIBS) \
$(COVERAGE_LDFLAGS) \
$(LIBOSMOABIS_LIBS) \
+ $(LIBOSMOSIGTRAN_LIBS) \
$(NULL)
diff --git a/src/osmo-bsc/osmo_bsc_api.c b/src/osmo-bsc/osmo_bsc_api.c
index 8c33e2b57..f7343f743 100644
--- a/src/osmo-bsc/osmo_bsc_api.c
+++ b/src/osmo-bsc/osmo_bsc_api.c
@@ -27,6 +27,7 @@
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/sccp/sccp.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#define return_when_not_connected(conn) \
if (!conn->sccp_con) {\
@@ -45,7 +46,7 @@
LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \
return; \
} \
- bsc_queue_for_msc(conn->sccp_con, resp);
+ osmo_bsc_sigtran_send(conn->sccp_con, resp);
static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause);
static int complete_layer3(struct gsm_subscriber_connection *conn,
@@ -263,7 +264,8 @@ static int complete_layer3(struct gsm_subscriber_connection *conn,
}
/* allocate resource for a new connection */
- ret = bsc_create_new_connection(conn, msc, send_ping);
+ //ret = bsc_create_new_connection(conn, msc, send_ping);
+ ret = osmo_bsc_sigtran_new_conn(conn, msc);
if (ret != BSC_CON_SUCCESS) {
/* allocation has failed */
@@ -292,13 +294,13 @@ static int complete_layer3(struct gsm_subscriber_connection *conn,
if (!resp) {
LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n");
sccp_connection_free(conn->sccp_con->sccp);
- bsc_delete_connection(conn->sccp_con);
+ osmo_bsc_sigtran_del_conn(conn->sccp_con);
return BSC_API_CONN_POL_REJECT;
}
- if (bsc_open_connection(conn->sccp_con, resp) != 0) {
+ if (osmo_bsc_sigtran_open_conn(conn->sccp_con, resp) != 0) {
sccp_connection_free(conn->sccp_con->sccp);
- bsc_delete_connection(conn->sccp_con);
+ osmo_bsc_sigtran_del_conn(conn->sccp_con);
msgb_free(resp);
return BSC_API_CONN_POL_REJECT;
}
@@ -433,11 +435,28 @@ static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_
struct msgb *resp;
return_when_not_connected(conn);
- 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);
+ if (is_ipaccess_bts(conn->bts) && conn->sccp_con->rtp_ip) {
+ /* NOTE: In a network that makes use of an IPA base station
+ * and AoIP, we have to wait until the BTS reports its RTP
+ * IP/Port combination back to BSC via RSL. Unfortunately, the
+ * IPA protocol sends its Abis assignment complete message
+ * before it sends its RTP IP/Port via IPACC. So we will now
+ * 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);
+ }
}
static void bsc_assign_fail(struct gsm_subscriber_connection *conn,
@@ -474,7 +493,7 @@ static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t ca
return 1;
}
- bsc_queue_for_msc(sccp, resp);
+ osmo_bsc_sigtran_send(sccp, resp);
return 1;
}
diff --git a/src/osmo-bsc/osmo_bsc_audio.c b/src/osmo-bsc/osmo_bsc_audio.c
index 116020900..b4ffa88f1 100644
--- a/src/osmo-bsc/osmo_bsc_audio.c
+++ b/src/osmo-bsc/osmo_bsc_audio.c
@@ -26,15 +26,57 @@
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#include <arpa/inet.h>
+/* Generate and send assignment complete message */
+static int send_aoip_ass_compl(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan)
+{
+ struct msgb *resp;
+ struct sockaddr_storage rtp_addr;
+ struct sockaddr_in rtp_addr_in;
+ struct gsm0808_speech_codec sc;
+
+ OSMO_ASSERT(lchan->abis_ip.ass_compl.valid == true);
+
+ /* Package RTP-Address data */
+ memset(&rtp_addr_in, 0, sizeof(rtp_addr_in));
+ rtp_addr_in.sin_family = AF_INET;
+ rtp_addr_in.sin_port = htons(lchan->abis_ip.bound_port);
+ rtp_addr_in.sin_addr.s_addr = htonl(lchan->abis_ip.bound_ip);
+ memset(&rtp_addr, 0, sizeof(rtp_addr));
+ memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in));
+
+ /* 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,
+ &rtp_addr,
+ &sc,
+ NULL);
+
+ if (!resp) {
+ LOGP(DMSC, LOGL_ERROR, "Failed to generate assignment completed message!\n"); \
+ return -EINVAL;
+ }
+
+ return osmo_bsc_sigtran_send(conn->sccp_con, resp);
+}
+
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
struct gsm_subscriber_connection *con;
struct gsm_lchan *lchan = signal_data;
int rc;
+ uint32_t rtp_ip;
if (subsys != SS_ABISIP)
return 0;
@@ -49,11 +91,19 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
* TODO: handle handover here... then the audio should go to
* the old mgcp port..
*/
+
/* we can ask it to connect now */
LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n",
con->sccp_con->rtp_port, lchan->abis_ip.conn_id);
- rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY),
+ /* If AoIP is in use, the rtp_ip, which has been communicated
+ * via the A interface as connect_ip */
+ if(con->sccp_con->rtp_ip)
+ rtp_ip = con->sccp_con->rtp_ip;
+ else
+ rtp_ip = ntohl(INADDR_ANY);
+
+ rc = rsl_ipacc_mdcx(lchan, rtp_ip,
con->sccp_con->rtp_port,
lchan->abis_ip.rtp_payload2);
if (rc < 0) {
@@ -61,6 +111,24 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
return rc;
}
break;
+
+ case S_ABISIP_MDCX_ACK:
+ if (con->ho_lchan) {
+ /* 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
+ * assignment complete message is transmitted */
+ LOGP(DMSC, LOGL_INFO," RTP connection handover complete\n");
+ } else if (is_ipaccess_bts(con->bts) && con->sccp_con->rtp_ip) {
+ /* NOTE: This is only relevant on AoIP networks with
+ * IPA based base stations. See also osmo_bsc_api.c,
+ * function bsc_assign_compl() */
+ LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL (POSTPONED)\n");
+ if (send_aoip_ass_compl(con, lchan) != 0)
+ return -EINVAL;
+ }
+ break;
+ break;
}
return 0;
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index 100f66441..4353b9ad0 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -29,11 +29,21 @@
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_utils.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+#include <openbsc/a_reset.h>
+#include <osmocom/core/byteswap.h>
+
+#define IP_V4_ADDR_LEN 4
/*
* helpers for the assignment command
*/
-enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio)
+
+/* Helper function for match_codec_pref(), looks up a matching permitted speech
+ * value for a given msc audio codec pref */
+enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support
+ *audio)
{
if (audio->hr) {
switch (audio->ver) {
@@ -47,8 +57,9 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a
return GSM0808_PERM_HR3;
break;
default:
- LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
- return GSM0808_PERM_FR1;
+ LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n",
+ audio->ver);
+ return GSM0808_PERM_FR1;
}
} else {
switch (audio->ver) {
@@ -62,12 +73,15 @@ enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *a
return GSM0808_PERM_FR3;
break;
default:
- LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
+ LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n",
+ audio->ver);
return GSM0808_PERM_HR1;
}
}
}
+/* Helper function for match_codec_pref(), looks up a matching chan mode for
+ * a given permitted speech value */
enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
{
switch (speech) {
@@ -83,16 +97,130 @@ enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
case GSM0808_PERM_FR3:
return GSM48_CMODE_SPEECH_AMR;
break;
+ default:
+ LOGP(DMSC, LOGL_FATAL,
+ "Unsupported permitted speech selected, assuming AMR as channel mode...\n");
+ return GSM48_CMODE_SPEECH_AMR;
+ }
+}
+
+/* Helper function for match_codec_pref(), tests if a given audio support
+ * matches one of the permitted speech settings of the channel type element.
+ * The matched permitted speech value is then also compared against the
+ * speech codec list. (optional, only relevant for AoIP) */
+static bool test_codec_pref(const struct gsm0808_channel_type *ct,
+ const struct gsm0808_speech_codec_list *scl,
+ uint8_t perm_spch)
+{
+ unsigned int i;
+ bool match = false;
+ struct gsm0808_speech_codec sc;
+ int rc;
+
+ /* Try to finde the given permitted speech value in the
+ * codec list of the channel type element */
+ for (i = 0; i < ct->perm_spch_len; i++) {
+ if (ct->perm_spch[i] == perm_spch) {
+ match = true;
+ break;
+ }
+ }
+
+ /* If we do not have a speech codec list to test against,
+ * we just exit early (will be always the case in non-AoIP networks) */
+ if (!scl)
+ return match;
+
+ /* If we failed to match until here, there is no
+ * point in testing further */
+ if (match == false)
+ return false;
+
+ /* Extrapolate speech codec data */
+ rc = gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
+ if (rc < 0)
+ return false;
+
+ /* Try to find extrapolated speech codec data in
+ * the speech codec list */
+ for (i = 0; i < scl->len; i++) {
+ if (memcmp(&sc, &scl->codec[i], sizeof(sc)) == 0)
+ return true;
}
- LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n");
- return GSM48_CMODE_SPEECH_AMR;
+ return false;
+}
+
+/* Helper function for bssmap_handle_assignm_req(), matches the codec
+ * preferences from the MSC with the codec preferences */
+static int match_codec_pref(int *full_rate, enum gsm48_chan_mode *chan_mode,
+ const struct gsm0808_channel_type *ct,
+ const struct gsm0808_speech_codec_list *scl,
+ const struct bsc_msc_data *msc)
+{
+ unsigned int i;
+ uint8_t perm_spch;
+ bool match = false;
+
+ for (i = 0; i < msc->audio_length; i++) {
+ perm_spch = audio_support_to_gsm88(msc->audio_support[i]);
+ if (test_codec_pref(ct, scl, perm_spch)) {
+ match = true;
+ break;
+ }
+ }
+
+ /* Exit without result, in case no match can be deteched */
+ if (!match) {
+ *full_rate = -1;
+ *chan_mode = GSM48_CMODE_SIGN;
+ return -1;
+ }
+
+ /* Check if the result is a half or full rate codec */
+ if (perm_spch == GSM0808_PERM_HR1 || perm_spch == GSM0808_PERM_HR2
+ || perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_HR4
+ || perm_spch == GSM0808_PERM_HR6)
+ *full_rate = 0;
+ else
+ *full_rate = 1;
+
+ /* Lookup a channel mode for the selected codec */
+ *chan_mode = gsm88_to_chan_mode(perm_spch);
+
+ return 0;
}
static int bssmap_handle_reset_ack(struct bsc_msc_data *msc,
struct msgb *msg, unsigned int length)
{
- LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
+ LOGP(DMSC, LOGL_NOTICE, "RESET ACK from MSC: %s\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance),
+ &msc->a.msc_addr));
+
+ /* Inform the FSM that controls the RESET/RESET-ACK procedure
+ * that we have successfully received the reset-ack message */
+ a_reset_ack_confirm(msc->a.reset);
+
+ return 0;
+}
+
+/* Handle MSC sided reset */
+static int bssmap_handle_reset(struct bsc_msc_data *msc,
+ struct msgb *msg, unsigned int length)
+{
+ LOGP(DMSC, LOGL_NOTICE, "RESET from MSC: %s\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance),
+ &msc->a.msc_addr));
+
+ /* Instruct the bsc to close all open sigtran connections and to
+ * close all active channels on the BTS side as well */
+ osmo_bsc_sigtran_reset(msc);
+
+ /* Inform the MSC that we have received the reset request and
+ * that we acted accordingly */
+ osmo_bsc_sigtran_tx_reset_ack(msc);
+
return 0;
}
@@ -199,7 +327,7 @@ static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn,
return -1;
}
- bsc_queue_for_msc(conn, resp);
+ osmo_bsc_sigtran_send(conn, resp);
return 0;
}
@@ -276,7 +404,7 @@ reject:
return -1;
}
- bsc_queue_for_msc(conn, resp);
+ osmo_bsc_sigtran_send(conn, resp);
return -1;
}
@@ -291,99 +419,141 @@ static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
struct msgb *resp;
struct bsc_msc_data *msc;
struct tlv_parsed tp;
- uint8_t *data;
- uint8_t timeslot;
- uint8_t multiplex;
+ uint8_t timeslot = 0;
+ uint8_t multiplex = 0;
enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
- int i, supported, port, full_rate = -1;
+ int port, full_rate = -1;
+ bool aoip = false;
+ struct sockaddr_storage rtp_addr;
+ struct sockaddr_in *rtp_addr_in;
+ struct gsm0808_channel_type ct;
+ struct gsm0808_speech_codec_list scl;
+ struct gsm0808_speech_codec_list *scl_ptr = NULL;
+ int rc;
+ const uint8_t *data;
+ char len;
if (!conn->conn) {
- LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
+ LOGP(DMSC, LOGL_ERROR,
+ "No lchan/msc_data in cipher mode command.\n");
return -1;
}
+ msc = conn->msc;
+
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
+ /* Check for channel type element, if its missing, immediately reject */
if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n");
goto reject;
}
- if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
- LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n");
+ /* 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->cic =
+ osmo_load16be(TLVP_VAL
+ (&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
+ timeslot = conn->cic & 0x1f;
+ multiplex = (conn->cic & ~0x1f) >> 5;
+ } else if (TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
+ /* Decode AoIP transport address element */
+ data = TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR);
+ len = TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR);
+ rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, data, len);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR,
+ "Unable to decode aoip transport address.\n");
+ goto reject;
+ }
+ aoip = true;
+ } else {
+ LOGP(DMSC, LOGL_ERROR,
+ "transport address missing. Audio routing will not work.\n");
goto reject;
}
- conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE));
- timeslot = conn->cic & 0x1f;
- multiplex = (conn->cic & ~0x1f) >> 5;
+ /* Decode speech codec list (AoIP) */
+ 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");
+ goto reject;
+ }
- /*
- * Currently we only support a limited subset of all
- * possible channel types. The limitation ends by not using
- * multi-slot, limiting the channel coding, speech...
- */
- if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) {
- LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n",
- TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
- goto reject;
+ /* Decode Speech Codec list */
+ data = TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST);
+ len = TLVP_LEN(&tp, GSM0808_IE_SPEECH_CODEC_LIST);
+ rc = gsm0808_dec_speech_codec_list(&scl, data, len);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR,
+ "Unable to decode speech codec list\n");
+ goto reject;
+ }
+ scl_ptr = &scl;
}
- /*
- * Try to figure out if we support the proposed speech codecs. For
- * now we will always pick the full rate codecs.
- */
-
- data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
- if ((data[0] & 0xf) != 0x1) {
- LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]);
+ /* Decode Channel Type element */
+ data = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
+ len = TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE);
+ rc = gsm0808_dec_channel_type(&ct, data, len);
+ if (rc < 0) {
+ LOGP(DMSC, LOGL_ERROR, "unable to decode channel type.\n");
goto reject;
}
- /*
- * go through the list of preferred codecs of our gsm network
- * and try to find it among the permitted codecs. If we found
- * it we will send chan_mode to the right mode and break the
- * inner loop. The outer loop will exit due chan_mode having
- * the correct value.
- */
- full_rate = 0;
- msc = conn->msc;
- for (supported = 0;
- chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length;
- ++supported) {
-
- int perm_val = audio_support_to_gsm88(msc->audio_support[supported]);
- for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
- if ((data[i] & 0x7f) == perm_val) {
- chan_mode = gsm88_to_chan_mode(perm_val);
- full_rate = (data[i] & 0x4) == 0;
- break;
- } else if ((data[i] & 0x80) == 0x00) {
- break;
- }
- }
+ /* 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) {
+ LOGP(DMSC, LOGL_ERROR,
+ "Unsupported channel type, currently only speech is supported!\n");
+ goto reject;
}
- if (chan_mode == GSM48_CMODE_SIGN) {
+ /* 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.\n");
goto reject;
}
- /* map it to a MGCP Endpoint and a RTP port */
- port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
- conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);
+ if (aoip == false) {
+ /* map it to a MGCP Endpoint and a RTP port */
+ port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
+ conn->rtp_port = rtp_calculate_port(port, msc->rtp_base);
+ conn->rtp_ip = 0;
+ } else {
+ /* use address / port supplied with the AoIP
+ * transport address element */
+ if (rtp_addr.ss_family == AF_INET) {
+ rtp_addr_in = (struct sockaddr_in *)&rtp_addr;
+ conn->rtp_port = osmo_ntohs(rtp_addr_in->sin_port);
+ memcpy(&conn->rtp_ip, &rtp_addr_in->sin_addr.s_addr,
+ IP_V4_ADDR_LEN);
+ conn->rtp_ip = osmo_ntohl(conn->rtp_ip);
+ } else {
+ LOGP(DMSC, LOGL_ERROR,
+ "Unsopported addressing scheme. (supports only IPV4)\n");
+ goto reject;
+ }
+ }
return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
reject:
- resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
+ resp =
+ gsm0808_create_assignment_failure
+ (GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
if (!resp) {
LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n");
return -1;
}
- bsc_queue_for_msc(conn, resp);
+ osmo_bsc_sigtran_send(conn, resp);
return -1;
}
@@ -404,6 +574,9 @@ static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc,
case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
ret = bssmap_handle_reset_ack(msc, msg, length);
break;
+ case BSS_MAP_MSG_RESET:
+ ret = bssmap_handle_reset(msc, msg, length);
+ break;
case BSS_MAP_MSG_PAGING:
ret = bssmap_handle_paging(msc, msg, length);
break;
@@ -524,8 +697,8 @@ int bsc_handle_udt(struct bsc_msc_data *msc,
return 0;
}
-int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn,
- struct msgb *msg, unsigned int len)
+int bsc_handle_dt(struct osmo_bsc_sccp_con *conn,
+ struct msgb *msg, unsigned int len)
{
if (len < sizeof(struct bssmap_header)) {
LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
diff --git a/src/osmo-bsc/osmo_bsc_main.c b/src/osmo-bsc/osmo_bsc_main.c
index 90651b95e..cf188a9e0 100644
--- a/src/osmo-bsc/osmo_bsc_main.c
+++ b/src/osmo-bsc/osmo_bsc_main.c
@@ -28,6 +28,7 @@
#include <openbsc/vty.h>
#include <openbsc/ipaccess.h>
#include <openbsc/ctrl.h>
+#include <openbsc/osmo_bsc_sigtran.h>
#include <osmocom/ctrl/control_cmd.h>
#include <osmocom/ctrl/control_if.h>
@@ -217,6 +218,10 @@ int main(int argc, char **argv)
bsc_msg_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE);
ctrl_vty_init(tall_bsc_ctx);
+ /* Initalize SS7 */
+ osmo_ss7_init();
+ osmo_ss7_vty_init_asp(tall_bsc_ctx);
+
INIT_LLIST_HEAD(&access_lists);
/* parse options */
@@ -269,9 +274,8 @@ int main(int argc, char **argv)
}
}
-
- if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) {
- LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n");
+ if (osmo_bsc_sigtran_init(&bsc_gsmnet->bsc_data->mscs) != 0) {
+ LOGP(DNM, LOGL_ERROR, "Failed to initalize sigtran backhaul.\n");
exit(1);
}
diff --git a/src/osmo-bsc/osmo_bsc_msc.c b/src/osmo-bsc/osmo_bsc_msc.c
index 8d02624b4..351fd2ced 100644
--- a/src/osmo-bsc/osmo_bsc_msc.c
+++ b/src/osmo-bsc/osmo_bsc_msc.c
@@ -42,7 +42,7 @@
#include <netinet/tcp.h>
#include <unistd.h>
-
+#if 0
static void initialize_if_needed(struct bsc_msc_connection *conn);
static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn);
static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp);
@@ -52,6 +52,8 @@ static void schedule_ping_pong(struct bsc_msc_data *data);
/*
* MGCP forwarding code
*/
+
+#endif
static int mgcp_do_read(struct osmo_fd *fd)
{
struct bsc_msc_data *data = (struct bsc_msc_data *) fd->data;
@@ -93,6 +95,7 @@ static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg)
return ret;
}
+#if 0
static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg)
{
struct msgb *mgcp;
@@ -115,6 +118,7 @@ static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg)
msgb_free(mgcp);
}
}
+#endif
static int mgcp_create_port(struct bsc_msc_data *data)
{
@@ -168,6 +172,7 @@ static int mgcp_create_port(struct bsc_msc_data *data)
return 0;
}
+
/*
* Send data to the network
*/
@@ -183,6 +188,7 @@ int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto
return 0;
}
+#if 0
int msc_queue_write_with_ping(struct bsc_msc_connection *conn,
struct msgb *msg, int proto)
{
@@ -356,10 +362,10 @@ static void msc_ping_timeout_cb(void *_data)
static void msc_pong_timeout_cb(void *_data)
{
- struct bsc_msc_data *data = (struct bsc_msc_data *) _data;
+// struct bsc_msc_data *data = (struct bsc_msc_data *) _data;
LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n");
- bsc_msc_lost(data->msc_con);
+// bsc_msc_lost(data->msc_con);
}
static void msc_connection_connected(struct bsc_msc_connection *con)
@@ -368,12 +374,12 @@ static void msc_connection_connected(struct bsc_msc_connection *con)
struct bsc_msc_data *data;
int ret, on;
on = 1;
- ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
- if (ret != 0)
- LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
+// ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
+// if (ret != 0)
+// LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno));
- data = (struct bsc_msc_data *) con->write_queue.bfd.data;
- msc_ping_timeout_cb(data);
+// data = (struct bsc_msc_data *) con->write_queue.bfd.data;
+// msc_ping_timeout_cb(data);
sig.data = data;
osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig);
@@ -385,20 +391,20 @@ static void msc_connection_connected(struct bsc_msc_connection *con)
*/
static void msc_connection_was_lost(struct bsc_msc_connection *msc)
{
- struct msc_signal_data sig;
- struct bsc_msc_data *data;
+// struct msc_signal_data sig;
+// struct bsc_msc_data *data;
LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n");
- data = (struct bsc_msc_data *) msc->write_queue.bfd.data;
- osmo_timer_del(&data->ping_timer);
- osmo_timer_del(&data->pong_timer);
-
- sig.data = data;
- osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig);
+// data = (struct bsc_msc_data *) msc->write_queue.bfd.data;
+// osmo_timer_del(&data->ping_timer);
+// osmo_timer_del(&data->pong_timer);
+//
+// sig.data = data;
+// osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig);
msc->is_authenticated = 0;
- bsc_msc_schedule_connect(msc);
+// bsc_msc_schedule_connect(msc);
}
static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn)
@@ -515,6 +521,8 @@ static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb
osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig);
}
+#endif
+
int osmo_bsc_msc_init(struct bsc_msc_data *data)
{
if (mgcp_create_port(data) != 0)
@@ -526,19 +534,24 @@ int osmo_bsc_msc_init(struct bsc_msc_data *data)
return -1;
}
- osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data);
- osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data);
+// osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data);
+// osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data);
data->msc_con->write_queue.bfd.data = data;
- data->msc_con->connection_loss = msc_connection_was_lost;
- data->msc_con->connected = msc_connection_connected;
- data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb;
- data->msc_con->write_queue.write_cb = msc_alink_do_write;
- bsc_msc_connect(data->msc_con);
+// data->msc_con->connection_loss = msc_connection_was_lost;
+// data->msc_con->connected = msc_connection_connected;
+// data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb;
+// data->msc_con->write_queue.write_cb = msc_alink_do_write;
+// bsc_msc_connect(data->msc_con);
+
+ data->msc_con->is_connected = 1;
+ data->msc_con->is_authenticated = 1;
+
return 0;
}
+
struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr)
{
struct bsc_msc_data *msc_data;
@@ -584,3 +597,4 @@ struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr)
return msc_data;
}
+
diff --git a/src/osmo-bsc/osmo_bsc_reset.c b/src/osmo-bsc/osmo_bsc_reset.c
new file mode 100644
index 000000000..0baf08089
--- /dev/null
+++ b/src/osmo-bsc/osmo_bsc_reset.c
@@ -0,0 +1,190 @@
+/* (C) 2017 by sysmocom s.f.m.c. GmbH
+ * 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/core/logging.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/core/fsm.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <openbsc/debug.h>
+#include <openbsc/bsc_msc_data.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+
+#define RESET_RESEND_INTERVAL 2 /* sec */
+#define RESET_RESEND_TIMER_NO 1234 /* FIXME: dig out the real timer number */
+#define BAD_CONNECTION_THRESOLD 3 /* connection failures */
+
+enum fsm_states {
+ ST_DISC, /* Disconnected from MSC */
+ ST_CONN, /* We have a confirmed connection to the MSC */
+};
+
+static const struct value_string fsm_state_names[] = {
+ {ST_DISC, "ST_DISC (disconnected)"},
+ {ST_CONN, "ST_CONN (connected)"},
+ {0, NULL},
+};
+
+enum fsm_evt {
+ EV_RESET_ACK, /* got reset acknowlegement from the MSC */
+ EV_N_DISCONNECT, /* lost a connection */
+ EV_N_CONNECT, /* made a successful connection */
+};
+
+static const struct value_string fsm_evt_names[] = {
+ {EV_RESET_ACK, "EV_RESET_ACK"},
+ {EV_N_DISCONNECT, "EV_N_DISCONNECT"},
+ {EV_N_CONNECT, "EV_N_CONNECT"},
+ {0, NULL},
+};
+
+/* Disconnected state */
+static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)data;
+
+ LOGP(DMSC, LOGL_NOTICE, "fsm-state (msc-reset): %s, fsm-event: %s, MSC No.: %i\n",
+ get_value_string(fsm_state_names, ST_DISC), get_value_string(fsm_evt_names, event), msc->nr);
+ msc->msc_con->msc_conn_loss_count = 0;
+ osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0);
+}
+
+/* Connected state */
+static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)data;
+
+ LOGP(DMSC, LOGL_NOTICE, "fsm-state (msc-reset): %s, fsm-event: %s, MSC No.: %i\n",
+ get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event), msc->nr);
+
+ OSMO_ASSERT(msc);
+
+ switch (event) {
+ case EV_N_DISCONNECT:
+ if (msc->msc_con->msc_conn_loss_count >= BAD_CONNECTION_THRESOLD) {
+ LOGP(DMSC, LOGL_NOTICE, "SIGTRAN connection to MSC No.: %i down, reconnecting...\n", msc->nr);
+ osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
+ } else
+ msc->msc_con->msc_conn_loss_count++;
+ break;
+ case EV_N_CONNECT:
+ msc->msc_con->msc_conn_loss_count = 0;
+ break;
+ }
+}
+
+/* Timer callback to retransmit the reset signal */
+static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi)
+{
+ struct bsc_msc_data *msc = (struct bsc_msc_data *)fi->priv;
+
+ LOGP(DMSC, LOGL_NOTICE, "reset-ack timeout (T%i) in state %s, MSC No.: %i, resending...\n", fi->T,
+ get_value_string(fsm_state_names, fi->state), msc->nr);
+
+ osmo_bsc_sigtran_reset(msc);
+ osmo_bsc_sigtran_tx_reset(msc);
+
+ osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
+ return 0;
+}
+
+static struct osmo_fsm_state fsm_states[] = {
+ [ST_DISC] = {
+ .in_event_mask = (1 << EV_RESET_ACK),
+ .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
+ .name = "DISC",
+ .action = fsm_disc_cb,
+ },
+ [ST_CONN] = {
+ .in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT),
+ .out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
+ .name = "CONN",
+ .action = fsm_conn_cb,
+ },
+};
+
+/* State machine definition */
+static struct osmo_fsm fsm = {
+ .name = "FSM RESET",
+ .states = fsm_states,
+ .num_states = ARRAY_SIZE(fsm_states),
+ .log_subsys = DMSC,
+ .timer_cb = fsm_reset_ack_timeout_cb,
+};
+
+/* Create and start state machine which handles the reset/reset-ack procedure */
+void start_reset_fsm(struct bsc_msc_data *msc)
+{
+ OSMO_ASSERT(msc);
+ OSMO_ASSERT(msc->msc_con);
+
+ osmo_fsm_register(&fsm);
+ msc->msc_con->fsm_reset = osmo_fsm_inst_alloc(&fsm, NULL, NULL, LOGL_DEBUG, "FSM RESET INST");
+ OSMO_ASSERT(msc->msc_con->fsm_reset);
+
+ msc->msc_con->fsm_reset->priv = msc;
+
+ /* kick off reset-ack sending mechanism */
+ osmo_fsm_inst_state_chg(msc->msc_con->fsm_reset, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
+}
+
+/* Confirm that we sucessfully received a reset acknowlege message */
+void reset_ack_confirm(struct bsc_msc_data *msc)
+{
+ OSMO_ASSERT(msc);
+ OSMO_ASSERT(msc->msc_con);
+ OSMO_ASSERT(msc->msc_con->fsm_reset);
+
+ osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_RESET_ACK, msc);
+}
+
+/* Report a failed connection */
+void report_conn_fail(struct bsc_msc_data *msc)
+{
+ OSMO_ASSERT(msc);
+ OSMO_ASSERT(msc->msc_con);
+ OSMO_ASSERT(msc->msc_con->fsm_reset);
+
+ osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_N_DISCONNECT, msc);
+}
+
+/* Report a successful connection */
+void report_conn_success(struct bsc_msc_data *msc)
+{
+ OSMO_ASSERT(msc);
+ OSMO_ASSERT(msc->msc_con);
+ OSMO_ASSERT(msc->msc_con->fsm_reset);
+
+ osmo_fsm_inst_dispatch(msc->msc_con->fsm_reset, EV_N_CONNECT, msc);
+}
+
+/* Check if we have a connection to a specified msc */
+bool sccp_conn_ready(struct bsc_msc_data *msc)
+{
+ OSMO_ASSERT(msc);
+ OSMO_ASSERT(msc->msc_con);
+ OSMO_ASSERT(msc->msc_con->fsm_reset);
+ if (msc->msc_con->fsm_reset->state == ST_CONN)
+ return true;
+
+ return false;
+}
diff --git a/src/osmo-bsc/osmo_bsc_sccp.c b/src/osmo-bsc/osmo_bsc_sccp.c
deleted file mode 100644
index e242390ef..000000000
--- a/src/osmo-bsc/osmo_bsc_sccp.c
+++ /dev/null
@@ -1,328 +0,0 @@
-/* Interaction with the SCCP subsystem */
-/*
- * (C) 2009-2014 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2009-2014 by On-Waves
- * 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 <openbsc/gsm_data.h>
-#include <openbsc/osmo_bsc.h>
-#include <openbsc/osmo_bsc_grace.h>
-#include <openbsc/bsc_msc_data.h>
-#include <openbsc/gsm_04_80.h>
-#include <openbsc/debug.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/signal.h>
-
-#include <osmocom/core/talloc.h>
-#include <osmocom/gsm/gsm0808.h>
-#include <osmocom/gsm/protocol/gsm_08_08.h>
-
-#include <osmocom/sccp/sccp.h>
-
-/* SCCP helper */
-#define SCCP_IT_TIMER 60
-
-static LLIST_HEAD(active_connections);
-
-static void free_queued(struct osmo_bsc_sccp_con *conn)
-{
- struct msgb *msg;
-
- while (!llist_empty(&conn->sccp_queue)) {
- /* this is not allowed to fail */
- msg = msgb_dequeue(&conn->sccp_queue);
- msgb_free(msg);
- }
-
- conn->sccp_queue_size = 0;
-}
-
-static void send_queued(struct osmo_bsc_sccp_con *conn)
-{
- struct msgb *msg;
-
- while (!llist_empty(&conn->sccp_queue)) {
- /* this is not allowed to fail */
- msg = msgb_dequeue(&conn->sccp_queue);
- sccp_connection_write(conn->sccp, msg);
- msgb_free(msg);
- conn->sccp_queue_size -= 1;
- }
-}
-
-static void msc_outgoing_sccp_data(struct sccp_connection *conn,
- struct msgb *msg, unsigned int len)
-{
- struct osmo_bsc_sccp_con *bsc_con =
- (struct osmo_bsc_sccp_con *) conn->data_ctx;
-
- bsc_handle_dt1(bsc_con, msg, len);
-}
-
-static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state)
-{
- struct osmo_bsc_sccp_con *con_data;
-
- if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
- con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
- if (con_data->conn) {
- LOGP(DMSC, LOGL_ERROR,
- "ERROR: The lchan is still associated.\n");
- gsm0808_clear(con_data->conn);
- bsc_subscr_con_free(con_data->conn);
- con_data->conn = NULL;
- }
-
- con_data->sccp = NULL;
- free_queued(con_data);
- sccp_connection_free(conn);
- bsc_delete_connection(con_data);
- } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) {
- LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn);
- con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx;
-
- osmo_timer_del(&con_data->sccp_cc_timeout);
- osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0);
-
- send_queued(con_data);
- }
-}
-
-static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data)
-{
- if (data->conn) {
- gsm0808_clear(data->conn);
- bsc_subscr_con_free(data->conn);
- data->conn = NULL;
- }
-
- free_queued(data);
- sccp_connection_force_free(data->sccp);
- data->sccp = NULL;
- bsc_delete_connection(data);
-}
-
-static void sccp_it_timeout(void *_data)
-{
- struct osmo_bsc_sccp_con *data =
- (struct osmo_bsc_sccp_con *) _data;
-
- sccp_connection_send_it(data->sccp);
- osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0);
-}
-
-static void sccp_cc_timeout(void *_data)
-{
- struct osmo_bsc_sccp_con *data =
- (struct osmo_bsc_sccp_con *) _data;
-
- if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED)
- return;
-
- LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n");
- bsc_sccp_force_free(data);
-}
-
-static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg,
- void *global_ctx, void *ctx)
-{
- struct bsc_msc_connection *msc_con;
-
- if (conn) {
- struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx;
- msc_con = bsc_con->msc->msc_con;
- if (bsc_con->send_ping) {
- bsc_con->send_ping = 0;
- msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP);
- return;
- }
- } else {
- msc_con = ctx;
- }
-
- msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP);
-}
-
-static int msc_sccp_accept(struct sccp_connection *connection, void *data)
-{
- LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n");
- return -1;
-}
-
-static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data)
-{
- struct bsc_msc_data *msc = (struct bsc_msc_data *) msgb->cb[0];
- return bsc_handle_udt(msc, msgb, length);
-}
-
-int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
-{
- struct sccp_connection *sccp = conn->sccp;
-
- if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
- LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
- msgb_free(msg);
- } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED
- && conn->sccp_queue_size == 0) {
- sccp_connection_write(sccp, msg);
- msgb_free(msg);
- } else if (conn->sccp_queue_size > 10) {
- LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp);
- msgb_free(msg);
- } else {
- LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size);
- conn->sccp_queue_size += 1;
- msgb_enqueue(&conn->sccp_queue, msg);
- }
-
- return 0;
-}
-
-enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn,
- struct bsc_msc_data *msc, int send_ping)
-{
- struct osmo_bsc_sccp_con *bsc_con;
- struct sccp_connection *sccp;
-
- /* This should not trigger */
- if (!msc || !msc->msc_con->is_authenticated) {
- LOGP(DMSC, LOGL_ERROR,
- "How did this happen? MSC is not connected. Dropping.\n");
- return BSC_CON_REJECT_NO_LINK;
- }
-
- if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) {
- LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
- return BSC_CON_REJECT_RF_GRACE;
- }
-
- sccp = sccp_connection_socket();
- if (!sccp) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n");
- return BSC_CON_NO_MEM;
- }
-
- bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
- if (!bsc_con) {
- LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n");
- sccp_connection_free(sccp);
- return BSC_CON_NO_MEM;
- }
-
- /* callbacks */
- sccp->state_cb = msc_outgoing_sccp_state;
- sccp->data_cb = msc_outgoing_sccp_data;
- sccp->data_ctx = bsc_con;
-
- bsc_con->send_ping = send_ping;
-
- /* prepare the timers */
- osmo_timer_setup(&bsc_con->sccp_it_timeout, sccp_it_timeout, bsc_con);
- osmo_timer_setup(&bsc_con->sccp_cc_timeout, sccp_cc_timeout, bsc_con);
-
- INIT_LLIST_HEAD(&bsc_con->sccp_queue);
-
- bsc_con->sccp = sccp;
- bsc_con->msc = msc;
- bsc_con->conn = conn;
- llist_add_tail(&bsc_con->entry, &active_connections);
- conn->sccp_con = bsc_con;
- return BSC_CON_SUCCESS;
-}
-
-int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
-{
- osmo_timer_schedule(&conn->sccp_cc_timeout, 10, 0);
- sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg);
- msgb_free(msg);
- return 0;
-}
-
-int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp)
-{
- if (!sccp)
- return 0;
-
- if (sccp->conn)
- LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n");
-
- llist_del(&sccp->entry);
- osmo_timer_del(&sccp->sccp_it_timeout);
- osmo_timer_del(&sccp->sccp_cc_timeout);
- talloc_free(sccp);
- return 0;
-}
-
-static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con)
-{
- struct gsm_subscriber_connection *conn = con->conn;
-
- /* send USSD notification if string configured and con->data is set */
- if (!conn)
- return;
-
- /* check for config string */
- if (!con->msc->ussd_msc_lost_txt)
- return;
- if (con->msc->ussd_msc_lost_txt[0] == '\0')
- return;
-
- /* send USSD notification */
- bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_msc_lost_txt);
- bsc_send_ussd_release_complete(conn);
-}
-
-static void bsc_notify_and_close_conns(struct bsc_msc_connection *msc_con)
-{
- struct osmo_bsc_sccp_con *con, *tmp;
-
- llist_for_each_entry_safe(con, tmp, &active_connections, entry) {
- if (con->msc->msc_con != msc_con)
- continue;
-
- bsc_notify_msc_lost(con);
- bsc_sccp_force_free(con);
- }
-}
-
-static int handle_msc_signal(unsigned int subsys, unsigned int signal,
- void *handler_data, void *signal_data)
-{
- struct msc_signal_data *msc;
-
- if (subsys != SS_MSC)
- return 0;
-
- msc = signal_data;
- if (signal == S_MSC_LOST)
- bsc_notify_and_close_conns(msc->data->msc_con);
-
- return 0;
-}
-
-int osmo_bsc_sccp_init(struct gsm_network *gsmnet)
-{
- sccp_set_log_area(DSCCP);
- sccp_system_init(msc_sccp_write_ipa, gsmnet);
- sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL);
- sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet);
-
- osmo_signal_register_handler(SS_MSC, handle_msc_signal, gsmnet);
-
- return 0;
-}
diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c
new file mode 100644
index 000000000..0f6ca334f
--- /dev/null
+++ b/src/osmo-bsc/osmo_bsc_sigtran.c
@@ -0,0 +1,561 @@
+/* (C) 2017 by sysmocom s.f.m.c. GmbH
+ * 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/core/utils.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+#include <osmocom/sigtran/sccp_sap.h>
+#include <osmocom/sccp/sccp_types.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/core/msgb.h>
+#include <openbsc/bsc_msc_data.h>
+#include <openbsc/debug.h>
+#include <openbsc/osmo_bsc.h>
+#include <openbsc/osmo_bsc_grace.h>
+#include <openbsc/osmo_bsc_sigtran.h>
+#include <openbsc/a_reset.h>
+#include <openbsc/gsm_04_80.h>
+
+/* A pointer to a list with all involved MSCs
+ * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */
+static struct llist_head *msc_list;
+
+#define RESET_INTERVAL 1 /* sek */
+#define SCCP_MSG_MAXSIZE 1024
+#define CS7_POINTCODE_DEFAULT_OFFSET 2
+
+/* Internal list with connections we currently maintain. This
+ * list is of type struct osmo_bsc_sccp_con */
+static LLIST_HEAD(active_connections);
+
+/* The SCCP stack will not assign connection IDs to us automatically, we
+ * will do this ourselves using a counter variable, that counts one up
+ * for every new connection */
+static uint32_t conn_id_counter;
+
+/* Helper function to Check if the given connection id is already assigned */
+static struct osmo_bsc_sccp_con *get_bsc_conn_by_conn_id(int conn_id)
+{
+ conn_id &= 0xFFFFFF;
+ struct osmo_bsc_sccp_con *bsc_con;
+
+ llist_for_each_entry(bsc_con, &active_connections, entry) {
+ if (bsc_con->conn_id == conn_id)
+ return bsc_con;
+ }
+
+ return NULL;
+}
+
+/* Pick a free connection id */
+static int pick_free_conn_id(const struct bsc_msc_data *msc)
+{
+ int conn_id = conn_id_counter;
+ int i;
+
+ for (i = 0; i < 0xFFFFFF; i++) {
+ conn_id++;
+ conn_id &= 0xFFFFFF;
+ if (get_bsc_conn_by_conn_id(conn_id) == false) {
+ conn_id_counter = conn_id;
+ return conn_id;
+ }
+ }
+
+ return -1;
+}
+
+/* Send reset to MSC */
+static void osmo_bsc_sigtran_tx_reset(const struct bsc_msc_data *msc)
+{
+ struct osmo_ss7_instance *ss7;
+ struct msgb *msg;
+
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ msg = gsm0808_create_reset();
+ osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr,
+ &msc->a.msc_addr, msg);
+}
+
+/* Send reset-ack to MSC */
+void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc)
+{
+ struct osmo_ss7_instance *ss7;
+ struct msgb *msg;
+ OSMO_ASSERT(msc);
+
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_NOTICE, "Sending RESET ACK to MSC: %s\n", osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+ msg = gsm0808_create_reset_ack();
+ osmo_sccp_tx_unitdata_msg(msc->a.sccp_user, &msc->a.bsc_addr,
+ &msc->a.msc_addr, msg);
+}
+
+/* Find an MSC by its sigtran point code */
+static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr)
+{
+ struct osmo_ss7_instance *ss7;
+ struct bsc_msc_data *msc;
+ llist_for_each_entry(msc, msc_list, entry) {
+ if (memcmp(msc_addr, &msc->a.msc_addr, sizeof(*msc_addr)) == 0)
+ return msc;
+ }
+
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_ERROR, "Unable to find MSC data under address: %s\n", osmo_sccp_addr_name(ss7, msc_addr));
+ return NULL;
+}
+
+/* Send data to MSC, use the connection id which MSC it is */
+static int handle_data_from_msc(int conn_id, struct msgb *msg)
+{
+ struct osmo_bsc_sccp_con *bsc_con = get_bsc_conn_by_conn_id(conn_id);
+ int rc = -EINVAL;
+
+ if (bsc_con) {
+ msg->l3h = msgb_l2(msg);
+ rc = bsc_handle_dt(bsc_con, msg, msgb_l2len(msg));
+ } else
+ LOGP(DMSC, LOGL_NOTICE, "incoming data from unknown connection id: %i\n", conn_id);
+
+ return rc;
+}
+
+/* Sent unitdata to MSC, use the point code to determine which MSC it is */
+static int handle_unitdata_from_msc(const struct osmo_sccp_addr *msc_addr, struct msgb *msg,
+ const struct osmo_sccp_user *scu)
+{
+ struct osmo_ss7_instance *ss7;
+ struct bsc_msc_data *msc = get_msc_by_addr(msc_addr);
+ int rc = -EINVAL;
+
+ if (msc) {
+ msg->l3h = msgb_l2(msg);
+ rc = bsc_handle_udt(msc, msg, msgb_l2len(msg));
+ } else {
+ ss7 = osmo_sccp_get_ss7(osmo_sccp_get_sccp(scu));
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_NOTICE, "incoming unitdata data from unknown remote address: %s\n",
+ osmo_sccp_addr_name(ss7, msc_addr));
+ }
+ return rc;
+}
+
+/* Callback function, called by the SSCP stack when data arrives */
+static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
+{
+ struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;
+ struct osmo_sccp_user *scu = _scu;
+ struct osmo_bsc_sccp_con *bsc_con;
+ int rc = 0;
+
+ switch (OSMO_PRIM_HDR(&scu_prim->oph)) {
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
+ /* Handle inbound UNITDATA */
+ DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
+ rc = handle_unitdata_from_msc(&scu_prim->u.unitdata.calling_addr, oph->msg, scu);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
+ /* Handle (Reject) 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);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM):
+ /* Handle outbound connection confirmation */
+ 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(DRANAP, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
+ /* Handle incoming connection oriented data */
+ DEBUGP(DMSC, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id,
+ osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
+
+ /* Incoming data is a sign of a vital connection */
+ bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
+ if (bsc_con)
+ a_reset_conn_success(bsc_con->msc->a.reset);
+
+ rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg);
+ break;
+
+ case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION):
+ /* indication of disconnect */
+ 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(DRANAP, "N-DISCONNECT.ind(%u, cause=%i)\n", scu_prim->u.disconnect.conn_id,
+ scu_prim->u.disconnect.cause);
+
+ bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
+ if (bsc_con) {
+ /* We might have a connectivity problem. Maybe we need to go
+ * through the reset procedure again? */
+ if (scu_prim->u.disconnect.cause == 0)
+ a_reset_conn_fail(bsc_con->msc->a.reset);
+
+ rc = osmo_bsc_sigtran_del_conn(bsc_con);
+ }
+ break;
+
+ default:
+ LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation);
+ break;
+ }
+
+ msgb_free(oph->msg);
+ return rc;
+}
+
+/* Allocate resources to make a new connection oriented sigtran connection
+ * (not the connection ittself!) */
+enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc)
+{
+ struct osmo_ss7_instance *ss7;
+ struct osmo_bsc_sccp_con *bsc_con;
+ int conn_id;
+
+ OSMO_ASSERT(conn);
+ OSMO_ASSERT(msc);
+
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_NOTICE, "Initializing resources for new SIGTRAN connection to MSC: %s...\n",
+ osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+
+ if (a_reset_conn_ready(msc->a.reset) == false) {
+ LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
+ return BSC_CON_REJECT_NO_LINK;
+ }
+
+ if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) {
+ LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n");
+ return BSC_CON_REJECT_RF_GRACE;
+ }
+
+ bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con);
+ if (!bsc_con) {
+ LOGP(DMSC, LOGL_ERROR, "Failed to allocate new SIGTRAN connection.\n");
+ return BSC_CON_NO_MEM;
+ }
+
+ bsc_con->msc = msc;
+ bsc_con->conn = conn;
+ llist_add_tail(&bsc_con->entry, &active_connections);
+ conn->sccp_con = bsc_con;
+
+ /* Pick a free connection id */
+ conn_id = pick_free_conn_id(msc);
+ if (conn_id < 0)
+ return BSC_CON_REJECT_NO_LINK;
+ bsc_con->conn_id = conn_id;
+
+ LOGP(DMSC, LOGL_NOTICE, "Allocated new connection id: %i\n", conn_id);
+
+ return BSC_CON_SUCCESS;
+}
+
+/* Open a new connection oriented sigtran connection */
+int osmo_bsc_sigtran_open_conn(const struct osmo_bsc_sccp_con *conn, struct msgb *msg)
+{
+ struct osmo_ss7_instance *ss7;
+ struct bsc_msc_data *msc;
+ int conn_id;
+ int rc;
+
+ OSMO_ASSERT(conn);
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(conn->msc);
+
+ msc = conn->msc;
+
+ if (a_reset_conn_ready(msc->a.reset) == false) {
+ LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
+ return -EINVAL;
+ }
+
+ conn_id = conn->conn_id;
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_NOTICE, "Opening new SIGTRAN connection (id=%i) to MSC: %s\n", conn_id,
+ osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+
+ rc = osmo_sccp_tx_conn_req_msg(msc->a.sccp_user, conn_id, &msc->a.bsc_addr,
+ &msc->a.msc_addr, msg);
+
+ return rc;
+}
+
+/* Send data to MSC */
+int osmo_bsc_sigtran_send(const struct osmo_bsc_sccp_con *conn, struct msgb *msg)
+{
+ struct osmo_ss7_instance *ss7;
+ int conn_id;
+ int rc;
+ struct bsc_msc_data *msc;
+
+ OSMO_ASSERT(conn);
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(conn->msc);
+
+ msc = conn->msc;
+
+ if (a_reset_conn_ready(msc->a.reset) == false) {
+ LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
+ return -EINVAL;
+ }
+
+ conn_id = conn->conn_id;
+
+ ss7 = osmo_ss7_instance_find(msc->a.cs7_instance);
+ OSMO_ASSERT(ss7);
+ LOGP(DMSC, LOGL_DEBUG, "Sending connection (id=%i) oriented data to MSC: %si\n",
+ conn_id, osmo_sccp_addr_name(ss7, &msc->a.msc_addr));
+
+ rc = osmo_sccp_tx_data_msg(msc->a.sccp_user, conn_id, msg);
+
+ 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 osmo_bsc_sccp_con *conn)
+{
+ if (!conn)
+ return 0;
+
+ if (conn->conn) {
+ LOGP(DMSC, LOGL_ERROR,
+ "sccp connection (id=%i) not cleared (gsm subscriber connection still active) -- forcefully clearing it now!\n",
+ conn->conn_id);
+ bsc_subscr_con_free(conn->conn);
+ conn->conn = NULL;
+
+ /* 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->msc->a.reset);
+ }
+
+ llist_del(&conn->entry);
+ talloc_free(conn);
+
+ return 0;
+}
+
+/* Send an USSD notification in case we loose the connection to the MSC */
+static void bsc_notify_msc_lost(const struct osmo_bsc_sccp_con *conn)
+{
+ struct gsm_subscriber_connection *subscr_conn;
+
+ /* Check if sccp conn is still present */
+ if (!conn)
+ return;
+ subscr_conn = conn->conn;
+
+ /* send USSD notification if string configured and conn->data is set */
+ if (!subscr_conn)
+ return;
+
+ /* check for config string */
+ if (!conn->msc->ussd_msc_lost_txt)
+ return;
+ if (conn->msc->ussd_msc_lost_txt[0] == '\0')
+ return;
+
+ /* send USSD notification */
+ bsc_send_ussd_notify(subscr_conn, 1, subscr_conn->sccp_con->msc->ussd_msc_lost_txt);
+ bsc_send_ussd_release_complete(subscr_conn);
+}
+
+/* Close all open sigtran connections and channels */
+void osmo_bsc_sigtran_reset(const struct bsc_msc_data *msc)
+{
+ struct osmo_bsc_sccp_con *conn;
+ struct osmo_bsc_sccp_con *conn_temp;
+ OSMO_ASSERT(msc);
+
+ /* Close all open connections */
+ llist_for_each_entry_safe(conn, conn_temp, &active_connections, entry) {
+
+ /* We only may close connections which actually belong to this
+ * MSC. All other open connections are left untouched */
+ if (conn->msc == msc) {
+ /* Notify active connection users via USSD that the MSC is down */
+ bsc_notify_msc_lost(conn);
+
+ /* Take down all occopied RF channels */
+ if (conn->conn)
+ gsm0808_clear(conn->conn);
+
+ /* Disconnect all Sigtran connections */
+ osmo_sccp_tx_disconn(msc->a.sccp_user, conn->conn_id, &msc->a.bsc_addr, 0);
+
+ /* Delete subscriber connection */
+ osmo_bsc_sigtran_del_conn(conn);
+ }
+ }
+}
+
+/* Callback function: Close all open connections */
+static void osmo_bsc_sigtran_reset_cb(const void *priv)
+{
+ struct bsc_msc_data *msc = (struct bsc_msc_data*) priv;
+
+ /* Shut down all ongoing traffic */
+ osmo_bsc_sigtran_reset(msc);
+
+ /* Send reset to MSC */
+ osmo_bsc_sigtran_tx_reset(msc);
+}
+
+/* Default point-code to be used as local address (BSC) */
+#define BSC_DEFAULT_PC "0.23.3"
+
+/* Default point-code to be used as remote address (MSC) */
+#define MSC_DEFAULT_PC "0.23.1"
+
+/* Initalize osmo sigtran backhaul */
+int osmo_bsc_sigtran_init(struct llist_head *mscs)
+{
+ bool free_attempt_used = false;
+ bool fail_on_next_invalid_cfg = false;
+
+ struct bsc_msc_data *msc;
+ char msc_name[32];
+ uint32_t default_pc;
+
+ OSMO_ASSERT(mscs);
+ msc_list = mscs;
+
+ llist_for_each_entry(msc, msc_list, entry) {
+ snprintf(msc_name, sizeof(msc_name), "msc-%u", msc->nr);
+ LOGP(DMSC, LOGL_NOTICE, "Initializing SCCP connection to MSC %s\n", msc_name);
+
+ /* Check if the VTY could determine a valid CS7 instance,
+ * use safe default in case none is set */
+ if (msc->a.cs7_instance_valid == false) {
+ msc->a.cs7_instance = 0;
+ if (fail_on_next_invalid_cfg)
+ goto fail_auto_cofiguration;
+ free_attempt_used = true;
+ }
+ LOGP(DMSC, LOGL_NOTICE, "CS7 Instance identifier, A-Interface: %u\n", msc->a.cs7_instance);
+
+ /* Pre-Check if there is an ss7 instance present */
+ if (osmo_ss7_instance_find(msc->a.cs7_instance) == NULL) {
+ if (fail_on_next_invalid_cfg)
+ goto fail_auto_cofiguration;
+ free_attempt_used = true;
+ }
+
+ /* SS7 Protocol stack */
+ default_pc = osmo_ss7_pointcode_parse(NULL, BSC_DEFAULT_PC);
+ msc->a.sccp =
+ osmo_sccp_simple_client_on_ss7_id(msc, msc->a.cs7_instance, msc_name, default_pc,
+ OSMO_SS7_ASP_PROT_M3UA, 0, NULL, 0, NULL);
+ if (!msc->a.sccp)
+ return -EINVAL;
+
+ /* Check if the sccp-address fullfills minimum requirements (SSN+PC is present,
+ * automatically recover addresses if the addresses are not set up properly) */
+ if (!osmo_sccp_check_addr(&msc->a.bsc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
+ if (fail_on_next_invalid_cfg)
+ goto fail_auto_cofiguration;
+ free_attempt_used = true;
+
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: invalid or missing local (BSC) SCCP address (a.bsc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr));
+ osmo_sccp_local_addr_by_instance(&msc->a.bsc_addr, msc->a.sccp, SCCP_SSN_BSSAP);
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: using automatically generated local (BSC) SCCP address (a.bsc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr));
+ } else {
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: using local (BSC) automatically SCCP address (a.msc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.bsc_addr));
+ }
+
+ if (!osmo_sccp_check_addr(&msc->a.msc_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) {
+ if (fail_on_next_invalid_cfg)
+ goto fail_auto_cofiguration;
+ free_attempt_used = true;
+
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: invalid or missing remote (MSC) SCCP address for the MSC (a.msc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr));
+ osmo_sccp_local_addr_by_instance(&msc->a.msc_addr, msc->a.sccp, SCCP_SSN_BSSAP);
+ msc->a.msc_addr.pc = osmo_ss7_pointcode_parse(NULL, MSC_DEFAULT_PC);
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: using automatically generated remote (MSC) SCCP address (a.msc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr));
+ free_attempt_used = true;
+ } else {
+ LOGP(DMSC, LOGL_NOTICE,
+ "A-interface: using remote (MSC) automatically SCCP address (a.msc_addr=%s)\n",
+ osmo_sccp_addr_name(osmo_ss7_instance_find(msc->a.cs7_instance), &msc->a.msc_addr));
+ }
+
+ /* Bind SCCP user */
+ msc->a.sccp_user = osmo_sccp_user_bind(msc->a.sccp, msc_name, sccp_sap_up, msc->a.bsc_addr.ssn);
+ if (!msc->a.sccp_user)
+ return -EINVAL;
+
+ /* Start MSC-Reset procedure */
+ msc->a.reset = a_reset_alloc(msc, msc_name, osmo_bsc_sigtran_reset_cb, msc);
+ if (!msc->a.reset)
+ return -EINVAL;
+
+ /* If we have detected that the SS7 configuration of the MSC we have just initalized
+ * was incomplete or completely missing, we can not tolerate another incomplete
+ * configuration. The reson for this is that we do only specify exactly one default
+ * pointcode pair. We also specify localhost as default IP-Address. If we have wanted
+ * to support multiple MSCs with automatic configuration we would be forced to invent
+ * a complex ruleset how to allocate the pointcodes and respective IP-Addresses.
+ * Furthermore, the situation where a single BSC is connected to multiple MSCs
+ * is a very rare situation anyway. In this case we expect the user to experienced
+ * enough to create a valid SS7/CS7 VTY configuration that does not lack any
+ * components */
+ if (free_attempt_used)
+ fail_on_next_invalid_cfg = true;
+ }
+
+ return 0;
+
+fail_auto_cofiguration:
+ LOGP(DMSC, LOGL_ERROR,
+ "A-interface: More than one invalid/inclomplete configuration detected, unable to revover - check config file!\n");
+ return -EINVAL;
+}
diff --git a/src/osmo-bsc/osmo_bsc_vty.c b/src/osmo-bsc/osmo_bsc_vty.c
index 2e2e99bfd..8edcbf390 100644
--- a/src/osmo-bsc/osmo_bsc_vty.c
+++ b/src/osmo-bsc/osmo_bsc_vty.c
@@ -28,6 +28,7 @@
#include <osmocom/core/talloc.h>
#include <osmocom/vty/logging.h>
+#include <osmocom/sccp/sccp_types.h>
#include <time.h>
@@ -184,6 +185,16 @@ static void write_msc(struct vty *vty, struct bsc_msc_data *msc)
/* write amr options */
write_msc_amr_options(vty, msc);
+
+ /* write sccp connection configuration */
+ if (msc->a.bsc_addr_name) {
+ vty_out(vty, " bsc-addr %s%s",
+ msc->a.bsc_addr_name, VTY_NEWLINE);
+ }
+ if (msc->a.msc_addr_name) {
+ vty_out(vty, " msc-addr %s%s",
+ msc->a.msc_addr_name, VTY_NEWLINE);
+ }
}
static int config_write_msc(struct vty *vty)
@@ -685,6 +696,87 @@ DEFUN(cfg_msc_no_acc_lst_name,
return CMD_SUCCESS;
}
+/* Make sure only standard SSN numbers are used. If no ssn number is
+ * configured, silently apply the default SSN */
+static void enforce_standard_ssn(struct vty *vty, struct osmo_sccp_addr *addr)
+{
+ if (addr->presence & OSMO_SCCP_ADDR_T_SSN) {
+ if (addr->ssn != SCCP_SSN_BSSAP)
+ vty_out(vty,
+ "setting an SSN (%u) different from the standard (%u) is not allowd, will use standard SSN for address: %s%s",
+ addr->ssn, SCCP_SSN_BSSAP, osmo_sccp_addr_dump(addr), VTY_NEWLINE);
+ }
+
+ addr->presence |= OSMO_SCCP_ADDR_T_SSN;
+ addr->ssn = SCCP_SSN_BSSAP;
+}
+
+DEFUN(cfg_msc_cs7_bsc_addr,
+ cfg_msc_cs7_bsc_addr_cmd,
+ "bsc-addr NAME",
+ "Calling Address (local address of this BSC)\n" "SCCP address name\n")
+{
+ struct bsc_msc_data *msc = bsc_msc_data(vty);
+ const char *bsc_addr_name = argv[0];
+ struct osmo_ss7_instance *ss7;
+
+ ss7 = osmo_sccp_addr_by_name(&msc->a.bsc_addr, bsc_addr_name);
+ if (!ss7) {
+ vty_out(vty, "No sccp address %s found%s", bsc_addr_name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Prevent mixing addresses from different CS7/SS7 instances */
+ if (msc->a.cs7_instance_valid) {
+ if (msc->a.cs7_instance != ss7->cfg.id) {
+ vty_out(vty,
+ "SCCP address %s from different CS7 instance%s",
+ bsc_addr_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ msc->a.cs7_instance = ss7->cfg.id;
+ msc->a.cs7_instance_valid = true;
+ enforce_standard_ssn(vty, &msc->a.bsc_addr);
+ msc->a.bsc_addr_name = talloc_strdup(msc, bsc_addr_name);
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_msc_cs7_msc_addr,
+ cfg_msc_cs7_msc_addr_cmd,
+ "msc-addr NAME",
+ "Called Address (remote address of the MSC)\n" "SCCP address name\n")
+{
+ struct bsc_msc_data *msc = bsc_msc_data(vty);
+ const char *msc_addr_name = argv[0];
+ struct osmo_ss7_instance *ss7;
+
+ ss7 = osmo_sccp_addr_by_name(&msc->a.msc_addr, msc_addr_name);
+ if (!ss7) {
+ vty_out(vty, "No sccp address %s found%s", msc_addr_name,
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* Prevent mixing addresses from different CS7/SS7 instances */
+ if (msc->a.cs7_instance_valid) {
+ if (msc->a.cs7_instance != ss7->cfg.id) {
+ vty_out(vty,
+ "SCCP address %s from different CS7 instance%s",
+ msc_addr_name, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ msc->a.cs7_instance = ss7->cfg.id;
+ msc->a.cs7_instance_valid = true;
+ enforce_standard_ssn(vty, &msc->a.msc_addr);
+ msc->a.msc_addr_name = talloc_strdup(msc, msc_addr_name);
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_net_bsc_mid_call_text,
cfg_net_bsc_mid_call_text_cmd,
"mid-call-text .TEXT",
@@ -931,6 +1023,8 @@ int bsc_vty_init_extra(void)
install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd);
install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd);
install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd);
+ install_element(MSC_NODE, &cfg_msc_cs7_bsc_addr_cmd);
+ install_element(MSC_NODE, &cfg_msc_cs7_msc_addr_cmd);
install_element_ve(&show_statistics_cmd);
install_element_ve(&show_mscs_cmd);