diff options
Diffstat (limited to 'src/osmo-bsc/osmo_bsc_bssap.c')
-rw-r--r-- | src/osmo-bsc/osmo_bsc_bssap.c | 304 |
1 files changed, 247 insertions, 57 deletions
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 60c858ffd..c6fbe23c8 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -19,6 +19,8 @@ * */ +#include <osmocom/mgcp_client/mgcp_client_fsm.h> + #include <osmocom/bsc/osmo_bsc.h> #include <osmocom/bsc/osmo_bsc_grace.h> #include <osmocom/bsc/osmo_bsc_rf.h> @@ -26,14 +28,20 @@ #include <osmocom/bsc/bsc_subscriber.h> #include <osmocom/bsc/paging.h> #include <osmocom/bsc/gsm_04_08_rr.h> +#include <osmocom/bsc/gsm_04_80.h> #include <osmocom/bsc/bsc_subscr_conn_fsm.h> #include <osmocom/bsc/codec_pref.h> +#include <osmocom/bsc/abis_rsl.h> +#include <osmocom/bsc/handover_fsm.h> + +#include <osmocom/gsm/protocol/gsm_08_08.h> #include <osmocom/gsm/gsm0808.h> #include <osmocom/bsc/osmo_bsc_sigtran.h> #include <osmocom/bsc/osmo_bsc_lcls.h> #include <osmocom/bsc/a_reset.h> -#include <osmocom/core/byteswap.h> +#include <osmocom/bsc/handover.h> #include <osmocom/core/fsm.h> +#include <osmocom/core/socket.h> #define IP_V4_ADDR_LEN 4 @@ -553,7 +561,6 @@ static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *con return 0; } - /* * Handle the assignment request message. * @@ -565,26 +572,24 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, struct msgb *resp; struct bsc_msc_data *msc; struct tlv_parsed tp; - uint8_t timeslot = 0; - uint8_t multiplex = 0; + uint16_t cic = 0; enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; - int full_rate = -1; + bool full_rate = false; bool aoip = false; struct sockaddr_storage rtp_addr; struct gsm0808_channel_type ct; - struct gsm0808_speech_codec_list *scl_ptr = NULL; uint8_t cause; int rc; + struct assignment_request req = {}; if (!conn) { LOGP(DMSC, LOGL_ERROR, - "No lchan/msc_data in cipher mode command.\n"); + "No lchan/msc_data in Assignment Request\n"); return -1; } msc = conn->sccp.msc; - if (msc->a.asp_proto != OSMO_SS7_ASP_PROT_IPA) - aoip = true; + aoip = gscon_is_aoip(conn); tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); @@ -616,10 +621,7 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, case GSM0808_CHAN_SPEECH: if (TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { /* CIC is permitted in both AoIP and SCCPlite */ - conn->user_plane.cic = - osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); - timeslot = conn->user_plane.cic & 0x1f; - multiplex = (conn->user_plane.cic & ~0x1f) >> 5; + cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); } else { if (!aoip) { /* no CIC but SCCPlite: illegal */ @@ -644,27 +646,18 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } - } else { - if (aoip) { - /* no AoIP transport level address but AoIP transport: illegal */ - LOGP(DMSC, LOGL_ERROR, "AoIP transport address missing in ASSIGN REQ, " - "audio would not work; rejecting\n"); - cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; - goto reject; - } + } else if (aoip) { + /* no AoIP transport level address but AoIP transport: illegal */ + LOGP(DMSC, LOGL_ERROR, "AoIP transport address missing in ASSIGN REQ, " + "audio would not work; rejecting\n"); + cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; + goto reject; } - /* Decode speech codec list (AoIP) */ - conn->codec_list_present = false; - if (aoip) { - - /* Check for speech codec list element */ - if (!TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory speech codec list not present.\n"); - cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; - goto reject; - } - + /* Decode speech codec list. First set len = 0. */ + conn->codec_list = (struct gsm0808_speech_codec_list){}; + /* Check for speech codec list element */ + if (TLVP_PRESENT(&tp, GSM0808_IE_SPEECH_CODEC_LIST)) { /* Decode Speech Codec list */ rc = gsm0808_dec_speech_codec_list(&conn->codec_list, TLVP_VAL(&tp, GSM0808_IE_SPEECH_CODEC_LIST), @@ -674,13 +667,20 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, cause = GSM0808_CAUSE_INCORRECT_VALUE; goto reject; } - conn->codec_list_present = true; - scl_ptr = &conn->codec_list; + } + + if (aoip && !conn->codec_list.len) { + LOGP(DMSC, LOGL_ERROR, "%s: AoIP speech mode Assignment Request:" + " Missing or empty Speech Codec List IE\n", bsc_subscr_name(conn->bsub)); + cause = GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING; + goto reject; } /* 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, conn_get_bts(conn)); + * local preferences of the BSC and BTS */ + rc = match_codec_pref(&chan_mode, &full_rate, &ct, &conn->codec_list, + msc->audio_support, msc->audio_length, + &conn_get_bts(conn)->codec); if (rc < 0) { LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type =" " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n", @@ -697,37 +697,37 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, get_value_string(gsm48_chan_mode_names, chan_mode), ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len)); - /* Forward the assignment request to lower layers */ + req = (struct assignment_request){ + .aoip = aoip, + .msc_assigned_cic = cic, + .chan_mode = chan_mode, + .full_rate = full_rate, + }; if (aoip) { - /* Store network side RTP connection information, we will - * process this address later after we have established an RTP - * connection to the BTS. This is just for organizational - * reasons, functional wise it would not matter when exactly - * the network side RTP connection is made, as long it is made - * before we return with the assignment complete message. */ - memcpy(&conn->user_plane.aoip_rtp_addr_remote, &rtp_addr, sizeof(rtp_addr)); - } else { - /* Note: In the sccp-lite case we to not perform any mgcp operation, - * (the MSC does that for us). We set conn->rtp_ip to 0 and check - * on this later. By this we know that we have to behave accordingly - * to sccp-lite. */ - conn->user_plane.rtp_port = mgcp_timeslot_to_port(multiplex, timeslot, msc->rtp_base); - conn->user_plane.rtp_ip = 0; + unsigned int rc = osmo_sockaddr_to_str_and_uint(req.msc_rtp_addr, + sizeof(req.msc_rtp_addr), + &req.msc_rtp_port, + (const struct sockaddr*)&rtp_addr); + if (!rc || rc >= sizeof(req.msc_rtp_addr)) { + LOGP(DMSC, LOGL_ERROR, "Assignment request: RTP address is too long\n"); + cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL; + goto reject; + } } - conn->user_plane.chan_mode = chan_mode; - conn->user_plane.full_rate = full_rate; - osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL); break; case GSM0808_CHAN_SIGN: - conn->user_plane.chan_mode = GSM48_CMODE_SIGN; - osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_A_ASSIGNMENT_CMD, NULL); + req = (struct assignment_request){ + .aoip = aoip, + .chan_mode = chan_mode, + }; break; default: cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS; goto reject; } - return 0; + return osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_ASSIGNMENT_START, &req); + reject: resp = gsm0808_create_assignment_failure(cause, NULL); OSMO_ASSERT(resp); @@ -736,6 +736,51 @@ reject: return -1; } +/* Handle Handover Command message, part of inter-BSC handover: + * This BSS sent a Handover Required message. + * The MSC contacts the remote BSS and receives from it an RR Handover Command; this BSSMAP Handover + * Command passes the RR Handover Command over to us and it's our job to forward to the MS. + * + * See 3GPP TS 48.008 ยง3.2.1.11 + */ +static int bssmap_handle_handover_cmd(struct gsm_subscriber_connection *conn, + struct msgb *msg, unsigned int length) +{ + struct tlv_parsed tp; + + if (!conn->ho.fi) { + LOGPFSML(conn->fi, LOGL_ERROR, + "Received Handover Command, but no handover was requested\n"); + /* Should we actually allow the MSC to make us handover without us having requested it + * first? Doesn't make any practical sense AFAICT. */ + return -EINVAL; + } + + 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_LAYER_3_INFORMATION)) { + LOGPFSML(conn->fi, LOGL_ERROR, + "Received Handover Command," + " but mandatory IE not present: Layer 3 Information\n"); + goto reject; + } + + /* Due to constness, need to declare this after tlv_parse(). */ + struct ho_out_rx_bssmap_ho_command rx = { + .l3_info = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION), + .l3_info_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION), + }; + + osmo_fsm_inst_dispatch(conn->ho.fi, HO_OUT_EV_BSSMAP_HO_COMMAND, &rx); + return 0; +reject: + /* No "Handover Command Reject" message or similar is specified, so we cannot reply in case of + * failure. Or is there?? */ + handover_end(conn, HO_RESULT_ERROR); + return -EINVAL; +} + static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length) { @@ -794,6 +839,9 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, case BSS_MAP_MSG_LCLS_CONNECT_CTRL: ret = bssmap_handle_lcls_connect_ctrl(conn, msg, length); break; + case BSS_MAP_MSG_HANDOVER_CMD: + ret = bssmap_handle_handover_cmd(conn, msg, length); + break; default: LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l4h[0])); @@ -803,6 +851,14 @@ static int bssmap_rcvmsg_dt1(struct gsm_subscriber_connection *conn, return ret; } +int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) +{ + bsc_send_ussd_notify(conn, 1, conn->sccp.msc->ussd_welcome_txt); + bsc_send_ussd_release_complete(conn); + + return 0; +} + static int dtap_rcvmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, unsigned int length) { @@ -907,3 +963,137 @@ int bsc_handle_dt(struct gsm_subscriber_connection *conn, return -1; } + +int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell_id_list2 *target_cells) +{ + int rc; + struct msgb *msg; + struct gsm0808_handover_required params = { + .cause = GSM0808_CAUSE_BETTER_CELL, + .cil = *target_cells, + .current_channel_type_1_present = true, + .current_channel_type_1 = gsm0808_current_channel_type_1(lchan->type), + }; + + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + params.speech_version_used_present = true; + params.speech_version_used = gsm0808_permitted_speech(lchan->type, + lchan->tch_mode); + if (!params.speech_version_used) { + LOG_HO(lchan->conn, LOGL_ERROR, "Cannot encode Speech Version (Used)" + " for BSSMAP Handover Required message\n"); + return -EINVAL; + } + break; + default: + break; + } + + msg = gsm0808_create_handover_required(¶ms); + if (!msg) { + LOG_HO(lchan->conn, LOGL_ERROR, "Cannot compose BSSMAP Handover Required message\n"); + return -EINVAL; + } + + rc = gscon_sigtran_send(lchan->conn, msg); + if (rc) { + LOG_HO(lchan->conn, LOGL_ERROR, "Cannot send BSSMAP Handover Required message\n"); + return rc; + } + + return 0; +} + +/* Inter-BSC MT HO, new BSS has allocated a channel and sends the RR Handover Command via MSC to the old + * BSS, encapsulated in a BSSMAP Handover Request Acknowledge. */ +int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct msgb *rr_ho_command) +{ + struct msgb *msg; + struct gsm_lchan *new_lchan = conn->ho.new_lchan; + + msg = gsm0808_create_handover_request_ack(rr_ho_command->data, rr_ho_command->len, + gsm0808_chosen_channel(new_lchan->type, + new_lchan->tch_mode), + new_lchan->encr.alg_id, + gsm0808_permitted_speech(new_lchan->type, + new_lchan->tch_mode)); + msgb_free(rr_ho_command); + if (!msg) + return -ENOMEM; + return osmo_bsc_sigtran_send(conn, msg); +} + +int bsc_tx_bssmap_ho_detect(struct gsm_subscriber_connection *conn) +{ + struct msgb *msg; + msg = gsm0808_create_handover_detect(); + if (!msg) + return -ENOMEM; + + return osmo_bsc_sigtran_send(conn, msg); +} + +enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection *conn, + struct gsm_lchan *lchan) +{ + int rc; + struct msgb *msg; + struct handover *ho = &conn->ho; + enum gsm0808_lcls_status lcls_status = lcls_get_status(conn); + + struct gsm0808_handover_complete params = { + .chosen_encr_alg_present = true, + .chosen_encr_alg = lchan->encr.alg_id, + + .chosen_channel_present = true, + .chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode), + + .lcls_bss_status_present = (lcls_status != 0xff), + .lcls_bss_status = lcls_status, + }; + + /* speech_codec_chosen */ + if (ho->new_lchan->activate.requires_voice_stream && gscon_is_aoip(conn)) { + int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); + params.speech_codec_chosen_present = true; + rc = gsm0808_speech_codec_from_chan_type(¶ms.speech_codec_chosen, perm_spch); + if (rc) { + LOG_HO(conn, LOGL_ERROR, "Unable to compose Speech Codec (Chosen)\n"); + return HO_RESULT_ERROR; + } + } + + msg = gsm0808_create_handover_complete(¶ms); + if (!msg) { + LOG_HO(conn, LOGL_ERROR, "Unable to compose BSSMAP Handover Complete message\n"); + return HO_RESULT_ERROR; + } + + rc = gscon_sigtran_send(conn, msg); + if (rc) { + LOG_HO(conn, LOGL_ERROR, "Cannot send BSSMAP Handover Complete message\n"); + return HO_RESULT_ERROR; + } + + return HO_RESULT_OK; +} + +void bsc_tx_bssmap_ho_failure(struct gsm_subscriber_connection *conn) +{ + int rc; + struct msgb *msg; + struct gsm0808_handover_failure params = {}; + + msg = gsm0808_create_handover_failure(¶ms); + if (!msg) { + LOG_HO(conn, LOGL_ERROR, "Unable to compose BSSMAP Handover Failure message\n"); + return; + } + + rc = gscon_sigtran_send(conn, msg); + if (rc) + LOG_HO(conn, LOGL_ERROR, "Cannot send BSSMAP Handover Failure message (rc=%d %s)\n", + rc, strerror(-rc)); +} |