aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc
diff options
context:
space:
mode:
authorPhilipp Maier <pmaier@sysmocom.de>2017-04-09 12:32:51 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2017-08-30 14:12:37 +0200
commitefe85d33d4948a20de1baec2e8956113714ec72e (patch)
tree144135f7b5ae2584ca71377c424024c9b535ed73 /src/osmo-bsc
parent868dd5d8d31f756e404a7bfb7896aed21d20f905 (diff)
Implement AoIP, port to M3UA SIGTRAN (large addition and refactoring)
This was originally a long series of commits converging to the final result seen in this patch. It does not make much sense to review the smaller steps' trial and error, we need to review this entire change as a whole. Implement AoIP in osmo-msc and osmo-bsc. Change over to the new libosmo-sigtran API with support for proper SCCP/M3UA/SCTP stacking, as mandated by 3GPP specifications for the IuCS and IuPS interfaces. From here on, a separate osmo-stp process is required for SCCP routing between OsmoBSC / OsmoHNBGW <-> OsmoMSC / OsmoSGSN jenkins.sh: build from libosmo-sccp and osmo-iuh master branches now for new M3UA SIGTRAN. Patch-by: pmaier, nhofmeyr, laforge Change-Id: I5ae4e05ee7c57cad341ea5e86af37c1f6b0ffa77
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 ae9410c9..5642fb2e 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 8c33e2b5..f7343f74 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 11602090..b4ffa88f 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 100f6644..4353b9ad 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 90651b95..cf188a9e 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 8d02624b..351fd2ce 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 00000000..0baf0808
--- /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 e242390e..00000000
--- 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 00000000..0f6ca334
--- /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 2e2e99bf..8edcbf39 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);