diff options
Diffstat (limited to 'src/osmo-bsc')
-rw-r--r-- | src/osmo-bsc/Makefile.am | 4 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_api.c | 41 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_audio.c | 70 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_bssap.c | 307 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_main.c | 10 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_msc.c | 62 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_reset.c | 190 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_sccp.c | 328 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_sigtran.c | 561 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_vty.c | 94 |
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); |