diff options
Diffstat (limited to 'src/libmsc')
32 files changed, 0 insertions, 14979 deletions
diff --git a/src/libmsc/Makefile.am b/src/libmsc/Makefile.am deleted file mode 100644 index c9b8bb4cc..000000000 --- a/src/libmsc/Makefile.am +++ /dev/null @@ -1,75 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(LIBSMPP34_CFLAGS) \ - $(LIBASN1C_CFLAGS) \ - $(LIBOSMOSIGTRAN_CFLAGS) \ - $(NULL) - -noinst_HEADERS = \ - meas_feed.h \ - $(NULL) - -noinst_LIBRARIES = \ - libmsc.a \ - $(NULL) - -libmsc_a_SOURCES = \ - a_iface.c \ - a_iface_bssap.c \ - auth.c \ - msc_vty.c \ - db.c \ - gsm_04_08.c \ - gsm_04_11.c \ - gsm_04_14.c \ - gsm_04_80.c \ - gsm_subscriber.c \ - mncc.c \ - mncc_builtin.c \ - mncc_sock.c \ - msc_ifaces.c \ - rrlp.c \ - silent_call.c \ - sms_queue.c \ - ussd.c \ - vty_interface_layer3.c \ - transaction.c \ - osmo_msc.c \ - ctrl_commands.c \ - meas_feed.c \ - subscr_conn.c \ - $(NULL) -if BUILD_IU -libmsc_a_SOURCES += \ - iucs.c \ - iucs_ranap.c \ - $(NULL) -else -libmsc_a_SOURCES += \ - iu_dummy.c \ - $(NULL) -endif - -if BUILD_SMPP -noinst_HEADERS += \ - smpp_smsc.h \ - $(NULL) - -libmsc_a_SOURCES += \ - smpp_smsc.c \ - smpp_openbsc.c \ - smpp_vty.c \ - smpp_utils.c \ - $(NULL) -endif diff --git a/src/libmsc/a_iface.c b/src/libmsc/a_iface.c deleted file mode 100644 index e473b7526..000000000 --- a/src/libmsc/a_iface.c +++ /dev/null @@ -1,591 +0,0 @@ -/* (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/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/sigtran/sccp_helpers.h> -#include <osmocom/sigtran/sccp_sap.h> -#include <osmocom/sigtran/osmo_ss7.h> -#include <osmocom/sigtran/protocol/m3ua.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/gsm0808_utils.h> -#include <openbsc/debug.h> -#include <openbsc/msc_ifaces.h> -#include <openbsc/a_iface.h> -#include <openbsc/a_iface_bssap.h> -#include <openbsc/transaction.h> -#include <osmocom/legacy_mgcp/mgcpgw_client.h> -#include <osmocom/core/byteswap.h> -#include <osmocom/sccp/sccp_types.h> -#include <openbsc/a_reset.h> -#include <openbsc/osmo_msc.h> - -/* A pointer to the GSM network we work with. By the current paradigm, - * there can only be one gsm_network per MSC. The pointer is set once - * when calling a_init() */ -static struct gsm_network *gsm_network = NULL; - -/* A struct to track currently active connections. We need that information - * to handle failure sitautions. In case of a problem, we must know which - * connections are currently open and which BSC is responsible. We also need - * the data to perform our connection checks (a_reset). All other logic will - * look at the connection ids and addresses that are supplied by the - * primitives */ -struct bsc_conn { - struct llist_head list; - uint32_t conn_id; /* Connection identifier */ -}; - -/* Internal list with connections we currently maintain. This - * list is of type struct bsc_conn (see above) */ -static LLIST_HEAD(active_connections); - -/* Record info of a new active connection in the active connection list */ -static void record_bsc_con(const void *ctx, uint32_t conn_id) -{ - struct bsc_conn *conn; - - conn = talloc_zero(ctx, struct bsc_conn); - OSMO_ASSERT(conn); - - conn->conn_id = conn_id; - - llist_add_tail(&conn->list, &active_connections); -} - -/* Delete info of a closed connection from the active connection list */ -void a_delete_bsc_con(uint32_t conn_id) -{ - struct bsc_conn *conn; - struct bsc_conn *conn_temp; - - LOGP(DMSC, LOGL_DEBUG, - "Removing connection from active sccp-connection list (conn_id=%i)\n", - conn_id); - - llist_for_each_entry_safe(conn, conn_temp, &active_connections, list) { - if (conn->conn_id == conn_id) { - llist_del(&conn->list); - talloc_free(conn); - } - } -} - -/* Check if a specified connection id has an active SCCP connection */ -static bool check_connection_active(uint32_t conn_id) -{ - struct bsc_conn *conn; - - /* Find the address for the current connection id */ - llist_for_each_entry(conn, &active_connections, list) { - if (conn->conn_id == conn_id) { - return true; - } - } - - return false; -} - -/* Get the reset context for a specifiec calling (BSC) address */ -static struct a_reset_ctx *get_reset_ctx_by_sccp_addr(const struct osmo_sccp_addr *addr) -{ - struct bsc_context *bsc_ctx; - struct osmo_ss7_instance *ss7; - - if (!addr) - return NULL; - - llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) { - if (memcmp(&bsc_ctx->bsc_addr, addr, sizeof(*addr)) == 0) - return bsc_ctx->reset; - } - - ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); - OSMO_ASSERT(ss7); - LOGP(DMSC, LOGL_ERROR, "The calling BSC (%s) is unknown to this MSC ...\n", - osmo_sccp_addr_name(ss7, addr)); - return NULL; -} - -/* Send DTAP message via A-interface */ -int a_iface_tx_dtap(struct msgb *msg) -{ - struct gsm_subscriber_connection *conn; - struct msgb *msg_resp; - - /* FIXME: Set this to some meaninful value! */ - uint8_t link_id = 0x00; - OSMO_ASSERT(msg); - conn = (struct gsm_subscriber_connection *)msg->dst; - OSMO_ASSERT(conn); - OSMO_ASSERT(conn->a.scu); - - LOGP(DMSC, LOGL_DEBUG, "Passing DTAP message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id); - - msg->l3h = msg->data; - msg_resp = gsm0808_create_dtap(msg, link_id); - if (!msg_resp) { - LOGP(DMSC, LOGL_ERROR, "Unable to generate BSSMAP DTAP message!\n"); - return -EINVAL; - } else - LOGP(DMSC, LOGL_DEBUG, "Massage will be sent as BSSMAP DTAP message!\n"); - - LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len)); - return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp); -} - -/* Send Cipher mode command via A-interface */ -int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn, - int cipher, const const uint8_t *key, int len, int include_imeisv) -{ - /* TODO generalize for A- and Iu interfaces, don't name after 08.08 */ - struct msgb *msg_resp; - struct gsm0808_encrypt_info ei; - - OSMO_ASSERT(conn); - - LOGP(DMSC, LOGL_DEBUG, "Passing Cipher mode command message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id); - uint8_t crm = 0x01; - uint8_t *crm_ptr = NULL; - - /* Setup encryption information */ - if (len > ENCRY_INFO_KEY_MAXLEN || !key) { - LOGP(DMSC, LOGL_ERROR, - "Cipher mode command message could not be generated due to invalid key! (conn_id=%i)\n", - conn->a.conn_id); - return -EINVAL; - } else { - memcpy(&ei.key, key, len); - ei.key_len = len; - } - - if (include_imeisv) - crm_ptr = &crm; - - ei.perm_algo[0] = (uint8_t) (1 << cipher); - ei.perm_algo_len = 1; - - msg_resp = gsm0808_create_cipher(&ei, crm_ptr); - LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len)); - - return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp); -} - -/* Page a subscriber via A-interface */ -int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac) -{ - struct bsc_context *bsc_ctx; - struct gsm0808_cell_id_list cil; - struct msgb *msg; - int page_count = 0; - struct osmo_ss7_instance *ss7; - - OSMO_ASSERT(imsi); - - cil.id_discr = CELL_IDENT_LAC; - cil.id_list_lac[0] = lac; - cil.id_list_len = 1; - - ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); - OSMO_ASSERT(ss7); - - /* Deliver paging request to all known BSCs */ - llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) { - if (a_reset_conn_ready(bsc_ctx->reset)) { - LOGP(DMSC, LOGL_DEBUG, - "Passing paging message from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n", - osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr), - osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac); - msg = gsm0808_create_paging(imsi, &tmsi, &cil, NULL); - osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, - &bsc_ctx->msc_addr, &bsc_ctx->bsc_addr, msg); - page_count++; - } else { - LOGP(DMSC, LOGL_DEBUG, - "Connection down, dropping paging from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n", - osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr), - osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac); - } - } - - if (page_count <= 0) - LOGP(DMSC, LOGL_ERROR, "Could not deliver paging because none of the associated BSCs is available!\n"); - - return page_count; -} - -/* Convert speech version field */ -static uint8_t convert_Abis_sv_to_A_sv(int speech_ver) -{ - /* The speech versions that are transmitted in the Bearer capability - * information element, that is transmitted on the Abis interfece - * use a different encoding than the permitted speech version - * identifier, that is signalled in the channel type element on the A - * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, - * 10.5.103 */ - - switch (speech_ver) { - case GSM48_BCAP_SV_FR: - return GSM0808_PERM_FR1; - break; - case GSM48_BCAP_SV_HR: - return GSM0808_PERM_HR1; - break; - case GSM48_BCAP_SV_EFR: - return GSM0808_PERM_FR2; - break; - case GSM48_BCAP_SV_AMR_F: - return GSM0808_PERM_FR3; - break; - case GSM48_BCAP_SV_AMR_H: - return GSM0808_PERM_HR3; - break; - case GSM48_BCAP_SV_AMR_OFW: - return GSM0808_PERM_FR4; - break; - case GSM48_BCAP_SV_AMR_OHW: - return GSM0808_PERM_HR4; - break; - case GSM48_BCAP_SV_AMR_FW: - return GSM0808_PERM_FR5; - break; - case GSM48_BCAP_SV_AMR_OH: - return GSM0808_PERM_HR6; - break; - } - - /* If nothing matches, tag the result as invalid */ - LOGP(DMSC, LOGL_ERROR, "Invalid permitted speech version / rate detected, discarding.\n"); - return 0xFF; -} - -/* Convert speech preference field */ -static uint8_t convert_Abis_prev_to_A_pref(int radio) -{ - /* The Radio channel requirement field that is transmitted in the - * Bearer capability information element, that is transmitted on the - * Abis interfece uses a different encoding than the Channel rate and - * type field that is signalled in the channel type element on the A - * interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008, - * 10.5.102 */ - - switch (radio) { - case GSM48_BCAP_RRQ_FR_ONLY: - return GSM0808_SPEECH_FULL_BM; - case GSM48_BCAP_RRQ_DUAL_FR: - return GSM0808_SPEECH_FULL_PREF; - case GSM48_BCAP_RRQ_DUAL_HR: - return GSM0808_SPEECH_HALF_PREF; - } - - LOGP(DMSC, LOGL_ERROR, "Invalid speech version / rate combination preference, defaulting to full rate.\n"); - return GSM0808_SPEECH_FULL_BM; -} - -/* Assemble the channel type field */ -static int enc_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc) -{ - unsigned int i; - uint8_t sv; - unsigned int count = 0; - bool only_gsm_hr = true; - - OSMO_ASSERT(ct); - OSMO_ASSERT(bc); - - ct->ch_indctr = GSM0808_CHAN_SPEECH; - - for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) { - if (bc->speech_ver[i] == -1) - break; - sv = convert_Abis_sv_to_A_sv(bc->speech_ver[i]); - if (sv != 0xFF) { - /* Detect if something else than - * GSM HR V1 is supported */ - if (sv == GSM0808_PERM_HR2 || - sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6) - only_gsm_hr = false; - - ct->perm_spch[count] = sv; - count++; - } - } - ct->perm_spch_len = count; - - if (only_gsm_hr) - /* Note: We must avoid the usage of GSM HR1 as this - * codec only offers very poor audio quality. If the - * MS only supports GSM HR1 (and full rate), and has - * a preference for half rate. Then we will ignore the - * preference and assume a preference for full rate. */ - ct->ch_rate_type = GSM0808_SPEECH_FULL_BM; - else - ct->ch_rate_type = convert_Abis_prev_to_A_pref(bc->radio); - - if (count) - return 0; - else - return -EINVAL; -} - -/* Assemble the speech codec field */ -static int enc_speech_codec_list(struct gsm0808_speech_codec_list *scl, const struct gsm0808_channel_type *ct) -{ - unsigned int i; - int rc; - - memset(scl, 0, sizeof(*scl)); - for (i = 0; i < ct->perm_spch_len; i++) { - rc = gsm0808_speech_codec_from_chan_type(&scl->codec[i], ct->perm_spch[i]); - if (rc != 0) - return -EINVAL; - } - scl->len = i; - - return 0; -} - -/* Send assignment request via A-interface */ -int a_iface_tx_assignment(const struct gsm_trans *trans) -{ - struct gsm_subscriber_connection *conn; - struct gsm0808_channel_type ct; - struct gsm0808_speech_codec_list scl; - uint32_t *ci_ptr = NULL; - struct msgb *msg; - struct sockaddr_storage rtp_addr; - struct sockaddr_in rtp_addr_in; - int rc; - - OSMO_ASSERT(trans); - conn = trans->conn; - OSMO_ASSERT(conn); - - LOGP(DMSC, LOGL_ERROR, "Sending assignment command to BSC (conn_id %u)\n", conn->a.conn_id); - - /* Channel type */ - rc = enc_channel_type(&ct, &trans->bearer_cap); - if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "Faild to generate channel type -- assignment not sent!\n"); - return -EINVAL; - } - - /* Speech codec list */ - rc = enc_speech_codec_list(&scl, &ct); - if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "Faild to generate Speech codec list -- assignment not sent!\n"); - return -EINVAL; - } - - /* 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 = osmo_htons(conn->rtp.port_subscr); - rtp_addr_in.sin_addr.s_addr = osmo_htonl(mgcpgw_client_remote_addr_n(gsm_network->mgcpgw.client)); - - memset(&rtp_addr, 0, sizeof(rtp_addr)); - memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in)); - - msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, &scl, ci_ptr); - - LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg->data, msg->len)); - return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg); -} - -/* Send clear command via A-interface */ -int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg; - - LOGP(DMSC, LOGL_NOTICE, "Sending clear command to BSC (conn_id=%u)\n", conn->a.conn_id); - - msg = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL); - return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg); -} - -/* Callback function: Close all open connections */ -static void a_reset_cb(const void *priv) -{ - struct msgb *msg; - struct bsc_context *bsc_ctx = (struct bsc_context*) priv; - struct osmo_ss7_instance *ss7; - - /* Skip if the A interface is not properly initalized yet */ - if (!gsm_network) - return; - - /* Clear all now orphaned subscriber connections */ - a_clear_all(bsc_ctx->sccp_user, &bsc_ctx->bsc_addr); - - /* Send reset to the remote BSC */ - ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); - OSMO_ASSERT(ss7); - LOGP(DMSC, LOGL_NOTICE, "Sending RESET to BSC %s\n", osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr)); - msg = gsm0808_create_reset(); - osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, &bsc_ctx->msc_addr, - &bsc_ctx->bsc_addr, msg); -} - -/* Add a new BSC connection to our internal list with known BSCs */ -static void add_bsc(const struct osmo_sccp_addr *msc_addr, const struct osmo_sccp_addr *bsc_addr, - struct osmo_sccp_user *scu) -{ - struct bsc_context *bsc_ctx; - struct osmo_ss7_instance *ss7; - - OSMO_ASSERT(bsc_addr); - OSMO_ASSERT(msc_addr); - OSMO_ASSERT(scu); - - /* Check if we already know this BSC, if yes, skip adding it. */ - if (get_reset_ctx_by_sccp_addr(bsc_addr)) - return; - - ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); - OSMO_ASSERT(ss7); - LOGP(DMSC, LOGL_NOTICE, "Adding new BSC connection for BSC %s...\n", osmo_sccp_addr_name(ss7, bsc_addr)); - - /* Generate and fill up a new bsc context */ - bsc_ctx = talloc_zero(gsm_network, struct bsc_context); - OSMO_ASSERT(bsc_ctx); - memcpy(&bsc_ctx->bsc_addr, bsc_addr, sizeof(*bsc_addr)); - memcpy(&bsc_ctx->msc_addr, msc_addr, sizeof(*msc_addr)); - bsc_ctx->sccp_user = scu; - llist_add_tail(&bsc_ctx->list, &gsm_network->a.bscs); - - /* Start reset procedure to make the new connection active */ - bsc_ctx->reset = a_reset_alloc(bsc_ctx, osmo_sccp_addr_name(ss7, bsc_addr), a_reset_cb, bsc_ctx); -} - -/* Callback function, called by the SSCP stack when data arrives */ -static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) -{ - struct osmo_sccp_user *scu = _scu; - struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; - int rc = 0; - struct a_conn_info a_conn_info; - memset(&a_conn_info, 0, sizeof(a_conn_info)); - a_conn_info.network = gsm_network; - a_conn_info.reset = NULL; - - switch (OSMO_PRIM_HDR(&scu_prim->oph)) { - case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): - /* Handle inbound connection indication */ - add_bsc(&scu_prim->u.connect.called_addr, &scu_prim->u.connect.calling_addr, scu); - a_conn_info.conn_id = scu_prim->u.connect.conn_id; - a_conn_info.msc_addr = &scu_prim->u.connect.called_addr; - a_conn_info.bsc_addr = &scu_prim->u.connect.calling_addr; - a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); - - if (a_reset_conn_ready(a_conn_info.reset) == false) { - rc = osmo_sccp_tx_disconn(scu, a_conn_info.conn_id, a_conn_info.msc_addr, - SCCP_RETURN_CAUSE_UNQUALIFIED); - break; - } - - osmo_sccp_tx_conn_resp(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, NULL, 0); - if (msgb_l2len(oph->msg) > 0) { - LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u, %s)\n", - scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - rc = sccp_rx_dt(scu, &a_conn_info, oph->msg); - } else - LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u)\n", scu_prim->u.connect.conn_id); - - record_bsc_con(scu, scu_prim->u.connect.conn_id); - break; - - case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): - /* Handle incoming connection oriented data */ - a_conn_info.conn_id = scu_prim->u.data.conn_id; - LOGP(DMSC, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", - scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - sccp_rx_dt(scu, &a_conn_info, oph->msg); - break; - - case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): - /* Handle inbound UNITDATA */ - add_bsc(&scu_prim->u.unitdata.called_addr, &scu_prim->u.unitdata.calling_addr, scu); - a_conn_info.msc_addr = &scu_prim->u.unitdata.called_addr; - a_conn_info.bsc_addr = &scu_prim->u.unitdata.calling_addr; - a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); - DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - sccp_rx_udt(scu, &a_conn_info, oph->msg); - break; - - default: - LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation); - break; - } - - return rc; -} - -/* Clear all subscriber connections on a specified BSC */ -void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr) -{ - struct gsm_subscriber_connection *conn; - struct gsm_subscriber_connection *conn_temp; - struct gsm_network *network = gsm_network; - - OSMO_ASSERT(scu); - OSMO_ASSERT(bsc_addr); - - llist_for_each_entry_safe(conn, conn_temp, &network->subscr_conns, entry) { - /* Clear only A connections and connections that actually - * belong to the specified BSC */ - if (conn->via_ran == RAN_GERAN_A && memcmp(bsc_addr, &conn->a.bsc_addr, sizeof(conn->a.bsc_addr)) == 0) { - LOGP(DMSC, LOGL_NOTICE, "Dropping orphaned subscriber connection (conn_id %i)\n", - conn->a.conn_id); - msc_clear_request(conn, GSM48_CC_CAUSE_SWITCH_CONG); - - /* If there is still an SCCP connection active, remove it now */ - if (check_connection_active(conn->a.conn_id)) { - osmo_sccp_tx_disconn(scu, conn->a.conn_id, bsc_addr, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - a_delete_bsc_con(conn->a.conn_id); - } - } - } -} - -/* Initalize A interface connection between to MSC and BSC */ -int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network) -{ - OSMO_ASSERT(sccp); - OSMO_ASSERT(network); - - /* FIXME: Remove hardcoded parameters, use parameters in parameter list */ - LOGP(DMSC, LOGL_NOTICE, "Initalizing SCCP connection to stp...\n"); - - /* Set GSM network variable, there can only be - * one network by design */ - if (gsm_network != NULL) { - OSMO_ASSERT(gsm_network == network); - } else - gsm_network = network; - - /* SCCP Protocol stack */ - osmo_sccp_user_bind(sccp, "OsmoMSC-A", sccp_sap_up, SCCP_SSN_BSSAP); - - return 0; -} diff --git a/src/libmsc/a_iface_bssap.c b/src/libmsc/a_iface_bssap.c deleted file mode 100644 index e8a229374..000000000 --- a/src/libmsc/a_iface_bssap.c +++ /dev/null @@ -1,716 +0,0 @@ -/* (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/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/sigtran/sccp_helpers.h> -#include <osmocom/sccp/sccp_types.h> -#include <osmocom/gsm/gsm0808.h> -#include <osmocom/gsm/gsm0808_utils.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/a_iface_bssap.h> -#include <openbsc/a_iface.h> -#include <openbsc/osmo_msc.h> -#include <osmocom/core/byteswap.h> -#include <openbsc/a_reset.h> - -#define IP_V4_ADDR_LEN 4 - -/* - * Helper functions to lookup and allocate subscribers - */ - -/* Allocate a new subscriber connection */ -static struct gsm_subscriber_connection *subscr_conn_allocate_a(const struct a_conn_info *a_conn_info, - struct gsm_network *network, - uint16_t lac, struct osmo_sccp_user *scu, int conn_id) -{ - struct gsm_subscriber_connection *conn; - - LOGP(DMSC, LOGL_NOTICE, "Allocating A-Interface subscriber conn: lac %i, conn_id %i\n", lac, conn_id); - - conn = talloc_zero(network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = network; - conn->via_ran = RAN_GERAN_A; - conn->lac = lac; - - conn->a.conn_id = conn_id; - conn->a.scu = scu; - - /* Also backup the calling address of the BSC, this allows us to - * identify later which BSC is responsible for this subscriber connection */ - memcpy(&conn->a.bsc_addr, a_conn_info->bsc_addr, sizeof(conn->a.bsc_addr)); - - llist_add_tail(&conn->entry, &network->subscr_conns); - LOGP(DMSC, LOGL_NOTICE, "A-Interface subscriber connection successfully allocated!\n"); - return conn; -} - -/* Return an existing A subscriber connection record for the given - * connection IDs, or return NULL if not found. */ -static struct gsm_subscriber_connection *subscr_conn_lookup_a(const struct gsm_network *network, int conn_id) -{ - struct gsm_subscriber_connection *conn; - - OSMO_ASSERT(network); - - DEBUGP(DMSC, "Looking for A subscriber: conn_id %i\n", conn_id); - - /* FIXME: log_subscribers() is defined in iucs.c as static inline, if - * maybe this function should be public to reach it from here? */ - /* log_subscribers(network); */ - - llist_for_each_entry(conn, &network->subscr_conns, entry) { - if (conn->via_ran == RAN_GERAN_A && conn->a.conn_id == conn_id) { - DEBUGP(DIUCS, "Found A subscriber for conn_id %i\n", conn_id); - return conn; - } - } - DEBUGP(DMSC, "No A subscriber found for conn_id %i\n", conn_id); - return NULL; -} - -/* - * BSSMAP handling for UNITDATA - */ - -/* Endpoint to handle BSSMAP reset */ -static void bssmap_rx_reset(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct osmo_ss7_instance *ss7; - - ss7 = osmo_ss7_instance_find(network->a.cs7_instance); - OSMO_ASSERT(ss7); - - LOGP(DMSC, LOGL_NOTICE, "Rx RESET from BSC %s, sending RESET ACK\n", - osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); - osmo_sccp_tx_unitdata_msg(scu, a_conn_info->msc_addr, a_conn_info->bsc_addr, gsm0808_create_reset_ack()); - - /* Make sure all orphand subscriber connections will be cleard */ - a_clear_all(scu, a_conn_info->bsc_addr); - - msgb_free(msg); -} - -/* Endpoint to handle BSSMAP reset acknowlegement */ -static void bssmap_rx_reset_ack(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, - struct msgb *msg) -{ - - struct gsm_network *network = a_conn_info->network; - struct osmo_ss7_instance *ss7; - - ss7 = osmo_ss7_instance_find(network->a.cs7_instance); - OSMO_ASSERT(ss7); - - if (a_conn_info->reset == NULL) { - LOGP(DMSC, LOGL_ERROR, "Received RESET ACK from an unknown BSC %s, ignoring...\n", - osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); - goto fail; - } - - LOGP(DMSC, LOGL_NOTICE, "Received RESET ACK from BSC %s\n", osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr)); - - /* Confirm that we managed to get the reset ack message - * towards the connection reset logic */ - a_reset_ack_confirm(a_conn_info->reset); - -fail: - msgb_free(msg); -} - -/* Handle UNITDATA BSSMAP messages */ -static void bssmap_rcvmsg_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - /* Note: When in the MSC role, RESET ACK is the only valid message that - * can be received via UNITDATA */ - - if (msgb_l3len(msg) < 1) { - LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n"); - return; - } - - LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0])); - - switch (msg->l3h[0]) { - case BSS_MAP_MSG_RESET: - bssmap_rx_reset(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_RESET_ACKNOWLEDGE: - bssmap_rx_reset_ack(scu, a_conn_info, msg); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented message format: %s -- message discarded!\n", - gsm0808_bssmap_name(msg->l3h[0])); - msgb_free(msg); - } -} - -/* Receive incoming connection less data messages via sccp */ -void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - /* Note: The only valid message type that can be received - * via UNITDATA are BSS Management messages */ - struct bssmap_header *bs; - - OSMO_ASSERT(scu); - OSMO_ASSERT(a_conn_info); - OSMO_ASSERT(msg); - - LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); - - if (msgb_l2len(msg) < sizeof(*bs)) { - LOGP(DMSC, LOGL_ERROR, "Error: Header is too short -- discarding message!\n"); - msgb_free(msg); - return; - } - - bs = (struct bssmap_header *)msgb_l2(msg); - if (bs->length < msgb_l2len(msg) - sizeof(*bs)) { - LOGP(DMSC, LOGL_ERROR, "Error: Message is too short -- discarding message!\n"); - msgb_free(msg); - return; - } - - switch (bs->type) { - case BSSAP_MSG_BSS_MANAGEMENT: - msg->l3h = &msg->l2h[sizeof(struct bssmap_header)]; - bssmap_rcvmsg_udt(scu, a_conn_info, msg); - break; - default: - LOGP(DMSC, LOGL_ERROR, - "Error: Unimplemented message type: %s -- message discarded!\n", gsm0808_bssmap_name(bs->type)); - msgb_free(msg); - } -} - -/* - * BSSMAP handling for connection oriented data - */ - -/* Endpoint to handle BSSMAP clear request */ -static int bssmap_rx_clear_rqst(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct tlv_parsed tp; - int rc; - struct msgb *msg_resp; - uint8_t cause; - struct gsm_subscriber_connection *conn; - - LOGP(DMSC, LOGL_NOTICE, "BSC requested to clear connection (conn_id=%i)\n", a_conn_info->conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { - LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); - goto fail; - } - cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0]; - - /* Respond with clear command */ - msg_resp = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL); - rc = osmo_sccp_tx_data_msg(scu, a_conn_info->conn_id, msg_resp); - - /* If possible, inform the MSC about the clear request */ - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - msc_clear_request(conn, cause); - - msgb_free(msg); - return rc; - -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle BSSMAP clear complete */ -static int bssmap_rx_clear_complete(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - int rc; - - LOGP(DMSC, LOGL_NOTICE, "Releasing connection (conn_id=%i)\n", a_conn_info->conn_id); - rc = osmo_sccp_tx_disconn(scu, a_conn_info->conn_id, - a_conn_info->msc_addr, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - - /* Remove the record from the list with active connections. */ - a_delete_bsc_con(a_conn_info->conn_id); - - msgb_free(msg); - return rc; -} - -/* Endpoint to handle layer 3 complete messages */ -static int bssmap_rx_l3_compl(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct tlv_parsed tp; - struct { - uint8_t ident; - struct gsm48_loc_area_id lai; - uint16_t ci; - } __attribute__ ((packed)) lai_ci; - uint16_t mcc; - uint16_t mnc; - uint16_t lac; - uint8_t data_length; - const uint8_t *data; - int rc; - - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - - LOGP(DMSC, LOGL_NOTICE, "BSC has completed layer 3 connection (conn_id=%i)\n", a_conn_info->conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER not present -- discarding message!\n"); - goto fail; - } - if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory LAYER 3 INFORMATION not present -- discarding message!\n"); - goto fail; - } - - /* Parse Cell ID element */ - /* FIXME: Encapsulate this in a parser/generator function inside - * libosmocore, add support for all specified cell identification - * discriminators (see 3GPP ts 3.2.2.17 Cell Identifier) */ - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER); - if (sizeof(lai_ci) != data_length) { - LOGP(DMSC, LOGL_ERROR, - "Unable to parse element CELL IDENTIFIER (wrong field length) -- discarding message!\n"); - goto fail; - } - memcpy(&lai_ci, data, sizeof(lai_ci)); - if (lai_ci.ident != CELL_IDENT_WHOLE_GLOBAL) { - LOGP(DMSC, LOGL_ERROR, - "Unable to parse element CELL IDENTIFIER (wrong cell identification discriminator) -- discarding message!\n"); - goto fail; - } - if (gsm48_decode_lai(&lai_ci.lai, &mcc, &mnc, &lac) != 0) { - LOGP(DMSC, LOGL_ERROR, - "Unable to parse element CELL IDENTIFIER (lai decoding failed) -- discarding message!\n"); - goto fail; - } - - /* Parse Layer 3 Information element */ - /* FIXME: This is probably to hackish, compiler also complains "assignment discards ‘const’ qualifier..." */ - msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); - msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); - - /* Create new subscriber context */ - conn = subscr_conn_allocate_a(a_conn_info, network, lac, scu, a_conn_info->conn_id); - - /* Handover location update to the MSC code */ - /* msc_compl_l3() takes ownership of dtap_msg - * message buffer */ - rc = msc_compl_l3(conn, msg, 0); - if (rc == MSC_CONN_ACCEPT) { - LOGP(DMSC, LOGL_NOTICE, "User has been accepted by MSC.\n"); - return 0; - } else if (rc == MSC_CONN_REJECT) - LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC.\n"); - else - LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC (unknown error)\n"); - - return -EINVAL; - -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle BSSMAP classmark update */ -static int bssmap_rx_classmark_upd(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct tlv_parsed tp; - const uint8_t *cm2 = NULL; - const uint8_t *cm3 = NULL; - uint8_t cm2_len = 0; - uint8_t cm3_len = 0; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - LOGP(DMSC, LOGL_NOTICE, "BSC sends clasmark update (conn_id=%i)\n", conn->a.conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory Classmark Information Type 2 not present -- discarding message!\n"); - goto fail; - } - - cm2 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2); - cm2_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2); - - if (TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3)) { - cm3 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3); - cm3_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3); - } - - /* Inform MSC about the classmark change */ - msc_classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); - - msgb_free(msg); - return 0; - -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle BSSMAP cipher mode complete */ -static int bssmap_rx_ciph_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, - struct msgb *msg) -{ - /* FIXME: The field GSM0808_IE_LAYER_3_MESSAGE_CONTENTS is optional by - * means of the specification. So there can be messages without L3 info. - * In this case, the code will crash becrause msc_cipher_mode_compl() - * is not able to deal with msg = NULL and apperently - * msc_cipher_mode_compl() was never meant to be used without L3 data. - * This needs to be discussed further! */ - - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct tlv_parsed tp; - uint8_t alg_id = 1; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode complete (conn_id=%i)\n", conn->a.conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - - if (TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)) { - alg_id = TLVP_VAL(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)[0] - 1; - } - - if (TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS)) { - msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS); - msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS); - } else { - msgb_free(msg); - msg = NULL; - } - - /* Hand over cipher mode complete message to the MSC, - * msc_cipher_mode_compl() takes ownership for msg */ - msc_cipher_mode_compl(conn, msg, alg_id); - - return 0; -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle BSSMAP cipher mode reject */ -static int bssmap_rx_ciph_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct tlv_parsed tp; - uint8_t cause; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode reject (conn_id=%i)\n", conn->a.conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)) { - LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); - goto fail; - } - - cause = TLVP_VAL(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)[0]; - LOGP(DMSC, LOGL_NOTICE, "Cipher mode rejection cause: %i\n", cause); - - /* FIXME: Can we do something meaningful here? e.g. report to the - * msc code somehow that the cipher mode command has failed. */ - - msgb_free(msg); - return 0; -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle BSSMAP assignment failure */ -static int bssmap_rx_ass_fail(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct tlv_parsed tp; - uint8_t cause; - uint8_t *rr_cause_ptr = NULL; - uint8_t rr_cause; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment failure message (conn_id=%i)\n", conn->a.conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { - LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); - goto fail; - } - cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0]; - - if (TLVP_PRESENT(&tp, GSM0808_IE_RR_CAUSE)) { - rr_cause = TLVP_VAL(&tp, GSM0808_IE_RR_CAUSE)[0]; - rr_cause_ptr = &rr_cause; - } - - /* FIXME: In AoIP, the Assignment failure will carry also an optional - * Codec List (BSS Supported) element. It has to be discussed if we - * can ignore this element. If not, The msc_assign_fail() function - * call has to change. However msc_assign_fail() does nothing in the - * end. So probably we can just leave it as it is. Even for AoIP */ - - /* Inform the MSC about the assignment failure event */ - msc_assign_fail(conn, cause, rr_cause_ptr); - - msgb_free(msg); - return 0; -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle sapi "n" reject */ -static int bssmap_rx_sapi_n_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, - struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct tlv_parsed tp; - uint8_t dlci; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - LOGP(DMSC, LOGL_NOTICE, "BSC sends sapi \"n\" reject message (conn_id=%i)\n", conn->a.conn_id); - - /* Note: The MSC code seems not to care about the cause code, but by - * the specification it is mandatory, so we check its presence. See - * also 3GPP TS 48.008 3.2.1.34 SAPI "n" REJECT */ - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) { - LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n"); - goto fail; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_DLCI)) { - LOGP(DMSC, LOGL_ERROR, "DLCI is missing -- discarding message!\n"); - goto fail; - } - dlci = TLVP_VAL(&tp, GSM0808_IE_DLCI)[0]; - - /* Inform the MSC about the sapi "n" reject event */ - msc_sapi_n_reject(conn, dlci); - - msgb_free(msg); - return 0; -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Endpoint to handle assignment complete */ -static int bssmap_rx_ass_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, - struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - struct mgcpgw_client *mgcp; - struct tlv_parsed tp; - struct sockaddr_storage rtp_addr; - struct sockaddr_in *rtp_addr_in; - int rc; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) - goto fail; - - mgcp = conn->network->mgcpgw.client; - OSMO_ASSERT(mgcp); - - LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment complete message (conn_id=%i)\n", conn->a.conn_id); - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) { - LOGP(DMSC, LOGL_ERROR, "AoIP transport identifier missing -- discarding message!\n"); - goto fail; - } - - /* Decode AoIP transport address element */ - rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR), - TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR)); - if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "Unable to decode aoip transport address.\n"); - goto fail; - } - - /* 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_subscr = osmo_ntohs(rtp_addr_in->sin_port); - /* FIXME: We also get the IP-Address of the remote (e.g. BTS) - * end with the response. Currently we just ignore that address. - * Instead we expect that our local MGCP gateway and the code - * controlling it, magically knows the IP of the remote end. */ - } else { - LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. (supports only IPV4)\n"); - goto fail; - } - - /* FIXME: Seems to be related to authentication or, - encryption. Is this really in the right place? */ - msc_rx_sec_mode_compl(conn); - - msgb_free(msg); - return 0; -fail: - msgb_free(msg); - return -EINVAL; -} - -/* Handle incoming connection oriented BSSMAP messages */ -static int rx_bssmap(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - if (msgb_l3len(msg) < 1) { - LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n"); - msgb_free(msg); - return -1; - } - - LOGP(DMSC, LOGL_NOTICE, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0])); - - switch (msg->l3h[0]) { - case BSS_MAP_MSG_CLEAR_RQST: - return bssmap_rx_clear_rqst(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_CLEAR_COMPLETE: - return bssmap_rx_clear_complete(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_COMPLETE_LAYER_3: - return bssmap_rx_l3_compl(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_CLASSMARK_UPDATE: - return bssmap_rx_classmark_upd(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_CIPHER_MODE_COMPLETE: - return bssmap_rx_ciph_compl(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_CIPHER_MODE_REJECT: - return bssmap_rx_ciph_rej(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_ASSIGMENT_FAILURE: - return bssmap_rx_ass_fail(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_SAPI_N_REJECT: - return bssmap_rx_sapi_n_rej(scu, a_conn_info, msg); - break; - case BSS_MAP_MSG_ASSIGMENT_COMPLETE: - return bssmap_rx_ass_compl(scu, a_conn_info, msg); - break; - default: - LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l3h[0])); - msgb_free(msg); - return -EINVAL; - } - - return -EINVAL; -} - -/* Endpoint to handle regular BSSAP DTAP messages */ -static int rx_dtap(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - struct gsm_network *network = a_conn_info->network; - struct gsm_subscriber_connection *conn; - - conn = subscr_conn_lookup_a(network, a_conn_info->conn_id); - if (!conn) { - msgb_free(msg); - return -EINVAL; - } - - LOGP(DMSC, LOGL_NOTICE, "BSC sends layer 3 dtap (conn_id=%i)\n", conn->a.conn_id); - - /* msc_dtap expects the dtap payload in l3h */ - msg->l3h = msg->l2h + 3; - - /* Forward dtap payload into the msc, - * msc_dtap() takes ownership for msg */ - msc_dtap(conn, conn->a.conn_id, msg); - - return 0; -} - -/* Handle incoming connection oriented messages */ -int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg) -{ - OSMO_ASSERT(scu); - OSMO_ASSERT(a_conn_info); - OSMO_ASSERT(msg); - - LOGP(DMSC, LOGL_NOTICE, "Rx BSC DT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); - - if (msgb_l2len(msg) < sizeof(struct bssmap_header)) { - LOGP(DMSC, LOGL_NOTICE, "The header is too short -- discarding message!\n"); - msgb_free(msg); - } - - switch (msg->l2h[0]) { - case BSSAP_MSG_BSS_MANAGEMENT: - msg->l3h = &msg->l2h[sizeof(struct bssmap_header)]; - return rx_bssmap(scu, a_conn_info, msg); - break; - case BSSAP_MSG_DTAP: - return rx_dtap(scu, a_conn_info, msg); - break; - default: - LOGP(DMSC, LOGL_ERROR, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l2h[0])); - msgb_free(msg); - return -EINVAL; - } - - return -EINVAL; -} diff --git a/src/libmsc/auth.c b/src/libmsc/auth.c deleted file mode 100644 index 9064ce6c4..000000000 --- a/src/libmsc/auth.c +++ /dev/null @@ -1,42 +0,0 @@ -/* Authentication related functions */ - -/* - * (C) 2010 by Sylvain Munaut <tnt@246tNt.com> - * - * 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/db.h> -#include <openbsc/debug.h> -#include <openbsc/auth.h> -#include <openbsc/gsm_data.h> - -#include <osmocom/gsm/comp128.h> -#include <osmocom/core/utils.h> - -#include <openssl/rand.h> - -#include <stdlib.h> - -const struct value_string auth_action_names[] = { - OSMO_VALUE_STRING(AUTH_ERROR), - OSMO_VALUE_STRING(AUTH_NOT_AVAIL), - OSMO_VALUE_STRING(AUTH_DO_AUTH_THEN_CIPH), - OSMO_VALUE_STRING(AUTH_DO_CIPH), - OSMO_VALUE_STRING(AUTH_DO_AUTH), - { 0, NULL } -}; diff --git a/src/libmsc/ctrl_commands.c b/src/libmsc/ctrl_commands.c deleted file mode 100644 index 9d1f0d4fa..000000000 --- a/src/libmsc/ctrl_commands.c +++ /dev/null @@ -1,88 +0,0 @@ -/* - * (C) 2014 by Holger Hans Peter Freyther - * (C) 2014 by sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/ctrl/control_cmd.h> -#include <osmocom/core/utils.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/db.h> -#include <openbsc/debug.h> -#include <openbsc/vlr.h> - -#include <stdbool.h> - -static struct gsm_network *msc_ctrl_net = NULL; - -static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d) -{ - return 0; -} - -static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Command moved to osmo-hlr, no longer available here"; - return CTRL_CMD_ERROR; -} - -CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1"); - -static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Command moved to osmo-hlr, no longer available here"; - return CTRL_CMD_ERROR; -} -CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1"); - -static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) -{ - struct vlr_subscr *vsub; - - if (!msc_ctrl_net) { - cmd->reply = "MSC CTRL commands not initialized"; - return CTRL_CMD_ERROR; - } - - if (!msc_ctrl_net->vlr) { - cmd->reply = "VLR not initialized"; - return CTRL_CMD_ERROR; - } - - cmd->reply = talloc_strdup(cmd, ""); - - llist_for_each_entry(vsub, &msc_ctrl_net->vlr->subscribers, list) { - cmd->reply = talloc_asprintf_append(cmd->reply, "%s,%s\n", - vsub->imsi, vsub->msisdn); - } - printf("%s\n", cmd->reply); /* <-- what? */ - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1"); - -int msc_ctrl_cmds_install(struct gsm_network *net) -{ - int rc = 0; - msc_ctrl_net = net; - - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); - return rc; -} diff --git a/src/libmsc/db.c b/src/libmsc/db.c deleted file mode 100644 index ae7e2876b..000000000 --- a/src/libmsc/db.c +++ /dev/null @@ -1,1007 +0,0 @@ -/* Simple HLR/VLR database backend using dbi */ -/* (C) 2008 by Jan Luebbe <jluebbe@debian.org> - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <stdint.h> -#include <inttypes.h> -#include <libgen.h> -#include <stdio.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <dbi/dbi.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/db.h> -#include <openbsc/debug.h> -#include <openbsc/vlr.h> - -#include <osmocom/gsm/protocol/gsm_23_003.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/statistics.h> -#include <osmocom/core/rate_ctr.h> -#include <osmocom/core/utils.h> - -#include <openssl/rand.h> - -static char *db_basename = NULL; -static char *db_dirname = NULL; -static dbi_conn conn; - -#define SCHEMA_REVISION "5" - -enum { - SCHEMA_META, - INSERT_META, - SCHEMA_SUBSCRIBER, - SCHEMA_AUTH, - SCHEMA_EQUIPMENT, - SCHEMA_EQUIPMENT_WATCH, - SCHEMA_SMS, - SCHEMA_VLR, - SCHEMA_APDU, - SCHEMA_COUNTERS, - SCHEMA_RATE, - SCHEMA_AUTHKEY, - SCHEMA_AUTHLAST, -}; - -static const char *create_stmts[] = { - [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "key TEXT UNIQUE NOT NULL, " - "value TEXT NOT NULL" - ")", - [INSERT_META] = "INSERT OR IGNORE INTO Meta " - "(key, value) " - "VALUES " - "('revision', " SCHEMA_REVISION ")", - [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "imsi NUMERIC UNIQUE NOT NULL, " - "name TEXT, " - "extension TEXT UNIQUE, " - "authorized INTEGER NOT NULL DEFAULT 0, " - "tmsi TEXT UNIQUE, " - "lac INTEGER NOT NULL DEFAULT 0, " - "expire_lu TIMESTAMP DEFAULT NULL" - ")", - [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "subscriber_id INTEGER UNIQUE NOT NULL, " - "created TIMESTAMP NOT NULL, " - "token TEXT UNIQUE NOT NULL" - ")", - [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "name TEXT, " - "classmark1 NUMERIC, " - "classmark2 BLOB, " - "classmark3 BLOB, " - "imei NUMERIC UNIQUE NOT NULL" - ")", - [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC NOT NULL, " - "equipment_id NUMERIC NOT NULL, " - "UNIQUE (subscriber_id, equipment_id) " - ")", - [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS (" - /* metadata, not part of sms */ - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "sent TIMESTAMP, " - "deliver_attempts INTEGER NOT NULL DEFAULT 0, " - /* data directly copied/derived from SMS */ - "valid_until TIMESTAMP, " - "reply_path_req INTEGER NOT NULL, " - "status_rep_req INTEGER NOT NULL, " - "is_report INTEGER NOT NULL, " - "msg_ref INTEGER NOT NULL, " - "protocol_id INTEGER NOT NULL, " - "data_coding_scheme INTEGER NOT NULL, " - "ud_hdr_ind INTEGER NOT NULL, " - "src_addr TEXT NOT NULL, " - "src_ton INTEGER NOT NULL, " - "src_npi INTEGER NOT NULL, " - "dest_addr TEXT NOT NULL, " - "dest_ton INTEGER NOT NULL, " - "dest_npi INTEGER NOT NULL, " - "user_data BLOB, " /* TP-UD */ - /* additional data, interpreted from SMS */ - "header BLOB, " /* UD Header */ - "text TEXT " /* decoded UD after UDH */ - ")", - [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC UNIQUE NOT NULL, " - "last_bts NUMERIC NOT NULL " - ")", - [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "apdu_id_flags INTEGER NOT NULL, " - "subscriber_id INTEGER NOT NULL, " - "apdu BLOB " - ")", - [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL " - ")", - [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "idx INTEGER NOT NULL " - ")", - [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys (" - "subscriber_id INTEGER PRIMARY KEY, " - "algorithm_id INTEGER NOT NULL, " - "a3a8_ki BLOB " - ")", - [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples (" - "subscriber_id INTEGER PRIMARY KEY, " - "issued TIMESTAMP NOT NULL, " - "use_count INTEGER NOT NULL DEFAULT 0, " - "key_seq INTEGER NOT NULL, " - "rand BLOB NOT NULL, " - "sres BLOB NOT NULL, " - "kc BLOB NOT NULL " - ")", -}; - -void db_error_func(dbi_conn conn, void *data) -{ - const char *msg; - dbi_conn_error(conn, &msg); - LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); - osmo_log_backtrace(DDB, LOGL_ERROR); -} - -static int update_db_revision_2(void) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "ALTER TABLE Subscriber " - "ADD COLUMN expire_lu " - "TIMESTAMP DEFAULT NULL"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to alter table Subscriber (upgrade from rev 2).\n"); - return -EINVAL; - } - dbi_result_free(result); - - result = dbi_conn_query(conn, - "UPDATE Meta " - "SET value = '3' " - "WHERE key = 'revision'"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to update DB schema revision (upgrade from rev 2).\n"); - return -EINVAL; - } - dbi_result_free(result); - - return 0; -} - -/** - * Copied from the normal sms_from_result_v3 to avoid having - * to make sure that the real routine will remain backward - * compatible. - */ -static struct gsm_sms *sms_from_result_v3(dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - long long unsigned int sender_id; - const char *text, *daddr; - const unsigned char *user_data; - char buf[32]; - char *quoted; - dbi_result result2; - const char *extension; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - /* find extension by id, assuming that the subscriber still exists in - * the db */ - sender_id = dbi_result_get_ulonglong(result, "sender_id"); - snprintf(buf, sizeof(buf), "%llu", sender_id); - - dbi_conn_quote_string_copy(conn, buf, "ed); - result2 = dbi_conn_queryf(conn, - "SELECT extension FROM Subscriber " - "WHERE id = %s ", quoted); - free(quoted); - extension = dbi_result_get_string(result2, "extension"); - if (extension) - osmo_strlcpy(sms->src.addr, extension, sizeof(sms->src.addr)); - dbi_result_free(result2); - /* got the extension */ - - sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_ulonglong(result, - "data_coding_scheme"); - - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) - osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - return sms; -} - -static int update_db_revision_3(void) -{ - dbi_result result; - struct gsm_sms *sms; - - LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n"); - - result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to begin transaction (upgrade from rev 3)\n"); - return -EINVAL; - } - dbi_result_free(result); - - /* Rename old SMS table to be able create a new one */ - result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to rename the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Create new SMS table with all the bells and whistles! */ - result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create a new SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Cycle through old messages and convert them to the new format */ - result = dbi_conn_query(conn, "SELECT * FROM SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed fetch messages from the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - while (dbi_result_next_row(result)) { - sms = sms_from_result_v3(result); - if (db_sms_store(sms) != 0) { - LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n"); - sms_free(sms); - dbi_result_free(result); - goto rollback; - } - sms_free(sms); - } - dbi_result_free(result); - - /* Remove the temporary table */ - result = dbi_conn_query(conn, "DROP TABLE SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to drop the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* We're done. Bump DB Meta revision to 4 */ - result = dbi_conn_query(conn, - "UPDATE Meta " - "SET value = '4' " - "WHERE key = 'revision'"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to update DB schema revision (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - result = dbi_conn_query(conn, "COMMIT TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to commit the transaction (upgrade from rev 3)\n"); - return -EINVAL; - } else { - dbi_result_free(result); - } - - /* Shrink DB file size by actually wiping out SMS_3 table data */ - result = dbi_conn_query(conn, "VACUUM"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "VACUUM failed. Ignoring it (upgrade from rev 3).\n"); - else - dbi_result_free(result); - - return 0; - -rollback: - result = dbi_conn_query(conn, "ROLLBACK TRANSACTION"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "Rollback failed (upgrade from rev 3).\n"); - else - dbi_result_free(result); - return -EINVAL; -} - -/* Just like v3, but there is a new message reference field for status reports, - * that is set to zero for existing entries since there is no way we can infer - * this. - */ -static struct gsm_sms *sms_from_result_v4(dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - const unsigned char *user_data; - const char *text, *addr; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_ulonglong(result, - "data_coding_scheme"); - - addr = dbi_result_get_string(result, "src_addr"); - osmo_strlcpy(sms->src.addr, addr, sizeof(sms->src.addr)); - sms->src.ton = dbi_result_get_ulonglong(result, "src_ton"); - sms->src.npi = dbi_result_get_ulonglong(result, "src_npi"); - - addr = dbi_result_get_string(result, "dest_addr"); - osmo_strlcpy(sms->dst.addr, addr, sizeof(sms->dst.addr)); - sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton"); - sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi"); - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - return sms; -} - -static int update_db_revision_4(void) -{ - dbi_result result; - struct gsm_sms *sms; - - LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 4\n"); - - result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to begin transaction (upgrade from rev 4)\n"); - return -EINVAL; - } - dbi_result_free(result); - - /* Rename old SMS table to be able create a new one */ - result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_4"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to rename the old SMS table (upgrade from rev 4).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Create new SMS table with all the bells and whistles! */ - result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create a new SMS table (upgrade from rev 4).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Cycle through old messages and convert them to the new format */ - result = dbi_conn_query(conn, "SELECT * FROM SMS_4"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed fetch messages from the old SMS table (upgrade from rev 4).\n"); - goto rollback; - } - while (dbi_result_next_row(result)) { - sms = sms_from_result_v4(result); - if (db_sms_store(sms) != 0) { - LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 4).\n"); - sms_free(sms); - dbi_result_free(result); - goto rollback; - } - sms_free(sms); - } - dbi_result_free(result); - - /* Remove the temporary table */ - result = dbi_conn_query(conn, "DROP TABLE SMS_4"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to drop the old SMS table (upgrade from rev 4).\n"); - goto rollback; - } - dbi_result_free(result); - - /* We're done. Bump DB Meta revision to 4 */ - result = dbi_conn_query(conn, - "UPDATE Meta " - "SET value = '5' " - "WHERE key = 'revision'"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to update DB schema revision (upgrade from rev 4).\n"); - goto rollback; - } - dbi_result_free(result); - - result = dbi_conn_query(conn, "COMMIT TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to commit the transaction (upgrade from rev 4)\n"); - return -EINVAL; - } else { - dbi_result_free(result); - } - - /* Shrink DB file size by actually wiping out SMS_4 table data */ - result = dbi_conn_query(conn, "VACUUM"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "VACUUM failed. Ignoring it (upgrade from rev 4).\n"); - else - dbi_result_free(result); - - return 0; - -rollback: - result = dbi_conn_query(conn, "ROLLBACK TRANSACTION"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "Rollback failed (upgrade from rev 4).\n"); - else - dbi_result_free(result); - return -EINVAL; -} - -static int check_db_revision(void) -{ - dbi_result result; - const char *rev_s; - int db_rev = 0; - - /* Make a query */ - result = dbi_conn_query(conn, - "SELECT value FROM Meta " - "WHERE key = 'revision'"); - - if (!result) - return -EINVAL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -EINVAL; - } - - /* Fetch the DB schema revision */ - rev_s = dbi_result_get_string(result, "value"); - if (!rev_s) { - dbi_result_free(result); - return -EINVAL; - } - - if (!strcmp(rev_s, SCHEMA_REVISION)) { - /* Everything is fine */ - dbi_result_free(result); - return 0; - } - - db_rev = atoi(rev_s); - dbi_result_free(result); - - /* Incremental migration waterfall */ - switch (db_rev) { - case 2: - if (update_db_revision_2()) - goto error; - case 3: - if (update_db_revision_3()) - goto error; - case 4: - if (update_db_revision_4()) - goto error; - - /* The end of waterfall */ - break; - default: - LOGP(DDB, LOGL_FATAL, - "Invalid database schema revision '%d'.\n", db_rev); - return -EINVAL; - } - - return 0; - -error: - LOGP(DDB, LOGL_FATAL, "Failed to update database " - "from schema revision '%d'.\n", db_rev); - return -EINVAL; -} - -static int db_configure(void) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "PRAGMA synchronous = FULL"); - if (!result) - return -EINVAL; - - dbi_result_free(result); - return 0; -} - -int db_init(const char *name) -{ - dbi_initialize(NULL); - - conn = dbi_conn_new("sqlite3"); - if (conn == NULL) { - LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); - return 1; - } - - dbi_conn_error_handler( conn, db_error_func, NULL ); - - /* MySQL - dbi_conn_set_option(conn, "host", "localhost"); - dbi_conn_set_option(conn, "username", "your_name"); - dbi_conn_set_option(conn, "password", "your_password"); - dbi_conn_set_option(conn, "dbname", "your_dbname"); - dbi_conn_set_option(conn, "encoding", "UTF-8"); - */ - - /* SqLite 3 */ - db_basename = strdup(name); - db_dirname = strdup(name); - dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); - dbi_conn_set_option(conn, "dbname", basename(db_basename)); - - if (dbi_conn_connect(conn) < 0) - goto out_err; - - return 0; - -out_err: - free(db_dirname); - free(db_basename); - db_dirname = db_basename = NULL; - return -1; -} - - -int db_prepare(void) -{ - dbi_result result; - int i; - - for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { - result = dbi_conn_query(conn, create_stmts[i]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create some table.\n"); - return 1; - } - dbi_result_free(result); - } - - if (check_db_revision() < 0) { - LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " - "please update your database schema\n"); - return -1; - } - - db_configure(); - - return 0; -} - -int db_fini(void) -{ - dbi_conn_close(conn); - dbi_shutdown(); - - free(db_dirname); - free(db_basename); - return 0; -} - -/* store an [unsent] SMS to the database */ -int db_sms_store(struct gsm_sms *sms) -{ - dbi_result result; - char *q_text, *q_daddr, *q_saddr; - unsigned char *q_udata; - char *validity_timestamp = "2222-2-2"; - - /* FIXME: generate validity timestamp based on validity_minutes */ - - dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); - dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); - dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr); - dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, - &q_udata); - - /* FIXME: correct validity period */ - result = dbi_conn_queryf(conn, - "INSERT INTO SMS " - "(created, valid_until, " - "reply_path_req, status_rep_req, is_report, " - "msg_ref, protocol_id, data_coding_scheme, " - "ud_hdr_ind, " - "user_data, text, " - "dest_addr, dest_ton, dest_npi, " - "src_addr, src_ton, src_npi) VALUES " - "(datetime('now'), %u, " - "%u, %u, %u, " - "%u, %u, %u, " - "%u, " - "%s, %s, " - "%s, %u, %u, " - "%s, %u, %u)", - validity_timestamp, - sms->reply_path_req, sms->status_rep_req, sms->is_report, - sms->msg_ref, sms->protocol_id, sms->data_coding_scheme, - sms->ud_hdr_ind, - q_udata, q_text, - q_daddr, sms->dst.ton, sms->dst.npi, - q_saddr, sms->src.ton, sms->src.npi); - free(q_text); - free(q_udata); - free(q_daddr); - free(q_saddr); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - const char *text, *daddr, *saddr; - const unsigned char *user_data; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - /* FIXME: validity */ - /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ - sms->created = dbi_result_get_datetime(result, "created"); - sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); - sms->is_report = dbi_result_get_ulonglong(result, "is_report"); - sms->msg_ref = dbi_result_get_ulonglong(result, "msg_ref"); - sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_ulonglong(result, - "data_coding_scheme"); - - sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi"); - sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton"); - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) - osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); - sms->receiver = vlr_subscr_find_by_msisdn(net->vlr, sms->dst.addr); - - sms->src.npi = dbi_result_get_ulonglong(result, "src_npi"); - sms->src.ton = dbi_result_get_ulonglong(result, "src_ton"); - saddr = dbi_result_get_string(result, "src_addr"); - if (saddr) - osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr)); - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - return sms; -} - -struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS WHERE SMS.id = %llu", id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net, - unsigned long long min_sms_id, - unsigned int max_failed) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS" - " WHERE sent IS NULL" - " AND id >= %llu" - " AND deliver_attempts <= %u" - " ORDER BY id LIMIT 1", - min_sms_id, max_failed); - - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS for a given subscriber */ -struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub, - unsigned int max_failed) -{ - struct gsm_network *net = vsub->vlr->user_ctx; - dbi_result result; - struct gsm_sms *sms; - - if (!vsub->lu_complete) - return NULL; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS" - " WHERE sent IS NULL" - " AND dest_addr=%s" - " AND deliver_attempts <= %u" - " ORDER BY id LIMIT 1", - vsub->msisdn, max_failed); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net, - const char *last_msisdn, - unsigned int max_failed) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS" - " WHERE sent IS NULL" - " AND dest_addr > '%s'" - " AND deliver_attempts <= %u" - " ORDER BY dest_addr, id LIMIT 1", - last_msisdn, max_failed); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* mark a given SMS as delivered */ -int db_sms_mark_delivered(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET sent = datetime('now') " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -/* increase the number of attempted deliveries */ -int db_sms_inc_deliver_attempts(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET deliver_attempts = deliver_attempts + 1 " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " - "SMS %llu.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -/* Drop all pending SMS to or from the given extension */ -int db_sms_delete_by_msisdn(const char *msisdn) -{ - dbi_result result; - if (!msisdn || !*msisdn) - return 0; - result = dbi_conn_queryf(conn, - "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s", - msisdn, msisdn); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete SMS for %s\n", msisdn); - return -1; - } - dbi_result_free(result); - return 0; -} - -int db_store_counter(struct osmo_counter *ctr) -{ - dbi_result result; - char *q_name; - - dbi_conn_quote_string_copy(conn, ctr->name, &q_name); - - result = dbi_conn_queryf(conn, - "INSERT INTO Counters " - "(timestamp,name,value) VALUES " - "(datetime('now'),%s,%lu)", q_name, ctr->value); - - free(q_name); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num, - char *q_prefix) -{ - dbi_result result; - char *q_name; - - dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name, - &q_name); - - result = dbi_conn_queryf(conn, - "Insert INTO RateCounters " - "(timestamp,name,idx,value) VALUES " - "(datetime('now'),%s.%s,%u,%"PRIu64")", - q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current); - - free(q_name); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -int db_store_rate_ctr_group(struct rate_ctr_group *ctrg) -{ - unsigned int i; - char *q_prefix; - - dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix); - - for (i = 0; i < ctrg->desc->num_ctr; i++) - db_store_rate_ctr(ctrg, i, q_prefix); - - free(q_prefix); - - return 0; -} diff --git a/src/libmsc/gsm_04_08.c b/src/libmsc/gsm_04_08.c deleted file mode 100644 index 21bc2b83b..000000000 --- a/src/libmsc/gsm_04_08.c +++ /dev/null @@ -1,3493 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2016 by Harald Welte <laforge@gnumonks.org> - * (C) 2008-2012 by Holger Hans Peter Freyther <zecke@selfish.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> -#include <errno.h> -#include <time.h> -#include <netinet/in.h> -#include <regex.h> -#include <sys/types.h> -#include <openssl/rand.h> - -#include "bscconfig.h" - -#include <openbsc/auth.h> -#include <openbsc/db.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_04_80.h> -#include <openbsc/gsm_04_14.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <osmocom/abis/trau_frame.h> -#include <openbsc/trau_mux.h> -#include <openbsc/rtp_proxy.h> -#include <openbsc/transaction.h> -#include <openbsc/ussd.h> -#include <openbsc/silent_call.h> -#include <openbsc/bsc_api.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/handover.h> -#include <openbsc/mncc_int.h> -#include <osmocom/abis/e1_input.h> -#include <osmocom/core/bitvec.h> -#include <openbsc/vlr.h> -#include <openbsc/msc_ifaces.h> - -#include <osmocom/gsm/gsm48.h> -#include <osmocom/gsm/gsm0480.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/crypt/auth.h> -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -#endif - -#include <openbsc/msc_ifaces.h> -#include <openbsc/a_iface.h> - -#include <assert.h> - - -void *tall_locop_ctx; -void *tall_authciphop_ctx; - -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, - uint32_t send_tmsi); -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - uint8_t pdisc, uint8_t msg_type); - -struct gsm_lai { - uint16_t mcc; - uint16_t mnc; - uint16_t lac; -}; - -static uint32_t new_callref = 0x80000001; - -void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) -{ - net->mncc_recv(net, msg); -} - -static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn, - struct gsm_trans *trans) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; - - /* if we get passed a transaction reference, do some common - * work that the caller no longer has to do */ - if (trans) { - gh->proto_discr = trans->protocol | (trans->transaction_id << 4); - } - - return msc_tx_dtap(conn, msg); -} - -int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) -{ - struct gsm48_hdr *gh; - struct msgb *ss_notify; - - ss_notify = gsm0480_create_notifySS(message); - if (!ss_notify) - return -1; - - gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); - uint8_t *data = msgb_push(ss_notify, 1); - data[0] = ss_notify->len - 1; - gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); - gh->msg_type = GSM48_MT_CC_FACILITY; - return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); -} - -/* clear all transactions globally; used in case of MNCC socket disconnect */ -void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) -{ - struct gsm_trans *trans, *temp; - - LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n"); - - llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) { - if (trans->protocol == protocol) { - trans->callref = 0; - trans_free(trans); - } - } -} - -/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ -static int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) -{ - struct msgb *msg; - - msg = gsm48_create_loc_upd_rej(cause); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); - return -1; - } - - LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT\n", - vlr_subscr_name(conn->vsub)); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, - uint32_t send_tmsi) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD ACC"); - struct gsm48_hdr *gh; - struct gsm48_loc_area_id *lai; - uint8_t *mid; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; - - lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm48_generate_lai(lai, conn->network->country_code, - conn->network->network_code, - conn->lac); - - if (send_tmsi == GSM_RESERVED_TMSI) { - /* we did not allocate a TMSI to the MS, so we need to - * include the IMSI in order for the MS to delete any - * old TMSI that might still be allocated */ - uint8_t mi[10]; - int len; - len = gsm48_generate_mid_from_imsi(mi, conn->vsub->imsi); - mid = msgb_put(msg, len); - memcpy(mid, mi, len); - DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT\n", - vlr_subscr_name(conn->vsub)); - } else { - /* Include the TMSI, which means that the MS will send a - * TMSI REALLOCATION COMPLETE, and we should wait for - * that until T3250 expiration */ - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, send_tmsi); - DEBUGP(DMM, "-> %s LOCATION UPDATE ACCEPT (TMSI = 0x%08x)\n", - vlr_subscr_name(conn->vsub), - send_tmsi); - } - /* TODO: Follow-on proceed */ - /* TODO: CTS permission */ - /* TODO: Equivalent PLMNs */ - /* TODO: Emergency Number List */ - /* TODO: Per-MS T3312 */ - - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ"); - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_ID_REQ; - gh->data[0] = id_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Parse Chapter 9.2.11 Identity Response */ -static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - - if (!conn->vsub) { - LOGP(DMM, LOGL_ERROR, - "Rx MM Identity Response: invalid: no subscriber\n"); - return -EINVAL; - } - - gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data); - - return vlr_subscr_rx_id_resp(conn->vsub, gh->data+1, gh->data[0]); -} - -/* FIXME: to libosmogsm */ -static const struct value_string lupd_names[] = { - { GSM48_LUPD_NORMAL, "NORMAL" }, - { GSM48_LUPD_PERIODIC, "PERIODIC" }, - { GSM48_LUPD_IMSI_ATT, "IMSI ATTACH" }, - { 0, NULL } -}; - -/* Chapter 9.2.15: Receive Location Updating Request. - * Keep this function non-static for direct invocation by unit tests. */ -int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - static const enum subscr_conn_from conn_from_lu = SUBSCR_CONN_FROM_LU; - struct gsm_network *net = conn->network; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_loc_upd_req *lu; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - enum vlr_lu_type vlr_lu_type = VLR_LU_TYPE_REGULAR; - uint32_t tmsi; - char *imsi; - struct osmo_location_area_id old_lai, new_lai; - struct osmo_fsm_inst *lu_fsm; - bool is_utran; - int rc; - - lu = (struct gsm48_loc_upd_req *) gh->data; - - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - - rc = msc_create_conn_fsm(conn, mi_string); - if (rc) - /* logging already happened in msc_create_conn_fsm() */ - return rc; - - conn->classmark.classmark1 = lu->classmark1; - conn->classmark.classmark1_set = true; - - DEBUGP(DMM, "LOCATION UPDATING REQUEST: MI(%s)=%s type=%s\n", - gsm48_mi_type_name(mi_type), mi_string, - get_value_string(lupd_names, lu->type)); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); - - switch (lu->type) { - case GSM48_LUPD_NORMAL: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]); - vlr_lu_type = VLR_LU_TYPE_REGULAR; - break; - case GSM48_LUPD_IMSI_ATT: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]); - vlr_lu_type = VLR_LU_TYPE_IMSI_ATTACH; - break; - case GSM48_LUPD_PERIODIC: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]); - vlr_lu_type = VLR_LU_TYPE_PERIODIC; - break; - } - - /* TODO: 10.5.1.6 MS Classmark for UMTS / Classmark 2 */ - /* TODO: 10.5.3.14 Aditional update parameters (CS fallback calls) */ - /* TODO: 10.5.7.8 Device properties */ - /* TODO: 10.5.1.15 MS network feature support */ - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - tmsi = GSM_RESERVED_TMSI; - imsi = mi_string; - break; - case GSM_MI_TYPE_TMSI: - tmsi = tmsi_from_string(mi_string); - imsi = NULL; - break; - default: - DEBUGPC(DMM, "unknown mobile identity type\n"); - tmsi = GSM_RESERVED_TMSI; - imsi = NULL; - break; - } - - gsm48_decode_lai(&lu->lai, &old_lai.plmn.mcc, - &old_lai.plmn.mnc, &old_lai.lac); - new_lai.plmn.mcc = conn->network->country_code; - new_lai.plmn.mnc = conn->network->network_code; - new_lai.lac = conn->lac; - DEBUGP(DMM, "LU/new-LAC: %u/%u\n", old_lai.lac, new_lai.lac); - - is_utran = (conn->via_ran == RAN_UTRAN_IU); - lu_fsm = vlr_loc_update(conn->conn_fsm, - SUBSCR_CONN_E_ACCEPTED, - SUBSCR_CONN_E_CN_CLOSE, - (void*)&conn_from_lu, - net->vlr, conn, vlr_lu_type, tmsi, imsi, - &old_lai, &new_lai, - is_utran || conn->network->authentication_required, - is_utran? VLR_CIPH_A5_3 - : conn->network->a5_encryption, - classmark_is_r99(&conn->classmark), - is_utran, - net->vlr->cfg.assign_tmsi); - if (!lu_fsm) { - DEBUGP(DRR, "%s: Can't start LU FSM\n", mi_string); - return 0; - } - - /* From vlr_loc_update() we expect an implicit dispatch of - * VLR_ULA_E_UPDATE_LA, and thus we expect msc_vlr_subscr_assoc() to - * already have been called and completed. Has an error occured? */ - - if (!conn->vsub || conn->vsub->lu_fsm != lu_fsm) { - LOGP(DRR, LOGL_ERROR, - "%s: internal error during Location Updating attempt\n", - mi_string); - return -EIO; - } - - return 0; -} - -/* Turn int into semi-octet representation: 98 => 0x89 */ -/* FIXME: libosmocore/libosmogsm */ -static uint8_t bcdify(uint8_t value) -{ - uint8_t ret; - - ret = value / 10; - ret |= (value % 10) << 4; - - return ret; -} - - -/* Section 9.2.15a */ -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 MM INF"); - struct gsm48_hdr *gh; - struct gsm_network *net = conn->network; - uint8_t *ptr8; - int name_len, name_pad; - - time_t cur_t; - struct tm* gmt_time; - struct tm* local_time; - int tzunits; - int dst = 0; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_INFO; - - if (net->name_long) { -#if 0 - name_len = strlen(net->name_long); - /* 10.5.3.5a */ - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len*2 +1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (uint16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_long[i]); - - /* FIXME: Use Cell Broadcast, not UCS-2, since - * UCS-2 is only supported by later revisions of the spec */ -#endif - name_len = (strlen(net->name_long)*7)/8; - name_pad = (8 - strlen(net->name_long)*7)%8; - if (name_pad > 0) - name_len++; - /* 10.5.3.5a */ - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len +1; - ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ - - ptr8 = msgb_put(msg, name_len); - gsm_7bit_encode_n(ptr8, name_len, net->name_long, NULL); - - } - - if (net->name_short) { -#if 0 - name_len = strlen(net->name_short); - /* 10.5.3.5a */ - ptr8 = (uint8_t *) msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_SHORT; - ptr8[1] = name_len*2 + 1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (uint16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_short[i]); -#endif - name_len = (strlen(net->name_short)*7)/8; - name_pad = (8 - strlen(net->name_short)*7)%8; - if (name_pad > 0) - name_len++; - /* 10.5.3.5a */ - ptr8 = (uint8_t *) msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_SHORT; - ptr8[1] = name_len +1; - ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ - - ptr8 = msgb_put(msg, name_len); - gsm_7bit_encode_n(ptr8, name_len, net->name_short, NULL); - - } - - /* Section 10.5.3.9 */ - cur_t = time(NULL); - gmt_time = gmtime(&cur_t); - - ptr8 = msgb_put(msg, 8); - ptr8[0] = GSM48_IE_NET_TIME_TZ; - ptr8[1] = bcdify(gmt_time->tm_year % 100); - ptr8[2] = bcdify(gmt_time->tm_mon + 1); - ptr8[3] = bcdify(gmt_time->tm_mday); - ptr8[4] = bcdify(gmt_time->tm_hour); - ptr8[5] = bcdify(gmt_time->tm_min); - ptr8[6] = bcdify(gmt_time->tm_sec); - - if (net->tz.override) { - /* Convert tz.hr and tz.mn to units */ - if (net->tz.hr < 0) { - tzunits = ((net->tz.hr/-1)*4); - tzunits = tzunits + (net->tz.mn/15); - ptr8[7] = bcdify(tzunits); - /* Set negative time */ - ptr8[7] |= 0x08; - } - else { - tzunits = net->tz.hr*4; - tzunits = tzunits + (net->tz.mn/15); - ptr8[7] = bcdify(tzunits); - } - /* Convert DST value */ - if (net->tz.dst >= 0 && net->tz.dst <= 2) - dst = net->tz.dst; - } - else { - /* Need to get GSM offset and convert into 15 min units */ - /* This probably breaks if gmtoff returns a value not evenly divisible by 15? */ -#ifdef HAVE_TM_GMTOFF_IN_TM - local_time = localtime(&cur_t); - tzunits = (local_time->tm_gmtoff/60)/15; -#else - /* find timezone offset */ - time_t utc; - double offsetFromUTC; - utc = mktime(gmt_time); - local_time = localtime(&cur_t); - offsetFromUTC = difftime(cur_t, utc); - if (local_time->tm_isdst) - offsetFromUTC += 3600.0; - tzunits = ((int)offsetFromUTC) / 60 / 15; -#endif - if (tzunits < 0) { - tzunits = tzunits/-1; - ptr8[7] = bcdify(tzunits); - /* Flip it to negative */ - ptr8[7] |= 0x08; - } - else - ptr8[7] = bcdify(tzunits); - - /* Does not support DST +2 */ - if (local_time->tm_isdst) - dst = 1; - } - - if (dst) { - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NET_DST; - ptr8[1] = 1; - ptr8[2] = dst; - } - - DEBUGP(DMM, "-> MM INFO\n"); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/*! Send an Authentication Request to MS on the given subscriber connection - * according to 3GPP/ETSI TS 24.008, Section 9.2.2. - * \param[in] conn Subscriber connection to send on. - * \param[in] rand Random challenge token to send, must be 16 bytes long. - * \param[in] autn r99: In case of UMTS mutual authentication, AUTN token to - * send; must be 16 bytes long, or pass NULL for plain GSM auth. - * \param[in] key_seq auth tuple's sequence number. - */ -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, - uint8_t *autn, int key_seq) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH REQ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); - - DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", osmo_hexdump_nospc(rand, 16)); - if (autn) - DEBUGP(DMM, " AUTH REQ (autn = %s)\n", osmo_hexdump_nospc(autn, 16)); - - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_AUTH_REQ; - - ar->key_seq = key_seq; - - /* 16 bytes RAND parameters */ - osmo_static_assert(sizeof(ar->rand) == 16, sizeof_auth_req_r99_rand); - if (rand) - memcpy(ar->rand, rand, 16); - - - /* 16 bytes AUTN */ - if (autn) - msgb_tlv_put(msg, GSM48_IE_AUTN, 16, autn); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.1 */ -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> AUTH REJECT\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); -} - -static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref); -static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result); - -static int cm_serv_reuse_conn(struct gsm_subscriber_connection *conn, const uint8_t *mi_lv) -{ - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - uint32_t tmsi; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi_lv+1, mi_lv[0]); - mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - if (vlr_subscr_matches_imsi(conn->vsub, mi_string)) - goto accept_reuse; - break; - case GSM_MI_TYPE_TMSI: - tmsi = osmo_load32be(mi_lv+2); - if (vlr_subscr_matches_tmsi(conn->vsub, tmsi)) - goto accept_reuse; - break; - case GSM_MI_TYPE_IMEI: - if (vlr_subscr_matches_imei(conn->vsub, mi_string)) - goto accept_reuse; - break; - default: - break; - } - - LOGP(DMM, LOGL_ERROR, "%s: CM Service Request with mismatching mobile identity: %s %s\n", - vlr_subscr_name(conn->vsub), gsm48_mi_type_name(mi_type), mi_string); - msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_ILLEGAL_SUBSCR); - return -EINVAL; - -accept_reuse: - DEBUGP(DMM, "%s: re-using already accepted connection\n", - vlr_subscr_name(conn->vsub)); - conn->received_cm_service_request = true; - return conn->network->vlr->ops.tx_cm_serv_acc(conn); -} - -/* - * Handle CM Service Requests - * a) Verify that the packet is long enough to contain the information - * we require otherwsie reject with INCORRECT_MESSAGE - * b) Try to parse the TMSI. If we do not have one reject - * c) Check that we know the subscriber with the TMSI otherwise reject - * with a HLR cause - * d) Set the subscriber on the conn and accept - * - * Keep this function non-static for direct invocation by unit tests. - */ -int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - static const enum subscr_conn_from conn_from_cm_service_req = - SUBSCR_CONN_FROM_CM_SERVICE_REQ; - struct gsm_network *net = conn->network; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_service_request *req = - (struct gsm48_service_request *)gh->data; - /* unfortunately in Phase1 the classmark2 length is variable */ - uint8_t classmark2_len = gh->data[1]; - uint8_t *classmark2 = gh->data+2; - uint8_t mi_len = *(classmark2 + classmark2_len); - uint8_t *mi = (classmark2 + classmark2_len + 1); - struct osmo_location_area_id lai; - bool is_utran; - int rc; - - lai.plmn.mcc = conn->network->country_code; - lai.plmn.mnc = conn->network->network_code; - lai.lac = conn->lac; - - DEBUGP(DMM, "<- CM SERVICE REQUEST "); - if (msg->data_len < sizeof(struct gsm48_service_request*)) { - DEBUGPC(DMM, "wrong sized message\n"); - return msc_gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - if (msg->data_len < req->mi_len + 6) { - DEBUGPC(DMM, "does not fit in packet\n"); - return msc_gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - mi_type = mi[0] & GSM_MI_TYPE_MASK; - - if (mi_type == GSM_MI_TYPE_IMSI) { - DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", - req->cm_service_type, gsm48_mi_type_name(mi_type), - mi_string); - } else if (mi_type == GSM_MI_TYPE_TMSI) { - DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", - req->cm_service_type, gsm48_mi_type_name(mi_type), - mi_string); - } else { - DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type); - return msc_gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); - memcpy(conn->classmark.classmark2, classmark2, classmark2_len); - conn->classmark.classmark2_len = classmark2_len; - - if (conn->conn_fsm) { - if (msc_subscr_conn_is_accepted(conn)) - return cm_serv_reuse_conn(conn, mi-1); - LOGP(DMM, LOGL_ERROR, "%s: connection already in use\n", - vlr_subscr_name(conn->vsub)); - msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR); - return -EINVAL; - } - - rc = msc_create_conn_fsm(conn, mi_string); - if (rc) { - msc_vlr_tx_cm_serv_rej(conn, VLR_PR_ARQ_RES_UNKNOWN_ERROR); - /* logging already happened in msc_create_conn_fsm() */ - return rc; - } - - is_utran = (conn->via_ran == RAN_UTRAN_IU); - vlr_proc_acc_req(conn->conn_fsm, - SUBSCR_CONN_E_ACCEPTED, - SUBSCR_CONN_E_CN_CLOSE, - (void*)&conn_from_cm_service_req, - net->vlr, conn, - VLR_PR_ARQ_T_CM_SERV_REQ, mi-1, &lai, - is_utran || conn->network->authentication_required, - is_utran? VLR_CIPH_A5_3 - : conn->network->a5_encryption, - classmark_is_r99(&conn->classmark), - is_utran); - - return 0; -} - -static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_network *network = conn->network; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_imsi_detach_ind *idi = - (struct gsm48_imsi_detach_ind *) gh->data; - uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - struct vlr_subscr *vsub = NULL; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); - DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - vsub = vlr_subscr_find_by_tmsi(network->vlr, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - vsub = vlr_subscr_find_by_imsi(network->vlr, mi_string); - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unimplemented mobile identity type\n", - gsm48_mi_type_name(mi_type), mi_string); - break; - default: - LOGP(DMM, LOGL_ERROR, "MI(%s)=%s: unknown mobile identity type\n", - gsm48_mi_type_name(mi_type), mi_string); - break; - } - - /* TODO? We used to remember the subscriber's classmark1 here and - * stored it in the old sqlite db, but now we store it in a conn that - * will be discarded anyway: */ - conn->classmark.classmark1 = idi->classmark1; - - if (!vsub) { - LOGP(DMM, LOGL_ERROR, "IMSI DETACH for unknown subscriber MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - } else { - LOGP(DMM, LOGL_INFO, "IMSI DETACH for %s\n", vlr_subscr_name(vsub)); - vlr_subscr_rx_imsi_detach(vsub); - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, vsub); - vlr_subscr_put(vsub); - } - - msc_subscr_conn_close(conn, 0); - return 0; -} - -static int gsm48_rx_mm_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); - - return 0; -} - -static int parse_gsm_auth_resp(uint8_t *res, uint8_t *res_len, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*ar)) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " l3 length invalid: %u\n", - vlr_subscr_name(conn->vsub), msgb_l3len(msg)); - return -EINVAL; - } - - *res_len = sizeof(ar->sres); - memcpy(res, ar->sres, sizeof(ar->sres)); - return 0; -} - -static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - uint8_t *data; - uint8_t iei; - uint8_t ie_len; - unsigned int data_len; - - /* First parse the GSM part */ - if (parse_gsm_auth_resp(res, res_len, conn, msg)) - return -EINVAL; - OSMO_ASSERT(*res_len == 4); - - /* Then add the extended res part */ - gh = msgb_l3(msg); - data = gh->data + sizeof(struct gsm48_auth_resp); - data_len = msgb_l3len(msg) - (data - (uint8_t*)msgb_l3(msg)); - - if (data_len < 3) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " l3 length invalid: %u\n", - vlr_subscr_name(conn->vsub), msgb_l3len(msg)); - return -EINVAL; - } - - iei = data[0]; - ie_len = data[1]; - if (iei != GSM48_IE_AUTH_RES_EXT) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION RESPONSE:" - " expected IEI 0x%02x, got 0x%02x\n", - vlr_subscr_name(conn->vsub), - GSM48_IE_AUTH_RES_EXT, iei); - return -EINVAL; - } - - if (ie_len > 12) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION RESPONSE:" - " extended Auth Resp IE 0x%02x is too large: %u bytes\n", - vlr_subscr_name(conn->vsub), GSM48_IE_AUTH_RES_EXT, ie_len); - return -EINVAL; - } - - *res_len += ie_len; - memcpy(res + 4, &data[2], ie_len); - return 0; -} - -/* Chapter 9.2.3: Authentication Response */ -static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - uint8_t res[16]; - uint8_t res_len; - int rc; - bool is_r99; - - if (!conn->vsub) { - LOGP(DMM, LOGL_ERROR, - "MM AUTHENTICATION RESPONSE: invalid: no subscriber\n"); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - if (msgb_l3len(msg) > - sizeof(struct gsm48_hdr) + sizeof(struct gsm48_auth_resp)) { - rc = parse_umts_auth_resp(res, &res_len, conn, msg); - is_r99 = true; - } else { - rc = parse_gsm_auth_resp(res, &res_len, conn, msg); - is_r99 = false; - } - - if (rc) { - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n", - vlr_subscr_name(conn->vsub), - is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres", - osmo_hexdump_nospc(res, res_len)); - - return vlr_subscr_rx_auth_resp(conn->vsub, is_r99, - conn->via_ran == RAN_UTRAN_IU, - res, res_len); -} - -static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t cause; - uint8_t auts_tag; - uint8_t auts_len; - uint8_t *auts; - - if (!conn->vsub) { - LOGP(DMM, LOGL_ERROR, - "MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n"); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - if (msgb_l3len(msg) < sizeof(*gh) + 1) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION FAILURE:" - " l3 length invalid: %u\n", - vlr_subscr_name(conn->vsub), msgb_l3len(msg)); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - cause = gh->data[0]; - - if (cause != GSM48_REJECT_SYNCH_FAILURE) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE: cause 0x%0x\n", - vlr_subscr_name(conn->vsub), cause); - vlr_subscr_rx_auth_fail(conn->vsub, NULL); - return 0; - } - - /* This is a Synch Failure procedure, which should pass an AUTS to - * resynchronize the sequence nr with the HLR. Expecting exactly one - * TLV with 14 bytes of AUTS. */ - - if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure: missing AUTS IE\n", - vlr_subscr_name(conn->vsub)); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - auts_tag = gh->data[1]; - auts_len = gh->data[2]; - auts = &gh->data[3]; - - if (auts_tag != GSM48_IE_AUTS - || auts_len != 14) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure:" - " expected AUTS IE 0x%02x of 14 bytes," - " got IE 0x%02x of %u bytes\n", - vlr_subscr_name(conn->vsub), - GSM48_IE_AUTS, auts_tag, auts_len); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2 + auts_len) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure msg: message truncated (%u)\n", - vlr_subscr_name(conn->vsub), msgb_l3len(msg)); - msc_subscr_conn_close(conn, GSM_CAUSE_AUTH_FAILED); - return -EINVAL; - } - - /* We have an AUTS IE with exactly 14 bytes of AUTS and the msgb is - * large enough. */ - - DEBUGP(DMM, "%s: MM R99 AUTHENTICATION SYNCH (AUTS = %s)\n", - vlr_subscr_name(conn->vsub), osmo_hexdump_nospc(auts, 14)); - - return vlr_subscr_rx_auth_fail(conn->vsub, auts); -} - -static int gsm48_rx_mm_tmsi_reall_compl(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - vlr_subscr_name(conn->vsub)); - if (!conn->vsub) { - LOGP(DMM, LOGL_ERROR, - "Rx MM TMSI Reallocation Complete: invalid: no subscriber\n"); - return -EINVAL; - } - return vlr_subscr_rx_tmsi_reall_compl(conn->vsub); -} - -/* Receive a GSM 04.08 Mobility Management (MM) message */ -static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gsm48_hdr_msg_type(gh)) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - rc = mm_rx_loc_upd_req(conn, msg); - break; - case GSM48_MT_MM_ID_RESP: - rc = mm_rx_id_resp(conn, msg); - break; - case GSM48_MT_MM_CM_SERV_REQ: - rc = gsm48_rx_mm_serv_req(conn, msg); - break; - case GSM48_MT_MM_STATUS: - rc = gsm48_rx_mm_status(msg); - break; - case GSM48_MT_MM_TMSI_REALL_COMPL: - rc = gsm48_rx_mm_tmsi_reall_compl(conn); - break; - case GSM48_MT_MM_IMSI_DETACH_IND: - rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); - break; - case GSM48_MT_MM_CM_REEST_REQ: - DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); - break; - case GSM48_MT_MM_AUTH_RESP: - rc = gsm48_rx_mm_auth_resp(conn, msg); - break; - case GSM48_MT_MM_AUTH_FAIL: - rc = gsm48_rx_mm_auth_fail(conn, msg); - break; - default: - LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -static uint8_t *gsm48_cm2_get_mi(uint8_t *classmark2_lv, unsigned int tot_len) -{ - /* Check the size for the classmark */ - if (tot_len < 1 + *classmark2_lv) - return NULL; - - uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; - if (tot_len < 2 + *classmark2_lv + mi_lv[0]) - return NULL; - - return mi_lv; -} - -/* Receive a PAGING RESPONSE message from the MS */ -static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - static const enum subscr_conn_from conn_from_paging_resp = - SUBSCR_CONN_FROM_PAGING_RESP; - struct gsm_network *net = conn->network; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_pag_resp *resp; - uint8_t *classmark2_lv = gh->data + 1; - uint8_t *mi_lv; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - int rc = 0; - struct osmo_location_area_id lai; - bool is_utran; - - lai.plmn.mcc = conn->network->country_code; - lai.plmn.mnc = conn->network->network_code; - lai.lac = conn->lac; - - resp = (struct gsm48_pag_resp *) &gh->data[0]; - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - mi_lv = gsm48_cm2_get_mi(classmark2_lv, msgb_l3len(msg) - sizeof(*gh)); - if (!mi_lv) { - /* FIXME */ - return -1; - } - - rc = msc_create_conn_fsm(conn, mi_string); - if (rc) - /* logging already happened in msc_create_conn_fsm() */ - return rc; - - memcpy(conn->classmark.classmark2, classmark2_lv+1, *classmark2_lv); - conn->classmark.classmark2_len = *classmark2_lv; - - is_utran = (conn->via_ran == RAN_UTRAN_IU); - vlr_proc_acc_req(conn->conn_fsm, - SUBSCR_CONN_E_ACCEPTED, - SUBSCR_CONN_E_CN_CLOSE, - (void*)&conn_from_paging_resp, - net->vlr, conn, - VLR_PR_ARQ_T_PAGING_RESP, mi_lv, &lai, - is_utran || conn->network->authentication_required, - is_utran? VLR_CIPH_A5_3 - : conn->network->a5_encryption, - classmark_is_r99(&conn->classmark), - is_utran); - - return 0; -} - -static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t apdu_id_flags; - uint8_t apdu_len; - uint8_t *apdu_data; - - apdu_id_flags = gh->data[0]; - apdu_len = gh->data[1]; - apdu_data = gh->data+2; - - DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s\n", - apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); - - /* we're not using the app info blob anywhere, so ignore. */ -#if 0 - return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); -#else - return 0; -#endif -} - -/* Receive a GSM 04.08 Radio Resource (RR) message */ -static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type) { - case GSM48_MT_RR_PAG_RESP: - rc = gsm48_rx_rr_pag_resp(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - rc = gsm48_rx_rr_app_info(conn, msg); - break; - default: - LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented %s GSM 04.08 RR " - "message\n", gsm48_rr_msg_name(gh->msg_type)); - break; - } - - return rc; -} - -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, - uint8_t apdu_len, const uint8_t *apdu) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 APP INF"); - struct gsm48_hdr *gh; - - DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", - apdu_id, apdu_len); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_APP_INFO; - gh->data[0] = apdu_id; - gh->data[1] = apdu_len; - memcpy(gh->data+2, apdu, apdu_len); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* FIXME: this count_statistics is a state machine behaviour. we should convert - * the complete call control into a state machine. Afterwards we can move this - * code into state transitions. - */ -static void count_statistics(struct gsm_trans *trans, int new_state) -{ - int old_state = trans->cc.state; - struct rate_ctr_group *msc = trans->net->msc_ctrs; - - if (old_state == new_state) - return; - - /* state incoming */ - switch (new_state) { - case GSM_CSTATE_ACTIVE: - osmo_counter_inc(trans->net->active_calls); - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_ACTIVE]); - break; - } - - /* state outgoing */ - switch (old_state) { - case GSM_CSTATE_ACTIVE: - osmo_counter_dec(trans->net->active_calls); - if (new_state == GSM_CSTATE_DISCONNECT_REQ || - new_state == GSM_CSTATE_DISCONNECT_IND) - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_COMPLETE]); - else - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_INCOMPLETE]); - break; - } -} - -/* Call Control */ - -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - -static void new_cc_state(struct gsm_trans *trans, int state) -{ - if (state > 31 || state < 0) - return; - - DEBUGP(DCC, "new state %s -> %s\n", - gsm48_cc_state_name(trans->cc.state), - gsm48_cc_state_name(state)); - - count_statistics(trans, state); - trans->cc.state = state; -} - -static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STATUS"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause, *call_state; - - gh->msg_type = GSM48_MT_CC_STATUS; - - cause = msgb_put(msg, 3); - cause[0] = 2; - cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; - cause[2] = 0x80 | 30; /* response to status inquiry */ - - call_state = msgb_put(msg, 1); - call_state[0] = 0xc0 | 0x00; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - uint8_t pdisc, uint8_t msg_type) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 TX SIMPLE"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->proto_discr = pdisc; - gh->msg_type = msg_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static void gsm48_stop_cc_timer(struct gsm_trans *trans) -{ - if (osmo_timer_pending(&trans->cc.timer)) { - DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); - osmo_timer_del(&trans->cc.timer); - trans->cc.Tcurrent = 0; - } -} - -static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, - int msg_type, struct gsm_mncc *mncc) -{ - struct msgb *msg; - unsigned char *data; - - DEBUGP(DMNCC, "transmit message %s\n", get_mncc_name(msg_type)); - -#if BEFORE_MSCSPLIT - /* Re-enable this log output once we can obtain this information via - * A-interface, see OS#2391. */ - if (trans) - if (trans->conn && trans->conn->lchan) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Sending '%s' to MNCC.\n", - trans->conn->lchan->ts->trx->bts->nr, - trans->conn->lchan->ts->trx->nr, - trans->conn->lchan->ts->nr, trans->transaction_id, - vlr_subscr_msisdn_or_name(trans->vsub), - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Sending '%s' to MNCC.\n", - vlr_subscr_msisdn_or_name(trans->vsub), - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " - "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); -#else - DEBUGP(DCC, "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); -#endif - - mncc->msg_type = msg_type; - - msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); - if (!msg) - return -ENOMEM; - - data = msgb_put(msg, sizeof(struct gsm_mncc)); - memcpy(data, mncc, sizeof(struct gsm_mncc)); - - cc_tx_to_mncc(net, msg); - - return 0; -} - -int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, - uint32_t callref, int location, int value) -{ - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(rel)); - rel.callref = callref; - mncc_set_cause(&rel, location, value); - if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) - return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); -} - -/* Call Control Specific transaction release. - * gets called by trans_free, DO NOT CALL YOURSELF! */ -void _gsm48_cc_trans_free(struct gsm_trans *trans) -{ - gsm48_stop_cc_timer(trans); - - /* Make sure call also gets released on the mgcp side */ - msc_call_release(trans); - - /* send release to L4, if callref still exists */ - if (trans->callref) { - /* Ressource unavailable */ - mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - } - if (trans->cc.state != GSM_CSTATE_NULL) - new_cc_state(trans, GSM_CSTATE_NULL); -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); - -/* call-back from paging the B-end of the connection */ -static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_transt) -{ - struct gsm_subscriber_connection *conn = _conn; - struct gsm_trans *transt = _transt; - - OSMO_ASSERT(!transt->conn); - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGP(DCC, "Paging subscr %s succeeded!\n", - vlr_subscr_msisdn_or_name(transt->vsub)); - OSMO_ASSERT(conn); - /* Assign conn */ - transt->conn = conn; - /* send SETUP request to called party */ - gsm48_cc_tx_setup(transt, &transt->cc.msg); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - DEBUGP(DCC, "Paging subscr %s expired!\n", - vlr_subscr_msisdn_or_name(transt->vsub)); - /* Temporarily out of order */ - mncc_release_ind(transt->net, transt, - transt->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - transt->callref = 0; - transt->paging_request = NULL; - trans_free(transt); - break; - default: - LOGP(DCC, LOGL_ERROR, "Unknown paging event %d\n", event); - break; - } - - transt->paging_request = NULL; - return 0; -} - -/* bridge channels of two transactions */ -static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) -{ - struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[0]); - struct gsm_trans *trans2 = trans_find_by_callref(net, bridge->callref[1]); - - if (!trans1 || !trans2) - return -EIO; - - if (!trans1->conn || !trans2->conn) - return -EIO; - - /* Which subscriber do we want to track trans1 or trans2? */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans1->vsub); - - return msc_call_bridge(trans1, trans2); -} - -static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) -{ - DEBUGP(DCC, "-> STATUS ENQ\n"); - return gsm48_cc_tx_status(trans, msg); -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); - -static void gsm48_cc_timeout(void *arg) -{ - struct gsm_trans *trans = arg; - int disconnect = 0, release = 0; - int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; - int mo_location = GSM48_CAUSE_LOC_USER; - int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; - int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; - struct gsm_mncc mo_rel, l4_rel; - - memset(&mo_rel, 0, sizeof(struct gsm_mncc)); - mo_rel.callref = trans->callref; - memset(&l4_rel, 0, sizeof(struct gsm_mncc)); - l4_rel.callref = trans->callref; - - switch(trans->cc.Tcurrent) { - case 0x303: - release = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x310: - disconnect = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x313: - disconnect = 1; - /* unknown, did not find it in the specs */ - break; - case 0x301: - disconnect = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x308: - if (!trans->cc.T308_second) { - /* restart T308 a second time */ - gsm48_cc_tx_release(trans, &trans->cc.msg); - trans->cc.T308_second = 1; - break; /* stay in release state */ - } - trans_free(trans); - return; -// release = 1; -// l4_cause = 14; -// break; - case 0x306: - release = 1; - mo_cause = trans->cc.msg.cause.value; - mo_location = trans->cc.msg.cause.location; - break; - case 0x323: - disconnect = 1; - break; - default: - release = 1; - } - - if (release && trans->callref) { - /* process release towards layer 4 */ - mncc_release_ind(trans->net, trans, trans->callref, - l4_location, l4_cause); - trans->callref = 0; - } - - if (disconnect && trans->callref) { - /* process disconnect towards layer 4 */ - mncc_set_cause(&l4_rel, l4_location, l4_cause); - mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &l4_rel); - } - - /* process disconnect towards mobile station */ - if (disconnect || release) { - mncc_set_cause(&mo_rel, mo_location, mo_cause); - mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; - mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; - mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; - mo_rel.cause.diag_len = 3; - - if (disconnect) - gsm48_cc_tx_disconnect(trans, &mo_rel); - if (release) - gsm48_cc_tx_release(trans, &mo_rel); - } - -} - -/* disconnect both calls from the bridge */ -static inline void disconnect_bridge(struct gsm_network *net, - struct gsm_mncc_bridge *bridge, int err) -{ - struct gsm_trans *trans0 = trans_find_by_callref(net, bridge->callref[0]); - struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[1]); - struct gsm_mncc mx_rel; - if (!trans0 || !trans1) - return; - - DEBUGP(DCC, "Failed to bridge TCH for calls %x <-> %x :: %s \n", - trans0->callref, trans1->callref, strerror(err)); - - memset(&mx_rel, 0, sizeof(struct gsm_mncc)); - mncc_set_cause(&mx_rel, GSM48_CAUSE_LOC_INN_NET, - GSM48_CC_CAUSE_CHAN_UNACCEPT); - - mx_rel.callref = trans0->callref; - gsm48_cc_tx_disconnect(trans0, &mx_rel); - - mx_rel.callref = trans1->callref; - gsm48_cc_tx_disconnect(trans1, &mx_rel); -} - -static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, - int sec, int micro) -{ - DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); - osmo_timer_setup(&trans->cc.timer, gsm48_cc_timeout, trans); - osmo_timer_schedule(&trans->cc.timer, sec, micro); - trans->cc.Tcurrent = current; -} - -static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc setup; - - memset(&setup, 0, sizeof(struct gsm_mncc)); - setup.callref = trans->callref; - - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* emergency setup is identified by msg_type */ - if (msg_type == GSM48_MT_CC_EMERG_SETUP) - setup.emergency = 1; - - /* use subscriber as calling party number */ - setup.fields |= MNCC_F_CALLING; - osmo_strlcpy(setup.calling.number, trans->vsub->msisdn, sizeof(setup.calling.number)); - osmo_strlcpy(setup.imsi, trans->vsub->imsi, sizeof(setup.imsi)); - - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - setup.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&setup.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - - /* Create a copy of the bearer capability - * in the transaction struct, so we can use - * this information later */ - memcpy(&trans->bearer_cap,&setup.bearer_cap, - sizeof(trans->bearer_cap)); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - setup.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&setup.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* called party bcd number */ - if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { - setup.fields |= MNCC_F_CALLED; - gsm48_decode_called(&setup.called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - setup.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&setup.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - setup.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&setup.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - /* CLIR suppression */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) - setup.clir.sup = 1; - /* CLIR invocation */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) - setup.clir.inv = 1; - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - setup.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&setup.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_INITIATED); - - LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", - vlr_subscr_name(trans->vsub), trans->vsub->msisdn, - setup.called.number); - - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]); - - /* indicate setup to MNCC */ - mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup); - - /* MNCC code will modify the channel asynchronously, we should - * ipaccess-bind only after the modification has been made to the - * lchan->tch_mode */ - return 0; -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STUP"); - struct gsm48_hdr *gh; - struct gsm_mncc *setup = arg; - int rc, trans_id; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - /* transaction id must not be assigned */ - if (trans->transaction_id != 0xff) { /* unasssigned */ - DEBUGP(DCC, "TX Setup with assigned transaction. " - "This is not allowed!\n"); - /* Temporarily out of order */ - rc = mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - - /* Get free transaction_id */ - trans_id = trans_assign_trans_id(trans->net, trans->vsub, - GSM48_PDISC_CC, 0); - if (trans_id < 0) { - /* no free transaction ID */ - rc = mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - trans->transaction_id = trans_id; - - gh->msg_type = GSM48_MT_CC_SETUP; - - gsm48_start_cc_timer(trans, 0x303, GSM48_T303); - - /* bearer capability */ - if (setup->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); - /* facility */ - if (setup->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &setup->facility); - /* progress */ - if (setup->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &setup->progress); - /* calling party BCD number */ - if (setup->fields & MNCC_F_CALLING) - gsm48_encode_calling(msg, &setup->calling); - /* called party BCD number */ - if (setup->fields & MNCC_F_CALLED) - gsm48_encode_called(msg, &setup->called); - /* user-user */ - if (setup->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &setup->useruser); - /* redirecting party BCD number */ - if (setup->fields & MNCC_F_REDIRECTING) - gsm48_encode_redirecting(msg, &setup->redirecting); - /* signal */ - if (setup->fields & MNCC_F_SIGNAL) - gsm48_encode_signal(msg, setup->signal); - - new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); - - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP]); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc call_conf; - int rc; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x310, GSM48_T310); - - memset(&call_conf, 0, sizeof(struct gsm_mncc)); - call_conf.callref = trans->callref; - - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); -#if 0 - /* repeat */ - if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) - call_conf.repeat = 1; - if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) - call_conf.repeat = 2; -#endif - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - call_conf.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&call_conf.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - - /* Create a copy of the bearer capability - * in the transaction struct, so we can use - * this information later */ - memcpy(&trans->bearer_cap,&call_conf.bearer_cap, - sizeof(trans->bearer_cap)); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - call_conf.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&call_conf.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - call_conf.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&call_conf.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - /* IMSI of called subscriber */ - osmo_strlcpy(call_conf.imsi, trans->vsub->imsi, sizeof(call_conf.imsi)); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - - /* Assign call (if not done yet) */ - if (trans->assignment_done == false) { - rc = msc_call_assignment(trans); - trans->assignment_done = true; - } - else - rc = 0; - - /* don't continue, if there were problems with - * the call assignment. */ - if (rc) - return rc; - - return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, - &call_conf); -} - -static int gsm48_cc_tx_call_proc_and_assign(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *proceeding = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - int rc; - - gh->msg_type = GSM48_MT_CC_CALL_PROC; - - new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); - - /* bearer capability */ - if (proceeding->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); - /* facility */ - if (proceeding->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &proceeding->facility); - /* progress */ - if (proceeding->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &proceeding->progress); - - rc = gsm48_conn_sendmsg(msg, trans->conn, trans); - if (rc) - return rc; - - /* Assign call (if not done yet) */ - if (trans->assignment_done == false) { - rc = msc_call_assignment(trans); - trans->assignment_done = true; - } - else - rc = 0; - - return rc; -} - -static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc alerting; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x301, GSM48_T301); - - memset(&alerting, 0, sizeof(struct gsm_mncc)); - alerting.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - alerting.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&alerting.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - - /* progress */ - if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { - alerting.fields |= MNCC_F_PROGRESS; - gsm48_decode_progress(&alerting.progress, - TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - alerting.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&alerting.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); - - return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND, - &alerting); -} - -static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *alerting = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC ALERT"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_ALERTING; - - /* facility */ - if (alerting->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &alerting->facility); - /* progress */ - if (alerting->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &alerting->progress); - /* user-user */ - if (alerting->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &alerting->useruser); - - new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *progress = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROGRESS"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_PROGRESS; - - /* progress */ - gsm48_encode_progress(msg, 1, &progress->progress); - /* user-user */ - if (progress->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &progress->useruser); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *connect = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSN 04.08 CC CON"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x313, GSM48_T313); - - /* facility */ - if (connect->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &connect->facility); - /* progress */ - if (connect->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &connect->progress); - /* connected number */ - if (connect->fields & MNCC_F_CONNECTED) - gsm48_encode_connected(msg, &connect->connected); - /* user-user */ - if (connect->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &connect->useruser); - - new_cc_state(trans, GSM_CSTATE_CONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc connect; - - gsm48_stop_cc_timer(trans); - - memset(&connect, 0, sizeof(struct gsm_mncc)); - connect.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* use subscriber as connected party number */ - connect.fields |= MNCC_F_CONNECTED; - osmo_strlcpy(connect.connected.number, trans->vsub->msisdn, sizeof(connect.connected.number)); - osmo_strlcpy(connect.imsi, trans->vsub->imsi, sizeof(connect.imsi)); - - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - connect.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&connect.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - connect.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&connect.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - connect.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&connect.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT]); - - return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect); -} - - -static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc connect_ack; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK]); - - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = trans->callref; - - return mncc_recvmsg(trans->net, trans, MNCC_SETUP_COMPL_IND, - &connect_ack); -} - -static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC CON ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT_ACK; - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc disc; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); - - memset(&disc, 0, sizeof(struct gsm_mncc)); - disc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - disc.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&disc.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - disc.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&disc.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - disc.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&disc.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - disc.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&disc.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &disc); - -} - -static struct gsm_mncc_cause default_cause = { - .location = GSM48_CAUSE_LOC_PRN_S_LU, - .coding = 0, - .rec = 0, - .rec_val = 0, - .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, - .diag_len = 0, - .diag = { 0 }, -}; - -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *disc = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC DISC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_DISCONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x306, GSM48_T306); - - /* cause */ - if (disc->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &disc->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - /* facility */ - if (disc->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &disc->facility); - /* progress */ - if (disc->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &disc->progress); - /* user-user */ - if (disc->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &disc->useruser); - - /* store disconnect cause for T306 expiry */ - memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { - /* release collision 5.4.5 */ - rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel); - } else { - rc = gsm48_tx_simple(trans->conn, - GSM48_PDISC_CC | (trans->transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel); - } - - new_cc_state(trans, GSM_CSTATE_NULL); - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RELEASE; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x308, GSM48_T308); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - trans->cc.T308_second = 0; - memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); - - if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) - new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc = 0; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->callref) { - switch (trans->cc.state) { - case GSM_CSTATE_CALL_PRESENT: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REJ_IND, &rel); - break; - case GSM_CSTATE_RELEASE_REQ: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REL_CNF, &rel); - break; - default: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REL_IND, &rel); - } - } - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL COMPL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - int ret; - - gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; - - trans->callref = 0; - - gsm48_stop_cc_timer(trans); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - ret = gsm48_conn_sendmsg(msg, trans->conn, trans); - - trans_free(trans); - - return ret; -} - -static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc fac; - - memset(&fac, 0, sizeof(struct gsm_mncc)); - fac.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - fac.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&fac.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - fac.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&fac.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_FACILITY_IND, &fac); -} - -static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *fac = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC FAC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_FACILITY; - - /* facility */ - gsm48_encode_facility(msg, 1, &fac->facility); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc hold; - - memset(&hold, 0, sizeof(struct gsm_mncc)); - hold.callref = trans->callref; - return mncc_recvmsg(trans->net, trans, MNCC_HOLD_IND, &hold); -} - -static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *hold_rej = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_REJ; - - /* cause */ - if (hold_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &hold_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc retrieve; - - memset(&retrieve, 0, sizeof(struct gsm_mncc)); - retrieve.callref = trans->callref; - return mncc_recvmsg(trans->net, trans, MNCC_RETRIEVE_IND, - &retrieve); -} - -static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *retrieve_rej = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_REJ; - - /* cause */ - if (retrieve_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &retrieve_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* keypad facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { - dtmf.fields |= MNCC_F_KEYPAD; - gsm48_decode_keypad(&dtmf.keypad, - TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_START_DTMF_IND, &dtmf); -} - -static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; - - /* keypad */ - if (dtmf->fields & MNCC_F_KEYPAD) - gsm48_encode_keypad(msg, dtmf->keypad); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; - - /* cause */ - if (dtmf->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &dtmf->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF STP ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - - return mncc_recvmsg(trans->net, trans, MNCC_STOP_DTMF_IND, &dtmf); -} - -static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - - /* Create a copy of the bearer capability - * in the transaction struct, so we can use - * this information later */ - memcpy(&trans->bearer_cap,&modify.bearer_cap, - sizeof(trans->bearer_cap)); - } - - new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_IND, &modify); -} - -static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY; - - gsm48_start_cc_timer(trans, 0x323, GSM48_T323); - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - - /* Create a copy of the bearer capability - * in the transaction struct, so we can use - * this information later */ - memcpy(&trans->bearer_cap,&modify.bearer_cap, - sizeof(trans->bearer_cap)); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_CNF, &modify); -} - -static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD COMPL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= GSM48_IE_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - - /* Create a copy of the bearer capability - * in the transaction struct, so we can use - * this information later */ - memcpy(&trans->bearer_cap,&modify.bearer_cap, - sizeof(trans->bearer_cap)); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - modify.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&modify.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_REJ, &modify); -} - -static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - /* cause */ - gsm48_encode_cause(msg, 1, &modify->cause); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *notify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC NOT"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_NOTIFY; - - /* notify */ - gsm48_encode_notify(msg, notify->notify); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); -// struct tlv_parsed tp; - struct gsm_mncc notify; - - memset(¬ify, 0, sizeof(struct gsm_mncc)); - notify.callref = trans->callref; -// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); - if (payload_len >= 1) - gsm48_decode_notify(¬ify.notify, gh->data); - - return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, ¬ify); -} - -static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *user = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USR INFO"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_USER_INFO; - - /* user-user */ - if (user->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 1, &user->useruser); - /* more data */ - if (user->more) - gsm48_encode_more(msg); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc user; - - memset(&user, 0, sizeof(struct gsm_mncc)); - user.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - user.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&user.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* more data */ - if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) - user.more = 1; - - return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); -} - -static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref, - int cmd, uint32_t addr, uint16_t port, uint32_t payload_type, - uint32_t payload_msg_type) -{ - uint8_t data[sizeof(struct gsm_mncc)]; - struct gsm_mncc_rtp *rtp; - - memset(&data, 0, sizeof(data)); - rtp = (struct gsm_mncc_rtp *) &data[0]; - - rtp->callref = callref; - rtp->msg_type = cmd; - rtp->ip = addr; - rtp->port = port; - rtp->payload_type = payload_type; - rtp->payload_msg_type = payload_msg_type; - mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data); -} - -static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd) -{ - int msg_type; - - /* FIXME This has to be set to some meaningful value. - * Possible options are: - * GSM_TCHF_FRAME, GSM_TCHF_FRAME_EFR, - * GSM_TCHH_FRAME, GSM_TCH_FRAME_AMR - * (0 if unknown) */ - msg_type = GSM_TCHF_FRAME; - - uint32_t addr = mgcpgw_client_remote_addr_n(net->mgcpgw.client); - uint16_t port = trans->conn->rtp.port_cn; - - /* FIXME: This has to be set to some meaningful value, - * before the MSC-Split, this value was pulled from - * lchan->abis_ip.rtp_payload */ - uint32_t payload_type = 0; - - return mncc_recv_rtp(net, trans->callref, cmd, - addr, - port, - payload_type, - msg_type); -} - -static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd) -{ - return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0); -} - -static int tch_rtp_create(struct gsm_network *net, uint32_t callref) -{ - struct gsm_trans *trans; - int rc; - - /* Find callref */ - trans = trans_find_by_callref(net, callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n"); - mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); - return -EIO; - } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); - if (!trans->conn) { - LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); - mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); - return 0; - } - - trans->conn->mncc_rtp_bridge = 1; - - /* When we call msc_call_assignment() we will trigger, depending - * on the RAN type the call assignment on the A or Iu interface. - * msc_call_assignment() also takes care about sending the CRCX - * command to the MGCP-GW. The CRCX will return the port number, - * where the PBX (e.g. Asterisk) will send its RTP stream to. We - * have to return this port number back to the MNCC by sending - * it back with the TCH_RTP_CREATE message. To make sure that - * this message is sent AFTER the response to CRCX from the - * MGCP-GW has arrived, we need will instruct msc_call_assignment() - * to take care of this by setting trans->tch_rtp_create to true. - * This will make sure that gsm48_tch_rtp_create() (below) is - * called as soon as the local port number has become known. */ - trans->tch_rtp_create = true; - - /* Assign call (if not done yet) */ - if (trans->assignment_done == false) { - rc = msc_call_assignment(trans); - trans->assignment_done = true; - } - else - rc = 0; - - return rc; -} - -/* Trigger TCH_RTP_CREATE acknowledgement */ -int gsm48_tch_rtp_create(struct gsm_trans *trans) -{ - /* This function is called as soon as the port, on which the - * mgcp-gw expects the incoming RTP stream from the remote - * end (e.g. Asterisk) is known. */ - - struct gsm_subscriber_connection *conn = trans->conn; - struct gsm_network *network = conn->network; - - mncc_recv_rtp_sock(network, trans, MNCC_RTP_CREATE); - return 0; -} - -static int tch_rtp_connect(struct gsm_network *net, void *arg) -{ - struct gsm_trans *trans; - struct gsm_mncc_rtp *rtp = arg; - - /* Find callref */ - trans = trans_find_by_callref(net, rtp->callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n"); - mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); - return -EIO; - } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); - if (!trans->conn) { - LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); - mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); - return 0; - } - - msc_call_connect(trans,rtp->port,rtp->ip); - return 0; -} - -static struct downstate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, void *arg); -} downstatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ - MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc_and_assign}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ - MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ - MNCC_SETUP_RSP, gsm48_cc_tx_connect}, - {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ - MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ - MNCC_SETUP_REQ, gsm48_cc_tx_setup}, - {SBIT(GSM_CSTATE_CONNECT_REQUEST), - MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, - /* signalling during call */ - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), - MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, - {ALL_STATES, - MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, - {ALL_STATES, - MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, - {ALL_STATES, - MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, - /* clearing */ - {SBIT(GSM_CSTATE_INITIATED), - MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ - MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - MNCC_REL_REQ, gsm48_cc_tx_release}, -}; - -#define DOWNSLLEN \ - (sizeof(downstatelist) / sizeof(struct downstate)) - - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) -{ - int i, rc = 0; - struct gsm_trans *trans = NULL, *transt; - struct gsm_subscriber_connection *conn = NULL; - struct gsm_mncc *data = arg, rel; - - DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); - - /* handle special messages */ - switch(msg_type) { - case MNCC_BRIDGE: - rc = tch_bridge(net, arg); - if (rc < 0) - disconnect_bridge(net, arg, -rc); - return rc; - case MNCC_RTP_CREATE: - return tch_rtp_create(net, data->callref); - case MNCC_RTP_CONNECT: - return tch_rtp_connect(net, arg); - case MNCC_RTP_FREE: - /* unused right now */ - return -EIO; - - case MNCC_FRAME_DROP: - case MNCC_FRAME_RECV: - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - case GSM_TCHH_FRAME: - case GSM_TCH_FRAME_AMR: - LOGP(DMNCC, LOGL_ERROR, "RTP streams must be handled externally; %s not supported.\n", - get_mncc_name(msg_type)); - return -ENOTSUP; - } - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = data->callref; - - /* Find callref */ - trans = trans_find_by_callref(net, data->callref); - - /* Callref unknown */ - if (!trans) { - struct vlr_subscr *vsub; - - if (msg_type != MNCC_SETUP_REQ) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown callref %d\n", data->called.number, - get_mncc_name(msg_type), data->callref); - /* Invalid call reference */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INVAL_TRANS_ID); - } - if (!data->called.number[0] && !data->imsi[0]) { - DEBUGP(DCC, "(bts - trx - ts - ti) " - "Received '%s' from MNCC with " - "no number or IMSI\n", get_mncc_name(msg_type)); - /* Invalid number */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INV_NR_FORMAT); - } - /* New transaction due to setup, find subscriber */ - if (data->called.number[0]) - vsub = vlr_subscr_find_by_msisdn(net->vlr, - data->called.number); - else - vsub = vlr_subscr_find_by_imsi(net->vlr, data->imsi); - - /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, vsub); - - /* If subscriber is not found */ - if (!vsub) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - /* Unknown subscriber */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_UNASSIGNED_NR); - } - /* If subscriber is not "attached" */ - if (!vsub->lac) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "detached subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - vlr_subscr_put(vsub); - /* Temporarily out of order */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - } - /* Create transaction */ - trans = trans_alloc(net, vsub, GSM48_PDISC_CC, 0xff, data->callref); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - vlr_subscr_put(vsub); - /* Ressource unavailable */ - mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - return -ENOMEM; - } - - /* Find conn */ - conn = connection_for_subscr(vsub); - - /* If subscriber has no conn */ - if (!conn) { - /* find transaction with this subscriber already paging */ - llist_for_each_entry(transt, &net->trans_list, entry) { - /* Transaction of our conn? */ - if (transt == trans || - transt->vsub != vsub) - continue; - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unallocated channel, paging already " - "started for lac %d.\n", - data->called.number, - get_mncc_name(msg_type), vsub->lac); - vlr_subscr_put(vsub); - trans_free(trans); - return 0; - } - /* store setup information until paging succeeds */ - memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); - - /* Request a channel */ - trans->paging_request = subscr_request_conn( - vsub, - setup_trig_pag_evt, - trans, - "MNCC: establish call"); - if (!trans->paging_request) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); - vlr_subscr_put(vsub); - trans_free(trans); - return 0; - } - vlr_subscr_put(vsub); - return 0; - } - - /* Assign conn */ - trans->conn = msc_subscr_conn_get(conn); - vlr_subscr_put(vsub); - } else { - /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans->vsub); - } - - if (trans->conn) - conn = trans->conn; - - /* if paging did not respond yet */ - if (!conn) { - DEBUGP(DCC, "(sub %s) " - "Received '%s' from MNCC in paging state\n", - vlr_subscr_msisdn_or_name(trans->vsub), - get_mncc_name(msg_type)); - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_NORM_CALL_CLEAR); - if (msg_type == MNCC_REL_REQ) - rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - else - rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); - trans->callref = 0; - trans_free(trans); - return rc; - } - - DEBUGP(DCC, "(ti %02x sub %s) " - "Received '%s' from MNCC in state %d (%s)\n", - trans->transaction_id, - vlr_subscr_msisdn_or_name(trans->conn->vsub), - get_mncc_name(msg_type), trans->cc.state, - gsm48_cc_state_name(trans->cc.state)); - - /* Find function for current state and message */ - for (i = 0; i < DOWNSLLEN; i++) - if ((msg_type == downstatelist[i].type) - && ((1 << trans->cc.state) & downstatelist[i].states)) - break; - if (i == DOWNSLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - rc = downstatelist[i].rout(trans, arg); - - return rc; -} - - -static struct datastate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, struct msgb *msg); -} datastatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ - GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ - GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ - GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ - GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, - /* signalling during call */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL), - GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, - {ALL_STATES, - GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, - /* clearing */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ - GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, - {ALL_STATES, /* 5.4.3.4 */ - GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, -}; - -#define DATASLLEN \ - (sizeof(datastatelist) / sizeof(struct datastate)) - -static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh); - struct gsm_trans *trans = NULL; - int i, rc = 0; - - if (msg_type & 0x80) { - DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); - return -EINVAL; - } - - if (!conn->vsub) { - LOGP(DCC, LOGL_ERROR, "Invalid conn: no subscriber\n"); - return -EINVAL; - } - - /* Find transaction */ - trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id); - -#if BEFORE_MSCSPLIT - /* Re-enable this log output once we can obtain this information via - * A-interface, see OS#2391. */ - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Received '%s' from MS in state %d (%s)\n", - conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - transaction_id, vlr_subscr_msisdn_or_name(conn->vsub), - gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, - gsm48_cc_state_name(trans?(trans->cc.state):0)); -#endif - - /* Create transaction */ - if (!trans) { - DEBUGP(DCC, "Unknown transaction ID %x, " - "creating new trans.\n", transaction_id); - /* Create transaction */ - trans = trans_alloc(conn->network, conn->vsub, - GSM48_PDISC_CC, - transaction_id, new_callref++); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - rc = gsm48_tx_simple(conn, - GSM48_PDISC_CC | (transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - return -ENOMEM; - } - /* Assign transaction */ - trans->conn = msc_subscr_conn_get(conn); - cm_service_request_concludes(conn, msg); - } - - /* find function for current state and message */ - for (i = 0; i < DATASLLEN; i++) - if ((msg_type == datastatelist[i].type) - && ((1 << trans->cc.state) & datastatelist[i].states)) - break; - if (i == DATASLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - assert(trans->vsub); - - rc = datastatelist[i].rout(trans, msg); - - msc_subscr_conn_communicating(conn); - return rc; -} - -static bool msg_is_initially_permitted(const struct gsm48_hdr *hdr) -{ - uint8_t pdisc = gsm48_hdr_pdisc(hdr); - uint8_t msg_type = gsm48_hdr_msg_type(hdr); - - switch (pdisc) { - case GSM48_PDISC_MM: - switch (msg_type) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - case GSM48_MT_MM_CM_SERV_REQ: - case GSM48_MT_MM_AUTH_RESP: - case GSM48_MT_MM_AUTH_FAIL: - case GSM48_MT_MM_ID_RESP: - case GSM48_MT_MM_TMSI_REALL_COMPL: - case GSM48_MT_MM_IMSI_DETACH_IND: - return true; - default: - break; - } - break; - case GSM48_PDISC_RR: - switch (msg_type) { - case GSM48_MT_RR_CIPH_M_COMPL: - case GSM48_MT_RR_PAG_RESP: - return true; - default: - break; - } - break; - default: - break; - } - - return false; -} - -void cm_service_request_concludes(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - - /* If a CM Service Request was received before, this is the request the - * conn was opened for. No need to wait for further messages. */ - - if (!conn->received_cm_service_request) - return; - - if (log_check_level(DMM, LOGL_DEBUG)) { - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - - DEBUGP(DMM, "%s: rx msg %s:" - " received_cm_service_request changes to false\n", - vlr_subscr_name(conn->vsub), - gsm48_pdisc_msgtype_name(pdisc, msg_type)); - } - conn->received_cm_service_request = false; -} - - -/* Main entry point for GSM 04.08/44.008 Layer 3 data (e.g. from the BSC). */ -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - int rc = 0; - - OSMO_ASSERT(conn); - OSMO_ASSERT(msg); - - LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message %s (0x%x:0x%x)\n", - gsm48_pdisc_msgtype_name(pdisc, gsm48_hdr_msg_type(gh)), - pdisc, gsm48_hdr_msg_type(gh)); - - if (!msc_subscr_conn_is_accepted(conn) - && !msg_is_initially_permitted(gh)) { - LOGP(DRLL, LOGL_ERROR, - "subscr %s: Message not permitted for initial conn: %s\n", - vlr_subscr_name(conn->vsub), - gsm48_pdisc_msgtype_name(pdisc, gsm48_hdr_msg_type(gh))); - return -EACCES; - } - - if (conn->vsub && conn->vsub->cs.attached_via_ran != conn->via_ran) { - LOGP(DMM, LOGL_ERROR, - "%s: Illegal situation: RAN type mismatch:" - " attached via %s, received message via %s\n", - vlr_subscr_name(conn->vsub), - ran_type_name(conn->vsub->cs.attached_via_ran), - ran_type_name(conn->via_ran)); - return -EACCES; - } - -#if 0 - if (silent_call_reroute(conn, msg)) - return silent_call_rx(conn, msg); -#endif - - switch (pdisc) { - case GSM48_PDISC_CC: - rc = gsm0408_rcv_cc(conn, msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(conn, msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(conn, msg); - break; - case GSM48_PDISC_SMS: - rc = gsm0411_rcv_sms(conn, msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - LOGP(DRLL, LOGL_NOTICE, "Unimplemented " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - rc = -ENOTSUP; - break; - case GSM48_PDISC_NC_SS: - rc = handle_rcv_ussd(conn, msg); - break; - case GSM48_PDISC_TEST: - rc = gsm0414_rcv_test(conn, msg); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "Unknown " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - rc = -EINVAL; - break; - } - - return rc; -} - -/*********************************************************************** - * VLR integration - ***********************************************************************/ - -/* VLR asks us to send an authentication request */ -static int msc_vlr_tx_auth_req(void *msc_conn_ref, struct gsm_auth_tuple *at, - bool send_autn) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return gsm48_tx_mm_auth_req(conn, at->vec.rand, - send_autn? at->vec.autn : NULL, - at->key_seq); -} - -/* VLR asks us to send an authentication reject */ -static int msc_vlr_tx_auth_rej(void *msc_conn_ref) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return gsm48_tx_mm_auth_rej(conn); -} - -/* VLR asks us to transmit an Identity Request of given type */ -static int msc_vlr_tx_id_req(void *msc_conn_ref, uint8_t mi_type) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return mm_tx_identity_req(conn, mi_type); -} - -/* VLR asks us to transmit a Location Update Accept */ -static int msc_vlr_tx_lu_acc(void *msc_conn_ref, uint32_t send_tmsi) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return gsm0408_loc_upd_acc(conn, send_tmsi); -} - -/* VLR asks us to transmit a Location Update Reject */ -static int msc_vlr_tx_lu_rej(void *msc_conn_ref, uint8_t cause) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return gsm0408_loc_upd_rej(conn, cause); -} - -/* VLR asks us to transmit a CM Service Accept */ -static int msc_vlr_tx_cm_serv_acc(void *msc_conn_ref) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return msc_gsm48_tx_mm_serv_ack(conn); -} - -static int msc_vlr_tx_common_id(void *msc_conn_ref) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - return msc_tx_common_id(conn); -} - -/* VLR asks us to transmit a CM Service Reject */ -static int msc_vlr_tx_cm_serv_rej(void *msc_conn_ref, enum vlr_proc_arq_result result) -{ - uint8_t cause; - struct gsm_subscriber_connection *conn = msc_conn_ref; - conn->received_cm_service_request = false; - - switch (result) { - default: - case VLR_PR_ARQ_RES_NONE: - case VLR_PR_ARQ_RES_SYSTEM_FAILURE: - case VLR_PR_ARQ_RES_UNKNOWN_ERROR: - cause = GSM48_REJECT_NETWORK_FAILURE; - break; - case VLR_PR_ARQ_RES_ILLEGAL_SUBSCR: - cause = GSM48_REJECT_LOC_NOT_ALLOWED; - break; - case VLR_PR_ARQ_RES_UNIDENT_SUBSCR: - cause = GSM48_REJECT_INVALID_MANDANTORY_INF; - break; - case VLR_PR_ARQ_RES_ROAMING_NOTALLOWED: - cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; - break; - case VLR_PR_ARQ_RES_ILLEGAL_EQUIP: - cause = GSM48_REJECT_ILLEGAL_MS; - break; - case VLR_PR_ARQ_RES_TIMEOUT: - cause = GSM48_REJECT_CONGESTION; - break; - }; - - return msc_gsm48_tx_mm_serv_rej(conn, cause); -} - -/* VLR asks us to start using ciphering */ -static int msc_vlr_set_ciph_mode(void *msc_conn_ref, - enum vlr_ciph ciph, - bool retrieve_imeisv) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - struct vlr_subscr *vsub; - struct gsm_auth_tuple *tuple; - - if (!conn || !conn->vsub) { - LOGP(DMM, LOGL_ERROR, "Cannot send Ciphering Mode Command to" - " NULL conn/subscriber"); - return -EINVAL; - } - - vsub = conn->vsub; - tuple = vsub->last_tuple; - - if (!tuple) { - LOGP(DMM, LOGL_ERROR, "subscr %s: Cannot send Ciphering Mode" - " Command: no auth tuple available\n", - vlr_subscr_name(vsub)); - return -EINVAL; - } - - switch (conn->via_ran) { - case RAN_GERAN_A: - DEBUGP(DMM, "-> CIPHER MODE COMMAND %s\n", - vlr_subscr_name(conn->vsub)); - return a_iface_tx_cipher_mode(conn, ciph, tuple->vec.kc, 8, - retrieve_imeisv); - case RAN_UTRAN_IU: -#ifdef BUILD_IU - DEBUGP(DMM, "-> SECURITY MODE CONTROL %s\n", - vlr_subscr_name(conn->vsub)); - return ranap_iu_tx_sec_mode_cmd(conn->iu.ue_ctx, &tuple->vec, 0, 1); -#else - LOGP(DMM, LOGL_ERROR, "Cannot send Security Mode Control over RAN_UTRAN_IU," - " built without Iu support\n"); - return -ENOTSUP; -#endif - - default: - break; - } - LOGP(DMM, LOGL_ERROR, - "%s: cannot start ciphering, unknown RAN type %d\n", - vlr_subscr_name(conn->vsub), conn->via_ran); - return -ENOTSUP; -} - -void msc_rx_sec_mode_compl(struct gsm_subscriber_connection *conn) -{ - struct vlr_ciph_result vlr_res = {}; - - if (!conn || !conn->vsub) { - LOGP(DMM, LOGL_ERROR, - "Rx Security Mode Complete for invalid conn\n"); - return; - } - - DEBUGP(DMM, "<- SECURITY MODE COMPLETE %s\n", - vlr_subscr_name(conn->vsub)); - - vlr_res.cause = VLR_CIPH_COMPL; - vlr_subscr_rx_ciph_res(conn->vsub, &vlr_res); -} - -/* VLR informs us that the subscriber data has somehow been modified */ -static void msc_vlr_subscr_update(struct vlr_subscr *subscr) -{ - /* FIXME */ -} - -/* VLR informs us that the subscriber has been associated with a conn */ -static void msc_vlr_subscr_assoc(void *msc_conn_ref, - struct vlr_subscr *vsub) -{ - struct gsm_subscriber_connection *conn = msc_conn_ref; - OSMO_ASSERT(!conn->vsub); - conn->vsub = vlr_subscr_get(vsub); - conn->vsub->cs.attached_via_ran = conn->via_ran; -} - -/* operations that we need to implement for libvlr */ -static const struct vlr_ops msc_vlr_ops = { - .tx_auth_req = msc_vlr_tx_auth_req, - .tx_auth_rej = msc_vlr_tx_auth_rej, - .tx_id_req = msc_vlr_tx_id_req, - .tx_lu_acc = msc_vlr_tx_lu_acc, - .tx_lu_rej = msc_vlr_tx_lu_rej, - .tx_cm_serv_acc = msc_vlr_tx_cm_serv_acc, - .tx_cm_serv_rej = msc_vlr_tx_cm_serv_rej, - .set_ciph_mode = msc_vlr_set_ciph_mode, - .tx_common_id = msc_vlr_tx_common_id, - .subscr_update = msc_vlr_subscr_update, - .subscr_assoc = msc_vlr_subscr_assoc, -}; - -/* Allocate net->vlr so that the VTY may configure the VLR's data structures */ -int msc_vlr_alloc(struct gsm_network *net) -{ - net->vlr = vlr_alloc(net, &msc_vlr_ops); - if (!net->vlr) - return -ENOMEM; - net->vlr->user_ctx = net; - return 0; -} - -/* Launch the VLR, i.e. its GSUP connection */ -int msc_vlr_start(struct gsm_network *net) -{ - OSMO_ASSERT(net->vlr); - return vlr_start("MSC", net->vlr, net->gsup_server_addr_str, - net->gsup_server_port); -} diff --git a/src/libmsc/gsm_04_11.c b/src/libmsc/gsm_04_11.c deleted file mode 100644 index 574fe281d..000000000 --- a/src/libmsc/gsm_04_11.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* Point-to-Point (PP) Short Message Service (SMS) - * Support on Mobile Radio Interface - * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ - -/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de> - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2010-2012 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 by On-Waves - * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> - * - * 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <netinet/in.h> - -#include "bscconfig.h" - -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/gsm0411_utils.h> -#include <osmocom/gsm/protocol/gsm_04_11.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/db.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/signal.h> -#include <openbsc/db.h> -#include <openbsc/transaction.h> -#include <openbsc/paging.h> -#include <openbsc/bsc_rll.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/msc_ifaces.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/vlr.h> - -#ifdef BUILD_SMPP -#include "smpp_smsc.h" -#endif - -void *tall_gsms_ctx; -static uint32_t new_callref = 0x40000001; - - -struct gsm_sms *sms_alloc(void) -{ - return talloc_zero(tall_gsms_ctx, struct gsm_sms); -} - -void sms_free(struct gsm_sms *sms) -{ - /* drop references to subscriber structure */ - if (sms->receiver) - vlr_subscr_put(sms->receiver); -#ifdef BUILD_SMPP - if (sms->smpp.esme) - smpp_esme_put(sms->smpp.esme); -#endif - - talloc_free(sms); -} - -struct gsm_sms *sms_from_text(struct vlr_subscr *receiver, - struct vlr_subscr *sender, - int dcs, const char *text) -{ - struct gsm_sms *sms = sms_alloc(); - - if (!sms) - return NULL; - - sms->receiver = vlr_subscr_get(receiver); - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - - osmo_strlcpy(sms->src.addr, sender->msisdn, sizeof(sms->src.addr)); - sms->reply_path_req = 0; - sms->status_rep_req = 0; - sms->ud_hdr_ind = 0; - sms->protocol_id = 0; /* implicit */ - sms->data_coding_scheme = dcs; - osmo_strlcpy(sms->dst.addr, receiver->msisdn, sizeof(sms->dst.addr)); - /* Generate user_data */ - sms->user_data_len = gsm_7bit_encode_n(sms->user_data, sizeof(sms->user_data), - sms->text, NULL); - - return sms; -} - - -static void send_signal(int sig_no, - struct gsm_trans *trans, - struct gsm_sms *sms, - int paging_result) -{ - struct sms_signal_data sig; - sig.trans = trans; - sig.sms = sms; - sig.paging_result = paging_result; - osmo_signal_dispatch(SS_SMS, sig_no, &sig); -} - -static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - DEBUGP(DLSMS, "GSM4.11 TX %s\n", osmo_hexdump(msg->data, msg->len)); - msg->l3h = msg->data; - return msc_tx_dtap(conn, msg); -} - -/* Prefix msg with a 04.08/04.11 CP header */ -static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, - uint8_t msg_type) -{ - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - /* Outgoing needs the highest bit set */ - gh->proto_discr = trans->protocol | (trans->transaction_id<<4); - gh->msg_type = msg_type; - - DEBUGP(DLSMS, "sending CP message (trans=%x)\n", trans->transaction_id); - - return gsm411_sendmsg(trans->conn, msg); -} - -/* mm_send: receive MMCCSMS sap message from SMC */ -static int gsm411_mm_send(struct gsm411_smc_inst *inst, int msg_type, - struct msgb *msg, int cp_msg_type) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smc_inst); - int rc = 0; - - switch (msg_type) { - case GSM411_MMSMS_EST_REQ: - /* recycle msg */ - rc = gsm411_smc_recv(inst, GSM411_MMSMS_EST_CNF, msg, 0); - msgb_free(msg); /* upper layer does not free msg */ - break; - case GSM411_MMSMS_DATA_REQ: - rc = gsm411_cp_sendmsg(msg, trans, cp_msg_type); - break; - case GSM411_MMSMS_REL_REQ: - DEBUGP(DLSMS, "Got MMSMS_REL_REQ, destroying transaction.\n"); - msgb_free(msg); - trans_free(trans); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled MMCCSMS msg 0x%x\n", msg_type); - msgb_free(msg); - rc = -EINVAL; - } - - return rc; -} - -/* mm_send: receive MNCCSMS sap message from SMR */ -int gsm411_mn_send(struct gsm411_smr_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smr_inst); - - /* forward to SMC */ - return gsm411_smc_send(&trans->sms.smc_inst, msg_type, msg); -} - -static int gsm340_rx_sms_submit(struct gsm_sms *gsms) -{ - if (db_sms_store(gsms) != 0) { - LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - /* dispatch a signal to tell higher level about it */ - send_signal(S_SMS_SUBMITTED, NULL, gsms, 0); - - return 0; -} - -/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ -static int gsm340_gen_oa_sub(uint8_t *oa, unsigned int oa_len, - const struct gsm_sms_addr *src) -{ - /* network specific, private numbering plan */ - return gsm340_gen_oa(oa, oa_len, src->ton, src->npi, src->addr); -} - -/* generate a msgb containing an 03.40 9.2.2.1 SMS-DELIVER TPDU derived from - * struct gsm_sms, returns total size of TPDU */ -static int gsm340_gen_sms_deliver_tpdu(struct msgb *msg, struct gsm_sms *sms) -{ - uint8_t *smsp; - uint8_t oa[12]; /* max len per 03.40 */ - uint8_t octet_len; - unsigned int old_msg_len = msg->len; - int oa_len; - - /* generate first octet with masked bits */ - smsp = msgb_put(msg, 1); - /* TP-MTI (message type indicator) */ - *smsp = GSM340_SMS_DELIVER_SC2MS; - /* TP-MMS (more messages to send) */ - if (0 /* FIXME */) - *smsp |= 0x04; - /* TP-SRI(deliver)/SRR(submit) */ - if (sms->status_rep_req) - *smsp |= 0x20; - /* TP-UDHI (indicating TP-UD contains a header) */ - if (sms->ud_hdr_ind) - *smsp |= 0x40; - - /* generate originator address */ - oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src); - if (oa_len < 0) - return -ENOSPC; - - smsp = msgb_put(msg, oa_len); - memcpy(smsp, oa, oa_len); - - /* generate TP-PID */ - smsp = msgb_put(msg, 1); - *smsp = sms->protocol_id; - - /* generate TP-DCS */ - smsp = msgb_put(msg, 1); - *smsp = sms->data_coding_scheme; - - /* generate TP-SCTS */ - smsp = msgb_put(msg, 7); - gsm340_gen_scts(smsp, time(NULL)); - - /* generate TP-UDL */ - smsp = msgb_put(msg, 1); - *smsp = sms->user_data_len; - - /* generate TP-UD */ - switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) { - case DCS_7BIT_DEFAULT: - octet_len = sms->user_data_len*7/8; - if (sms->user_data_len*7%8 != 0) - octet_len++; - /* Warning, user_data_len indicates the amount of septets - * (characters), we need amount of octets occupied */ - smsp = msgb_put(msg, octet_len); - memcpy(smsp, sms->user_data, octet_len); - break; - case DCS_UCS2: - case DCS_8BIT_DATA: - smsp = msgb_put(msg, sms->user_data_len); - memcpy(smsp, sms->user_data, sms->user_data_len); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", - sms->data_coding_scheme); - break; - } - - return msg->len - old_msg_len; -} - -/* As defined by GSM 03.40, Section 9.2.2.3. */ -static int gsm340_gen_sms_status_report_tpdu(struct msgb *msg, - struct gsm_sms *sms) -{ - unsigned int old_msg_len = msg->len; - uint8_t oa[12]; /* max len per 03.40 */ - uint8_t *smsp; - int oa_len; - - /* generate first octet with masked bits */ - smsp = msgb_put(msg, 1); - /* TP-MTI (message type indicator) */ - *smsp = GSM340_SMS_STATUS_REP_SC2MS; - /* TP-MMS (more messages to send) */ - if (0 /* FIXME */) - *smsp |= 0x04; - /* TP-MR (message reference) */ - smsp = msgb_put(msg, 1); - *smsp = sms->msg_ref; - - /* generate recipient address */ - oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src); - if (oa_len < 0) - return -ENOSPC; - - smsp = msgb_put(msg, oa_len); - memcpy(smsp, oa, oa_len); - - /* generate TP-SCTS (Service centre timestamp) */ - smsp = msgb_put(msg, 7); - gsm340_gen_scts(smsp, sms->created); - - /* generate TP-DT (Discharge time, in TP-SCTS format). */ - smsp = msgb_put(msg, 7); - gsm340_gen_scts(smsp, sms->created); - - /* TP-ST (status) */ - smsp = msgb_put(msg, 1); - /* From GSM 03.40, Section 9.2.3.15, 0x00 means OK. */ - *smsp = 0x00; - - LOGP(DLSMS, LOGL_INFO, "sending status report for SMS reference %x\n", - sms->msg_ref); - - return msg->len - old_msg_len; -} - -static int sms_route_mt_sms(struct gsm_subscriber_connection *conn, - struct gsm_sms *gsms) -{ - int rc; - -#ifdef BUILD_SMPP - int smpp_first = smpp_route_smpp_first(gsms, conn); - - /* - * Route through SMPP first before going to the local database. In case - * of a unroutable message and no local subscriber, SMPP will be tried - * twice. In case of an unknown subscriber continue with the normal - * delivery of the SMS. - */ - if (smpp_first) { - rc = smpp_try_deliver(gsms, conn); - if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) - /* unknown subscriber, try local */ - goto try_local; - if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.", - vlr_subscr_name(conn->vsub), rc); - rc = GSM411_RP_CAUSE_MO_TEMP_FAIL; - /* rc will be logged by gsm411_send_rp_error() */ - rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[ - MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]); - } - return rc; - } - -try_local: -#endif - - /* determine gsms->receiver based on dialled number */ - gsms->receiver = vlr_subscr_find_by_msisdn(conn->network->vlr, - gsms->dst.addr); - if (!gsms->receiver) { -#ifdef BUILD_SMPP - /* Avoid a second look-up */ - if (smpp_first) { - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); - return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - } - - rc = smpp_try_deliver(gsms, conn); - if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) { - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); - } else if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.", - vlr_subscr_name(conn->vsub), rc); - rc = GSM411_RP_CAUSE_MO_TEMP_FAIL; - /* rc will be logged by gsm411_send_rp_error() */ - rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[ - MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]); - } -#else - rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); -#endif - } - - return rc; -} - - -/* process an incoming TPDU (called from RP-DATA) - * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ -static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg, - uint32_t gsm411_msg_ref) -{ - struct gsm_subscriber_connection *conn = trans->conn; - uint8_t *smsp = msgb_sms(msg); - struct gsm_sms *gsms; - unsigned int sms_alphabet; - uint8_t sms_mti, sms_vpf; - uint8_t *sms_vp; - uint8_t da_len_bytes; - uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ - int rc = 0; - - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED]); - - gsms = sms_alloc(); - if (!gsms) - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - /* invert those fields where 0 means active/present */ - sms_mti = *smsp & 0x03; - sms_vpf = (*smsp & 0x18) >> 3; - gsms->status_rep_req = (*smsp & 0x20) >> 5; - gsms->ud_hdr_ind = (*smsp & 0x40); - /* - * Not evaluating MMS (More Messages to Send) because the - * lchan stays open anyway. - * Not evaluating RP (Reply Path) because we're not aware of its - * benefits. - */ - - smsp++; - gsms->msg_ref = *smsp++; - - gsms->gsm411.transaction_id = trans->transaction_id; - gsms->gsm411.msg_ref = gsm411_msg_ref; - - /* length in bytes of the destination address */ - da_len_bytes = 2 + *smsp/2 + *smsp%2; - if (da_len_bytes > 12) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } else if (da_len_bytes < 4) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } - memset(address_lv, 0, sizeof(address_lv)); - memcpy(address_lv, smsp, da_len_bytes); - /* mangle first byte to reflect length in bytes, not digits */ - address_lv[0] = da_len_bytes - 1; - - gsms->dst.ton = (address_lv[1] >> 4) & 7; - gsms->dst.npi = address_lv[1] & 0xF; - /* convert to real number */ - gsm48_decode_bcd_number(gsms->dst.addr, - sizeof(gsms->dst.addr), address_lv, 1); - smsp += da_len_bytes; - - gsms->protocol_id = *smsp++; - gsms->data_coding_scheme = *smsp++; - - sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); - if (sms_alphabet == 0xffffffff) { - rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - goto out; - } - - switch (sms_vpf) { - case GSM340_TP_VPF_RELATIVE: - sms_vp = smsp++; - break; - case GSM340_TP_VPF_ABSOLUTE: - case GSM340_TP_VPF_ENHANCED: - sms_vp = smsp; - /* the additional functionality indicator... */ - if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) - smsp++; - smsp += 7; - break; - case GSM340_TP_VPF_NONE: - sms_vp = 0; - break; - default: - LOGP(DLSMS, LOGL_NOTICE, - "SMS Validity period not implemented: 0x%02x\n", sms_vpf); - rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - goto out; - } - gsms->user_data_len = *smsp++; - if (gsms->user_data_len) { - memcpy(gsms->user_data, smsp, gsms->user_data_len); - - switch (sms_alphabet) { - case DCS_7BIT_DEFAULT: - gsm_7bit_decode_n(gsms->text, sizeof(gsms->text), smsp, - gsms->user_data_len); - break; - case DCS_8BIT_DATA: - case DCS_UCS2: - case DCS_NONE: - break; - } - } - - osmo_strlcpy(gsms->src.addr, conn->vsub->msisdn, sizeof(gsms->src.addr)); - - LOGP(DLSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, " - "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " - "UserDataLength: 0x%02x, UserData: \"%s\"\n", - vlr_subscr_name(conn->vsub), sms_mti, sms_vpf, gsms->msg_ref, - gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr, - gsms->user_data_len, - sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : - osmo_hexdump(gsms->user_data, gsms->user_data_len)); - - gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp); - - /* FIXME: This looks very wrong */ - send_signal(0, NULL, gsms, 0); - - rc = sms_route_mt_sms(conn, gsms); - - /* This SMS got routed through SMPP or no receiver exists. */ - if (!gsms->receiver) - return rc; - - switch (sms_mti) { - case GSM340_SMS_SUBMIT_MS2SC: - /* MS is submitting a SMS */ - rc = gsm340_rx_sms_submit(gsms); - break; - case GSM340_SMS_COMMAND_MS2SC: - case GSM340_SMS_DELIVER_REP_MS2SC: - LOGP(DLSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); - rc = GSM411_RP_CAUSE_IE_NOTEXIST; - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti); - rc = GSM411_RP_CAUSE_IE_NOTEXIST; - break; - } -out: - sms_free(gsms); - - return rc; -} - -/* Prefix msg with a RP-DATA header and send as SMR DATA */ -static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, - uint8_t rp_msg_type, uint8_t rp_msg_ref, - int rl_msg_type) -{ - struct gsm411_rp_hdr *rp; - uint8_t len = msg->len; - - /* GSM 04.11 RP-DATA header */ - rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); - rp->len = len + 2; - rp->msg_type = rp_msg_type; - rp->msg_ref = rp_msg_ref; - - return gsm411_smr_send(inst, rl_msg_type, msg); -} - -int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - DEBUGP(DLSMS, "TX: SMS RP ACK\n"); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ACK_MT, - msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref, - uint8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - msgb_tv_put(msg, 1, cause); - - LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_ERROR_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -/* Receive a 04.11 TPDU inside RP-DATA / user data */ -static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph, - uint8_t src_len, uint8_t *src, - uint8_t dst_len, uint8_t *dst, - uint8_t tpdu_len, uint8_t *tpdu) -{ - int rc = 0; - - if (src_len && src) - LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); - - if (!dst_len || !dst || !tpdu_len || !tpdu) { - LOGP(DLSMS, LOGL_ERROR, - "RP-DATA (MO) without DST or TPDU ?!?\n"); - gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_INV_MAND_INF); - return -EIO; - } - msg->l4h = tpdu; - - DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len)); - - rc = gsm340_rx_tpdu(trans, msg, rph->msg_ref); - if (rc == 0) - return gsm411_send_rp_ack(trans, rph->msg_ref); - else if (rc > 0) - return gsm411_send_rp_error(trans, rph->msg_ref, rc); - else - return rc; -} - -/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ -static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - uint8_t src_len, dst_len, rpud_len; - uint8_t *src = NULL, *dst = NULL , *rp_ud = NULL; - - /* in the MO case, this should always be zero length */ - src_len = rph->data[0]; - if (src_len) - src = &rph->data[1]; - - dst_len = rph->data[1+src_len]; - if (dst_len) - dst = &rph->data[1+src_len+1]; - - rpud_len = rph->data[1+src_len+1+dst_len]; - if (rpud_len) - rp_ud = &rph->data[1+src_len+1+dst_len+1]; - - DEBUGP(DLSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", - src_len, dst_len, rpud_len); - return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, - rpud_len, rp_ud); -} - -static struct gsm_sms *sms_report_alloc(struct gsm_sms *sms) -{ - struct gsm_sms *sms_report; - int len; - - sms_report = sms_alloc(); - OSMO_ASSERT(sms_report); - - sms_report->msg_ref = sms->msg_ref; - sms_report->protocol_id = sms->protocol_id; - sms_report->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; - - /* Invert address to send status report back to origin. */ - sms_report->src = sms->dst; - sms_report->dst = sms->src; - - /* As specified by Appendix B. Delivery Receipt Format. - * TODO: Many fields in this string are just set with dummy values, - * revisit this. - */ - len = snprintf((char *)sms_report->user_data, - sizeof(sms_report->user_data), - "id:%.08llu sub:000 dlvrd:000 submit date:YYMMDDhhmm done date:YYMMDDhhmm stat:DELIVRD err:000 text:%.20s", - sms->id, sms->text); - sms_report->user_data_len = len; - LOGP(DLSMS, LOGL_NOTICE, "%s\n", sms_report->user_data); - - /* This represents a sms report. */ - sms_report->is_report = true; - - return sms_report; -} - -static void sms_status_report(struct gsm_sms *gsms, - struct gsm_subscriber_connection *conn) -{ - struct gsm_sms *sms_report; - int rc; - - sms_report = sms_report_alloc(gsms); - - rc = sms_route_mt_sms(conn, sms_report); - if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, - "Failed to send status report! err=%d\n", rc); - } - - /* No route via SMPP, send the GSM 03.40 status-report now. */ - if (gsms->receiver) - gsm340_rx_sms_submit(sms_report); - - LOGP(DLSMS, LOGL_NOTICE, "Status report has been sent\n"); - - sms_free(sms_report); -} - -/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ -static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_sms *sms = trans->sms.sms; - - /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it - * successfully received a SMS. We can now safely mark it as - * transmitted */ - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); - } - - /* mark this SMS as sent in database */ - db_sms_mark_delivered(sms); - - send_signal(S_SMS_DELIVERED, trans, sms, 0); - - if (sms->status_rep_req) - sms_status_report(sms, trans->conn); - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_network *net = trans->conn->network; - struct gsm_sms *sms = trans->sms.sms; - uint8_t cause_len = rph->data[0]; - uint8_t cause = rph->data[1]; - - /* Error in response to MT RP_DATA, i.e. the MS did not - * successfully receive the SMS. We need to investigate - * the cause and take action depending on it */ - - LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", - vlr_subscr_name(trans->conn->vsub), cause_len, cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, - "RX RP-ERR, but no sms in transaction?!?\n"); - return -EINVAL; -#if 0 - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); -#endif - } - - if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { - /* MS has not enough memory to store the message. We need - * to store this in our database and wait for a SMMA message */ - /* FIXME */ - send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0); - rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM]); - } else { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_OTHER]); - } - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - int rc; - - rc = gsm411_send_rp_ack(trans, rph->msg_ref); - - /* MS tells us that it has memory for more SMS, we need - * to check if we have any pending messages for it and then - * transfer those */ - send_signal(S_SMS_SMMA, trans, NULL, 0); - - return rc; -} - -/* receive RL DATA */ -static int gsm411_rx_rl_data(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_DATA_MO: - DEBUGP(DLSMS, "RX SMS RP-DATA (MO)\n"); - rc = gsm411_rx_rp_data(msg, trans, rp_data); - break; - case GSM411_MT_RP_SMMA_MO: - DEBUGP(DLSMS, "RX SMS RP-SMMA\n"); - rc = gsm411_rx_rp_smma(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive RL REPORT */ -static int gsm411_rx_rl_report(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_ACK_MO: - DEBUGP(DLSMS, "RX SMS RP-ACK (MO)\n"); - rc = gsm411_rx_rp_ack(msg, trans, rp_data); - break; - case GSM411_MT_RP_ERROR_MO: - DEBUGP(DLSMS, "RX SMS RP-ERROR (MO)\n"); - rc = gsm411_rx_rp_error(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive SM-RL sap message from SMR - * NOTE: Message is freed by sender - */ -int gsm411_rl_recv(struct gsm411_smr_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smr_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_SM_RL_DATA_IND: - rc = gsm411_rx_rl_data(msg, gh, trans); - break; - case GSM411_SM_RL_REPORT_IND: - if (gh) - rc = gsm411_rx_rl_report(msg, gh, trans); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled SM-RL message 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* receive MNCCSMS sap message from SMC - * NOTE: Message is freed by sender - */ -static int gsm411_mn_recv(struct gsm411_smc_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smc_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_MNSMS_EST_IND: - case GSM411_MNSMS_DATA_IND: - DEBUGP(DLSMS, "MNSMS-DATA/EST-IND\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - case GSM411_MNSMS_ERROR_IND: - if (gh) - DEBUGP(DLSMS, "MNSMS-ERROR-IND, cause %d (%s)\n", - gh->data[0], - get_value_string(gsm411_cp_cause_strs, - gh->data[0])); - else - DEBUGP(DLSMS, "MNSMS-ERROR-IND, no cause\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled MNCCSMS msg 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ -int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gh->msg_type; - uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh); - struct gsm_trans *trans; - int new_trans = 0; - int rc = 0; - - if (!conn->vsub) - return -EIO; - /* FIXME: send some error message */ - - DEBUGP(DLSMS, "receiving data (trans_id=%x, msg_type=%s)\n", transaction_id, - gsm48_pdisc_msgtype_name(gsm48_hdr_pdisc(gh), gsm48_hdr_msg_type(gh))); - - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, transaction_id); - - /* - * A transaction we created but don't know about? - */ - if (!trans && (transaction_id & 0x8) == 0) { - LOGP(DLSMS, LOGL_ERROR, "trans_id=%x allocated by us but known " - "to us anymore. We are ignoring it, maybe a CP-ERROR " - "from a MS?\n", - transaction_id); - return -EINVAL; - } - - if (!trans) { - DEBUGP(DLSMS, " -> (new transaction)\n"); - trans = trans_alloc(conn->network, conn->vsub, - GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - DEBUGP(DLSMS, " -> No memory for trans\n"); - /* FIXME: send some error message */ - return -ENOMEM; - } - gsm411_smc_init(&trans->sms.smc_inst, 0, 1, - gsm411_mn_recv, gsm411_mm_send); - gsm411_smr_init(&trans->sms.smr_inst, 0, 1, - gsm411_rl_recv, gsm411_mn_send); - - trans->conn = msc_subscr_conn_get(conn); - - new_trans = 1; - cm_service_request_concludes(conn, msg); - } - - /* 5.4: For MO, if a CP-DATA is received for a new - * transaction, equals reception of an implicit - * last CP-ACK for previous transaction */ - if (trans->sms.smc_inst.cp_state == GSM411_CPS_IDLE - && msg_type == GSM411_MT_CP_DATA) { - int i; - struct gsm_trans *ptrans; - - /* Scan through all remote initiated transactions */ - for (i=8; i<15; i++) { - if (i == transaction_id) - continue; - - ptrans = trans_find_by_id(conn, GSM48_PDISC_SMS, i); - if (!ptrans) - continue; - - DEBUGP(DLSMS, "Implicit CP-ACK for trans_id=%x\n", i); - - /* Finish it for good */ - trans_free(ptrans); - } - } - - msc_subscr_conn_communicating(conn); - - gsm411_smc_recv(&trans->sms.smc_inst, - (new_trans) ? GSM411_MMSMS_EST_IND : GSM411_MMSMS_DATA_IND, - msg, msg_type); - - return rc; -} - -/* Take a SMS in gsm_sms structure and send it through an already - * existing conn. We also assume that the caller ensured this conn already - * has a SAPI3 RLL connection! */ -int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms) -{ - struct msgb *msg = gsm411_msgb_alloc(); - struct gsm_trans *trans; - uint8_t *data, *rp_ud_len; - uint8_t msg_ref = sms_next_rp_msg_ref(&conn->next_rp_ref); - int transaction_id; - int rc; - - transaction_id = - trans_assign_trans_id(conn->network, conn->vsub, - GSM48_PDISC_SMS, 0); - if (transaction_id == -1) { - LOGP(DLSMS, LOGL_ERROR, "No available transaction ids\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - msgb_free(msg); - return -EBUSY; - } - - DEBUGP(DLSMS, "%s()\n", __func__); - - /* FIXME: allocate transaction with message reference */ - trans = trans_alloc(conn->network, conn->vsub, - GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - msgb_free(msg); - /* FIXME: send some error message */ - return -ENOMEM; - } - gsm411_smc_init(&trans->sms.smc_inst, sms->id, 1, - gsm411_mn_recv, gsm411_mm_send); - gsm411_smr_init(&trans->sms.smr_inst, sms->id, 1, - gsm411_rl_recv, gsm411_mn_send); - trans->sms.sms = sms; - - trans->conn = msc_subscr_conn_get(conn); - - /* Hardcode SMSC Originating Address for now */ - data = (uint8_t *)msgb_put(msg, 8); - data[0] = 0x07; /* originator length == 7 */ - data[1] = 0x91; /* type of number: international, ISDN */ - data[2] = 0x44; /* 447785016005 */ - data[3] = 0x77; - data[4] = 0x58; - data[5] = 0x10; - data[6] = 0x06; - data[7] = 0x50; - - /* Hardcoded Destination Address */ - data = (uint8_t *)msgb_put(msg, 1); - data[0] = 0; /* destination length == 0 */ - - /* obtain a pointer for the rp_ud_len, so we can fill it later */ - rp_ud_len = (uint8_t *)msgb_put(msg, 1); - - if (sms->is_report) { - /* generate the 03.40 SMS-STATUS-REPORT TPDU */ - rc = gsm340_gen_sms_status_report_tpdu(msg, sms); - } else { - /* generate the 03.40 SMS-DELIVER TPDU */ - rc = gsm340_gen_sms_deliver_tpdu(msg, sms); - } - if (rc < 0) { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - sms_free(sms); - trans->sms.sms = NULL; - trans_free(trans); - msgb_free(msg); - return rc; - } - - *rp_ud_len = rc; - - DEBUGP(DLSMS, "TX: SMS DELIVER\n"); - - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_DELIVERED]); - db_sms_inc_deliver_attempts(trans->sms.sms); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_DATA_MT, msg_ref, GSM411_SM_RL_DATA_REQ); -} - -/* paging callback. Here we get called if paging a subscriber has - * succeeded or failed. */ -static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_sms) -{ - struct gsm_subscriber_connection *conn = _conn; - struct gsm_sms *sms = _sms; - int rc = 0; - - DEBUGP(DLSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," - "conn=%p, sms=%p/id: %llu)\n", hooknum, event, msg, conn, sms, sms->id); - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - gsm411_send_sms(conn, sms); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, event); - sms_free(sms); - rc = -ETIMEDOUT; - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled paging event: %d\n", event); - } - - return rc; -} - -/* high-level function to send a SMS to a given subscriber. The function - * will take care of paging the subscriber, establishing the RLL SAPI3 - * connection, etc. */ -int gsm411_send_sms_subscr(struct vlr_subscr *vsub, - struct gsm_sms *sms) -{ - struct gsm_subscriber_connection *conn; - void *res; - - /* check if we already have an open conn to the subscriber. - * if yes, send the SMS this way */ - conn = connection_for_subscr(vsub); - if (conn) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS via already open connection %p to %s\n", - conn, vlr_subscr_name(vsub)); - return gsm411_send_sms(conn, sms); - } - - /* if not, we have to start paging */ - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n", - vlr_subscr_name(vsub)); - res = subscr_request_conn(vsub, paging_cb_send_sms, sms, "send SMS"); - if (!res) { - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY); - sms_free(sms); - } - return 0; -} - -void _gsm411_sms_trans_free(struct gsm_trans *trans) -{ - /* cleanup SMS instance */ - gsm411_smr_clear(&trans->sms.smr_inst); - trans->sms.smr_inst.rl_recv = NULL; - trans->sms.smr_inst.mn_send = NULL; - - gsm411_smc_clear(&trans->sms.smc_inst); - trans->sms.smc_inst.mn_recv = NULL; - trans->sms.smc_inst.mm_send = NULL; - - if (trans->sms.sms) { - LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n"); - send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0); - sms_free(trans->sms.sms); - trans->sms.sms = NULL; - } -} - -/* Process incoming SAPI N-REJECT from BSC */ -void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net; - struct gsm_trans *trans, *tmp; - - net = conn->network; - - llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) { - struct gsm_sms *sms; - - if (trans->conn != conn) - continue; - if (trans->protocol != GSM48_PDISC_SMS) - continue; - - sms = trans->sms.sms; - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, "SAPI Reject but no SMS.\n"); - continue; - } - - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - sms_free(sms); - trans->sms.sms = NULL; - trans_free(trans); - } -} - diff --git a/src/libmsc/gsm_04_80.c b/src/libmsc/gsm_04_80.c deleted file mode 100644 index bec1d26f4..000000000 --- a/src/libmsc/gsm_04_80.c +++ /dev/null @@ -1,155 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009 by Mike Haben <michael.haben@btinternet.com> - * - * 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_04_80.h> -#include <openbsc/msc_ifaces.h> - -#include <osmocom/gsm/gsm0480.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/tlv.h> - -static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) -{ - uint8_t *data = msgb_push(msgb, 2); - - data[0] = tag; - data[1] = msgb->len - 2; - return data; -} - -static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, - uint8_t value) -{ - uint8_t *data = msgb_push(msgb, 3); - - data[0] = tag; - data[1] = 1; - data[2] = value; - return data; -} - - -/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, const char *response_text, - const struct ss_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP"); - struct gsm48_hdr *gh; - uint8_t *ptr8; - int response_len; - - /* First put the payload text into the message */ - ptr8 = msgb_put(msg, 0); - gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len); - msgb_put(msg, response_len); - - /* Then wrap it as an Octet String */ - msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); - - /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); - - /* Then wrap these as a Sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, - GSM0480_OP_CODE_PROCESS_USS_REQ); - - /* Wrap the operation code and IA5 string as a sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Return Result component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id - | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return msc_tx_dtap(conn, msg); -} - -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - const struct ss_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ"); - struct gsm48_hdr *gh; - - /* First insert the problem code */ - msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, - GSM_0480_GEN_PROB_CODE_UNRECOGNISED); - - /* Before it insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Reject component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return msc_tx_dtap(conn, msg); -} - -int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) -{ - struct msgb *msg = gsm0480_create_ussd_notify(level, text); - if (!msg) - return -1; - return msc_tx_dtap(conn, msg); -} - -int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm0480_create_ussd_release_complete(); - if (!msg) - return -1; - return msc_tx_dtap(conn, msg); -} diff --git a/src/libmsc/gsm_subscriber.c b/src/libmsc/gsm_subscriber.c deleted file mode 100644 index 09540c16c..000000000 --- a/src/libmsc/gsm_subscriber.c +++ /dev/null @@ -1,190 +0,0 @@ -/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ - -/* (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2009,2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include "../../bscconfig.h" - -#include <unistd.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <assert.h> -#include <time.h> -#include <stdbool.h> - -#include <osmocom/core/talloc.h> - -#include <osmocom/vty/vty.h> - -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -#else -#include <openbsc/iu_dummy.h> -#endif - -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/debug.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <openbsc/db.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/vlr.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/msc_ifaces.h> -#include <openbsc/a_iface.h> - -int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct subscr_request *request, *tmp; - struct gsm_subscriber_connection *conn = data; - struct vlr_subscr *vsub = param; - struct paging_signal_data sig_data; - - OSMO_ASSERT(vsub); - OSMO_ASSERT(hooknum == GSM_HOOK_RR_PAGING); - OSMO_ASSERT(!(conn && (conn->vsub != vsub))); - OSMO_ASSERT(!((event == GSM_PAGING_SUCCEEDED) && !conn)); - - LOGP(DPAG, LOGL_DEBUG, "Paging %s for %s (event=%d)\n", - event == GSM_PAGING_SUCCEEDED ? "success" : "failure", - vlr_subscr_name(vsub), event); - - if (!vsub->cs.is_paging) { - LOGP(DPAG, LOGL_ERROR, - "Paging Response received for subscriber" - " that is not paging.\n"); - return -EINVAL; - } - - if (event == GSM_PAGING_SUCCEEDED) - msc_stop_paging(vsub); - - /* Inform parts of the system we don't know */ - sig_data.vsub = vsub; - sig_data.conn = conn; - sig_data.paging_result = event; - osmo_signal_dispatch(SS_PAGING, - event == GSM_PAGING_SUCCEEDED ? - S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, - &sig_data); - - llist_for_each_entry_safe(request, tmp, &vsub->cs.requests, entry) { - llist_del(&request->entry); - if (request->cbfn) { - LOGP(DPAG, LOGL_DEBUG, "Calling paging cbfn.\n"); - request->cbfn(hooknum, event, msg, data, request->param); - } else - LOGP(DPAG, LOGL_DEBUG, "Paging without action.\n"); - talloc_free(request); - } - - /* balanced with the moment we start paging */ - vsub->cs.is_paging = false; - vlr_subscr_put(vsub); - return 0; -} - -int msc_paging_request(struct vlr_subscr *vsub) -{ - /* The subscriber was last seen in subscr->lac. Find out which - * BSCs/RNCs are responsible and send them a paging request via open - * SCCP connections (if any). */ - /* TODO Implementing only RNC paging, since this is code on the iu branch. - * Need to add BSC paging at some point. */ - switch (vsub->cs.attached_via_ran) { - case RAN_GERAN_A: - return a_iface_tx_paging(vsub->imsi, vsub->tmsi, vsub->lac); - case RAN_UTRAN_IU: - return ranap_iu_page_cs(vsub->imsi, - vsub->tmsi == GSM_RESERVED_TMSI? - NULL : &vsub->tmsi, - vsub->lac); - default: - break; - } - - LOGP(DPAG, LOGL_ERROR, "%s: Cannot page, subscriber not attached\n", - vlr_subscr_name(vsub)); - return -EINVAL; -} - -/*! \brief Start a paging request for vsub, call cbfn(param) when done. - * \param vsub subscriber to page. - * \param cbfn function to call when the conn is established. - * \param param caller defined param to pass to cbfn(). - * \param label human readable label of the request kind used for logging. - */ -struct subscr_request *subscr_request_conn(struct vlr_subscr *vsub, - gsm_cbfn *cbfn, void *param, - const char *label) -{ - int rc; - struct subscr_request *request; - - /* Start paging.. we know it is async so we can do it before */ - if (!vsub->cs.is_paging) { - LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet, start paging.\n", - vlr_subscr_name(vsub)); - rc = msc_paging_request(vsub); - if (rc <= 0) { - LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n", - vlr_subscr_name(vsub), rc); - return NULL; - } - /* reduced on the first paging callback */ - vlr_subscr_get(vsub); - vsub->cs.is_paging = true; - } else { - LOGP(DMM, LOGL_DEBUG, "Subscriber %s already paged.\n", - vlr_subscr_name(vsub)); - } - - /* TODO: Stop paging in case of memory allocation failure */ - request = talloc_zero(vsub, struct subscr_request); - if (!request) - return NULL; - - request->cbfn = cbfn; - request->param = param; - llist_add_tail(&request->entry, &vsub->cs.requests); - return request; -} - -void subscr_remove_request(struct subscr_request *request) -{ - llist_del(&request->entry); - talloc_free(request); -} - -struct gsm_subscriber_connection *connection_for_subscr(struct vlr_subscr *vsub) -{ - struct gsm_network *net = vsub->vlr->user_ctx; - struct gsm_subscriber_connection *conn; - - llist_for_each_entry(conn, &net->subscr_conns, entry) { - if (conn->vsub == vsub) - return conn; - } - - return NULL; -} diff --git a/src/libmsc/iucs.c b/src/libmsc/iucs.c deleted file mode 100644 index 04b9ece7d..000000000 --- a/src/libmsc/iucs.c +++ /dev/null @@ -1,189 +0,0 @@ -/* Code to manage MSC subscriber connections over IuCS interface */ - -/* - * (C) 2016,2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * - * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> - * - * 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 <inttypes.h> - -#include <osmocom/core/logging.h> -#include <osmocom/ranap/iu_client.h> -#include <openbsc/debug.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/vlr.h> - -/* For A-interface see libbsc/bsc_api.c subscr_con_allocate() */ -static struct gsm_subscriber_connection *subscr_conn_allocate_iu(struct gsm_network *network, - struct ranap_ue_conn_ctx *ue, - uint16_t lac) -{ - struct gsm_subscriber_connection *conn; - - DEBUGP(DIUCS, "Allocating IuCS subscriber conn: lac %d, conn_id %" PRIx32 "\n", - lac, ue->conn_id); - - conn = talloc_zero(network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = network; - conn->via_ran = RAN_UTRAN_IU; - conn->iu.ue_ctx = ue; - conn->iu.ue_ctx->rab_assign_addr_enc = network->iu.rab_assign_addr_enc; - conn->lac = lac; - - llist_add_tail(&conn->entry, &network->subscr_conns); - return conn; -} - -static int same_ue_conn(struct ranap_ue_conn_ctx *a, struct ranap_ue_conn_ctx *b) -{ - if (a == b) - return 1; - return (a->conn_id == b->conn_id); -} - -static inline void log_subscribers(struct gsm_network *network) -{ - if (!log_check_level(DIUCS, LOGL_DEBUG)) - return; - - struct gsm_subscriber_connection *conn; - int i = 0; - llist_for_each_entry(conn, &network->subscr_conns, entry) { - DEBUGP(DIUCS, "%3d: %s", i, vlr_subscr_name(conn->vsub)); - switch (conn->via_ran) { - case RAN_UTRAN_IU: - DEBUGPC(DIUCS, " Iu"); - if (conn->iu.ue_ctx) { - DEBUGPC(DIUCS, " conn_id %d", - conn->iu.ue_ctx->conn_id - ); - } - break; - case RAN_GERAN_A: - DEBUGPC(DIUCS, " A"); - /* TODO log A-interface connection details */ - break; - case RAN_UNKNOWN: - DEBUGPC(DIUCS, " ?"); - break; - default: - DEBUGPC(DIUCS, " invalid"); - break; - } - DEBUGPC(DIUCS, "\n"); - i++; - } - DEBUGP(DIUCS, "subscribers registered: %d\n", i); -} - -/* Return an existing IuCS subscriber connection record for the given - * connection IDs, or return NULL if not found. */ -struct gsm_subscriber_connection *subscr_conn_lookup_iu( - struct gsm_network *network, - struct ranap_ue_conn_ctx *ue) -{ - struct gsm_subscriber_connection *conn; - - DEBUGP(DIUCS, "Looking for IuCS subscriber: conn_id %" PRIx32 "\n", - ue->conn_id); - log_subscribers(network); - - llist_for_each_entry(conn, &network->subscr_conns, entry) { - if (conn->via_ran != RAN_UTRAN_IU) - continue; - if (!same_ue_conn(conn->iu.ue_ctx, ue)) - continue; - DEBUGP(DIUCS, "Found IuCS subscriber for conn_id %" PRIx32 "\n", - ue->conn_id); - return conn; - } - DEBUGP(DIUCS, "No IuCS subscriber found for conn_id %" PRIx32 "\n", - ue->conn_id); - return NULL; -} - -/* Receive MM/CC/... message from IuCS (SCCP user SAP). - * msg->dst must reference a struct ranap_ue_conn_ctx, which identifies the peer that - * sent the msg. - * - * For A-interface see libbsc/bsc_api.c gsm0408_rcvmsg(). */ -int gsm0408_rcvmsg_iucs(struct gsm_network *network, struct msgb *msg, - uint16_t *lac) -{ - int rc; - struct ranap_ue_conn_ctx *ue_ctx; - struct gsm_subscriber_connection *conn; - - ue_ctx = (struct ranap_ue_conn_ctx*)msg->dst; - - /* TODO: are there message types that could allow us to skip this - * search? */ - conn = subscr_conn_lookup_iu(network, ue_ctx); - - if (conn && lac && (conn->lac != *lac)) { - LOGP(DIUCS, LOGL_ERROR, "IuCS subscriber has changed LAC" - " within the same connection, discarding connection:" - " %s from LAC %d to %d\n", - vlr_subscr_name(conn->vsub), conn->lac, *lac); - /* Deallocate conn with previous LAC */ - msc_subscr_conn_close(conn, GSM_CAUSE_INV_MAND_INFO); - /* At this point we could be tolerant and allocate a new - * connection, but changing the LAC within the same connection - * is shifty. Rather cancel everything. */ - return -1; - } - - if (conn) { - /* Make sure we don't receive RR over IuCS; otherwise all - * messages handled by gsm0408_dispatch() are of interest (CC, - * MM, SMS, NS_SS, maybe even MM_GPRS and SM_GPRS). */ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gh->proto_discr & 0x0f; - OSMO_ASSERT(pdisc != GSM48_PDISC_RR); - - msc_dtap(conn, ue_ctx->conn_id, msg); - rc = 0; - } else { - /* allocate a new connection */ - - if (!lac) { - LOGP(DIUCS, LOGL_ERROR, "New IuCS subscriber" - " but no LAC available. Expecting an InitialUE" - " message containing a LAI IE." - " Dropping connection.\n"); - return -1; - } - - conn = subscr_conn_allocate_iu(network, ue_ctx, *lac); - if (!conn) - abort(); - - /* ownership of conn hereby goes to the MSC: */ - rc = msc_compl_l3(conn, msg, 0); - } - - return rc; -} diff --git a/src/libmsc/iucs_ranap.c b/src/libmsc/iucs_ranap.c deleted file mode 100644 index 45de1caca..000000000 --- a/src/libmsc/iucs_ranap.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Implementation of RANAP messages to/from an MSC via an Iu-CS interface. - * This keeps direct RANAP dependencies out of libmsc. */ - -/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> - * - * 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 "../../bscconfig.h" - -#ifdef BUILD_IU - -#include <osmocom/core/logging.h> - -#include <osmocom/ranap/ranap_ies_defs.h> -#include <osmocom/ranap/iu_client.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/iucs.h> -#include <openbsc/vlr.h> -#include <openbsc/iucs_ranap.h> -#include <openbsc/osmo_msc.h> - -/* To continue authorization after a Security Mode Complete */ -int gsm0408_authorize(struct gsm_subscriber_connection *conn); - -static int iucs_rx_rab_assign(struct gsm_subscriber_connection *conn, - RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) -{ - uint8_t rab_id; - RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; - - rab_id = item->rAB_ID.buf[0]; - - LOGP(DIUCS, LOGL_NOTICE, - "Received RAB assignment event for %s rab_id=%hhd\n", - vlr_subscr_name(conn->vsub), rab_id); - - return 0; -} - -int iucs_rx_sec_mode_compl(struct gsm_subscriber_connection *conn, - RANAP_SecurityModeCompleteIEs_t *ies) -{ - OSMO_ASSERT(conn->via_ran == RAN_UTRAN_IU); - - /* TODO evalute ies */ - - msc_rx_sec_mode_compl(conn); - return 0; -} - -int iucs_rx_ranap_event(struct gsm_network *network, - struct ranap_ue_conn_ctx *ue_ctx, int type, void *data) -{ - struct gsm_subscriber_connection *conn; - - conn = subscr_conn_lookup_iu(network, ue_ctx); - - if (!conn) { - LOGP(DRANAP, LOGL_ERROR, "Cannot find subscriber for IU event %u\n", type); - return -1; - } - - switch (type) { - case RANAP_IU_EVENT_IU_RELEASE: - case RANAP_IU_EVENT_LINK_INVALIDATED: - LOGP(DIUCS, LOGL_INFO, "IuCS release for %s\n", - vlr_subscr_name(conn->vsub)); - msc_subscr_conn_close(conn, 0); - return 0; - - case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE: - LOGP(DIUCS, LOGL_INFO, "IuCS security mode complete for %s\n", - vlr_subscr_name(conn->vsub)); - return iucs_rx_sec_mode_compl(conn, - (RANAP_SecurityModeCompleteIEs_t*)data); - case RANAP_IU_EVENT_RAB_ASSIGN: - return iucs_rx_rab_assign(conn, - (RANAP_RAB_SetupOrModifiedItemIEs_t*)data); - default: - LOGP(DIUCS, LOGL_NOTICE, "Unknown message received:" - " RANAP event: %i\n", type); - return -1; - } -} - -#endif /* BUILD_IU */ diff --git a/src/libmsc/meas_feed.c b/src/libmsc/meas_feed.c deleted file mode 100644 index 1e7b4cd51..000000000 --- a/src/libmsc/meas_feed.c +++ /dev/null @@ -1,168 +0,0 @@ -/* UDP-Feed of measurement reports */ - -#include <unistd.h> - -#include <sys/socket.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/write_queue.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/vty.h> - -#include <openbsc/meas_rep.h> -#include <openbsc/signal.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/meas_feed.h> -#include <openbsc/vty.h> -#include <openbsc/vlr.h> - -#include "meas_feed.h" - -struct meas_feed_state { - struct osmo_wqueue wqueue; - char scenario[31+1]; - char *dst_host; - uint16_t dst_port; -}; - - -static struct meas_feed_state g_mfs; - -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct msgb *msg; - struct meas_feed_meas *mfm; - struct vlr_subscr *vsub; - - /* ignore measurements as long as we don't know who it is */ - if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->vsub) - return 0; - - vsub = mr->lchan->conn->vsub; - - msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed"); - if (!msg) - return 0; - - /* fill in the header */ - mfm = (struct meas_feed_meas *) msgb_put(msg, sizeof(*mfm)); - mfm->hdr.msg_type = MEAS_FEED_MEAS; - mfm->hdr.version = MEAS_FEED_VERSION; - - /* fill in MEAS_FEED_MEAS specific header */ - osmo_strlcpy(mfm->imsi, vsub->imsi, sizeof(mfm->imsi)); - osmo_strlcpy(mfm->name, vsub->name, sizeof(mfm->name)); - osmo_strlcpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario)); - - /* copy the entire measurement report */ - memcpy(&mfm->mr, mr, sizeof(mfm->mr)); - - /* copy channel information */ - /* we assume that the measurement report always belong to some timeslot */ - mfm->lchan_type = (uint8_t)mr->lchan->type; - mfm->pchan_type = (uint8_t)mr->lchan->ts->pchan; - mfm->bts_nr = mr->lchan->ts->trx->bts->nr; - mfm->trx_nr = mr->lchan->ts->trx->nr; - mfm->ts_nr = mr->lchan->ts->nr; - mfm->ss_nr = mr->lchan->nr; - - /* and send it to the socket */ - if (osmo_wqueue_enqueue(&g_mfs.wqueue, msg) != 0) - msgb_free(msg); - - return 0; -} - -static int meas_feed_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *sdata = signal_data; - - if (subsys != SS_LCHAN) - return 0; - - if (signal == S_LCHAN_MEAS_REP) - process_meas_rep(sdata->mr); - - return 0; -} - -static int feed_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - return write(ofd->fd, msgb_data(msg), msgb_length(msg)); -} - -static int feed_read_cb(struct osmo_fd *ofd) -{ - int rc; - char buf[256]; - - rc = read(ofd->fd, buf, sizeof(buf)); - ofd->fd &= ~BSC_FD_READ; - - return rc; -} - -int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port) -{ - int rc; - int already_initialized = 0; - - if (g_mfs.wqueue.bfd.fd) - already_initialized = 1; - - - if (already_initialized && - !strcmp(dst_host, g_mfs.dst_host) && - dst_port == g_mfs.dst_port) - return 0; - - if (!already_initialized) { - osmo_wqueue_init(&g_mfs.wqueue, 10); - g_mfs.wqueue.write_cb = feed_write_cb; - g_mfs.wqueue.read_cb = feed_read_cb; - osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL); - } - - if (already_initialized) { - osmo_wqueue_clear(&g_mfs.wqueue); - osmo_fd_unregister(&g_mfs.wqueue.bfd); - close(g_mfs.wqueue.bfd.fd); - /* don't set to zero, as that would mean 'not yet initialized' */ - g_mfs.wqueue.bfd.fd = -1; - } - rc = osmo_sock_init_ofd(&g_mfs.wqueue.bfd, AF_UNSPEC, SOCK_DGRAM, - IPPROTO_UDP, dst_host, dst_port, - OSMO_SOCK_F_CONNECT); - if (rc < 0) - return rc; - - g_mfs.wqueue.bfd.when &= ~BSC_FD_READ; - - if (g_mfs.dst_host) - talloc_free(g_mfs.dst_host); - g_mfs.dst_host = talloc_strdup(NULL, dst_host); - g_mfs.dst_port = dst_port; - - return 0; -} - -void meas_feed_cfg_get(char **host, uint16_t *port) -{ - *port = g_mfs.dst_port; - *host = g_mfs.dst_host; -} - -void meas_feed_scenario_set(const char *name) -{ - osmo_strlcpy(g_mfs.scenario, name, sizeof(g_mfs.scenario)); -} - -const char *meas_feed_scenario_get(void) -{ - return g_mfs.scenario; -} diff --git a/src/libmsc/meas_feed.h b/src/libmsc/meas_feed.h deleted file mode 100644 index 782a9616c..000000000 --- a/src/libmsc/meas_feed.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _INT_MEAS_FEED_H -#define _INT_MEAS_FEED_H - -#include <stdint.h> - -int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port); -void meas_feed_cfg_get(char **host, uint16_t *port); - -void meas_feed_scenario_set(const char *name); -const char *meas_feed_scenario_get(void); - -#endif /* _INT_MEAS_FEED_H */ diff --git a/src/libmsc/mncc.c b/src/libmsc/mncc.c deleted file mode 100644 index 8110eadca..000000000 --- a/src/libmsc/mncc.c +++ /dev/null @@ -1,107 +0,0 @@ -/* mncc.c - utility routines for the MNCC API between the 04.08 - * message parsing and the actual Call Control logic */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de> - * 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 <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> - -#include <openbsc/gsm_04_08.h> -#include <openbsc/debug.h> -#include <openbsc/mncc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/transaction.h> -#include <openbsc/rtp_proxy.h> - - -static const struct value_string mncc_names[] = { - { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, - { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, - { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, - { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, - { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, - { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, - { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, - { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, - { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, - { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, - { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, - { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, - { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, - { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, - { MNCC_DISC_IND, "MNCC_DISC_IND" }, - { MNCC_REL_REQ, "MNCC_REL_REQ" }, - { MNCC_REL_IND, "MNCC_REL_IND" }, - { MNCC_REL_CNF, "MNCC_REL_CNF" }, - { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, - { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, - { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, - { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, - { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, - { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, - { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, - { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, - { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, - { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, - { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, - { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, - { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, - { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, - { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, - { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, - { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, - { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, - { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, - { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, - { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, - { MNCC_REJ_IND, "MNCC_REJ_IND" }, - { MNCC_BRIDGE, "MNCC_BRIDGE" }, - { MNCC_FRAME_RECV, "MNCC_FRAME_RECV" }, - { MNCC_FRAME_DROP, "MNCC_FRAME_DROP" }, - { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" }, - { MNCC_RTP_CREATE, "MNCC_RTP_CREATE" }, - { MNCC_RTP_CONNECT, "MNCC_RTP_CONNECT" }, - { MNCC_RTP_FREE, "MNCC_RTP_FREE" }, - { GSM_TCHF_FRAME, "GSM_TCHF_FRAME" }, - { GSM_TCHF_FRAME_EFR, "GSM_TCHF_FRAME_EFR" }, - { GSM_TCHH_FRAME, "GSM_TCHH_FRAME" }, - { GSM_TCH_FRAME_AMR, "GSM_TCH_FRAME_AMR" }, - { GSM_BAD_FRAME, "GSM_BAD_FRAME" }, - { 0, NULL }, -}; - -const char *get_mncc_name(int value) -{ - return get_value_string(mncc_names, value); -} - -void mncc_set_cause(struct gsm_mncc *data, int loc, int val) -{ - data->fields |= MNCC_F_CAUSE; - data->cause.location = loc; - data->cause.value = val; -} - diff --git a/src/libmsc/mncc_builtin.c b/src/libmsc/mncc_builtin.c deleted file mode 100644 index ac6e7345d..000000000 --- a/src/libmsc/mncc_builtin.c +++ /dev/null @@ -1,383 +0,0 @@ -/* mncc_builtin.c - default, minimal built-in MNCC Application for - * standalone bsc_hack (network-in-the-box mode) */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/gsm_04_08.h> -#include <openbsc/debug.h> -#include <openbsc/mncc.h> -#include <openbsc/mncc_int.h> -#include <osmocom/core/talloc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/transaction.h> -#include <openbsc/rtp_proxy.h> - -void *tall_call_ctx; - -static LLIST_HEAD(call_list); - -static uint32_t new_callref = 0x00000001; - -struct mncc_int mncc_int = { - .def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 }, -}; - -static void free_call(struct gsm_call *call) -{ - llist_del(&call->entry); - DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); - talloc_free(call); -} - - -static struct gsm_call *get_call_ref(uint32_t callref) -{ - struct gsm_call *callt; - - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) - return callt; - } - return NULL; -} - -/* on incoming call, look up database and send setup to remote subscr. */ -static int mncc_setup_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *setup) -{ - struct gsm_mncc mncc; - struct gsm_call *remote; - - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - - /* already have remote call */ - if (call->remote_ref) - return 0; - - /* transfer mode 1 would be packet mode, which was never specified */ - if (setup->bearer_cap.mode != 0) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " - "packet mode\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* we currently only do speech */ - if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " - "voice calls\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* create remote call */ - if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) { - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - goto out_reject; - } - llist_add_tail(&remote->entry, &call_list); - remote->net = call->net; - remote->callref = new_callref++; - DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", - call->callref, remote->callref); - - /* link remote call */ - call->remote_ref = remote->callref; - remote->remote_ref = call->callref; - - /* send call proceeding */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); - - /* modify mode */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Modify channel mode\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); - - /* send setup to remote */ -// setup->fields |= MNCC_F_SIGNAL; -// setup->signal = GSM48_SIGNAL_DIALTONE; - setup->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_SETUP_REQ, setup); - -out_reject: - mncc_tx_to_cc(call->net, MNCC_REJ_REQ, &mncc); - free_call(call); - return 0; -} - -static int mncc_alert_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *alert) -{ - struct gsm_call *remote; - - /* send alerting to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - alert->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_ALERT_REQ, alert); -} - -static int mncc_notify_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *notify) -{ - struct gsm_call *remote; - - /* send notify to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - notify->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_NOTIFY_REQ, notify); -} - -static int mncc_setup_cnf(struct gsm_call *call, int msg_type, - struct gsm_mncc *connect) -{ - struct gsm_mncc connect_ack, frame_recv; - struct gsm_network *net = call->net; - struct gsm_call *remote; - struct gsm_mncc_bridge bridge = { .msg_type = MNCC_BRIDGE }; - - /* acknowledge connect */ - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); - - /* send connect message to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - connect->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); - mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); - - /* bridge tch */ - bridge.callref[0] = call->callref; - bridge.callref[1] = call->remote_ref; - DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); - - /* proxy mode */ - if (!net->handover.active) { - /* in the no-handover case, we can bridge, i.e. use - * the old RTP proxy code */ - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, &bridge); - } else { - /* in case of handover, we need to re-write the RTP - * SSRC, sequence and timestamp values and thus - * need to enable RTP receive for both directions */ - memset(&frame_recv, 0, sizeof(struct gsm_mncc)); - frame_recv.callref = call->callref; - mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - frame_recv.callref = call->remote_ref; - return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - } -} - -static int mncc_disc_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *disc) -{ - struct gsm_call *remote; - - /* send release */ - DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", - call->callref, disc->cause.value); - mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); - - /* send disc to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - return 0; - } - disc->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", - remote->callref, disc->cause.value); - return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); -} - -static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - struct gsm_call *remote; - - /* send release to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - free_call(call); - return 0; - } - - rel->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", - call->callref, rel->cause.value); - - /* - * Release this side of the call right now. Otherwise we end up - * in this method for the other call and will also try to release - * it and then we will end up with a double free and a crash - */ - free_call(call); - mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); - - return 0; -} - -static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - free_call(call); - return 0; -} - -/* Internal MNCC handler input function (from CC -> MNCC -> here) */ -int int_mncc_recv(struct gsm_network *net, struct msgb *msg) -{ - void *arg = msgb_data(msg); - struct gsm_mncc *data = arg; - int msg_type = data->msg_type; - int callref; - struct gsm_call *call = NULL, *callt; - int rc = 0; - - /* Special messages */ - switch(msg_type) { - } - - /* find callref */ - callref = data->callref; - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) { - call = callt; - break; - } - } - - /* create callref, if setup is received */ - if (!call) { - if (msg_type != MNCC_SETUP_IND) - goto out_free; /* drop */ - /* create call */ - if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = callref; - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - mncc_tx_to_cc(net, MNCC_REL_REQ, &rel); - goto out_free; - } - llist_add_tail(&call->entry, &call_list); - call->net = net; - call->callref = callref; - DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); - } - - if (mncc_is_data_frame(msg_type)) { - LOGP(DMNCC, LOGL_ERROR, "(call %x) Received data frame, which is not supported.\n", - call->callref); - goto out_free; - } - - DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, - get_mncc_name(msg_type)); - - switch(msg_type) { - case MNCC_SETUP_IND: - rc = mncc_setup_ind(call, msg_type, arg); - break; - case MNCC_SETUP_CNF: - rc = mncc_setup_cnf(call, msg_type, arg); - break; - case MNCC_SETUP_COMPL_IND: - break; - case MNCC_CALL_CONF_IND: - /* we now need to MODIFY the channel */ - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); - break; - case MNCC_ALERT_IND: - rc = mncc_alert_ind(call, msg_type, arg); - break; - case MNCC_NOTIFY_IND: - rc = mncc_notify_ind(call, msg_type, arg); - break; - case MNCC_DISC_IND: - rc = mncc_disc_ind(call, msg_type, arg); - break; - case MNCC_REL_IND: - case MNCC_REJ_IND: - rc = mncc_rel_ind(call, msg_type, arg); - break; - case MNCC_REL_CNF: - rc = mncc_rel_cnf(call, msg_type, arg); - break; - case MNCC_FACILITY_IND: - break; - case MNCC_START_DTMF_IND: - rc = mncc_tx_to_cc(net, MNCC_START_DTMF_REJ, data); - break; - case MNCC_STOP_DTMF_IND: - rc = mncc_tx_to_cc(net, MNCC_STOP_DTMF_RSP, data); - break; - case MNCC_MODIFY_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); - break; - case MNCC_MODIFY_CNF: - break; - case MNCC_HOLD_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); - break; - case MNCC_RETRIEVE_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); - break; - default: - LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); - break; - } - -out_free: - msgb_free(msg); - - return rc; -} diff --git a/src/libmsc/mncc_sock.c b/src/libmsc/mncc_sock.c deleted file mode 100644 index 0c696f2d9..000000000 --- a/src/libmsc/mncc_sock.c +++ /dev/null @@ -1,317 +0,0 @@ -/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de> - * (C) 2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/socket.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> - -#include <openbsc/debug.h> -#include <openbsc/mncc.h> -#include <openbsc/gsm_data.h> - -struct mncc_sock_state { - struct gsm_network *net; - struct osmo_fd listen_bfd; /* fd for listen socket */ - struct osmo_fd conn_bfd; /* fd for connection to lcr */ -}; - -/* input from CC code into mncc_sock */ -int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg) -{ - struct gsm_mncc *mncc_in = (struct gsm_mncc *) msgb_data(msg); - int msg_type = mncc_in->msg_type; - - /* Check if we currently have a MNCC handler connected */ - if (net->mncc_state->conn_bfd.fd < 0) { - LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app " - "but socket is gone\n", get_mncc_name(msg_type)); - if (!mncc_is_data_frame(msg_type)) { - /* release the request */ - struct gsm_mncc mncc_out; - memset(&mncc_out, 0, sizeof(mncc_out)); - mncc_out.callref = mncc_in->callref; - mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_TEMP_FAILURE); - mncc_tx_to_cc(net, MNCC_REL_REQ, &mncc_out); - } - /* free the original message */ - msgb_free(msg); - return -1; - } - - /* FIXME: check for some maximum queue depth? */ - - /* Actually enqueue the message and mark socket write need */ - msgb_enqueue(&net->upqueue, msg); - net->mncc_state->conn_bfd.when |= BSC_FD_WRITE; - return 0; -} - -static void mncc_sock_close(struct mncc_sock_state *state) -{ - struct osmo_fd *bfd = &state->conn_bfd; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - osmo_fd_unregister(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - - /* release all exisitng calls */ - gsm0408_clear_all_trans(state->net, GSM48_PDISC_CC); - - /* flush the queue */ - while (!llist_empty(&state->net->upqueue)) { - struct msgb *msg = msgb_dequeue(&state->net->upqueue); - msgb_free(msg); - } -} - -static int mncc_sock_read(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct gsm_mncc *mncc_prim; - struct msgb *msg; - int rc; - - msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx"); - if (!msg) - return -ENOMEM; - - mncc_prim = (struct gsm_mncc *) msg->tail; - - rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); - if (rc == 0) - goto close; - - if (rc < 0) { - if (errno == EAGAIN) - return 0; - goto close; - } - - rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim); - - /* as we always synchronously process the message in mncc_send() and - * its callbacks, we can free the message here. */ - msgb_free(msg); - - return rc; - -close: - msgb_free(msg); - mncc_sock_close(state); - return -1; -} - -static int mncc_sock_write(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = bfd->data; - struct gsm_network *net = state->net; - int rc; - - while (!llist_empty(&net->upqueue)) { - struct msgb *msg, *msg2; - struct gsm_mncc *mncc_prim; - - /* peek at the beginning of the queue */ - msg = llist_entry(net->upqueue.next, struct msgb, list); - mncc_prim = (struct gsm_mncc *)msg->data; - - bfd->when &= ~BSC_FD_WRITE; - - /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ - if (!msgb_length(msg)) { - LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO " - "bytes!\n", mncc_prim->msg_type); - goto dontsend; - } - - /* try to send it over the socket */ - rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) - goto close; - if (rc < 0) { - if (errno == EAGAIN) { - bfd->when |= BSC_FD_WRITE; - break; - } - goto close; - } - -dontsend: - /* _after_ we send it, we can deueue */ - msg2 = msgb_dequeue(&net->upqueue); - assert(msg == msg2); - msgb_free(msg); - } - return 0; - -close: - mncc_sock_close(state); - - return -1; -} - -static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags) -{ - int rc = 0; - - if (flags & BSC_FD_READ) - rc = mncc_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = mncc_sock_write(bfd); - - return rc; -} - -/** - * Send a version indication to the remote. - */ -static void queue_hello(struct mncc_sock_state *mncc) -{ - struct gsm_mncc_hello *hello; - struct msgb *msg; - - msg = msgb_alloc(512, "mncc hello"); - if (!msg) { - LOGP(DMNCC, LOGL_ERROR, "Failed to allocate hello.\n"); - mncc_sock_close(mncc); - return; - } - - hello = (struct gsm_mncc_hello *) msgb_put(msg, sizeof(*hello)); - hello->msg_type = MNCC_SOCKET_HELLO; - hello->version = MNCC_SOCK_VERSION; - hello->mncc_size = sizeof(struct gsm_mncc); - hello->data_frame_size = sizeof(struct gsm_data_frame); - hello->called_offset = offsetof(struct gsm_mncc, called); - hello->signal_offset = offsetof(struct gsm_mncc, signal); - hello->emergency_offset = offsetof(struct gsm_mncc, emergency); - - msgb_enqueue(&mncc->net->upqueue, msg); - mncc->conn_bfd.when |= BSC_FD_WRITE; -} - -/* accept a new connection */ -static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct osmo_fd *conn_bfd = &state->conn_bfd; - struct sockaddr_un un_addr; - socklen_t len; - int rc; - - len = sizeof(un_addr); - rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - if (conn_bfd->fd >= 0) { - LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have " - "another active connection ?!?\n"); - /* We already have one MNCC app connected, this is all we support */ - state->listen_bfd.when &= ~BSC_FD_READ; - close(rc); - return 0; - } - - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = mncc_sock_cb; - conn_bfd->data = state; - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " - "call control application\n"); - - queue_hello(state); - return 0; -} - - -int mncc_sock_init(struct gsm_network *net, const char *sock_path) -{ - struct mncc_sock_state *state; - struct osmo_fd *bfd; - int rc; - - state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); - if (!state) - return -ENOMEM; - - state->net = net; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path, - OSMO_SOCK_F_BIND); - if (bfd->fd < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s: %s\n", - sock_path, strerror(errno)); - talloc_free(state); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = mncc_sock_accept; - bfd->data = state; - - rc = osmo_fd_register(bfd); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - net->mncc_state = state; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC socket at %s\n", sock_path); - return 0; -} diff --git a/src/libmsc/msc_ifaces.c b/src/libmsc/msc_ifaces.c deleted file mode 100644 index 161a10019..000000000 --- a/src/libmsc/msc_ifaces.c +++ /dev/null @@ -1,423 +0,0 @@ -/* Implementation for MSC decisions which interface to send messages out on. */ - -/* (C) 2016 by sysmocom s.m.f.c GmbH <info@sysmocom.de> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <osmocom/core/logging.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/msc_ifaces.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/transaction.h> -#include <osmocom/legacy_mgcp/mgcp.h> -#include <osmocom/legacy_mgcp/mgcpgw_client.h> -#include <openbsc/vlr.h> -#include <openbsc/a_iface.h> - -#include "../../bscconfig.h" - -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -extern struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, - uint32_t rtp_ip, - uint16_t rtp_port, - bool use_x213_nsap); -#else -#include <openbsc/iu_dummy.h> -#endif /* BUILD_IU */ - -static int msc_tx(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - if (!conn) - return -EINVAL; - if (!msg) - return -EINVAL; - - DEBUGP(DMSC, "msc_tx %u bytes to %s via %s\n", - msg->len, vlr_subscr_name(conn->vsub), - ran_type_name(conn->via_ran)); - switch (conn->via_ran) { - case RAN_GERAN_A: - msg->dst = conn; - return a_iface_tx_dtap(msg); - - case RAN_UTRAN_IU: - msg->dst = conn->iu.ue_ctx; - return ranap_iu_tx(msg, 0); - - default: - LOGP(DMSC, LOGL_ERROR, - "msc_tx(): conn->via_ran invalid (%d)\n", - conn->via_ran); - return -1; - } -} - - -int msc_tx_dtap(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - return msc_tx(conn, msg); -} - - -/* 9.2.5 CM service accept */ -int msc_gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg; - struct gsm48_hdr *gh; - - if (!conn) - return -EINVAL; - - msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACC"); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; - - DEBUGP(DMM, "-> CM SERVICE ACCEPT %s\n", - vlr_subscr_name(conn->vsub)); - - return msc_tx_dtap(conn, msg); -} - -/* 9.2.6 CM service reject */ -int msc_gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, - enum gsm48_reject_value value) -{ - struct msgb *msg; - - if (!conn) - return -EINVAL; - - conn->received_cm_service_request = false; - - msg = gsm48_create_mm_serv_rej(value); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); - return -1; - } - - DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); - - return msc_tx_dtap(conn, msg); -} - -int msc_tx_common_id(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return -EINVAL; - - /* Common ID is only sent over IuCS */ - if (conn->via_ran != RAN_UTRAN_IU) { - LOGP(DMM, LOGL_INFO, - "%s: Asked to transmit Common ID, but skipping" - " because this is not on UTRAN\n", - vlr_subscr_name(conn->vsub)); - return 0; - } - - DEBUGP(DIUCS, "%s: tx CommonID %s\n", - vlr_subscr_name(conn->vsub), conn->vsub->imsi); - return ranap_iu_tx_common_id(conn->iu.ue_ctx, conn->vsub->imsi); -} - -static int iu_rab_act_cs(struct ranap_ue_conn_ctx *uectx, uint8_t rab_id, - uint32_t rtp_ip, uint16_t rtp_port) -{ -#ifdef BUILD_IU - struct msgb *msg; - bool use_x213_nsap; - uint32_t conn_id = uectx->conn_id; - - use_x213_nsap = (uectx->rab_assign_addr_enc == RANAP_NSAP_ADDR_ENC_X213); - - LOGP(DIUCS, LOGL_DEBUG, "Assigning RAB: conn_id=%u, rab_id=%d," - " rtp=%x:%u, use_x213_nsap=%d\n", conn_id, rab_id, rtp_ip, - rtp_port, use_x213_nsap); - - msg = ranap_new_msg_rab_assign_voice(rab_id, rtp_ip, rtp_port, - use_x213_nsap); - msg->l2h = msg->data; - - if (ranap_iu_rab_act(uectx, msg)) - LOGP(DIUCS, LOGL_ERROR, "Failed to send RAB Assignment:" - " conn_id=%d rab_id=%d rtp=%x:%u\n", - conn_id, rab_id, rtp_ip, rtp_port); - return 0; -#else - LOGP(DMSC, LOGL_ERROR, "Cannot send Iu RAB Assignment: built without Iu support\n"); - return -ENOTSUP; -#endif -} - -static void mgcp_response_rab_act_cs_crcx(struct mgcp_response *r, void *priv) -{ - struct gsm_trans *trans = priv; - struct gsm_subscriber_connection *conn = trans->conn; - uint32_t rtp_ip; - int rc; - - if (r->head.response_code != 200) { - LOGP(DMGCP, LOGL_ERROR, - "MGCPGW response yields error: %d %s\n", - r->head.response_code, r->head.comment); - goto rab_act_cs_error; - } - - rc = mgcp_response_parse_params(r); - if (rc) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot parse MGCP response, for %s\n", - vlr_subscr_name(trans->vsub)); - goto rab_act_cs_error; - } - - conn->rtp.port_cn = r->audio_port; - - rtp_ip = mgcpgw_client_remote_addr_n(conn->network->mgcpgw.client); - - if (trans->conn->via_ran == RAN_UTRAN_IU) { - /* Assign a voice channel via RANAP on 3G */ - if (iu_rab_act_cs(conn->iu.ue_ctx, conn->iu.rab_id, rtp_ip, conn->rtp.port_subscr)) - goto rab_act_cs_error; - } else if (trans->conn->via_ran == RAN_GERAN_A) { - /* Assign a voice channel via A on 2G */ - if (a_iface_tx_assignment(trans)) - goto rab_act_cs_error; - } else - goto rab_act_cs_error; - - /* Respond back to MNCC (if requested) */ - if (trans->tch_rtp_create) { - if (gsm48_tch_rtp_create(trans)) - goto rab_act_cs_error; - } - return; - -rab_act_cs_error: - /* FIXME abort call, invalidate conn, ... */ - LOGP(DMSC, LOGL_ERROR, "%s: failure during assignment\n", - vlr_subscr_name(trans->vsub)); - return; -} - -int msc_call_assignment(struct gsm_trans *trans) -{ - struct gsm_subscriber_connection *conn; - struct mgcpgw_client *mgcp; - struct msgb *msg; - uint16_t bts_base; - - if (!trans) - return -EINVAL; - if (!trans->conn) - return -EINVAL; - - conn = trans->conn; - mgcp = conn->network->mgcpgw.client; - -#ifdef BUILD_IU - /* FIXME: HACK. where to scope the RAB Id? At the conn / subscriber / ranap_ue_conn_ctx? */ - static uint8_t next_iu_rab_id = 1; - if (conn->via_ran == RAN_UTRAN_IU) - conn->iu.rab_id = next_iu_rab_id ++; -#endif - - conn->rtp.mgcp_rtp_endpoint = - mgcpgw_client_next_endpoint(conn->network->mgcpgw.client); - - /* This will calculate the port we assign to the BTS via AoIP - * assignment command (or rab-assignment on 3G) The BTS will send - * its RTP traffic to that port on the MGCPGW side. The MGCPGW only - * gets the endpoint ID via the CRCX. It will do the same calculation - * on his side too to get knowledge of the rtp port. */ - bts_base = mgcpgw_client_conf_actual(mgcp)->bts_base; - conn->rtp.port_subscr = bts_base + 2 * conn->rtp.mgcp_rtp_endpoint; - - /* Establish the RTP stream first as looping back to the originator. - * The MDCX will patch through to the counterpart. TODO: play a ring - * tone instead. */ - msg = mgcp_msg_crcx(mgcp, conn->rtp.mgcp_rtp_endpoint, - conn->rtp.mgcp_rtp_endpoint, MGCP_CONN_LOOPBACK); - return mgcpgw_client_tx(mgcp, msg, mgcp_response_rab_act_cs_crcx, trans); -} - -static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv); - -static void mgcp_bridge(struct gsm_trans *from, struct gsm_trans *to, - enum bridge_state state, - enum mgcp_connection_mode mode) -{ - struct gsm_subscriber_connection *conn1 = from->conn; - struct gsm_subscriber_connection *conn2 = to->conn; - struct mgcpgw_client *mgcp = conn1->network->mgcpgw.client; - const char *ip; - struct msgb *msg; - - OSMO_ASSERT(mgcp); - - from->bridge.peer = to; - from->bridge.state = state; - - /* Loop back to the same MGCP GW */ - ip = mgcpgw_client_remote_addr_str(mgcp); - - msg = mgcp_msg_mdcx(mgcp, - conn1->rtp.mgcp_rtp_endpoint, - ip, conn2->rtp.port_cn, - mode); - if (mgcpgw_client_tx(mgcp, msg, mgcp_response_bridge_mdcx, from)) - LOGP(DMGCP, LOGL_ERROR, - "Failed to send MDCX message for %s\n", - vlr_subscr_name(from->vsub)); -} - -static void mgcp_response_bridge_mdcx(struct mgcp_response *r, void *priv) -{ - struct gsm_trans *trans = priv; - struct gsm_trans *peer = trans->bridge.peer; - - switch (trans->bridge.state) { - case BRIDGE_STATE_LOOPBACK_PENDING: - trans->bridge.state = BRIDGE_STATE_LOOPBACK_ESTABLISHED; - - switch (peer->bridge.state) { - case BRIDGE_STATE_LOOPBACK_PENDING: - /* Wait until the other is done as well. */ - return; - case BRIDGE_STATE_LOOPBACK_ESTABLISHED: - /* Now that both are in loopback, switch both to - * forwarding. */ - mgcp_bridge(trans, peer, BRIDGE_STATE_BRIDGE_PENDING, - MGCP_CONN_RECV_SEND); - mgcp_bridge(peer, trans, BRIDGE_STATE_BRIDGE_PENDING, - MGCP_CONN_RECV_SEND); - break; - default: - LOGP(DMGCP, LOGL_ERROR, - "Unexpected bridge state: %d for %s\n", - trans->bridge.state, vlr_subscr_name(trans->vsub)); - break; - } - break; - - case BRIDGE_STATE_BRIDGE_PENDING: - trans->bridge.state = BRIDGE_STATE_BRIDGE_ESTABLISHED; - break; - - default: - LOGP(DMGCP, LOGL_ERROR, - "Unexpected bridge state: %d for %s\n", - trans->bridge.state, vlr_subscr_name(trans->vsub)); - break; - } -} - -int msc_call_connect(struct gsm_trans *trans, uint16_t port, uint32_t ip) -{ - /* With this function we inform the MGCP-GW where (ip/port) it - * has to send its outgoing voic traffic. The receiving end will - * usually be a PBX (e.g. Asterisk). The IP-Address we tell, will - * not only be used to direct the traffic, it will also be used - * as a filter to make sure only RTP packets from the right - * remote end will reach the BSS. This is also the reason why - * inbound audio will not work until this step is performed */ - - /* NOTE: This function is used when msc_call_bridge(), is not - * applicable. This is usually the case when an external MNCC - * is in use */ - - struct gsm_subscriber_connection *conn; - struct mgcpgw_client *mgcp; - struct msgb *msg; - - if (!trans) - return -EINVAL; - if (!trans->conn) - return -EINVAL; - if (!trans->conn->network) - return -EINVAL; - if (!trans->conn->network->mgcpgw.client) - return -EINVAL; - - mgcp = trans->conn->network->mgcpgw.client; - - struct in_addr ip_addr; - ip_addr.s_addr = ntohl(ip); - - conn = trans->conn; - - msg = mgcp_msg_mdcx(mgcp, - conn->rtp.mgcp_rtp_endpoint, - inet_ntoa(ip_addr), port, MGCP_CONN_RECV_SEND); - if (mgcpgw_client_tx(mgcp, msg, NULL, trans)) - LOGP(DMGCP, LOGL_ERROR, - "Failed to send MDCX message for %s\n", - vlr_subscr_name(trans->vsub)); - - return 0; -} - -int msc_call_bridge(struct gsm_trans *trans1, struct gsm_trans *trans2) -{ - if (!trans1) - return -EINVAL; - if (!trans2) - return -EINVAL; - - /* First setup as loopback and configure the counterparts' endpoints, - * so that when transmission starts the originating addresses are - * already known to be valid. The mgcp callback will continue. */ - mgcp_bridge(trans1, trans2, BRIDGE_STATE_LOOPBACK_PENDING, - MGCP_CONN_LOOPBACK); - mgcp_bridge(trans2, trans1, BRIDGE_STATE_LOOPBACK_PENDING, - MGCP_CONN_LOOPBACK); - - return 0; -} - -void msc_call_release(struct gsm_trans *trans) -{ - struct msgb *msg; - struct gsm_subscriber_connection *conn; - struct mgcpgw_client *mgcp; - - if (!trans) - return; - if (!trans->conn) - return; - if (!trans->conn->network) - return; - - conn = trans->conn; - mgcp = conn->network->mgcpgw.client; - - /* Send DLCX */ - msg = mgcp_msg_dlcx(mgcp, conn->rtp.mgcp_rtp_endpoint, - conn->rtp.mgcp_rtp_endpoint); - if (mgcpgw_client_tx(mgcp, msg, NULL, NULL)) - LOGP(DMGCP, LOGL_ERROR, - "Failed to send DLCX message for %s\n", - vlr_subscr_name(trans->vsub)); - - /* Release endpoint id */ - mgcpgw_client_release_endpoint(conn->rtp.mgcp_rtp_endpoint, mgcp); -} diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c deleted file mode 100644 index 50679aa01..000000000 --- a/src/libmsc/msc_vty.c +++ /dev/null @@ -1,162 +0,0 @@ -/* MSC interface to quagga VTY */ -/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> - * Based on OpenBSC interface to quagga VTY (libmsc/vty_interface_layer3.c) - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2009-2011 by Holger Hans Peter Freyther - * 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/>. - * - */ - -/* NOTE: I would have liked to call this the MSC_NODE instead of the MSC_NODE, - * but MSC_NODE already exists to configure a remote MSC for osmo-bsc. */ - -#include "../../bscconfig.h" - -#include <inttypes.h> - -#include <osmocom/vty/command.h> -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -#endif - -#include <openbsc/vty.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/vlr.h> - -static struct cmd_node msc_node = { - MSC_NODE, - "%s(config-msc)# ", - 1, -}; - -DEFUN(cfg_msc, cfg_msc_cmd, - "msc", "Configure MSC options") -{ - vty->node = MSC_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_msc_assign_tmsi, cfg_msc_assign_tmsi_cmd, - "assign-tmsi", - "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->vlr->cfg.assign_tmsi = true; - return CMD_SUCCESS; -} - -DEFUN(cfg_msc_no_assign_tmsi, cfg_msc_no_assign_tmsi_cmd, - "no assign-tmsi", - NO_STR "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->vlr->cfg.assign_tmsi = false; - return CMD_SUCCESS; -} - -DEFUN(cfg_msc_cs7_instance_a, - cfg_msc_cs7_instance_a_cmd, - "cs7-instance-a <0-15>", - "Set SS7 to be used by the A-Interface.\n" "SS7 instance reference number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->a.cs7_instance = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_msc_cs7_instance_iu, - cfg_msc_cs7_instance_iu_cmd, - "cs7-instance-iu <0-15>", - "Set SS7 to be used by the Iu-Interface.\n" "SS7 instance reference number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->iu.cs7_instance = atoi(argv[0]); - return CMD_SUCCESS; -} - -static int config_write_msc(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "msc%s", VTY_NEWLINE); - vty_out(vty, " %sassign-tmsi%s", - gsmnet->vlr->cfg.assign_tmsi? "" : "no ", VTY_NEWLINE); - - vty_out(vty, " cs7-instance-a %u%s", gsmnet->a.cs7_instance, - VTY_NEWLINE); - vty_out(vty, " cs7-instance-iu %u%s", gsmnet->iu.cs7_instance, - VTY_NEWLINE); - - mgcpgw_client_config_write(vty, " "); -#ifdef BUILD_IU - ranap_iu_vty_config_write(vty, " "); -#endif - - return CMD_SUCCESS; -} - -static int config_write_net(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "network%s", VTY_NEWLINE); - vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); - vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); - vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); - vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); - vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); - vty_out(vty, " location updating reject cause %u%s", - gsmnet->reject_cause, VTY_NEWLINE); - vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); - vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); - if (gsmnet->tz.override != 0) { - if (gsmnet->tz.dst) - vty_out(vty, " timezone %d %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst, - VTY_NEWLINE); - else - vty_out(vty, " timezone %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE); - } - if (gsmnet->t3212 == 0) - vty_out(vty, " no periodic location update%s", VTY_NEWLINE); - else - vty_out(vty, " periodic location update %u%s", - gsmnet->t3212 * 6, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -void msc_vty_init(struct gsm_network *msc_network) -{ - common_cs_vty_init(msc_network, config_write_net); - - install_element(CONFIG_NODE, &cfg_msc_cmd); - install_node(&msc_node, config_write_msc); - vty_install_default(MSC_NODE); - install_element(MSC_NODE, &cfg_msc_assign_tmsi_cmd); - install_element(MSC_NODE, &cfg_msc_no_assign_tmsi_cmd); - install_element(MSC_NODE, &cfg_msc_cs7_instance_a_cmd); - install_element(MSC_NODE, &cfg_msc_cs7_instance_iu_cmd); - - mgcpgw_client_vty_init(msc_network, MSC_NODE, &msc_network->mgcpgw.conf); -#ifdef BUILD_IU - ranap_iu_vty_init(MSC_NODE, &msc_network->iu.rab_assign_addr_enc); -#endif -} diff --git a/src/libmsc/osmo_msc.c b/src/libmsc/osmo_msc.c deleted file mode 100644 index 4d24f22a6..000000000 --- a/src/libmsc/osmo_msc.c +++ /dev/null @@ -1,387 +0,0 @@ -/* main MSC management code... */ - -/* - * (C) 2010,2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 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/osmo_msc.h> -#include <openbsc/bsc_api.h> -#include <openbsc/debug.h> -#include <openbsc/transaction.h> -#include <openbsc/db.h> -#include <openbsc/vlr.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/a_iface.h> - -#include <openbsc/gsm_04_11.h> - -#include "../../bscconfig.h" -#ifdef BUILD_IU -#include <osmocom/ranap/iu_client.h> -#else -#include <openbsc/iu_dummy.h> -#endif - -/* Receive a SAPI-N-REJECT from BSC */ -void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) -{ - int sapi = dlci & 0x7; - - if (sapi == UM_SAPI_SMS) - gsm411_sapi_n_reject(conn); -} - -static void subscr_conn_bump(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - if (!conn->conn_fsm) - return; - if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED - || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) { - DEBUGP(DMM, "%s: bump: conn still being established (%s)\n", - vlr_subscr_name(conn->vsub), - osmo_fsm_inst_state_name(conn->conn_fsm)); - return; - } - osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_BUMP, NULL); -} - -/* receive a Level 3 Complete message and return MSC_CONN_ACCEPT or - * MSC_CONN_REJECT */ -int msc_compl_l3(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint16_t chosen_channel) -{ - msc_subscr_conn_get(conn); - gsm0408_dispatch(conn, msg); - - /* Bump whether the conn wants to be closed */ - subscr_conn_bump(conn); - - /* If this should be kept, the conn->conn_fsm has placed a use_count */ - msc_subscr_conn_put(conn); - - /* Always return acceptance, because even if the conn was not accepted, - * we assumed ownership of it and the caller shall not interfere with - * that. We may even already have discarded the conn. */ - return MSC_CONN_ACCEPT; - -#if 0 - /* - * If this is a silent call we want the channel to remain open as long as - * possible and this is why we accept this connection regardless of any - * pending transaction or ongoing operation. - */ - if (conn->silent_call) - return MSC_CONN_ACCEPT; - if (conn->loc_operation || conn->sec_operation || conn->anch_operation) - return MSC_CONN_ACCEPT; - if (trans_has_conn(conn)) - return MSC_CONN_ACCEPT; - - LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n"); - return MSC_CONN_REJECT; -#endif -} - -/* Receive a DTAP message from BSC */ -void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) -{ - msc_subscr_conn_get(conn); - gsm0408_dispatch(conn, msg); - - /* Bump whether the conn wants to be closed */ - subscr_conn_bump(conn); - msc_subscr_conn_put(conn); -} - -/* Receive an ASSIGNMENT COMPLETE from BSC */ -void msc_assign_compl(struct gsm_subscriber_connection *conn, - uint8_t rr_cause, uint8_t chosen_channel, - uint8_t encr_alg_id, uint8_t speec) -{ - LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n"); -} - -/* Receive an ASSIGNMENT FAILURE from BSC */ -void msc_assign_fail(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause) -{ - LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n"); -} - -/* Receive a CLASSMARK CHANGE from BSC */ -void msc_classmark_chg(struct gsm_subscriber_connection *conn, - const uint8_t *cm2, uint8_t cm2_len, - const uint8_t *cm3, uint8_t cm3_len) -{ - if (cm2 && cm2_len) { - if (cm2_len > sizeof(conn->classmark.classmark2)) { - LOGP(DRR, LOGL_NOTICE, "%s: classmark2 is %u bytes, truncating at %zu bytes\n", - vlr_subscr_name(conn->vsub), cm2_len, sizeof(conn->classmark.classmark2)); - cm2_len = sizeof(conn->classmark.classmark2); - } - conn->classmark.classmark2_len = cm2_len; - memcpy(conn->classmark.classmark2, cm2, cm2_len); - } - if (cm3 && cm3_len) { - if (cm3_len > sizeof(conn->classmark.classmark3)) { - LOGP(DRR, LOGL_NOTICE, "%s: classmark3 is %u bytes, truncating at %zu bytes\n", - vlr_subscr_name(conn->vsub), cm3_len, sizeof(conn->classmark.classmark3)); - cm3_len = sizeof(conn->classmark.classmark3); - } - conn->classmark.classmark3_len = cm3_len; - memcpy(conn->classmark.classmark3, cm3, cm3_len); - } -} - -/* Receive a CIPHERING MODE COMPLETE from BSC */ -void msc_cipher_mode_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint8_t alg_id) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - uint8_t mi_type; - char imeisv[GSM48_MI_SIZE] = ""; - struct vlr_ciph_result ciph_res = { .cause = VLR_CIPH_REJECT }; - - if (!gh) { - LOGP(DRR, LOGL_ERROR, "invalid: msgb without l3 header\n"); - return; - } - - if (!conn) { - LOGP(DRR, LOGL_ERROR, - "invalid: rx Ciphering Mode Complete on NULL conn\n"); - return; - } - if (!conn->vsub) { - LOGP(DRR, LOGL_ERROR, - "invalid: rx Ciphering Mode Complete for NULL subscr\n"); - return; - } - - DEBUGP(DRR, "%s: CIPHERING MODE COMPLETE\n", - vlr_subscr_name(conn->vsub)); - - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) { - mi_type = TLVP_VAL(&tp, GSM48_IE_MOBILE_ID)[0] & GSM_MI_TYPE_MASK; - if (mi_type == GSM_MI_TYPE_IMEISV - && TLVP_LEN(&tp, GSM48_IE_MOBILE_ID) > 0) { - gsm48_mi_to_string(imeisv, sizeof(imeisv), - TLVP_VAL(&tp, GSM48_IE_MOBILE_ID), - TLVP_LEN(&tp, GSM48_IE_MOBILE_ID)); - ciph_res.imeisv = imeisv; - } - } - - ciph_res.cause = VLR_CIPH_COMPL; - vlr_subscr_rx_ciph_res(conn->vsub, &ciph_res); -} - -struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network) -{ - struct gsm_subscriber_connection *conn; - - conn = talloc_zero(network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = network; - llist_add_tail(&conn->entry, &network->subscr_conns); - return conn; -} - -void msc_subscr_cleanup(struct vlr_subscr *vsub) -{ - if (!vsub) - return; - vsub->lu_fsm = NULL; -} - -void msc_subscr_con_cleanup(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - if (conn->vsub) { - DEBUGP(DRLL, "subscr %s: Freeing subscriber connection\n", - vlr_subscr_name(conn->vsub)); - msc_subscr_cleanup(conn->vsub); - vlr_subscr_put(conn->vsub); - conn->vsub = NULL; - } else - DEBUGP(DRLL, "Freeing subscriber connection" - " with NULL subscriber\n"); - - if (!conn->conn_fsm) - return; - - osmo_fsm_inst_term(conn->conn_fsm, - (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED) - ? OSMO_FSM_TERM_REGULAR - : OSMO_FSM_TERM_ERROR, - NULL); -} - -void msc_subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - msc_subscr_con_cleanup(conn); - - llist_del(&conn->entry); - talloc_free(conn); -} - -/* Receive a CLEAR REQUEST from BSC */ -int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - msc_subscr_conn_close(conn, cause); - return 1; -} - -/* MSC-level operations to be called by libbsc in NITB */ -static struct bsc_api msc_handler = { - .sapi_n_reject = msc_sapi_n_reject, - .compl_l3 = msc_compl_l3, - .dtap = msc_dtap, - .clear_request = msc_clear_request, - .assign_compl = msc_assign_compl, - .assign_fail = msc_assign_fail, - .classmark_chg = msc_classmark_chg, - .cipher_mode_compl = msc_cipher_mode_compl, - .conn_cleanup = msc_subscr_con_cleanup, -}; - -struct bsc_api *msc_bsc_api() { - return &msc_handler; -} - -static void msc_subscr_conn_release_all(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - if (conn->in_release) - return; - conn->in_release = true; - - /* If we're closing in a middle of a trans, we need to clean up */ - trans_conn_closed(conn); - - switch (conn->via_ran) { - case RAN_UTRAN_IU: - ranap_iu_tx_release(conn->iu.ue_ctx, NULL); - /* FIXME: keep the conn until the Iu Release Outcome is - * received from the UE, or a timeout expires. For now, the log - * says "unknown UE" for each release outcome. */ - break; - case RAN_GERAN_A: - a_iface_tx_clear_cmd(conn); - break; - default: - LOGP(DMM, LOGL_ERROR, "%s: Unknown RAN type, cannot tx release/clear\n", - vlr_subscr_name(conn->vsub)); - break; - } -} - -/* If the conn->conn_fsm is still present, dispatch SUBSCR_CONN_E_CN_CLOSE - * event to gracefully terminate the connection. If the conn_fsm is already - * cleared, call msc_subscr_conn_release_all() to take release actions. - * \param cause a GSM_CAUSE_* constant, e.g. GSM_CAUSE_AUTH_FAILED. - */ -void msc_subscr_conn_close(struct gsm_subscriber_connection *conn, - uint32_t cause) -{ - if (!conn) - return; - if (conn->in_release) { - DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):" - " already dispatching release, ignore.\n", - vlr_subscr_name(conn->vsub), cause); - return; - } - if (!conn->conn_fsm) { - DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u): no conn fsm," - " releasing directly without release event.\n", - vlr_subscr_name(conn->vsub), cause); - /* In case of an IMSI Detach, we don't have conn_fsm. Release - * anyway to ensure a timely Iu Release / BSSMAP Clear. */ - msc_subscr_conn_release_all(conn, cause); - return; - } - if (conn->conn_fsm->state == SUBSCR_CONN_S_RELEASED) { - DEBUGP(DMM, "msc_subscr_conn_close(vsub=%s, cause=%u):" - " conn fsm already releasing, ignore.\n", - vlr_subscr_name(conn->vsub), cause); - return; - } - osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_CN_CLOSE, &cause); -} - -/* increment the ref-count. Needs to be called by every user */ -struct gsm_subscriber_connection * -_msc_subscr_conn_get(struct gsm_subscriber_connection *conn, - const char *file, int line) -{ - OSMO_ASSERT(conn); - - if (conn->in_release) - return NULL; - - conn->use_count++; - LOGPSRC(DREF, LOGL_DEBUG, file, line, - "%s: MSC conn use + 1 == %u\n", - vlr_subscr_name(conn->vsub), conn->use_count); - - return conn; -} - -/* decrement the ref-count. Once it reaches zero, we release */ -void _msc_subscr_conn_put(struct gsm_subscriber_connection *conn, - const char *file, int line) -{ - OSMO_ASSERT(conn); - - if (conn->use_count == 0) { - LOGPSRC(DREF, LOGL_ERROR, file, line, - "%s: MSC conn use - 1 failed: is already 0\n", - vlr_subscr_name(conn->vsub)); - return; - } - - conn->use_count--; - LOGPSRC(DREF, LOGL_DEBUG, file, line, - "%s: MSC conn use - 1 == %u\n", - vlr_subscr_name(conn->vsub), conn->use_count); - - if (conn->use_count == 0) - msc_subscr_con_free(conn); -} - -void msc_stop_paging(struct vlr_subscr *vsub) -{ - DEBUGP(DPAG, "Paging can stop for %s\n", vlr_subscr_name(vsub)); - /* tell BSCs and RNCs to stop paging? How? */ -} diff --git a/src/libmsc/rrlp.c b/src/libmsc/rrlp.c deleted file mode 100644 index cd3da066b..000000000 --- a/src/libmsc/rrlp.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - - - -#include <openbsc/gsm_04_08.h> -#include <openbsc/signal.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/chan_alloc.h> - -/* RRLP msPositionReq, nsBased, - * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ -static const uint8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; - -/* RRLP msPositionReq, msBasedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; - -/* RRLP msPositionReq, msAssistedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; - -static int send_rrlp_req(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net = conn->network; - const uint8_t *req; - - switch (net->rrlp.mode) { - case RRLP_MODE_MS_BASED: - req = ms_based_pos_req; - break; - case RRLP_MODE_MS_PREF: - req = ms_pref_pos_req; - break; - case RRLP_MODE_ASS_PREF: - req = ass_pref_pos_req; - break; - case RRLP_MODE_NONE: - default: - return 0; - } - - return gsm48_send_rr_app_info(conn, 0x00, - sizeof(ms_based_pos_req), req); -} - -static int subscr_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct vlr_subscr *vsub; - struct gsm_subscriber_connection *conn; - - switch (signal) { - case S_SUBSCR_ATTACHED: - /* A subscriber has attached. */ - vsub = signal_data; - conn = connection_for_subscr(vsub); - if (!conn) - break; - send_rrlp_req(conn); - break; - } - return 0; -} - -static int paging_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct paging_signal_data *psig_data = signal_data; - - switch (signal) { - case S_PAGING_SUCCEEDED: - /* A subscriber has attached. */ - send_rrlp_req(psig_data->conn); - break; - case S_PAGING_EXPIRED: - break; - } - return 0; -} - -void on_dso_load_rrlp(void) -{ - osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL); - osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL); -} diff --git a/src/libmsc/silent_call.c b/src/libmsc/silent_call.c deleted file mode 100644 index 7af7a8055..000000000 --- a/src/libmsc/silent_call.c +++ /dev/null @@ -1,165 +0,0 @@ -/* GSM silent call feature */ - -/* - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> - -#include <osmocom/core/msgb.h> -#include <openbsc/signal.h> -#include <openbsc/debug.h> -#include <openbsc/paging.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/osmo_msc.h> - -/* paging of the requested subscriber has completed */ -static int paging_cb_silent(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_data) -{ - struct gsm_subscriber_connection *conn = _conn; - struct scall_signal_data sigdata; - int rc = 0; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - DEBUGP(DLSMS, "paging_cb_silent: "); - - sigdata.conn = conn; - sigdata.data = _data; - - switch (event) { - case GSM_PAGING_SUCCEEDED: -#if BEFORE_MSCSPLIT - /* Re-enable this log output once we can obtain this information via - * A-interface, see OS#2391. */ - DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n", - conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); -#endif - conn->silent_call = 1; - msc_subscr_conn_get(conn); - /* increment lchan reference count */ - osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - case GSM_PAGING_OOM: - DEBUGP(DLSMS, "expired\n"); - osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata); - break; - default: - rc = -EINVAL; - break; - } - - return rc; -} - -#if 0 -/* receive a layer 3 message from a silent call */ -int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - /* FIXME: do something like sending it through a UDP port */ - LOGP(DLSMS, LOGL_NOTICE, "Discarding L3 message from a silent call.\n"); - return 0; -} -#endif - -struct msg_match { - uint8_t pdisc; - uint8_t msg_type; -}; - -/* list of messages that are handled inside OpenBSC, even in a silent call */ -static const struct msg_match silent_call_accept[] = { - { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, - { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, -}; - -#if 0 -/* decide if we need to reroute a message as part of a silent call */ -int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - int i; - - /* if we're not part of a silent call, never reroute */ - if (!conn->silent_call) - return 0; - - /* check if we are a special message that is handled in openbsc */ - for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { - if (silent_call_accept[i].pdisc == pdisc && - silent_call_accept[i].msg_type == msg_type) - return 0; - } - - /* otherwise, reroute */ - LOGP(DLSMS, LOGL_INFO, "Rerouting L3 message from a silent call.\n"); - return 1; -} -#endif - - -/* initiate a silent call with a given subscriber */ -int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type) -{ - struct subscr_request *req; - - /* FIXME the VTY command allows selecting a silent call channel type. - * This doesn't apply to the situation after MSCSPLIT with an - * A-interface. */ - req = subscr_request_conn(vsub, paging_cb_silent, data, - "establish silent call"); - return req != NULL; -} - -/* end a silent call with a given subscriber */ -int gsm_silent_call_stop(struct vlr_subscr *vsub) -{ - struct gsm_subscriber_connection *conn; - - conn = connection_for_subscr(vsub); - if (!conn) - return -EINVAL; - - /* did we actually establish a silent call for this guy? */ - if (!conn->silent_call) - return -EINVAL; - -#if BEFORE_MSCSPLIT - /* Re-enable this log output once we can obtain this information via - * A-interface, see OS#2391. */ - DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n", - conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); -#endif - - conn->silent_call = 0; - msc_subscr_conn_put(conn); - - return 0; -} diff --git a/src/libmsc/smpp_openbsc.c b/src/libmsc/smpp_openbsc.c deleted file mode 100644 index 431cb4dfd..000000000 --- a/src/libmsc/smpp_openbsc.c +++ /dev/null @@ -1,794 +0,0 @@ -/* OpenBSC SMPP 3.4 interface, SMSC-side implementation */ - -/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <stdint.h> -#include <errno.h> - -#include <smpp34.h> -#include <smpp34_structs.h> -#include <smpp34_params.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/protocol/gsm_04_11.h> -#include <osmocom/gsm/protocol/smpp34_osmocom.h> - -#include <openbsc/gsm_subscriber.h> -#include <openbsc/debug.h> -#include <openbsc/db.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/gsm_data.h> -#include <openbsc/signal.h> -#include <openbsc/transaction.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/vlr.h> - -#include "smpp_smsc.h" - -/*! \brief find vlr_subscr for a given SMPP NPI/TON/Address */ -static struct vlr_subscr *subscr_by_dst(struct gsm_network *net, - uint8_t npi, uint8_t ton, - const char *addr) -{ - struct vlr_subscr *vsub = NULL; - - switch (npi) { - case NPI_Land_Mobile_E212: - vsub = vlr_subscr_find_by_imsi(net->vlr, addr); - break; - case NPI_ISDN_E163_E164: - case NPI_Private: - vsub = vlr_subscr_find_by_msisdn(net->vlr, addr); - break; - default: - LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi); - break; - } - - log_set_context(LOG_CTX_VLR_SUBSCR, vsub); - return vsub; -} - -static int smpp34_submit_tlv_msg_payload(const struct tlv_t *t, - const struct submit_sm_t *submit, - const uint8_t **sms_msg, - unsigned int *sms_msg_len) -{ - if (submit->sm_length) { - LOGP(DLSMS, LOGL_ERROR, - "SMPP cannot have payload in TLV _and_ in the header\n"); - return -1; - } - *sms_msg = t->value.octet; - *sms_msg_len = t->length; - - return 0; -} - -/*! \brief convert from submit_sm_t to gsm_sms */ -static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net, - const struct submit_sm_t *submit) -{ - const uint8_t *sms_msg = NULL; - unsigned int sms_msg_len = 0; - struct vlr_subscr *dest; - uint16_t msg_ref = 0; - struct gsm_sms *sms; - struct tlv_t *t; - int mode; - - dest = subscr_by_dst(net, submit->dest_addr_npi, - submit->dest_addr_ton, - (const char *)submit->destination_addr); - if (!dest) { - LOGP(DLSMS, LOGL_NOTICE, "SMPP SUBMIT-SM for unknown subscriber: " - "%s (NPI=%u)\n", submit->destination_addr, - submit->dest_addr_npi); - return ESME_RINVDSTADR; - } - - smpp34_tlv_for_each(t, submit->tlv) { - switch (t->tag) { - case TLVID_message_payload: - if (smpp34_submit_tlv_msg_payload(t, submit, &sms_msg, - &sms_msg_len) < 0) { - vlr_subscr_put(dest); - return ESME_ROPTPARNOTALLWD; - } - break; - case TLVID_user_message_reference: - msg_ref = t->value.val16; - break; - default: - break; - } - } - - if (!sms_msg) { - if (submit->sm_length > 0 && submit->sm_length < 255) { - sms_msg = submit->short_message; - sms_msg_len = submit->sm_length; - } else { - LOGP(DLSMS, LOGL_ERROR, - "SMPP neither message payload nor valid sm_length.\n"); - vlr_subscr_put(dest); - return ESME_RINVPARLEN; - } - } - - sms = sms_alloc(); - sms->source = SMS_SOURCE_SMPP; - sms->smpp.sequence_nr = submit->sequence_number; - sms->status_rep_req = submit->registered_delivery; - sms->msg_ref = msg_ref; - - /* fill in the destination address */ - sms->receiver = dest; - sms->dst.ton = submit->dest_addr_ton; - sms->dst.npi = submit->dest_addr_npi; - osmo_strlcpy(sms->dst.addr, dest->msisdn, sizeof(sms->dst.addr)); - - /* fill in the source address */ - sms->src.ton = submit->source_addr_ton; - sms->src.npi = submit->source_addr_npi; - osmo_strlcpy(sms->src.addr, (char *)submit->source_addr, - sizeof(sms->src.addr)); - - if (submit->esm_class == SMPP34_DELIVERY_ACK) - sms->is_report = true; - - if (submit->esm_class & SMPP34_UDHI_IND) - sms->ud_hdr_ind = 1; - - if (submit->esm_class & SMPP34_REPLY_PATH) { - sms->reply_path_req = 1; -#warning Implement reply path - } - - if (submit->data_coding == 0x00 || /* SMSC default */ - submit->data_coding == 0x01) { /* GSM default alphabet */ - sms->data_coding_scheme = GSM338_DCS_1111_7BIT; - mode = MODE_7BIT; - } else if ((submit->data_coding & 0xFC) == 0xF0) { /* 03.38 DCS default */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_7BIT; - } else if (submit->data_coding == 0x02 || - submit->data_coding == 0x04) { - /* 8-bit binary */ - sms->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; - mode = MODE_8BIT; - } else if ((submit->data_coding & 0xFC) == 0xF4) { /* 03.38 DCS 8bit */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_8BIT; - } else if (submit->data_coding == 0x08) { - /* UCS-2 */ - sms->data_coding_scheme = (2 << 2); - mode = MODE_8BIT; - } else { - sms_free(sms); - LOGP(DLSMS, LOGL_ERROR, "SMPP Unknown Data Coding 0x%02x\n", - submit->data_coding); - return ESME_RUNKNOWNERR; - } - - if (mode == MODE_7BIT) { - uint8_t ud_len = 0, padbits = 0; - sms->data_coding_scheme = GSM338_DCS_1111_7BIT; - if (sms->ud_hdr_ind) { - ud_len = *sms_msg + 1; - printf("copying %u bytes user data...\n", ud_len); - memcpy(sms->user_data, sms_msg, - OSMO_MIN(ud_len, sizeof(sms->user_data))); - sms_msg += ud_len; - sms_msg_len -= ud_len; - padbits = 7 - (ud_len % 7); - } - gsm_septets2octets(sms->user_data+ud_len, sms_msg, - sms_msg_len, padbits); - sms->user_data_len = (ud_len*8 + padbits)/7 + sms_msg_len;/* SEPTETS */ - /* FIXME: sms->text */ - } else { - memcpy(sms->user_data, sms_msg, sms_msg_len); - sms->user_data_len = sms_msg_len; - } - - *psms = sms; - return ESME_ROK; -} - -/*! \brief handle incoming libsmpp34 ssubmit_sm_t from remote ESME */ -int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, - struct submit_sm_resp_t *submit_r) -{ - struct gsm_sms *sms; - struct gsm_network *net = esme->smsc->priv; - struct sms_signal_data sig; - int rc = -1; - - rc = submit_to_sms(&sms, net, submit); - if (rc != ESME_ROK) { - submit_r->command_status = rc; - return 0; - } - smpp_esme_get(esme); - sms->smpp.esme = esme; - sms->protocol_id = submit->protocol_id; - - switch (submit->esm_class & SMPP34_MSG_MODE_MASK) { - case 0: /* default */ - case 1: /* datagram */ - case 3: /* store-and-forward */ - rc = db_sms_store(sms); - sms_free(sms); - sms = NULL; - if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "SMPP SUBMIT-SM: Unable to " - "store SMS in database\n"); - submit_r->command_status = ESME_RSYSERR; - return 0; - } - strcpy((char *)submit_r->message_id, "msg_id_not_implemented"); - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Stored in DB\n"); - - memset(&sig, 0, sizeof(sig)); - osmo_signal_dispatch(SS_SMS, S_SMS_SUBMITTED, &sig); - rc = 0; - break; - case 2: /* forward (i.e. transaction) mode */ - LOGP(DLSMS, LOGL_DEBUG, "SMPP SUBMIT-SM: Forwarding in " - "real time (Transaction/Forward mode)\n"); - sms->smpp.transaction_mode = 1; - gsm411_send_sms_subscr(sms->receiver, sms); - rc = 1; /* don't send any response yet */ - break; - } - return rc; -} - -static void alert_all_esme(struct smsc *smsc, struct vlr_subscr *vsub, - uint8_t smpp_avail_status) -{ - struct osmo_esme *esme; - - llist_for_each_entry(esme, &smsc->esme_list, list) { - /* we currently send an alert notification to each ESME that is - * connected, and do not require a (non-existant) delivery - * pending flag to be set before, FIXME: make this VTY - * configurable */ - if (esme->acl && esme->acl->deliver_src_imsi) { - smpp_tx_alert(esme, TON_Subscriber_Number, - NPI_Land_Mobile_E212, - vsub->imsi, smpp_avail_status); - } else { - smpp_tx_alert(esme, TON_Network_Specific, - NPI_ISDN_E163_E164, - vsub->msisdn, smpp_avail_status); - } - } -} - - -/*! \brief signal handler for status of attempted SMS deliveries */ -static int smpp_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_signal_data *sig_sms = signal_data; - struct gsm_sms *sms = sig_sms->sms; - struct smsc *smsc = handler_data; - int rc = 0; - - if (!sms) - return 0; - - if (sms->source != SMS_SOURCE_SMPP) - return 0; - - switch (signal) { - case S_SMS_MEM_EXCEEDED: - /* fall-through: There is no ESME_Rxxx result code to - * indicate a MEMORY EXCEEDED in transaction mode back - * to the ESME */ - case S_SMS_UNKNOWN_ERROR: - if (sms->smpp.transaction_mode) { - /* Send back the SUBMIT-SM response with apropriate error */ - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Error\n"); - rc = smpp_tx_submit_r(sms->smpp.esme, - sms->smpp.sequence_nr, - ESME_RDELIVERYFAILURE, - sms->smpp.msg_id); - } - break; - case S_SMS_DELIVERED: - /* SMS layer tells us the delivery has been completed */ - if (sms->smpp.transaction_mode) { - /* Send back the SUBMIT-SM response */ - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Success\n"); - rc = smpp_tx_submit_r(sms->smpp.esme, - sms->smpp.sequence_nr, - ESME_ROK, sms->smpp.msg_id); - } - break; - case S_SMS_SMMA: - if (!sig_sms->trans || !sig_sms->trans->vsub) { - /* SMMA without a subscriber? strange... */ - LOGP(DLSMS, LOGL_NOTICE, "SMMA without subscriber?\n"); - break; - } - - /* There's no real 1:1 match for SMMA in SMPP. However, - * an ALERT NOTIFICATION seems to be the most logical - * choice */ - alert_all_esme(smsc, sig_sms->trans->vsub, 0); - break; - } - - return rc; -} - -/*! \brief signal handler for subscriber related signals */ -static int smpp_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct vlr_subscr *vsub = signal_data; - struct smsc *smsc = handler_data; - uint8_t smpp_avail_status; - - /* determine the smpp_avail_status depending on attach/detach */ - switch (signal) { - case S_SUBSCR_ATTACHED: - smpp_avail_status = 0; - break; - case S_SUBSCR_DETACHED: - smpp_avail_status = 2; - break; - default: - return 0; - } - - alert_all_esme(smsc, vsub, smpp_avail_status); - - return 0; -} - -/* GSM 03.38 6.2.1 Character expanding (no decode!) */ -static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) -{ - int i = 0; - int shift = 0; - uint8_t c; - - /* skip the user data header */ - if (ud_hdr_ind) { - /* get user data header length + 1 (for the 'user data header length'-field) */ - shift = ((user_data[0] + 1) * 8) / 7; - if ((((user_data[0] + 1) * 8) % 7) != 0) - shift++; - septet_l = septet_l - shift; - } - - for (i = 0; i < septet_l; i++) { - c = - ((user_data[((i + shift) * 7 + 7) >> 3] << - (7 - (((i + shift) * 7 + 7) & 7))) | - (user_data[((i + shift) * 7) >> 3] >> - (((i + shift) * 7) & 7))) & 0x7f; - - *(text++) = c; - } - - *text = '\0'; - - return i; -} - - -/* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */ -void append_tlv(tlv_t **req_tlv, uint16_t tag, - const uint8_t *data, uint16_t len) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = len; - memcpy(tlv.value.octet, data, tlv.length); - build_tlv(req_tlv, &tlv); -} -void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 1; - tlv.value.val08 = val; - build_tlv(req_tlv, &tlv); -} -void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 2; - tlv.value.val16 = val; - build_tlv(req_tlv, &tlv); -} - -#if BEFORE_MSCSPLIT -/* We currently have no lchan information. Re-add after A-interface, see OS#2390. */ -/* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */ -static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan) -{ - int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - const struct gsm_meas_rep *mr = &lchan->meas_rep[idx]; - const struct gsm_meas_rep_unidir *ul_meas = &mr->ul; - const struct gsm_meas_rep_unidir *dl_meas = &mr->dl; - - /* Osmocom vendor-specific SMPP34 extensions */ - append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn); - if (mr->flags & MEAS_REP_F_MS_L1) { - uint8_t ms_dbm; - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta); - ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr); - append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm); - } else if (mr->flags & MEAS_REP_F_MS_TO) /* Save Timing Offset field = MS Timing Offset + 63 */ - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset + 63); - - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_ul, - rxlev2dbm(ul_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_ul, ul_meas->full.rx_qual); - - if (mr->flags & MEAS_REP_F_DL_VALID) { - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_dl, - rxlev2dbm(dl_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_dl, - dl_meas->full.rx_qual); - } - - if (lchan->conn && lchan->conn->vsub) { - struct vlr_subscr *vsub = lchan->conn->vsub; - size_t imei_len = strlen(vsub->imei); - if (imei_len) - append_tlv(req_tlv, TLVID_osmo_imei, - (uint8_t *)vsub->imei, imei_len+1); - } -} -#endif - -struct { - uint32_t smpp_status_code; - uint8_t gsm411_cause; -} smpp_to_gsm411_err_array[] = { - - /* Seems like most phones don't care about the failure cause, - * although some will display a different notification for - * GSM411_RP_CAUSE_MO_NUM_UNASSIGNED - * Some provoke a display of "Try again later" - * while others a more definitive "Message sending failed" - */ - - { ESME_RSYSERR, GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER }, - { ESME_RINVDSTADR, GSM411_RP_CAUSE_MO_NUM_UNASSIGNED }, - { ESME_RMSGQFUL, GSM411_RP_CAUSE_MO_CONGESTION }, - { ESME_RINVSRCADR, GSM411_RP_CAUSE_MO_SMS_REJECTED }, - { ESME_RINVMSGID, GSM411_RP_CAUSE_INV_TRANS_REF } -}; - -static int smpp_to_gsm411_err(uint32_t smpp_status_code, int *gsm411_cause) -{ - int i; - for (i = 0; i < ARRAY_SIZE(smpp_to_gsm411_err_array); i++) { - if (smpp_to_gsm411_err_array[i].smpp_status_code != smpp_status_code) - continue; - *gsm411_cause = smpp_to_gsm411_err_array[i].gsm411_cause; - return 0; - } - return -1; -} - -static void smpp_cmd_free(struct osmo_smpp_cmd *cmd) -{ - osmo_timer_del(&cmd->response_timer); - llist_del(&cmd->list); - vlr_subscr_put(cmd->vsub); - talloc_free(cmd); -} - -void smpp_cmd_flush_pending(struct osmo_esme *esme) -{ - struct osmo_smpp_cmd *cmd, *next; - - llist_for_each_entry_safe(cmd, next, &esme->smpp_cmd_list, list) - smpp_cmd_free(cmd); -} - -void smpp_cmd_ack(struct osmo_smpp_cmd *cmd) -{ - struct gsm_subscriber_connection *conn; - struct gsm_trans *trans; - - if (cmd->is_report) - goto out; - - conn = connection_for_subscr(cmd->vsub); - if (!conn) { - LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n"); - goto out; - } - - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, cmd->gsm411_trans_id); - if (!trans) { - LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n", - cmd->gsm411_trans_id); - goto out; - } - - gsm411_send_rp_ack(trans, cmd->gsm411_msg_ref); -out: - smpp_cmd_free(cmd); -} - -void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status) -{ - struct gsm_subscriber_connection *conn; - struct gsm_trans *trans; - int gsm411_cause; - - if (cmd->is_report) - goto out; - - conn = connection_for_subscr(cmd->vsub); - if (!conn) { - LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n"); - goto out; - } - - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, cmd->gsm411_trans_id); - if (!trans) { - LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n", - cmd->gsm411_trans_id); - goto out; - } - - if (smpp_to_gsm411_err(status, &gsm411_cause) < 0) - gsm411_cause = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - gsm411_send_rp_error(trans, cmd->gsm411_msg_ref, gsm411_cause); -out: - smpp_cmd_free(cmd); -} - -static void smpp_deliver_sm_cb(void *data) -{ - smpp_cmd_err(data, ESME_RSYSERR); -} - -static int smpp_cmd_enqueue(struct osmo_esme *esme, - struct vlr_subscr *vsub, struct gsm_sms *sms, - uint32_t sequence_number) -{ - struct osmo_smpp_cmd *cmd; - - cmd = talloc_zero(esme, struct osmo_smpp_cmd); - if (!cmd) - return -1; - - cmd->sequence_nr = sequence_number; - cmd->is_report = sms->is_report; - cmd->gsm411_msg_ref = sms->gsm411.msg_ref; - cmd->gsm411_trans_id = sms->gsm411.transaction_id; - cmd->vsub = vlr_subscr_get(vsub); - - /* FIXME: No predefined value for this response_timer as specified by - * SMPP 3.4 specs, section 7.2. Make this configurable? Don't forget - * lchan keeps busy until we get a reply to this SMPP command. Too high - * value may exhaust resources. - */ - osmo_timer_setup(&cmd->response_timer, smpp_deliver_sm_cb, cmd); - osmo_timer_schedule(&cmd->response_timer, 5, 0); - llist_add_tail(&cmd->list, &esme->smpp_cmd_list); - - return 0; -} - -struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme, - uint32_t sequence_nr) -{ - struct osmo_smpp_cmd *cmd; - - llist_for_each_entry(cmd, &esme->smpp_cmd_list, list) { - if (cmd->sequence_nr == sequence_nr) - return cmd; - } - return NULL; -} - -static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms, - struct gsm_subscriber_connection *conn) -{ - struct deliver_sm_t deliver; - int mode, ret; - uint8_t dcs; - - memset(&deliver, 0, sizeof(deliver)); - deliver.command_length = 0; - deliver.command_id = DELIVER_SM; - deliver.command_status = ESME_ROK; - - strcpy((char *)deliver.service_type, "CMT"); - if (esme->acl && esme->acl->deliver_src_imsi) { - deliver.source_addr_ton = TON_Subscriber_Number; - deliver.source_addr_npi = NPI_Land_Mobile_E212; - snprintf((char *)deliver.source_addr, - sizeof(deliver.source_addr), "%s", - conn->vsub->imsi); - } else { - deliver.source_addr_ton = TON_Network_Specific; - deliver.source_addr_npi = NPI_ISDN_E163_E164; - snprintf((char *)deliver.source_addr, - sizeof(deliver.source_addr), "%s", - conn->vsub->msisdn); - } - - deliver.dest_addr_ton = sms->dst.ton; - deliver.dest_addr_npi = sms->dst.npi; - memcpy(deliver.destination_addr, sms->dst.addr, - sizeof(deliver.destination_addr)); - - if (sms->is_report) - deliver.esm_class = SMPP34_DELIVERY_RECEIPT; - else - deliver.esm_class = SMPP34_DATAGRAM_MODE; - - if (sms->ud_hdr_ind) - deliver.esm_class |= SMPP34_UDHI_IND; - if (sms->reply_path_req) - deliver.esm_class |= SMPP34_REPLY_PATH; - - deliver.protocol_id = sms->protocol_id; - deliver.priority_flag = 0; - if (sms->status_rep_req) - deliver.registered_delivery = SMPP34_DELIVERY_RECEIPT_ON; - - /* Figure out SMPP DCS from TP-DCS */ - dcs = sms->data_coding_scheme; - if (smpp_determine_scheme(dcs, &deliver.data_coding, &mode) == -1) - return -1; - - /* Transparently pass on DCS via SMPP if requested */ - if (esme->acl && esme->acl->dcs_transparent) - deliver.data_coding = dcs; - - if (mode == MODE_7BIT) { - uint8_t *dst = deliver.short_message; - - /* SMPP has this strange notion of putting 7bit SMS in - * an octet-aligned mode */ - if (sms->ud_hdr_ind) { - /* length (bytes) of UDH inside UD */ - uint8_t udh_len = sms->user_data[0] + 1; - - /* copy over the UDH */ - memcpy(dst, sms->user_data, udh_len); - dst += udh_len; - deliver.sm_length = udh_len; - } - /* add decoded text */ - deliver.sm_length += gsm_7bit_expand((char *)dst, sms->user_data, sms->user_data_len, sms->ud_hdr_ind); - } else { - deliver.sm_length = sms->user_data_len; - memcpy(deliver.short_message, sms->user_data, deliver.sm_length); - } - -#if BEFORE_MSCSPLIT - /* We currently have no lchan information. Re-add after A-interface, see OS#2390. */ - if (esme->acl && esme->acl->osmocom_ext && conn->lchan) - append_osmo_tlvs(&deliver.tlv, conn->lchan); -#endif - - append_tlv_u16(&deliver.tlv, TLVID_user_message_reference, - sms->msg_ref); - - ret = smpp_tx_deliver(esme, &deliver); - if (ret < 0) - return ret; - - return smpp_cmd_enqueue(esme, conn->vsub, sms, - deliver.sequence_number); -} - -static struct smsc *g_smsc; - -int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) -{ - return g_smsc->smpp_first; -} - -int smpp_try_deliver(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn) -{ - struct osmo_esme *esme; - struct osmo_smpp_addr dst; - int rc; - - memset(&dst, 0, sizeof(dst)); - dst.ton = sms->dst.ton; - dst.npi = sms->dst.npi; - memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); - - rc = smpp_route(g_smsc, &dst, &esme); - if (!rc) - rc = deliver_to_esme(esme, sms, conn); - - return rc; -} - -struct smsc *smsc_from_vty(struct vty *v) -{ - /* FIXME: this is ugly */ - return g_smsc; -} - -/*! \brief Allocate the OpenBSC SMPP interface struct and init VTY. */ -int smpp_openbsc_alloc_init(void *ctx) -{ - g_smsc = smpp_smsc_alloc_init(ctx); - if (!g_smsc) { - LOGP(DSMPP, LOGL_FATAL, "Cannot allocate smsc struct\n"); - return -1; - } - return smpp_vty_init(); -} - -/*! \brief Launch the OpenBSC SMPP interface with the parameters set from VTY. - */ -int smpp_openbsc_start(struct gsm_network *net) -{ - int rc; - g_smsc->priv = net; - - /* If a VTY configuration has taken place, the values have been stored - * in the smsc struct. Otherwise, use the defaults (NULL -> any, 0 -> - * default SMPP port, see smpp_smsc_bind()). */ - rc = smpp_smsc_start(g_smsc, g_smsc->bind_addr, g_smsc->listen_port); - if (rc < 0) - return rc; - - rc = osmo_signal_register_handler(SS_SMS, smpp_sms_cb, g_smsc); - if (rc < 0) - return rc; - rc = osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, g_smsc); - if (rc < 0) - return rc; - - return 0; -} - diff --git a/src/libmsc/smpp_smsc.c b/src/libmsc/smpp_smsc.c deleted file mode 100644 index 04afc49bb..000000000 --- a/src/libmsc/smpp_smsc.c +++ /dev/null @@ -1,1037 +0,0 @@ -/* SMPP 3.4 interface, SMSC-side implementation */ -/* (C) 2012 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <stdint.h> -#include <errno.h> -#include <limits.h> - -#include <sys/socket.h> -#include <netinet/in.h> - -#include <smpp34.h> -#include <smpp34_structs.h> -#include <smpp34_params.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/logging.h> -#include <osmocom/core/write_queue.h> -#include <osmocom/core/talloc.h> - -#include "smpp_smsc.h" - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> - -/*! \brief Ugly wrapper. libsmpp34 should do this itself! */ -#define SMPP34_UNPACK(rc, type, str, data, len) \ - memset(str, 0, sizeof(*str)); \ - rc = smpp34_unpack(type, str, data, len) - -enum emse_bind { - ESME_BIND_RX = 0x01, - ESME_BIND_TX = 0x02, -}; - -const struct value_string smpp_status_strs[] = { - { ESME_ROK, "No Error" }, - { ESME_RINVMSGLEN, "Message Length is invalid" }, - { ESME_RINVCMDLEN, "Command Length is invalid" }, - { ESME_RINVCMDID, "Invalid Command ID" }, - { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, - { ESME_RALYBND, "ESME Already in Bound State" }, - { ESME_RINVPRTFLG, "Invalid Priority Flag" }, - { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, - { ESME_RSYSERR, "System Error" }, - { ESME_RINVSRCADR, "Invalid Source Address" }, - { ESME_RINVDSTADR, "Invalid Destination Address" }, - { ESME_RINVMSGID, "Message ID is invalid" }, - { ESME_RBINDFAIL, "Bind failed" }, - { ESME_RINVPASWD, "Invalid Password" }, - { ESME_RINVSYSID, "Invalid System ID" }, - { ESME_RCANCELFAIL, "Cancel SM Failed" }, - { ESME_RREPLACEFAIL, "Replace SM Failed" }, - { ESME_RMSGQFUL, "Message Queue Full" }, - { ESME_RINVSERTYP, "Invalid Service Type" }, - { ESME_RINVNUMDESTS, "Invalid number of destinations" }, - { ESME_RINVDLNAME, "Invalid Distribution List name" }, - { ESME_RINVDESTFLAG, "Destination flag is invalid" }, - { ESME_RINVSUBREP, "Invalid submit with replace request" }, - { ESME_RINVESMCLASS, "Invalid esm_class field data" }, - { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, - { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, - { ESME_RINVSRCTON, "Invalid Source address TON" }, - { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, - { ESME_RINVDSTTON, "Invalid Destination address TON" }, - { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, - { ESME_RINVSYSTYP, "Invalid system_type field" }, - { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, - { ESME_RINVNUMMSGS, "Invalid number of messages" }, - { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, - { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, - { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, - { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, - { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, - { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, - { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, - { ESME_RQUERYFAIL, "query_sm request failed" }, - { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, - { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, - { ESME_RINVPARLEN, "Invalid Parameter Length" }, - { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, - { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, - { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, - { ESME_RUNKNOWNERR, "Unknown Error" }, - { 0, NULL } -}; - -/*! \brief compare if two SMPP addresses are equal */ -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b) -{ - if (a->ton == b->ton && - a->npi == b->npi && - !strcmp(a->addr, b->addr)) - return 1; - - return 0; -} - - -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - llist_for_each_entry(acl, &smsc->acl_list, list) { - if (!strcmp(acl->system_id, sys_id)) - return acl; - } - - return NULL; -} - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - if (strlen(sys_id) > SMPP_SYS_ID_LEN) - return NULL; - - if (smpp_acl_by_system_id(smsc, sys_id)) - return NULL; - - acl = talloc_zero(smsc, struct osmo_smpp_acl); - if (!acl) - return NULL; - - acl->smsc = smsc; - strcpy(acl->system_id, sys_id); - INIT_LLIST_HEAD(&acl->route_list); - - llist_add_tail(&acl->list, &smsc->acl_list); - - return acl; -} - -void smpp_acl_delete(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r, *r2; - - llist_del(&acl->list); - - /* kill any active ESMEs */ - if (acl->esme) { - struct osmo_esme *esme = acl->esme; - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - esme->acl = NULL; - smpp_esme_put(esme); - } - - /* delete all routes for this ACL */ - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - llist_del(&r->list); - llist_del(&r->global_list); - talloc_free(r); - } - - talloc_free(acl); -} - -static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r; - - r = talloc_zero(acl, struct osmo_smpp_route); - if (!r) - return NULL; - - llist_add_tail(&r->list, &acl->route_list); - llist_add_tail(&r->global_list, &acl->smsc->route_list); - - return r; -} - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r; - - llist_for_each_entry(r, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) - return -EEXIST; - } - - r = route_alloc(acl); - if (!r) - return -ENOMEM; - r->type = SMPP_ROUTE_PREFIX; - r->acl = acl; - memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); - - return 0; -} - -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r, *r2; - - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) { - llist_del(&r->list); - talloc_free(r); - return 0; - } - } - - return -ENODEV; -} - - -/*! \brief increaes the use/reference count */ -void smpp_esme_get(struct osmo_esme *esme) -{ - esme->use++; -} - -static void esme_destroy(struct osmo_esme *esme) -{ - osmo_wqueue_clear(&esme->wqueue); - if (esme->wqueue.bfd.fd >= 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - } - smpp_cmd_flush_pending(esme); - llist_del(&esme->list); - talloc_free(esme); -} - -static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) -{ - esme->own_seq_nr++; - if (esme->own_seq_nr > 0x7fffffff) - esme->own_seq_nr = 1; - - return esme->own_seq_nr; -} - -/*! \brief decrease the use/reference count, free if it is 0 */ -void smpp_esme_put(struct osmo_esme *esme) -{ - esme->use--; - if (esme->use <= 0) - esme_destroy(esme); -} - -/*! \brief try to find a SMPP route (ESME) for given destination */ -int smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest, struct osmo_esme **pesme) -{ - struct osmo_smpp_route *r; - struct osmo_smpp_acl *acl = NULL; - - DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", - dest->ton, dest->npi, dest->addr); - - /* search for a specific route */ - llist_for_each_entry(r, &smsc->route_list, global_list) { - switch (r->type) { - case SMPP_ROUTE_PREFIX: - DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", - r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, - r->acl->system_id); - if (r->u.prefix.ton == dest->ton && - r->u.prefix.npi == dest->npi && - !strncmp(r->u.prefix.addr, dest->addr, - strlen(r->u.prefix.addr))) { - DEBUGP(DSMPP, "Found prefix route ACL\n"); - acl = r->acl; - } - break; - default: - break; - } - - if (acl) - break; - } - - if (!acl) { - /* check for default route */ - if (smsc->def_route) { - DEBUGP(DSMPP, "Using existing default route\n"); - acl = smsc->def_route; - } - } - - if (acl && acl->esme) { - struct osmo_esme *esme; - DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); - esme = acl->esme; - if (esme->bind_flags & ESME_BIND_RX) { - *pesme = esme; - return 0; - } else - LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " - "but not bound for Rx, discarding MO SMS\n", - esme->system_id); - } - - *pesme = NULL; - if (acl) - return GSM48_CC_CAUSE_NETWORK_OOO; - else - return GSM48_CC_CAUSE_UNASSIGNED_NR; -} - - -/*! \brief initialize the libsmpp34 data structure for a response */ -#define INIT_RESP(type, resp, req) { \ - memset((resp), 0, sizeof(*(resp))); \ - (resp)->command_length = 0; \ - (resp)->command_id = type; \ - (resp)->command_status = ESME_ROK; \ - (resp)->sequence_number = (req)->sequence_number; \ -} - -/*! \brief pack a libsmpp34 data strcutrure and send it to the ESME */ -#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) -static int pack_and_send(struct osmo_esme *esme, uint32_t type, void *ptr) -{ - struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); - int rc, rlen; - if (!msg) - return -ENOMEM; - - rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); - if (rc != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", - esme->system_id, smpp34_strerror); - msgb_free(msg); - return -EINVAL; - } - msgb_put(msg, rlen); - - if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", - esme->system_id); - msgb_free(msg); - return -EAGAIN; - } - return 0; -} - -/*! \brief transmit a generic NACK to a remote ESME */ -static int smpp_tx_gen_nack(struct osmo_esme *esme, uint32_t seq, uint32_t status) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - - nack.command_length = 0; - nack.command_id = GENERIC_NACK; - nack.sequence_number = seq; - nack.command_status = status; - - LOGP(DSMPP, LOGL_ERROR, "[%s] Tx GENERIC NACK: %s\n", - esme->system_id, str_command_status(status, buf)); - - return PACK_AND_SEND(esme, &nack); -} - -/*! \brief retrieve SMPP command ID from a msgb */ -static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg) + 4; - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief retrieve SMPP sequence number from a msgb */ -static inline uint32_t smpp_msgb_seq(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg); - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief handle an incoming SMPP generic NACK */ -static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - int rc; - - SMPP34_UNPACK(rc, GENERIC_NACK, &nack, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_ERROR, "[%s] Rx GENERIC NACK: %s\n", - esme->system_id, str_command_status(nack.command_status, buf)); - - return 0; -} - -static int _process_bind(struct osmo_esme *esme, uint8_t if_version, - uint32_t bind_flags, const char *sys_id, - const char *passwd) -{ - struct osmo_smpp_acl *acl; - - if (if_version != SMPP_VERSION) - return ESME_RSYSERR; - - if (esme->bind_flags) - return ESME_RALYBND; - - esme->smpp_version = if_version; - snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); - - acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); - if (!esme->smsc->accept_all) { - if (!acl) { - /* This system is unknown */ - return ESME_RINVSYSID; - } else { - if (strlen(acl->passwd) && - strcmp(acl->passwd, passwd)) { - return ESME_RINVPASWD; - } - } - } - if (acl) { - esme->acl = acl; - acl->esme = esme; - } - - esme->bind_flags = bind_flags; - - return ESME_ROK; -} - - -/*! \brief handle an incoming SMPP BIND RECEIVER */ -static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_receiver_t bind; - struct bind_receiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSMITTER */ -static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transmitter_t bind; - struct bind_transmitter_resp_t bind_r; - struct tlv_t tlv; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSMITTER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - /* build response */ - snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", - esme->smsc->system_id); - - /* add interface version TLV */ - tlv.tag = TLVID_sc_interface_version; - tlv.length = sizeof(uint8_t); - tlv.value.val16 = esme->smpp_version; - build_tlv(&bind_r.tlv, &tlv); - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSCEIVER */ -static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transceiver_t bind; - struct bind_transceiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSCEIVER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP UNBIND */ -static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) -{ - struct unbind_t unbind; - struct unbind_resp_t unbind_r; - int rc; - - SMPP34_UNPACK(rc, UNBIND, &unbind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(UNBIND_RESP, &unbind_r, &unbind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx UNBIND\n", esme->system_id); - - if (esme->bind_flags == 0) { - unbind_r.command_status = ESME_RINVBNDSTS; - goto err; - } - - esme->bind_flags = 0; -err: - return PACK_AND_SEND(esme, &unbind_r); -} - -/*! \brief handle an incoming SMPP ENQUIRE LINK */ -static int smpp_handle_enq_link(struct osmo_esme *esme, struct msgb *msg) -{ - struct enquire_link_t enq; - struct enquire_link_resp_t enq_r; - int rc; - - SMPP34_UNPACK(rc, ENQUIRE_LINK, &enq, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Rx Enquire Link\n", esme->system_id); - - INIT_RESP(ENQUIRE_LINK_RESP, &enq_r, &enq); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx Enquire Link Response\n", esme->system_id); - - return PACK_AND_SEND(esme, &enq_r); -} - -/*! \brief send a SUBMIT-SM RESPONSE to a remote ESME */ -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id) -{ - struct submit_sm_resp_t submit_r; - - memset(&submit_r, 0, sizeof(submit_r)); - submit_r.command_length = 0; - submit_r.command_id = SUBMIT_SM_RESP; - submit_r.command_status = command_status; - submit_r.sequence_number= sequence_nr; - snprintf((char *) submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id); - - return PACK_AND_SEND(esme, &submit_r); -} - -static const struct value_string smpp_avail_strs[] = { - { 0, "Available" }, - { 1, "Denied" }, - { 2, "Unavailable" }, - { 0, NULL } -}; - -/*! \brief send an ALERT_NOTIFICATION to a remote ESME */ -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status) -{ - struct alert_notification_t alert; - struct tlv_t tlv; - - memset(&alert, 0, sizeof(alert)); - alert.command_length = 0; - alert.command_id = ALERT_NOTIFICATION; - alert.command_status = ESME_ROK; - alert.sequence_number = esme_inc_seq_nr(esme); - alert.source_addr_ton = ton; - alert.source_addr_npi = npi; - snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); - - tlv.tag = TLVID_ms_availability_status; - tlv.length = sizeof(uint8_t); - tlv.value.val08 = avail_status; - build_tlv(&alert.tlv, &tlv); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx ALERT_NOTIFICATION (%s/%u/%u): %s\n", - esme->system_id, alert.source_addr, alert.source_addr_ton, - alert.source_addr_npi, - get_value_string(smpp_avail_strs, avail_status)); - - return PACK_AND_SEND(esme, &alert); -} - -/* \brief send a DELIVER-SM message to given ESME */ -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) -{ - deliver->sequence_number = esme_inc_seq_nr(esme); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx DELIVER-SM (from %s)\n", - esme->system_id, deliver->source_addr); - - return PACK_AND_SEND(esme, deliver); -} - -/*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ -static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) -{ - struct deliver_sm_resp_t deliver_r; - struct osmo_smpp_cmd *cmd; - int rc; - - memset(&deliver_r, 0, sizeof(deliver_r)); - SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - cmd = smpp_cmd_find_by_seqnum(esme, deliver_r.sequence_number); - if (!cmd) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Rx DELIVER-SM RESP !? (%s)\n", - esme->system_id, get_value_string(smpp_status_strs, - deliver_r.command_status)); - return -1; - } - - if (deliver_r.command_status == ESME_ROK) - smpp_cmd_ack(cmd); - else - smpp_cmd_err(cmd, deliver_r.command_status); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", - esme->system_id, get_value_string(smpp_status_strs, - deliver_r.command_status)); - - return 0; -} - -/*! \brief handle an incoming SMPP SUBMIT-SM */ -static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) -{ - struct submit_sm_t submit; - struct submit_sm_resp_t submit_r; - int rc; - - memset(&submit, 0, sizeof(submit)); - SMPP34_UNPACK(rc, SUBMIT_SM, &submit, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - if (!(esme->bind_flags & ESME_BIND_TX)) { - submit_r.command_status = ESME_RINVBNDSTS; - return PACK_AND_SEND(esme, &submit_r); - } - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx SUBMIT-SM (%s/%u/%u)\n", - esme->system_id, submit.destination_addr, - submit.dest_addr_ton, submit.dest_addr_npi); - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - rc = handle_smpp_submit(esme, &submit, &submit_r); - if (rc == 0) - return PACK_AND_SEND(esme, &submit_r); - - return rc; -} - -/*! \brief one complete SMPP PDU from the ESME has been received */ -static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg __uses) -{ - uint32_t cmd_id = smpp_msgb_cmdid(msg); - int rc = 0; - - LOGP(DSMPP, LOGL_DEBUG, "[%s] smpp_pdu_rx(%s)\n", esme->system_id, - osmo_hexdump(msgb_data(msg), msgb_length(msg))); - - switch (cmd_id) { - case GENERIC_NACK: - rc = smpp_handle_gen_nack(esme, msg); - break; - case BIND_RECEIVER: - rc = smpp_handle_bind_rx(esme, msg); - break; - case BIND_TRANSMITTER: - rc = smpp_handle_bind_tx(esme, msg); - break; - case BIND_TRANSCEIVER: - rc = smpp_handle_bind_trx(esme, msg); - break; - case UNBIND: - rc = smpp_handle_unbind(esme, msg); - break; - case ENQUIRE_LINK: - rc = smpp_handle_enq_link(esme, msg); - break; - case SUBMIT_SM: - rc = smpp_handle_submit(esme, msg); - break; - case DELIVER_SM_RESP: - rc = smpp_handle_deliver_resp(esme, msg); - break; - case DELIVER_SM: - break; - case DATA_SM: - break; - case CANCEL_SM: - case QUERY_SM: - case REPLACE_SM: - case SUBMIT_MULTI: - LOGP(DSMPP, LOGL_NOTICE, "[%s] Unimplemented PDU Command " - "0x%08x\n", esme->system_id, cmd_id); - break; - default: - LOGP(DSMPP, LOGL_ERROR, "[%s] Unknown PDU Command 0x%08x\n", - esme->system_id, cmd_id); - rc = smpp_tx_gen_nack(esme, smpp_msgb_seq(msg), ESME_RINVCMDID); - break; - } - - return rc; -} - -/* This macro should be called after a call to read() in the read_cb of an - * osmo_fd to properly check for errors. - * rc is the return value of read, err_label is the label to jump to in case of - * an error. The code there should handle closing the connection. - * FIXME: This code should go in libosmocore utils.h so it can be used by other - * projects as well. - * */ -#define OSMO_FD_CHECK_READ(rc, err_label) \ - if (rc < 0) { \ - /* EINTR is a non-fatal error, just try again */ \ - if (errno == EINTR) \ - return 0; \ - goto err_label; \ - } else if (rc == 0) { \ - goto err_label; \ - } - -/* !\brief call-back when per-ESME TCP socket has some data to be read */ -static int esme_link_read_cb(struct osmo_fd *ofd) -{ - struct osmo_esme *esme = ofd->data; - uint32_t len; - uint8_t *lenptr = (uint8_t *) &len; - uint8_t *cur; - struct msgb *msg; - ssize_t rdlen, rc; - - switch (esme->read_state) { - case READ_ST_IN_LEN: - rdlen = sizeof(uint32_t) - esme->read_idx; - rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); - if (rc < 0) - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", - esme->system_id, rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - - if (esme->read_idx >= sizeof(uint32_t)) { - esme->read_len = ntohl(len); - if (esme->read_len < 8 || esme->read_len > UINT16_MAX) { - LOGP(DSMPP, LOGL_ERROR, "[%s] length invalid %u\n", - esme->system_id, esme->read_len); - goto dead_socket; - } - - msg = msgb_alloc(esme->read_len, "SMPP Rx"); - if (!msg) - return -ENOMEM; - esme->read_msg = msg; - cur = msgb_put(msg, sizeof(uint32_t)); - memcpy(cur, lenptr, sizeof(uint32_t)); - esme->read_state = READ_ST_IN_MSG; - esme->read_idx = sizeof(uint32_t); - } - break; - case READ_ST_IN_MSG: - msg = esme->read_msg; - rdlen = esme->read_len - esme->read_idx; - rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); - if (rc < 0) - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", - esme->system_id, rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - msgb_put(msg, rc); - - if (esme->read_idx >= esme->read_len) { - rc = smpp_pdu_rx(esme, esme->read_msg); - msgb_free(esme->read_msg); - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; - } - break; - } - - return 0; -dead_socket: - msgb_free(esme->read_msg); - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - - return 0; -} - -/* call-back of write queue once it wishes to write a message to the socket */ -static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - struct osmo_esme *esme = ofd->data; - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - } else if (rc < msgb_length(msg)) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); - return -1; - } - - return 0; -} - -/* callback for already-accepted new TCP socket */ -static int link_accept_cb(struct smsc *smsc, int fd, - struct sockaddr_storage *s, socklen_t s_len) -{ - struct osmo_esme *esme = talloc_zero(smsc, struct osmo_esme); - if (!esme) { - close(fd); - return -ENOMEM; - } - - INIT_LLIST_HEAD(&esme->smpp_cmd_list); - smpp_esme_get(esme); - esme->own_seq_nr = rand(); - esme_inc_seq_nr(esme); - esme->smsc = smsc; - osmo_wqueue_init(&esme->wqueue, 10); - esme->wqueue.bfd.fd = fd; - esme->wqueue.bfd.data = esme; - esme->wqueue.bfd.when = BSC_FD_READ; - - if (osmo_fd_register(&esme->wqueue.bfd) != 0) { - close(fd); - talloc_free(esme); - return -EIO; - } - - esme->wqueue.read_cb = esme_link_read_cb; - esme->wqueue.write_cb = esme_link_write_cb; - - esme->sa_len = OSMO_MIN(sizeof(esme->sa), s_len); - memcpy(&esme->sa, s, esme->sa_len); - - llist_add_tail(&esme->list, &smsc->esme_list); - - return 0; -} - -/* callback of listening TCP socket */ -static int smsc_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - struct sockaddr_storage sa; - socklen_t sa_len = sizeof(sa); - - rc = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "Accept returns %d (%s)\n", - rc, strerror(errno)); - return rc; - } - return link_accept_cb(ofd->data, rc, &sa, sa_len); -} - -/*! \brief allocate and initialize an smsc struct from talloc context ctx. */ -struct smsc *smpp_smsc_alloc_init(void *ctx) -{ - struct smsc *smsc = talloc_zero(ctx, struct smsc); - - INIT_LLIST_HEAD(&smsc->esme_list); - INIT_LLIST_HEAD(&smsc->acl_list); - INIT_LLIST_HEAD(&smsc->route_list); - - smsc->listen_ofd.data = smsc; - smsc->listen_ofd.cb = smsc_fd_cb; - - return smsc; -} - -/*! \brief Set the SMPP address and port without binding. */ -int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - talloc_free((void*)smsc->bind_addr); - smsc->bind_addr = NULL; - if (bind_addr) { - smsc->bind_addr = talloc_strdup(smsc, bind_addr); - if (!smsc->bind_addr) - return -ENOMEM; - } - smsc->listen_port = port; - return 0; -} - -/*! \brief Bind to given address and port and accept connections. - * \param[in] bind_addr Local IP address, may be NULL for any. - * \param[in] port TCP port number, may be 0 for default SMPP (2775). - */ -int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - int rc; - - /* default port for SMPP */ - if (!port) - port = 2775; - - smpp_smsc_stop(smsc); - - LOGP(DSMPP, LOGL_NOTICE, "SMPP at %s %d\n", - bind_addr? bind_addr : "0.0.0.0", port); - - rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, bind_addr, port, - OSMO_SOCK_F_BIND); - if (rc < 0) - return rc; - - /* store new address and port */ - rc = smpp_smsc_conf(smsc, bind_addr, port); - if (rc) - smpp_smsc_stop(smsc); - return rc; -} - -/*! \brief Change a running connection to a different address/port, and upon - * error switch back to the running configuration. */ -int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - int rc; - - rc = smpp_smsc_start(smsc, bind_addr, port); - if (rc) - /* if there is an error, try to re-bind to the old port */ - return smpp_smsc_start(smsc, smsc->bind_addr, smsc->listen_port); - return 0; -} - -/*! /brief Close SMPP connection. */ -void smpp_smsc_stop(struct smsc *smsc) -{ - if (smsc->listen_ofd.fd > 0) { - close(smsc->listen_ofd.fd); - smsc->listen_ofd.fd = 0; - osmo_fd_unregister(&smsc->listen_ofd); - } -} diff --git a/src/libmsc/smpp_smsc.h b/src/libmsc/smpp_smsc.h deleted file mode 100644 index 755e68577..000000000 --- a/src/libmsc/smpp_smsc.h +++ /dev/null @@ -1,167 +0,0 @@ -#ifndef _SMPP_SMSC_H -#define _SMPP_SMSC_H - -#include <sys/socket.h> -#include <netinet/in.h> - -#include <osmocom/core/utils.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/write_queue.h> -#include <osmocom/core/timer.h> - -#include <smpp34.h> -#include <smpp34_structs.h> -#include <smpp34_params.h> - -#define SMPP_SYS_ID_LEN 16 -#define SMPP_PASSWD_LEN 16 - -#define MODE_7BIT 7 -#define MODE_8BIT 8 - -enum esme_read_state { - READ_ST_IN_LEN = 0, - READ_ST_IN_MSG = 1, -}; - -struct osmo_smpp_acl; - -struct osmo_smpp_addr { - uint8_t ton; - uint8_t npi; - char addr[21+1]; -}; - -struct osmo_esme { - struct llist_head list; - struct smsc *smsc; - struct osmo_smpp_acl *acl; - int use; - - struct llist_head smpp_cmd_list; - - uint32_t own_seq_nr; - - struct osmo_wqueue wqueue; - struct sockaddr_storage sa; - socklen_t sa_len; - - enum esme_read_state read_state; - uint32_t read_len; - uint32_t read_idx; - struct msgb *read_msg; - - uint8_t smpp_version; - char system_id[SMPP_SYS_ID_LEN+1]; - - uint8_t bind_flags; -}; - -struct osmo_smpp_acl { - struct llist_head list; - struct smsc *smsc; - struct osmo_esme *esme; - char *description; - char system_id[SMPP_SYS_ID_LEN+1]; - char passwd[SMPP_PASSWD_LEN+1]; - int default_route; - int deliver_src_imsi; - int osmocom_ext; - int dcs_transparent; - struct llist_head route_list; -}; - -enum osmo_smpp_rtype { - SMPP_ROUTE_NONE, - SMPP_ROUTE_PREFIX, -}; - -struct osmo_smpp_route { - struct llist_head list; /*!< in acl.route_list */ - struct llist_head global_list; /*!< in smsc->route_list */ - struct osmo_smpp_acl *acl; - enum osmo_smpp_rtype type; - union { - struct osmo_smpp_addr prefix; - } u; -}; - -struct osmo_smpp_cmd { - struct llist_head list; - struct vlr_subscr *vsub; - uint32_t sequence_nr; - uint32_t gsm411_msg_ref; - uint8_t gsm411_trans_id; - bool is_report; - struct osmo_timer_list response_timer; -}; - -struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme, - uint32_t sequence_number); -void smpp_cmd_ack(struct osmo_smpp_cmd *cmd); -void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status); -void smpp_cmd_flush_pending(struct osmo_esme *esme); - -struct smsc { - struct osmo_fd listen_ofd; - struct llist_head esme_list; - struct llist_head acl_list; - struct llist_head route_list; - const char *bind_addr; - uint16_t listen_port; - char system_id[SMPP_SYS_ID_LEN+1]; - int accept_all; - int smpp_first; - struct osmo_smpp_acl *def_route; - void *priv; -}; - -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b); - -struct smsc *smpp_smsc_alloc_init(void *ctx); -int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port); -int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port); -int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port); -void smpp_smsc_stop(struct smsc *smsc); - -void smpp_esme_get(struct osmo_esme *esme); -void smpp_esme_put(struct osmo_esme *esme); - -int smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest, struct osmo_esme **emse); - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id); -void smpp_acl_delete(struct osmo_smpp_acl *acl); - -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id); - -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status); - -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); - -int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, - struct submit_sm_resp_t *submit_r); - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); - -int smpp_vty_init(void); - -int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode); - - - -struct gsm_sms; -struct gsm_subscriber_connection; - -int smpp_route_smpp_first(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn); -int smpp_try_deliver(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn); -#endif diff --git a/src/libmsc/smpp_utils.c b/src/libmsc/smpp_utils.c deleted file mode 100644 index d0850d8c1..000000000 --- a/src/libmsc/smpp_utils.c +++ /dev/null @@ -1,62 +0,0 @@ - -/* (C) 2012-2013 by Harald Welte <laforge@gnumonks.org> - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "smpp_smsc.h" -#include <openbsc/debug.h> - - -int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode) -{ - if ((dcs & 0xF0) == 0xF0) { - if (dcs & 0x04) { - /* bit 2 == 1: 8bit data */ - *data_coding = 0x02; - *mode = MODE_8BIT; - } else { - /* bit 2 == 0: default alphabet */ - *data_coding = 0x01; - *mode = MODE_7BIT; - } - } else if ((dcs & 0xE0) == 0) { - switch (dcs & 0xC) { - case 0: - *data_coding = 0x01; - *mode = MODE_7BIT; - break; - case 4: - *data_coding = 0x02; - *mode = MODE_8BIT; - break; - case 8: - *data_coding = 0x08; /* UCS-2 */ - *mode = MODE_8BIT; - break; - default: - goto unknown_mo; - } - } else { -unknown_mo: - LOGP(DLSMS, LOGL_ERROR, "SMPP MO Unknown Data Coding 0x%02x\n", dcs); - return -1; - } - - return 0; - -} diff --git a/src/libmsc/smpp_vty.c b/src/libmsc/smpp_vty.c deleted file mode 100644 index 13467f182..000000000 --- a/src/libmsc/smpp_vty.c +++ /dev/null @@ -1,612 +0,0 @@ -/* SMPP vty interface */ - -/* (C) 2012 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <ctype.h> -#include <string.h> -#include <errno.h> -#include <netdb.h> -#include <sys/socket.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/buffer.h> -#include <osmocom/vty/vty.h> - -#include <osmocom/core/linuxlist.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/talloc.h> - -#include <openbsc/vty.h> - -#include "smpp_smsc.h" - -struct smsc *smsc_from_vty(struct vty *v); - -static struct cmd_node smpp_node = { - SMPP_NODE, - "%s(config-smpp)# ", - 1, -}; - -static struct cmd_node esme_node = { - SMPP_ESME_NODE, - "%s(config-smpp-esme)# ", - 1, -}; - -DEFUN(cfg_smpp, cfg_smpp_cmd, - "smpp", "Configure SMPP SMS Interface") -{ - vty->node = SMPP_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_first, cfg_smpp_first_cmd, - "smpp-first", - "Try SMPP routes before the subscriber DB\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - smsc->smpp_first = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_no_smpp_first, cfg_no_smpp_first_cmd, - "no smpp-first", - NO_STR "Try SMPP before routes before the subscriber DB\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - smsc->smpp_first = 0; - return CMD_SUCCESS; -} - -static int smpp_local_tcp(struct vty *vty, - const char *bind_addr, uint16_t port) -{ - struct smsc *smsc = smsc_from_vty(vty); - int is_running = smsc->listen_ofd.fd > 0; - int same_bind_addr; - int rc; - - /* If it is not up yet, don't rebind, just set values. */ - if (!is_running) { - rc = smpp_smsc_conf(smsc, bind_addr, port); - if (rc < 0) { - vty_out(vty, "%% Cannot configure new address:port%s", - VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; - } - - rc = smpp_smsc_restart(smsc, bind_addr, port); - if (rc < 0) { - vty_out(vty, "%% Cannot bind to new port %s:%u nor to" - " old port %s:%u%s", - bind_addr? bind_addr : "0.0.0.0", - port, - smsc->bind_addr? smsc->bind_addr : "0.0.0.0", - smsc->listen_port, - VTY_NEWLINE); - return CMD_WARNING; - } - - same_bind_addr = (bind_addr == smsc->bind_addr) - || (bind_addr && smsc->bind_addr - && (strcmp(bind_addr, smsc->bind_addr) == 0)); - - if (!same_bind_addr || port != smsc->listen_port) { - vty_out(vty, "%% Cannot bind to new port %s:%u, staying on" - " old port %s:%u%s", - bind_addr? bind_addr : "0.0.0.0", - port, - smsc->bind_addr? smsc->bind_addr : "0.0.0.0", - smsc->listen_port, - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_port, cfg_smpp_port_cmd, - "local-tcp-port <1-65535>", - "Set the local TCP port on which we listen for SMPP\n" - "TCP port number") -{ - struct smsc *smsc = smsc_from_vty(vty); - uint16_t port = atoi(argv[0]); - return smpp_local_tcp(vty, smsc->bind_addr, port); -} - -DEFUN(cfg_smpp_addr_port, cfg_smpp_addr_port_cmd, - "local-tcp-ip A.B.C.D <1-65535>", - "Set the local IP address and TCP port on which we listen for SMPP\n" - "Local IP address\n" - "TCP port number") -{ - const char *bind_addr = argv[0]; - uint16_t port = atoi(argv[1]); - return smpp_local_tcp(vty, bind_addr, port); -} - -DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, - "system-id ID", - "Set the System ID of this SMSC\n" - "Alphanumeric SMSC System ID\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (strlen(argv[0])+1 > sizeof(smsc->system_id)) - return CMD_WARNING; - - strcpy(smsc->system_id, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, - "policy (accept-all|closed)", - "Set the authentication policy of this SMSC\n" - "Accept all SMPP connections independeint of system ID / passwd\n" - "Accept only SMPP connections from ESMEs explicitly configured") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (!strcmp(argv[0], "accept-all")) - smsc->accept_all = 1; - else - smsc->accept_all = 0; - - return CMD_SUCCESS; -} - - -static int config_write_smpp(struct vty *vty) -{ - struct smsc *smsc = smsc_from_vty(vty); - - vty_out(vty, "smpp%s", VTY_NEWLINE); - if (smsc->bind_addr) - vty_out(vty, " local-tcp-ip %s %u%s", smsc->bind_addr, - smsc->listen_port, VTY_NEWLINE); - else - vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, - VTY_NEWLINE); - if (strlen(smsc->system_id) > 0) - vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); - vty_out(vty, " policy %s%s", - smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); - vty_out(vty, " %ssmpp-first%s", - smsc->smpp_first ? "" : "no ", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme, cfg_esme_cmd, - "esme NAME", - "Configure a particular ESME\n" - "Alphanumeric System ID of the ESME to be configured\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_smpp_acl *acl; - const char *id = argv[0]; - - if (strlen(id) > 16) { - vty_out(vty, "%% System ID cannot be more than 16 " - "characters long%s", VTY_NEWLINE); - return CMD_WARNING; - } - acl = smpp_acl_by_system_id(smsc, id); - if (!acl) { - acl = smpp_acl_alloc(smsc, id); - if (!acl) - return CMD_WARNING; - } - - vty->index = acl; - vty->index_sub = &acl->description; - vty->node = SMPP_ESME_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_esme, cfg_no_esme_cmd, - "no esme NAME", - NO_STR "Remove ESME configuration\n" - "Alphanumeric System ID of the ESME to be removed\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_smpp_acl *acl; - const char *id = argv[0]; - - acl = smpp_acl_by_system_id(smsc, id); - if (!acl) { - vty_out(vty, "%% ESME with system id '%s' unknown%s", - id, VTY_NEWLINE); - return CMD_WARNING; - } - - /* FIXME: close the connection, free data structure, etc. */ - - smpp_acl_delete(acl); - - return CMD_SUCCESS; -} - - -DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd, - "password PASSWORD", - "Set the password for this ESME\n" - "Alphanumeric password string\n") -{ - struct osmo_smpp_acl *acl = vty->index; - - if (strlen(argv[0])+1 > sizeof(acl->passwd)) - return CMD_WARNING; - - strcpy(acl->passwd, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd, - "no password", - NO_STR "Remove the password for this ESME\n") -{ - struct osmo_smpp_acl *acl = vty->index; - - memset(acl->passwd, 0, sizeof(acl->passwd)); - - return CMD_SUCCESS; -} - -static int osmo_is_digits(const char *str) -{ - int i; - for (i = 0; i < strlen(str); i++) { - if (!isdigit(str[i])) - return 0; - } - return 1; -} - -static const struct value_string route_errstr[] = { - { -EEXIST, "Route already exists" }, - { -ENODEV, "Route does not exist" }, - { -ENOMEM, "No memory" }, - { -EINVAL, "Invalid" }, - { 0, NULL } -}; - -static const struct value_string smpp_ton_str_short[] = { - { TON_Unknown, "unknown" }, - { TON_International, "international" }, - { TON_National, "national" }, - { TON_Network_Specific, "network" }, - { TON_Subscriber_Number,"subscriber" }, - { TON_Alphanumeric, "alpha" }, - { TON_Abbreviated, "abbrev" }, - { 0, NULL } -}; - -static const struct value_string smpp_npi_str_short[] = { - { NPI_Unknown, "unknown" }, - { NPI_ISDN_E163_E164, "isdn" }, - { NPI_Data_X121, "x121" }, - { NPI_Telex_F69, "f69" }, - { NPI_Land_Mobile_E212, "e212" }, - { NPI_National, "national" }, - { NPI_Private, "private" }, - { NPI_ERMES, "ermes" }, - { NPI_Internet_IP, "ip" }, - { NPI_WAP_Client_Id, "wap" }, - { 0, NULL } -}; - - -#define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n" -#define SMPP_ROUTE_P_STR SMPP_ROUTE_STR "Prefix-match route\n" -#define SMPP_PREFIX_STR "Destination number prefix\n" - -#define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)" -#define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)" -#define TON_STR "Unknown type-of-number\n" \ - "International type-of-number\n" \ - "National type-of-number\n" \ - "Network specific type-of-number\n" \ - "Subscriber type-of-number\n" \ - "Alphanumeric type-of-number\n" \ - "Abbreviated type-of-number\n" -#define NPI_STR "Unknown numbering plan\n" \ - "ISDN (E.164) numbering plan\n" \ - "X.121 numbering plan\n" \ - "F.69 numbering plan\n" \ - "E.212 numbering plan\n" \ - "National numbering plan\n" \ - "Private numbering plan\n" \ - "ERMES numbering plan\n" \ - "IP numbering plan\n" \ - "WAP numbeing plan\n" - -DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd, - "route prefix " TON_CMD " " NPI_CMD " PREFIX", - SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) -{ - struct osmo_smpp_acl *acl = vty->index; - struct osmo_smpp_addr pfx; - int rc; - - /* check if DESTINATION is all-digits */ - if (!osmo_is_digits(argv[2])) { - vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); - return CMD_WARNING; - } - - pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); - pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); - snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); - - rc = smpp_route_pfx_add(acl, &pfx); - if (rc < 0) { - vty_out(vty, "%% error adding prefix route: %s%s", - get_value_string(route_errstr, rc), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd, - "no route prefix " TON_CMD " " NPI_CMD " PREFIX", - NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) -{ - struct osmo_smpp_acl *acl = vty->index; - struct osmo_smpp_addr pfx; - int rc; - - /* check if DESTINATION is all-digits */ - if (!osmo_is_digits(argv[2])) { - vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); - return CMD_WARNING; - } - - pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); - pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); - snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); - - rc = smpp_route_pfx_del(acl, &pfx); - if (rc < 0) { - vty_out(vty, "%% error removing prefix route: %s%s", - get_value_string(route_errstr, rc), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; - -} - - -DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd, - "default-route", - "Set this ESME as default-route for all SMS to unknown destinations") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->default_route = 1; - - if (!acl->smsc->def_route) - acl->smsc->def_route = acl; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd, - "no default-route", NO_STR - "Remove this ESME as default-route for all SMS to unknown destinations") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->default_route = 0; - - /* remove currently active default route, if it was created by - * this ACL */ - if (acl->smsc->def_route && acl->smsc->def_route == acl) - acl->smsc->def_route = NULL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_del_src_imsi, cfg_esme_del_src_imsi_cmd, - "deliver-src-imsi", - "Enable the use of IMSI as source address in DELIVER") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->deliver_src_imsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_del_src_imsi, cfg_esme_no_del_src_imsi_cmd, - "no deliver-src-imsi", NO_STR - "Disable the use of IMSI as source address in DELIVER") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->deliver_src_imsi = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_osmo_ext, cfg_esme_osmo_ext_cmd, - "osmocom-extensions", - "Enable the use of Osmocom SMPP Extensions for this ESME") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->osmocom_ext = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_osmo_ext, cfg_esme_no_osmo_ext_cmd, - "no osmocom-extensions", NO_STR - "Disable the use of Osmocom SMPP Extensions for this ESME") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->osmocom_ext = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_dcs_transp, cfg_esme_dcs_transp_cmd, - "dcs-transparent", - "Enable the transparent pass-through of TP-DCS to SMPP DataCoding") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->dcs_transparent = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_dcs_transp, cfg_esme_no_dcs_transp_cmd, - "no dcs-transparent", NO_STR - "Disable the transparent pass-through of TP-DCS to SMPP DataCoding") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->dcs_transparent = 0; - - return CMD_SUCCESS; -} - - -static void dump_one_esme(struct vty *vty, struct osmo_esme *esme) -{ - char host[128], serv[128]; - - host[0] = 0; - serv[0] = 0; - getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len, - host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV); - - vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s", - esme->system_id, esme->acl ? esme->acl->passwd : "", - esme->smpp_version, VTY_NEWLINE); - vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE); - if (esme->smsc->def_route == esme->acl) - vty_out(vty, " Is current default route%s", VTY_NEWLINE); -} - -DEFUN(show_esme, show_esme_cmd, - "show smpp esme", - SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_esme *esme; - - llist_for_each_entry(esme, &smsc->esme_list, list) - dump_one_esme(vty, esme); - - return CMD_SUCCESS; -} - -static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r) -{ - switch (r->type) { - case SMPP_ROUTE_PREFIX: - vty_out(vty, " route prefix %s ", - get_value_string(smpp_ton_str_short, r->u.prefix.ton)); - vty_out(vty, "%s %s%s", - get_value_string(smpp_npi_str_short, r->u.prefix.npi), - r->u.prefix.addr, VTY_NEWLINE); - break; - case SMPP_ROUTE_NONE: - break; - } -} - -static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r; - - vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE); - if (strlen(acl->passwd)) - vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE); - if (acl->default_route) - vty_out(vty, " default-route%s", VTY_NEWLINE); - if (acl->deliver_src_imsi) - vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE); - if (acl->osmocom_ext) - vty_out(vty, " osmocom-extensions%s", VTY_NEWLINE); - if (acl->dcs_transparent) - vty_out(vty, " dcs-transparent%s", VTY_NEWLINE); - - llist_for_each_entry(r, &acl->route_list, list) - write_esme_route_single(vty, r); -} - -static int config_write_esme(struct vty *v) -{ - struct smsc *smsc = smsc_from_vty(v); - struct osmo_smpp_acl *acl; - - llist_for_each_entry(acl, &smsc->acl_list, list) - config_write_esme_single(v, acl); - - return CMD_SUCCESS; -} - -int smpp_vty_init(void) -{ - install_node(&smpp_node, config_write_smpp); - vty_install_default(SMPP_NODE); - install_element(CONFIG_NODE, &cfg_smpp_cmd); - - install_element(SMPP_NODE, &cfg_smpp_first_cmd); - install_element(SMPP_NODE, &cfg_no_smpp_first_cmd); - install_element(SMPP_NODE, &cfg_smpp_port_cmd); - install_element(SMPP_NODE, &cfg_smpp_addr_port_cmd); - install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); - install_element(SMPP_NODE, &cfg_smpp_policy_cmd); - install_element(SMPP_NODE, &cfg_esme_cmd); - install_element(SMPP_NODE, &cfg_no_esme_cmd); - - install_node(&esme_node, config_write_esme); - vty_install_default(SMPP_ESME_NODE); - install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_route_pfx_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_del_src_imsi_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_del_src_imsi_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_osmo_ext_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_osmo_ext_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_dcs_transp_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_dcs_transp_cmd); - - install_element_ve(&show_esme_cmd); - - return 0; -} diff --git a/src/libmsc/sms_queue.c b/src/libmsc/sms_queue.c deleted file mode 100644 index fe7a608be..000000000 --- a/src/libmsc/sms_queue.c +++ /dev/null @@ -1,578 +0,0 @@ -/* SMS queue to continously attempt to deliver SMS */ -/* - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** - * The difficulty of such a queue is to send a lot of SMS without - * overloading the paging subsystem and the database and other users - * of the MSC. To make the best use we would need to know the number - * of pending paging requests, then throttle the number of SMS we - * want to send and such. - * We will start with a very simple SMS Queue and then try to speed - * things up by collecting data from other parts of the system. - */ - -#include <limits.h> - -#include <openbsc/sms_queue.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/db.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_11.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/signal.h> -#include <openbsc/vlr.h> - -#include <osmocom/core/talloc.h> - -#include <osmocom/vty/vty.h> - -/* - * One pending SMS that we wait for. - */ -struct gsm_sms_pending { - struct llist_head entry; - - struct vlr_subscr *vsub; - unsigned long long sms_id; - int failed_attempts; - int resend; -}; - -struct gsm_sms_queue { - struct osmo_timer_list resend_pending; - struct osmo_timer_list push_queue; - struct gsm_network *network; - int max_fail; - int max_pending; - int pending; - - struct llist_head pending_sms; - - char last_msisdn[GSM_EXTENSION_LENGTH+1]; -}; - -static int sms_subscr_cb(unsigned int, unsigned int, void *, void *); -static int sms_sms_cb(unsigned int, unsigned int, void *, void *); - -static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq, - struct gsm_sms *sms) -{ - struct gsm_sms_pending *pending; - - llist_for_each_entry(pending, &smsq->pending_sms, entry) { - if (pending->sms_id == sms->id) - return pending; - } - - return NULL; -} - -static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms) -{ - return sms_find_pending(smsq, sms) != NULL; -} - -static struct gsm_sms_pending *sms_subscriber_find_pending( - struct gsm_sms_queue *smsq, - struct vlr_subscr *vsub) -{ - struct gsm_sms_pending *pending; - - llist_for_each_entry(pending, &smsq->pending_sms, entry) { - if (pending->vsub == vsub) - return pending; - } - - return NULL; -} - -static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq, - struct vlr_subscr *vsub) -{ - return sms_subscriber_find_pending(smsq, vsub) != NULL; -} - -static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq, - struct gsm_sms *sms) -{ - struct gsm_sms_pending *pending; - - pending = talloc_zero(smsq, struct gsm_sms_pending); - if (!pending) - return NULL; - - pending->vsub = vlr_subscr_get(sms->receiver); - pending->sms_id = sms->id; - return pending; -} - -static void sms_pending_free(struct gsm_sms_pending *pending) -{ - vlr_subscr_put(pending->vsub); - llist_del(&pending->entry); - talloc_free(pending); -} - -static void sms_pending_resend(struct gsm_sms_pending *pending) -{ - struct gsm_network *net = pending->vsub->vlr->user_ctx; - struct gsm_sms_queue *smsq; - LOGP(DLSMS, LOGL_DEBUG, - "Scheduling resend of SMS %llu.\n", pending->sms_id); - - pending->resend = 1; - - smsq = net->sms_queue; - if (osmo_timer_pending(&smsq->resend_pending)) - return; - - osmo_timer_schedule(&smsq->resend_pending, 1, 0); -} - -static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error) -{ - struct gsm_network *net = pending->vsub->vlr->user_ctx; - struct gsm_sms_queue *smsq; - - LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n", - pending->sms_id, pending->failed_attempts); - - smsq = net->sms_queue; - if (++pending->failed_attempts < smsq->max_fail) - return sms_pending_resend(pending); - - sms_pending_free(pending); - smsq->pending -= 1; - sms_queue_trigger(smsq); -} - -/* - * Resend all SMS that are scheduled for a resend. This is done to - * avoid an immediate failure. - */ -static void sms_resend_pending(void *_data) -{ - struct gsm_sms_pending *pending, *tmp; - struct gsm_sms_queue *smsq = _data; - - llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { - struct gsm_sms *sms; - if (!pending->resend) - continue; - - sms = db_sms_get(smsq->network, pending->sms_id); - - /* the sms is gone? Move to the next */ - if (!sms) { - sms_pending_free(pending); - smsq->pending -= 1; - sms_queue_trigger(smsq); - } else { - pending->resend = 0; - gsm411_send_sms_subscr(sms->receiver, sms); - } - } -} - -/* Find the next pending SMS by cycling through the recipients. We could also - * cycle through the pending SMS, but that might cause us to keep trying to - * send SMS to the same few subscribers repeatedly while not servicing other - * subscribers for a long time. By walking the list of recipient MSISDNs, we - * ensure that all subscribers get their fair time to receive SMS. */ -struct gsm_sms *smsq_take_next_sms(struct gsm_network *net, - char *last_msisdn, - size_t last_msisdn_buflen) -{ - struct gsm_sms *sms; - int wrapped = 0; - int sanity = 100; - char started_with_msisdn[last_msisdn_buflen]; - - osmo_strlcpy(started_with_msisdn, last_msisdn, - sizeof(started_with_msisdn)); - - while (wrapped < 2 && (--sanity)) { - /* If we wrapped around and passed the first msisdn, we're - * through the entire SMS DB; end it. */ - if (wrapped && strcmp(last_msisdn, started_with_msisdn) >= 0) - break; - - sms = db_sms_get_next_unsent_rr_msisdn(net, last_msisdn, 9); - if (!sms) { - last_msisdn[0] = '\0'; - wrapped ++; - continue; - } - - /* Whatever happens, next time around service another recipient - */ - osmo_strlcpy(last_msisdn, sms->dst.addr, last_msisdn_buflen); - - /* Is the subscriber attached? If not, go to next SMS */ - if (!sms->receiver || !sms->receiver->lu_complete) - continue; - - return sms; - } - - DEBUGP(DLSMS, "SMS queue: no SMS to be sent\n"); - return NULL; -} - -/** - * I will submit up to max_pending - pending SMS to the - * subsystem. - */ -static void sms_submit_pending(void *_data) -{ - struct gsm_sms_queue *smsq = _data; - int attempts = smsq->max_pending - smsq->pending; - int initialized = 0; - unsigned long long first_sub = 0; - int attempted = 0, rounds = 0; - - LOGP(DLSMS, LOGL_DEBUG, "Attempting to send %d SMS\n", attempts); - - do { - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - - sms = smsq_take_next_sms(smsq->network, smsq->last_msisdn, - sizeof(smsq->last_msisdn)); - if (!sms) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n", - attempted); - break; - } - - rounds += 1; - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS round %d\n", rounds); - - /* - * This code needs to detect a loop. It assumes that no SMS - * will vanish during the time this is executed. We will remember - * the id of the first GSM subscriber we see and then will - * compare this. The Database code should make sure that we will - * see all other subscribers first before seeing this one again. - * - * It is always scary to have an infinite loop like this. - */ - if (!initialized) { - first_sub = sms->receiver->id; - initialized = 1; - } else if (first_sub == sms->receiver->id) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (loop) (%d attempted)\n", - attempted); - sms_free(sms); - break; - } - - /* no need to send a pending sms */ - if (sms_is_in_pending(smsq, sms)) { - LOGP(DLSMS, LOGL_DEBUG, - "SMSqueue with pending sms: %llu. Skipping\n", sms->id); - sms_free(sms); - continue; - } - - /* no need to send a SMS with the same receiver */ - if (sms_subscriber_is_pending(smsq, sms->receiver)) { - LOGP(DLSMS, LOGL_DEBUG, - "SMSqueue with pending sub: %llu. Skipping\n", sms->receiver->id); - sms_free(sms); - continue; - } - - pending = sms_pending_from(smsq, sms); - if (!pending) { - LOGP(DLSMS, LOGL_ERROR, - "Failed to create pending SMS entry.\n"); - sms_free(sms); - continue; - } - - attempted += 1; - smsq->pending += 1; - llist_add_tail(&pending->entry, &smsq->pending_sms); - gsm411_send_sms_subscr(sms->receiver, sms); - } while (attempted < attempts && rounds < 1000); - - LOGP(DLSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds); -} - -/** - * Send the next SMS or trigger the queue - */ -static void sms_send_next(struct vlr_subscr *vsub) -{ - struct gsm_network *net = vsub->vlr->user_ctx; - struct gsm_sms_queue *smsq = net->sms_queue; - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - /* the subscriber should not be in the queue */ - OSMO_ASSERT(!sms_subscriber_is_pending(smsq, vsub)); - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX); - if (!sms) - goto no_pending_sms; - - /* The sms should not be scheduled right now */ - OSMO_ASSERT(!sms_is_in_pending(smsq, sms)); - - /* Remember that we deliver this SMS and send it */ - pending = sms_pending_from(smsq, sms); - if (!pending) { - LOGP(DLSMS, LOGL_ERROR, - "Failed to create pending SMS entry.\n"); - sms_free(sms); - goto no_pending_sms; - } - - smsq->pending += 1; - llist_add_tail(&pending->entry, &smsq->pending_sms); - gsm411_send_sms_subscr(sms->receiver, sms); - return; - -no_pending_sms: - /* Try to send the SMS to avoid the queue being stuck */ - sms_submit_pending(net->sms_queue); -} - -/* - * Kick off the queue again. - */ -int sms_queue_trigger(struct gsm_sms_queue *smsq) -{ - LOGP(DLSMS, LOGL_DEBUG, "Triggering SMS queue\n"); - if (osmo_timer_pending(&smsq->push_queue)) - return 0; - - osmo_timer_schedule(&smsq->push_queue, 1, 0); - return 0; -} - -int sms_queue_start(struct gsm_network *network, int max_pending) -{ - struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue); - if (!sms) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n"); - return -1; - } - - osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network); - osmo_signal_register_handler(SS_SMS, sms_sms_cb, network); - - network->sms_queue = sms; - INIT_LLIST_HEAD(&sms->pending_sms); - sms->max_fail = 1; - sms->network = network; - sms->max_pending = max_pending; - osmo_timer_setup(&sms->push_queue, sms_submit_pending, sms); - osmo_timer_setup(&sms->resend_pending, sms_resend_pending, sms); - - sms_submit_pending(sms); - - return 0; -} - -static int sub_ready_for_sm(struct gsm_network *net, struct vlr_subscr *vsub) -{ - struct gsm_sms *sms; - struct gsm_sms_pending *pending; - struct gsm_subscriber_connection *conn; - - /* - * The code used to be very clever and tried to submit - * a SMS during the Location Updating Request. This has - * two issues: - * 1.) The Phone might not be ready yet, e.g. the C155 - * will not respond to the Submit when it is booting. - * 2.) The queue is already trying to submit SMS to the - * user and by not responding to the paging request - * we will set the LAC back to 0. We would have to - * stop the paging and move things over. - * - * We need to be careful in what we try here. - */ - - /* check if we have pending requests */ - pending = sms_subscriber_find_pending(net->sms_queue, vsub); - if (pending) { - LOGP(DMSC, LOGL_NOTICE, - "Pending paging while subscriber %llu attached.\n", - vsub->id); - return 0; - } - - conn = connection_for_subscr(vsub); - if (!conn) - return -1; - - /* Now try to deliver any pending SMS to this sub */ - sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX); - if (!sms) - return -1; - gsm411_send_sms(conn, sms); - return 0; -} - -static int sms_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct vlr_subscr *vsub = signal_data; - - if (signal != S_SUBSCR_ATTACHED) - return 0; - - /* this is readyForSM */ - return sub_ready_for_sm(handler_data, vsub); -} - -static int sms_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *network = handler_data; - struct sms_signal_data *sig_sms = signal_data; - struct gsm_sms_pending *pending; - struct vlr_subscr *vsub; - - /* We got a new SMS and maybe should launch the queue again. */ - if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { - /* TODO: For SMMA we might want to re-use the radio connection. */ - sms_queue_trigger(network->sms_queue); - return 0; - } - - if (!sig_sms->sms) - return -1; - - - /* - * Find the entry of our queue. The SMS subsystem will submit - * sms that are not in our control as we just have a channel - * open anyway. - */ - pending = sms_find_pending(network->sms_queue, sig_sms->sms); - if (!pending) - return 0; - - switch (signal) { - case S_SMS_DELIVERED: - /* Remember the subscriber and clear the pending entry */ - network->sms_queue->pending -= 1; - vsub = vlr_subscr_get(pending->vsub); - sms_pending_free(pending); - /* Attempt to send another SMS to this subscriber */ - sms_send_next(vsub); - vlr_subscr_put(vsub); - break; - case S_SMS_MEM_EXCEEDED: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - case S_SMS_UNKNOWN_ERROR: - /* - * There can be many reasons for this failure. E.g. the paging - * timed out, the subscriber was not paged at all, or there was - * a protocol error. The current strategy is to try sending the - * next SMS for busy/oom and to retransmit when we have paged. - * - * When the paging expires three times we will disable the - * subscriber. If we have some kind of other transmit error we - * should flag the SMS as bad. - */ - switch (sig_sms->paging_result) { - case 0: - /* BAD SMS? */ - db_sms_inc_deliver_attempts(sig_sms->sms); - sms_pending_failed(pending, 0); - break; - case GSM_PAGING_EXPIRED: - sms_pending_failed(pending, 1); - break; - - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", - sig_sms->paging_result); - } - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", - sig_sms->paging_result); - } - - return 0; -} - -/* VTY helper functions */ -int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty) -{ - struct gsm_sms_pending *pending; - - vty_out(vty, "SMSqueue with max_pending: %d pending: %d%s", - smsq->max_pending, smsq->pending, VTY_NEWLINE); - - llist_for_each_entry(pending, &smsq->pending_sms, entry) - vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s", - pending->vsub->id, pending->sms_id, - pending->failed_attempts, VTY_NEWLINE); - return 0; -} - -int sms_queue_set_max_pending(struct gsm_sms_queue *smsq, int max_pending) -{ - LOGP(DLSMS, LOGL_NOTICE, "SMSqueue old max: %d new: %d\n", - smsq->max_pending, max_pending); - smsq->max_pending = max_pending; - return 0; -} - -int sms_queue_set_max_failure(struct gsm_sms_queue *smsq, int max_fail) -{ - LOGP(DLSMS, LOGL_NOTICE, "SMSqueue max failure old: %d new: %d\n", - smsq->max_fail, max_fail); - smsq->max_fail = max_fail; - return 0; -} - -int sms_queue_clear(struct gsm_sms_queue *smsq) -{ - struct gsm_sms_pending *pending, *tmp; - - llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { - LOGP(DLSMS, LOGL_NOTICE, - "SMSqueue clearing for sub %llu\n", pending->vsub->id); - sms_pending_free(pending); - } - - smsq->pending = 0; - return 0; -} diff --git a/src/libmsc/subscr_conn.c b/src/libmsc/subscr_conn.c deleted file mode 100644 index bcab8e48c..000000000 --- a/src/libmsc/subscr_conn.c +++ /dev/null @@ -1,359 +0,0 @@ -/* MSC subscriber connection implementation */ - -/* - * (C) 2016 by sysmocom s.m.f.c. <info@sysmocom.de> - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * 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/fsm.h> -#include <osmocom/core/signal.h> - -#include <openbsc/osmo_msc.h> -#include <openbsc/vlr.h> -#include <openbsc/debug.h> -#include <openbsc/transaction.h> -#include <openbsc/signal.h> -#include <openbsc/a_iface.h> - -#define SUBSCR_CONN_TIMEOUT 5 /* seconds */ - -static const struct value_string subscr_conn_fsm_event_names[] = { - OSMO_VALUE_STRING(SUBSCR_CONN_E_INVALID), - OSMO_VALUE_STRING(SUBSCR_CONN_E_START), - OSMO_VALUE_STRING(SUBSCR_CONN_E_ACCEPTED), - OSMO_VALUE_STRING(SUBSCR_CONN_E_COMMUNICATING), - OSMO_VALUE_STRING(SUBSCR_CONN_E_BUMP), - OSMO_VALUE_STRING(SUBSCR_CONN_E_MO_CLOSE), - OSMO_VALUE_STRING(SUBSCR_CONN_E_CN_CLOSE), - { 0, NULL } -}; - -const struct value_string subscr_conn_from_names[] = { - OSMO_VALUE_STRING(SUBSCR_CONN_FROM_INVALID), - OSMO_VALUE_STRING(SUBSCR_CONN_FROM_LU), - OSMO_VALUE_STRING(SUBSCR_CONN_FROM_CM_SERVICE_REQ), - OSMO_VALUE_STRING(SUBSCR_CONN_FROM_PAGING_RESP), - { 0, NULL } -}; - -static void paging_event(struct gsm_subscriber_connection *conn, - enum gsm_paging_event pe) -{ - subscr_paging_dispatch(GSM_HOOK_RR_PAGING, pe, NULL, conn, conn->vsub); -} - -void subscr_conn_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - OSMO_ASSERT(event == SUBSCR_CONN_E_START); - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_NEW, - SUBSCR_CONN_TIMEOUT, 0); -} - -void subscr_conn_fsm_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct gsm_subscriber_connection *conn = fi->priv; - enum subscr_conn_from from = SUBSCR_CONN_FROM_INVALID; - bool success; - - if (data) { - from = *(enum subscr_conn_from*)data; - LOGPFSM(fi, "%s\n", subscr_conn_from_name(from)); - } - - /* If accepted, transition the state, all other cases mean failure. */ - switch (event) { - case SUBSCR_CONN_E_ACCEPTED: - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_ACCEPTED, - SUBSCR_CONN_TIMEOUT, 0); - break; - - case SUBSCR_CONN_E_MO_CLOSE: - case SUBSCR_CONN_E_CN_CLOSE: - if (data) - LOGPFSM(fi, "Close event, cause %u\n", - *(uint32_t*)data); - /* will release further below, see - * 'if (fi->state != SUBSCR_CONN_S_ACCEPTED)' */ - break; - - default: - LOGPFSML(fi, LOGL_ERROR, - "Unexpected event: %d %s\n", event, - osmo_fsm_event_name(fi->fsm, event)); - break; - } - - success = (fi->state == SUBSCR_CONN_S_ACCEPTED); - - if (from == SUBSCR_CONN_FROM_LU) - rate_ctr_inc(&conn->network->msc_ctrs->ctr[ - success ? MSC_CTR_LOC_UPDATE_COMPLETED - : MSC_CTR_LOC_UPDATE_FAILED]); - - /* signal paging success or failure in case this was a paging */ - if (from == SUBSCR_CONN_FROM_PAGING_RESP) - paging_event(conn, - success ? GSM_PAGING_SUCCEEDED - : GSM_PAGING_EXPIRED); - - /* FIXME rate counters */ - /*rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]);*/ - - /* On failure, discard the conn */ - if (!success) { - /* TODO: on MO_CLOSE or CN_CLOSE, first go to RELEASING and - * await BSC/RNC confirmation? */ - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); - return; - } - - if (from == SUBSCR_CONN_FROM_CM_SERVICE_REQ) { - conn->received_cm_service_request = true; - LOGPFSML(fi, LOGL_DEBUG, "received_cm_service_request = true\n"); - } - - osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_BUMP, data); -} - -static void subscr_conn_fsm_bump(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct gsm_subscriber_connection *conn = fi->priv; - struct gsm_trans *trans; - - if (conn->silent_call) { - LOGPFSML(fi, LOGL_DEBUG, "bump: silent call still active\n"); - return; - } - - if (conn->received_cm_service_request) { - LOGPFSML(fi, LOGL_DEBUG, "bump: still awaiting first request after a CM Service Request\n"); - return; - } - - if (conn->vsub && !llist_empty(&conn->vsub->cs.requests)) { - struct subscr_request *sr; - if (!log_check_level(fi->fsm->log_subsys, LOGL_DEBUG)) { - llist_for_each_entry(sr, &conn->vsub->cs.requests, entry) { - LOGPFSML(fi, LOGL_DEBUG, "bump: still active: %s\n", - sr->label); - } - } - return; - } - - if ((trans = trans_has_conn(conn))) { - LOGPFSML(fi, LOGL_DEBUG, - "bump: connection still has active transaction: %s\n", - gsm48_pdisc_name(trans->protocol)); - return; - } - - LOGPFSML(fi, LOGL_DEBUG, "bump: releasing conn\n"); - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); -} - -static void subscr_conn_fsm_accepted_enter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct gsm_subscriber_connection *conn = fi->priv; - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, conn->vsub); -} - -static void subscr_conn_fsm_accepted(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - switch (event) { - case SUBSCR_CONN_E_COMMUNICATING: - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_COMMUNICATING, 0, 0); - return; - - case SUBSCR_CONN_E_BUMP: - subscr_conn_fsm_bump(fi, event, data); - return; - - default: - break; - } - /* Whatever unexpected happens in the accepted state, it means release. - * Even if an unexpected event is passed, the safest thing to do is - * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */ - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); -} - -static void subscr_conn_fsm_communicating(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - switch (event) { - case SUBSCR_CONN_E_COMMUNICATING: - /* no-op */ - return; - - case SUBSCR_CONN_E_BUMP: - subscr_conn_fsm_bump(fi, event, data); - return; - - default: - break; - } - /* Whatever unexpected happens in the accepted state, it means release. - * Even if an unexpected event is passed, the safest thing to do is - * discard the conn. We don't expect another SUBSCR_CONN_E_ACCEPTED. */ - osmo_fsm_inst_state_chg(fi, SUBSCR_CONN_S_RELEASED, 0, 0); -} - -static void subscr_conn_fsm_cleanup(struct osmo_fsm_inst *fi, - enum osmo_fsm_term_cause cause) -{ - struct gsm_subscriber_connection *conn = fi->priv; - fi->priv = NULL; - - if (!conn) - return; - conn->conn_fsm = NULL; - msc_subscr_conn_close(conn, cause); - msc_subscr_conn_put(conn); -} - -int subscr_conn_fsm_timeout(struct osmo_fsm_inst *fi) -{ - struct gsm_subscriber_connection *conn = fi->priv; - if (conn) - vlr_subscr_conn_timeout(conn->vsub); - osmo_fsm_inst_dispatch(fi, SUBSCR_CONN_E_CN_CLOSE, NULL); - return 0; -} - -static void subscr_conn_fsm_release(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -#define S(x) (1 << (x)) - -static const struct osmo_fsm_state subscr_conn_fsm_states[] = { - [SUBSCR_CONN_S_INIT] = { - .name = OSMO_STRINGIFY(SUBSCR_CONN_S_INIT), - .in_event_mask = S(SUBSCR_CONN_E_START), - .out_state_mask = S(SUBSCR_CONN_S_NEW), - .action = subscr_conn_fsm_init, - }, - [SUBSCR_CONN_S_NEW] = { - .name = OSMO_STRINGIFY(SUBSCR_CONN_S_NEW), - .in_event_mask = S(SUBSCR_CONN_E_ACCEPTED) | - S(SUBSCR_CONN_E_MO_CLOSE) | - S(SUBSCR_CONN_E_CN_CLOSE), - .out_state_mask = S(SUBSCR_CONN_S_ACCEPTED) | - S(SUBSCR_CONN_S_RELEASED), - .action = subscr_conn_fsm_new, - }, - [SUBSCR_CONN_S_ACCEPTED] = { - .name = OSMO_STRINGIFY(SUBSCR_CONN_S_ACCEPTED), - /* allow everything to release for any odd behavior */ - .in_event_mask = S(SUBSCR_CONN_E_COMMUNICATING) | - S(SUBSCR_CONN_E_BUMP) | - S(SUBSCR_CONN_E_ACCEPTED) | - S(SUBSCR_CONN_E_MO_CLOSE) | - S(SUBSCR_CONN_E_CN_CLOSE), - .out_state_mask = S(SUBSCR_CONN_S_RELEASED) | - S(SUBSCR_CONN_S_COMMUNICATING), - .onenter = subscr_conn_fsm_accepted_enter, - .action = subscr_conn_fsm_accepted, - }, - [SUBSCR_CONN_S_COMMUNICATING] = { - .name = OSMO_STRINGIFY(SUBSCR_CONN_S_COMMUNICATING), - /* allow everything to release for any odd behavior */ - .in_event_mask = S(SUBSCR_CONN_E_BUMP) | - S(SUBSCR_CONN_E_ACCEPTED) | - S(SUBSCR_CONN_E_COMMUNICATING) | - S(SUBSCR_CONN_E_MO_CLOSE) | - S(SUBSCR_CONN_E_CN_CLOSE), - .out_state_mask = S(SUBSCR_CONN_S_RELEASED), - .action = subscr_conn_fsm_communicating, - }, - [SUBSCR_CONN_S_RELEASED] = { - .name = OSMO_STRINGIFY(SUBSCR_CONN_S_RELEASED), - .onenter = subscr_conn_fsm_release, - }, -}; - -static struct osmo_fsm subscr_conn_fsm = { - .name = "Subscr_Conn", - .states = subscr_conn_fsm_states, - .num_states = ARRAY_SIZE(subscr_conn_fsm_states), - .allstate_event_mask = 0, - .allstate_action = NULL, - .log_subsys = DMM, - .event_names = subscr_conn_fsm_event_names, - .cleanup = subscr_conn_fsm_cleanup, - .timer_cb = subscr_conn_fsm_timeout, -}; - -int msc_create_conn_fsm(struct gsm_subscriber_connection *conn, const char *id) -{ - struct osmo_fsm_inst *fi; - OSMO_ASSERT(conn); - - if (conn->conn_fsm) { - LOGP(DMM, LOGL_ERROR, - "%s: Error: connection already in use\n", id); - return -EINVAL; - } - - /* Allocate the FSM not with the subscr_conn. Semantically it would - * make sense, but in subscr_conn_fsm_cleanup(), we want to discard the - * subscriber connection. If the FSM is freed along with the subscriber - * connection, then in _osmo_fsm_inst_term() the osmo_fsm_inst_free() - * that follows the cleanup() call would run into a double free. */ - fi = osmo_fsm_inst_alloc(&subscr_conn_fsm, conn->network, - msc_subscr_conn_get(conn), - LOGL_DEBUG, id); - - if (!fi) { - LOGP(DMM, LOGL_ERROR, - "%s: Failed to allocate subscr conn master FSM\n", id); - return -ENOMEM; - } - conn->conn_fsm = fi; - osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_START, NULL); - return 0; -} - -bool msc_subscr_conn_is_accepted(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return false; - if (!conn->vsub) - return false; - if (!conn->conn_fsm) - return false; - if (!(conn->conn_fsm->state == SUBSCR_CONN_S_ACCEPTED - || conn->conn_fsm->state == SUBSCR_CONN_S_COMMUNICATING)) - return false; - return true; -} - -void msc_subscr_conn_communicating(struct gsm_subscriber_connection *conn) -{ - OSMO_ASSERT(conn); - osmo_fsm_inst_dispatch(conn->conn_fsm, SUBSCR_CONN_E_COMMUNICATING, - NULL); -} - -void msc_subscr_conn_init(void) -{ - osmo_fsm_register(&subscr_conn_fsm); -} diff --git a/src/libmsc/transaction.c b/src/libmsc/transaction.c deleted file mode 100644 index 28e0914a8..000000000 --- a/src/libmsc/transaction.c +++ /dev/null @@ -1,221 +0,0 @@ -/* GSM 04.07 Transaction handling */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/transaction.h> -#include <openbsc/gsm_data.h> -#include <openbsc/mncc.h> -#include <openbsc/debug.h> -#include <osmocom/core/talloc.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/mncc.h> -#include <openbsc/paging.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/vlr.h> - -void *tall_trans_ctx; - -void _gsm48_cc_trans_free(struct gsm_trans *trans); - -/*! Find a transaction in connection for given protocol + transaction ID - * \param[in] conn Connection in whihc we want to find transaction - * \param[in] proto Protocol of transaction - * \param[in] trans_id Transaction ID of transaction - * \returns Matching transaction, if any - */ -struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn, - uint8_t proto, uint8_t trans_id) -{ - struct gsm_trans *trans; - struct gsm_network *net = conn->network; - struct vlr_subscr *vsub = conn->vsub; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->vsub == vsub && - trans->protocol == proto && - trans->transaction_id == trans_id) - return trans; - } - return NULL; -} - -/*! Find a transaction by call reference - * \param[in] net Network in which we should search - * \param[in] callref Call Reference of transaction - * \returns Matching transaction, if any - */ -struct gsm_trans *trans_find_by_callref(struct gsm_network *net, - uint32_t callref) -{ - struct gsm_trans *trans; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->callref == callref) - return trans; - } - return NULL; -} - -/*! Allocate a new transaction and add it to network list - * \param[in] net Netwokr in which we allocate transaction - * \param[in] subscr Subscriber for which we allocate transaction - * \param[in] protocol Protocol (CC/SMS/...) - * \param[in] callref Call Reference - * \returns Transaction - */ -struct gsm_trans *trans_alloc(struct gsm_network *net, - struct vlr_subscr *vsub, - uint8_t protocol, uint8_t trans_id, - uint32_t callref) -{ - struct gsm_trans *trans; - - DEBUGP(DCC, "subscr=%p, net=%p\n", vsub, net); - - /* a valid subscriber is indispensable */ - if (vsub == NULL) { - LOGP(DCC, LOGL_NOTICE, - "unable to alloc transaction, invalid subscriber (NULL)\n"); - return NULL; - } - - trans = talloc_zero(tall_trans_ctx, struct gsm_trans); - if (!trans) - return NULL; - - trans->vsub = vlr_subscr_get(vsub); - - trans->protocol = protocol; - trans->transaction_id = trans_id; - trans->callref = callref; - - trans->net = net; - llist_add_tail(&trans->entry, &net->trans_list); - - return trans; -} - -/*! Release a transaction - * \param[in] trans Transaction to be released - */ -void trans_free(struct gsm_trans *trans) -{ - switch (trans->protocol) { - case GSM48_PDISC_CC: - _gsm48_cc_trans_free(trans); - break; - case GSM48_PDISC_SMS: - _gsm411_sms_trans_free(trans); - break; - } - - if (trans->paging_request) { - subscr_remove_request(trans->paging_request); - trans->paging_request = NULL; - } - - if (trans->vsub) { - vlr_subscr_put(trans->vsub); - trans->vsub = NULL; - } - - llist_del(&trans->entry); - - if (trans->conn) - msc_subscr_conn_put(trans->conn); - - trans->conn = NULL; - talloc_free(trans); -} - -/*! allocate an unused transaction ID for the given subscriber - * in the given protocol using the ti_flag specified - * \param[in] net GSM network - * \param[in] subscr Subscriber for which to find ID - * \param[in] protocol Protocol for whihc to find ID - * \param[in] ti_flag FIXME - */ -int trans_assign_trans_id(struct gsm_network *net, struct vlr_subscr *vsub, - uint8_t protocol, uint8_t ti_flag) -{ - struct gsm_trans *trans; - unsigned int used_tid_bitmask = 0; - int i, j, h; - - if (ti_flag) - ti_flag = 0x8; - - /* generate bitmask of already-used TIDs for this (subscr,proto) */ - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->vsub != vsub || - trans->protocol != protocol || - trans->transaction_id == 0xff) - continue; - used_tid_bitmask |= (1 << trans->transaction_id); - } - - /* find a new one, trying to go in a 'circular' pattern */ - for (h = 6; h > 0; h--) - if (used_tid_bitmask & (1 << (h | ti_flag))) - break; - for (i = 0; i < 7; i++) { - j = ((h + i) % 7) | ti_flag; - if ((used_tid_bitmask & (1 << j)) == 0) - return j; - } - - return -1; -} - -/*! Check if we have any transaction for given connection - * \param[in] conn Connection to check - * \returns 1 in case there is a transaction, 0 otherwise - */ -struct gsm_trans *trans_has_conn(const struct gsm_subscriber_connection *conn) -{ - struct gsm_trans *trans; - - llist_for_each_entry(trans, &conn->network->trans_list, entry) - if (trans->conn == conn) - return trans; - - return NULL; -} - -/*! Free all transactions associated with a connection, presumably when the - * conn is being closed. The transaction code will inform the CC or SMS - * facilities, which will then send the necessary release indications. - * \param[in] conn Connection that is going to be closed. - */ -void trans_conn_closed(struct gsm_subscriber_connection *conn) -{ - struct gsm_trans *trans; - - /* As part of the CC REL_IND the remote leg might be released and this - * will trigger another call to trans_free. This is something the llist - * macro can not handle and we need to re-iterate the list every time. - */ -restart: - llist_for_each_entry(trans, &conn->network->trans_list, entry) { - if (trans->conn == conn) { - trans_free(trans); - goto restart; - } - } -} diff --git a/src/libmsc/ussd.c b/src/libmsc/ussd.c deleted file mode 100644 index 81a356690..000000000 --- a/src/libmsc/ussd.c +++ /dev/null @@ -1,103 +0,0 @@ -/* Network-specific handling of mobile-originated USSDs. */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2009 by Mike Haben <michael.haben@btinternet.com> - * - * 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/>. - * - */ - -/* This module defines the network-specific handling of mobile-originated - USSD messages. */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/gsm_04_80.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/debug.h> -#include <openbsc/osmo_msc.h> -#include <openbsc/vlr.h> - -/* Declarations of USSD strings to be recognised */ -const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; - -/* Forward declarations of network-specific handler functions */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req); - - -/* Entrypoint - handler function common to all mobile-originated USSDs */ -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - int rc; - struct ss_request req; - struct gsm48_hdr *gh; - - /* TODO: Use subscriber_connection ref-counting if we ever want - * to keep the connection alive due ot ongoing USSD exchange. - * As we answer everytying synchronously so far, there's no need - * yet */ - - cm_service_request_concludes(conn, msg); - - memset(&req, 0, sizeof(req)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req); - if (!rc) { - DEBUGP(DMM, "Unhandled SS\n"); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - return rc; - } - - /* Interrogation or releaseComplete? */ - if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) { - if (req.ss_code > 0) { - /* Assume interrogateSS or modification of it and reject */ - rc = gsm0480_send_ussd_reject(conn, msg, &req); - return rc; - } - /* Still assuming a Release-Complete and returning */ - return 0; - } - - msc_subscr_conn_communicating(conn); - if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, msg, &req); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - } - - return rc; -} - -/* A network-specific handler function */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req) -{ - char *own_number = conn->vsub->msisdn; - char response_string[GSM_EXTENSION_LENGTH + 20]; - - DEBUGP(DMM, "%s: MSISDN = %s\n", vlr_subscr_name(conn->vsub), - own_number); - - /* Need trailing CR as EOT character */ - snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); - return gsm0480_send_ussd_response(conn, msg, response_string, req); -} diff --git a/src/libmsc/vty_interface_layer3.c b/src/libmsc/vty_interface_layer3.c deleted file mode 100644 index 864597d6c..000000000 --- a/src/libmsc/vty_interface_layer3.c +++ /dev/null @@ -1,979 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2009-2011 by Holger Hans Peter Freyther - * 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 <stdlib.h> -#include <limits.h> -#include <unistd.h> -#include <time.h> -#include <inttypes.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/buffer.h> -#include <osmocom/vty/vty.h> - -#include <arpa/inet.h> - -#include <osmocom/core/linuxlist.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/bsc_subscriber.h> -#include <openbsc/silent_call.h> -#include <openbsc/gsm_04_11.h> -#include <osmocom/abis/e1_input.h> -#include <openbsc/abis_nm.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/core/utils.h> -#include <openbsc/db.h> -#include <osmocom/core/talloc.h> -#include <openbsc/signal.h> -#include <openbsc/debug.h> -#include <openbsc/vty.h> -#include <openbsc/gsm_04_80.h> -#include <openbsc/gsm_04_14.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/sms_queue.h> -#include <openbsc/mncc_int.h> -#include <openbsc/handover.h> -#include <openbsc/vlr.h> - -#include <osmocom/vty/logging.h> - -#include <openbsc/osmo_msc.h> - -#include "meas_feed.h" - -extern struct gsm_network *gsmnet_from_vty(struct vty *v); - -static void subscr_dump_full_vty(struct vty *vty, struct vlr_subscr *vsub) -{ - int reqs; - struct llist_head *entry; - char expire_time[200]; - - if (strlen(vsub->name)) - vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE); - if (strlen(vsub->msisdn)) - vty_out(vty, " Extension: %s%s", vsub->msisdn, - VTY_NEWLINE); - vty_out(vty, " LAC: %d/0x%x%s", - vsub->lac, vsub->lac, VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE); - if (vsub->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", vsub->tmsi, - VTY_NEWLINE); - if (vsub->tmsi_new != GSM_RESERVED_TMSI) - vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new, - VTY_NEWLINE); - -#if 0 - /* TODO: add this to vlr_subscr? */ - if (vsub->auth_info.auth_algo != AUTH_ALGO_NONE) { - struct gsm_auth_info *i = &vsub->auth_info; - vty_out(vty, " A3A8 algorithm id: %d%s", - i->auth_algo, VTY_NEWLINE); - vty_out(vty, " A3A8 Ki: %s%s", - osmo_hexdump(i->a3a8_ki, i->a3a8_ki_len), - VTY_NEWLINE); - } -#endif - - if (vsub->last_tuple) { - struct gsm_auth_tuple *t = vsub->last_tuple; - vty_out(vty, " A3A8 last tuple (used %d times):%s", - t->use_count, VTY_NEWLINE); - vty_out(vty, " seq # : %d%s", - t->key_seq, VTY_NEWLINE); - vty_out(vty, " RAND : %s%s", - osmo_hexdump(t->vec.rand, sizeof(t->vec.rand)), - VTY_NEWLINE); - vty_out(vty, " SRES : %s%s", - osmo_hexdump(t->vec.sres, sizeof(t->vec.sres)), - VTY_NEWLINE); - vty_out(vty, " Kc : %s%s", - osmo_hexdump(t->vec.kc, sizeof(t->vec.kc)), - VTY_NEWLINE); - } - - /* print the expiration time of a subscriber */ - strftime(expire_time, sizeof(expire_time), - "%a, %d %b %Y %T %z", localtime(&vsub->expire_lu)); - expire_time[sizeof(expire_time) - 1] = '\0'; - vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); - - reqs = 0; - llist_for_each(entry, &vsub->cs.requests) - reqs += 1; - vty_out(vty, " Paging: %s paging for %d requests%s", - vsub->cs.is_paging ? "is" : "not", reqs, VTY_NEWLINE); - vty_out(vty, " Use count: %u%s", vsub->use_count, VTY_NEWLINE); -} - - -/* Subscriber */ -DEFUN(show_subscr_cache, - show_subscr_cache_cmd, - "show subscriber cache", - SHOW_STR "Show information about subscribers\n" - "Display contents of subscriber cache\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub; - int count = 0; - - llist_for_each_entry(vsub, &gsmnet->vlr->subscribers, list) { - if (++count > 100) { - vty_out(vty, "%% More than %d subscribers in cache," - " stopping here.%s", count-1, VTY_NEWLINE); - break; - } - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_full_vty(vty, vsub); - } - - return CMD_SUCCESS; -} - -DEFUN(sms_send_pend, - sms_send_pend_cmd, - "sms send pending", - "SMS related commands\n" "SMS Sending related commands\n" - "Send all pending SMS") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_sms *sms; - unsigned long long sms_id = 0; - - while (1) { - sms = db_sms_get_next_unsent(gsmnet, sms_id, UINT_MAX); - if (!sms) - break; - - if (sms->receiver) - gsm411_send_sms_subscr(sms->receiver, sms); - - sms_id = sms->id + 1; - } - - return CMD_SUCCESS; -} - -static int _send_sms_str(struct vlr_subscr *receiver, - struct vlr_subscr *sender, - char *str, uint8_t tp_pid) -{ - struct gsm_network *net = receiver->vlr->user_ctx; - struct gsm_sms *sms; - - sms = sms_from_text(receiver, sender, 0, str); - sms->protocol_id = tp_pid; - - /* store in database for the queue */ - if (db_sms_store(sms) != 0) { - LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - sms_free(sms); - return CMD_WARNING; - } - LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n"); - - sms_free(sms); - sms_queue_trigger(net->sms_queue); - return CMD_SUCCESS; -} - -static struct vlr_subscr *get_vsub_by_argv(struct gsm_network *gsmnet, - const char *type, - const char *id) -{ - if (!strcmp(type, "extension") || !strcmp(type, "msisdn")) - return vlr_subscr_find_by_msisdn(gsmnet->vlr, id); - else if (!strcmp(type, "imsi") || !strcmp(type, "id")) - return vlr_subscr_find_by_imsi(gsmnet->vlr, id); - else if (!strcmp(type, "tmsi")) - return vlr_subscr_find_by_tmsi(gsmnet->vlr, atoi(id)); - - return NULL; -} -#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" -#define SUBSCR_HELP "Operations on a Subscriber\n" \ - "Identify subscriber by extension (phone number)\n" \ - "Identify subscriber by IMSI\n" \ - "Identify subscriber by TMSI\n" \ - "Identify subscriber by database ID\n" \ - "Identifier for the subscriber\n" - -DEFUN(show_subscr, - show_subscr_cmd, - "show subscriber " SUBSCR_TYPES " ID", - SHOW_STR SUBSCR_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], - argv[1]); - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_dump_full_vty(vty, vsub); - - vlr_subscr_put(vsub); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_create, - subscriber_create_cmd, - "subscriber create imsi ID", - "Operations on a Subscriber\n" \ - "Create new subscriber\n" \ - "Identify the subscriber by his IMSI\n" \ - "Identifier for the subscriber\n") -{ - vty_out(vty, "%% 'subscriber create' now needs to be done at osmo-hlr%s", - VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN(subscriber_send_pending_sms, - subscriber_send_pending_sms_cmd, - "subscriber " SUBSCR_TYPES " ID sms pending-send", - SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub; - struct gsm_sms *sms; - - vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - sms = db_sms_get_unsent_for_subscr(vsub, UINT_MAX); - if (sms) - gsm411_send_sms_subscr(sms->receiver, sms); - - vlr_subscr_put(vsub); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_send_sms, - subscriber_send_sms_cmd, - "subscriber " SUBSCR_TYPES " ID sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", - SUBSCR_HELP "SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]); - char *str; - int rc; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - if (!sender) { - vty_out(vty, "%% No sender found for %s %s%s", - argv[2], argv[3], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - str = argv_concat(argv, argc, 4); - rc = _send_sms_str(vsub, sender, str, 0); - talloc_free(str); - -err: - if (sender) - vlr_subscr_put(sender); - - if (vsub) - vlr_subscr_put(vsub); - - return rc; -} - -DEFUN(subscriber_silent_sms, - subscriber_silent_sms_cmd, - - "subscriber " SUBSCR_TYPES " ID silent-sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", - SUBSCR_HELP "Silent SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - struct vlr_subscr *sender = get_vsub_by_argv(gsmnet, argv[2], argv[3]); - char *str; - int rc; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - if (!sender) { - vty_out(vty, "%% No sender found for %s %s%s", - argv[2], argv[3], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - str = argv_concat(argv, argc, 4); - rc = _send_sms_str(vsub, sender, str, 64); - talloc_free(str); - -err: - if (sender) - vlr_subscr_put(sender); - - if (vsub) - vlr_subscr_put(vsub); - - return rc; -} - -#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" -#define CHAN_TYPE_HELP \ - "Any channel\n" \ - "TCH/F channel\n" \ - "Any TCH channel\n" \ - "SDCCH channel\n" - -DEFUN(subscriber_silent_call_start, - subscriber_silent_call_start_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", - SUBSCR_HELP "Silent call operation\n" "Start silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - int rc, type; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[2], "tch/f")) - type = RSL_CHANNEED_TCH_F; - else if (!strcmp(argv[2], "tch/any")) - type = RSL_CHANNEED_TCH_ForH; - else if (!strcmp(argv[2], "sdcch")) - type = RSL_CHANNEED_SDCCH; - else - type = RSL_CHANNEED_ANY; /* Defaults to ANY */ - - rc = gsm_silent_call_start(vsub, vty, type); - if (rc <= 0) { - vty_out(vty, "%% Subscriber not attached%s", - VTY_NEWLINE); - vlr_subscr_put(vsub); - return CMD_WARNING; - } - - vlr_subscr_put(vsub); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_silent_call_stop, - subscriber_silent_call_stop_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call stop", - SUBSCR_HELP "Silent call operation\n" "Stop silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - int rc; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - rc = gsm_silent_call_stop(vsub); - if (rc < 0) { - vlr_subscr_put(vsub); - return CMD_WARNING; - } - - vlr_subscr_put(vsub); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_ussd_notify, - subscriber_ussd_notify_cmd, - "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT", - SUBSCR_HELP "Send a USSD notify to the subscriber\n" - "Alerting Level 0\n" - "Alerting Level 1\n" - "Alerting Level 2\n" - "Text of USSD message to send\n") -{ - char *text; - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - int level; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - level = atoi(argv[2]); - text = argv_concat(argv, argc, 3); - if (!text) { - vlr_subscr_put(vsub); - return CMD_WARNING; - } - - conn = connection_for_subscr(vsub); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - vlr_subscr_put(vsub); - talloc_free(text); - return CMD_WARNING; - } - - msc_send_ussd_notify(conn, level, text); - msc_send_ussd_release_complete(conn); - - vlr_subscr_put(vsub); - talloc_free(text); - return CMD_SUCCESS; -} - -static int loop_by_char(uint8_t ch) -{ - switch (ch) { - case 'a': - return GSM414_LOOP_A; - case 'b': - return GSM414_LOOP_B; - case 'c': - return GSM414_LOOP_C; - case 'd': - return GSM414_LOOP_D; - case 'e': - return GSM414_LOOP_E; - case 'f': - return GSM414_LOOP_F; - case 'i': - return GSM414_LOOP_I; - } - return -1; -} - -DEFUN(subscriber_mstest_close, - subscriber_mstest_close_cmd, - "subscriber " SUBSCR_TYPES " ID ms-test close-loop (a|b|c|d|e|f|i)", - SUBSCR_HELP "Send a TS 04.14 MS Test Command to subscriber\n" - "Close a TCH Loop inside the MS\n" - "Loop Type A\n" - "Loop Type B\n" - "Loop Type C\n" - "Loop Type D\n" - "Loop Type E\n" - "Loop Type F\n" - "Loop Type I\n") -{ - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - const char *loop_str; - int loop_mode; - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - loop_str = argv[2]; - loop_mode = loop_by_char(loop_str[0]); - - conn = connection_for_subscr(vsub); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - vlr_subscr_put(vsub); - return CMD_WARNING; - } - - gsm0414_tx_close_tch_loop_cmd(conn, loop_mode); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_mstest_open, - subscriber_mstest_open_cmd, - "subscriber " SUBSCR_TYPES " ID ms-test open-loop", - SUBSCR_HELP "Send a TS 04.14 MS Test Command to subscriber\n" - "Open a TCH Loop inside the MS\n") -{ - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - conn = connection_for_subscr(vsub); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - vlr_subscr_put(vsub); - return CMD_WARNING; - } - - gsm0414_tx_open_loop_cmd(conn); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_expire, - ena_subscr_expire_cmd, - "subscriber " SUBSCR_TYPES " ID expire", - SUBSCR_HELP "Expire the subscriber Now\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], - argv[1]); - - if (!vsub) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (vsub->lu_complete) { - vsub->lu_complete = false; - vlr_subscr_put(vsub); - vty_out(vty, "%% VLR released subscriber %s%s", - vlr_subscr_name(vsub), VTY_NEWLINE); - } - - if (vsub->use_count > 1) - vty_out(vty, "%% Subscriber %s is still in use," - " should be released soon%s", - vlr_subscr_name(vsub), VTY_NEWLINE); - - vlr_subscr_put(vsub); - return CMD_SUCCESS; -} - -#define A3A8_ALG_TYPES "(none|xor|comp128v1)" -#define A3A8_ALG_HELP \ - "Use No A3A8 algorithm\n" \ - "Use XOR algorithm\n" \ - "Use COMP128v1 algorithm\n" - -DEFUN(ena_subscr_a3a8, - ena_subscr_a3a8_cmd, - "subscriber " SUBSCR_TYPES " ID a3a8 " A3A8_ALG_TYPES " [KI]", - SUBSCR_HELP "Set a3a8 parameters for the subscriber\n" - A3A8_ALG_HELP "Encryption Key Ki\n") -{ - vty_out(vty, "%% 'subscriber a3a8' is no longer supported.%s" - "%% This is now up to osmo-hlr.%s", - VTY_NEWLINE, VTY_NEWLINE); - return CMD_WARNING; -} - -DEFUN(subscriber_update, - subscriber_update_cmd, - "subscriber " SUBSCR_TYPES " ID update", - SUBSCR_HELP "Update the subscriber data from the dabase.\n") -{ - vty_out(vty, "%% 'subscriber update' is no longer supported.%s", - VTY_NEWLINE); - return CMD_WARNING; -} - -static int scall_cbfn(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct scall_signal_data *sigdata = signal_data; - struct vty *vty = sigdata->data; - - switch (signal) { - case S_SCALL_SUCCESS: - vty_out(vty, "%% silent call success%s", VTY_NEWLINE); - break; - case S_SCALL_EXPIRED: - vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); - break; - } - return 0; -} - -DEFUN(show_stats, - show_stats_cmd, - "show statistics", - SHOW_STR "Display network statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC].current, - VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications : %lu%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH].current, - VTY_NEWLINE); - vty_out(vty, "Location Updating Results: %lu completed, %lu failed%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED].current, - VTY_NEWLINE); - vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", - net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED].current, - net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL].current, - net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT].current, - net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED].current, - net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED].current, - VTY_NEWLINE); - vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", - net->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER].current, - VTY_NEWLINE); - vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", - net->msc_ctrs->ctr[MSC_CTR_SMS_DELIVERED].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_OTHER].current, - VTY_NEWLINE); - vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", - net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP].current, - net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK].current, - VTY_NEWLINE); - vty_out(vty, "MT Calls : %lu setup, %lu connect%s", - net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP].current, - net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT].current, - VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_smsqueue, - show_smsqueue_cmd, - "show sms-queue", - SHOW_STR "Display SMSqueue statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_stats(net->sms_queue, vty); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_trigger, - smsqueue_trigger_cmd, - "sms-queue trigger", - "SMS Queue\n" "Trigger sending messages\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_trigger(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_max, - smsqueue_max_cmd, - "sms-queue max-pending <1-500>", - "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_pending(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_clear, - smsqueue_clear_cmd, - "sms-queue clear", - "SMS Queue\n" "Clear the queue of pending SMS\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_clear(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_fail, - smsqueue_fail_cmd, - "sms-queue max-failure <1-500>", - "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - - -DEFUN(cfg_mncc_int, cfg_mncc_int_cmd, - "mncc-int", "Configure internal MNCC handler") -{ - vty->node = MNCC_INT_NODE; - - return CMD_SUCCESS; -} - -static struct cmd_node mncc_int_node = { - MNCC_INT_NODE, - "%s(config-mncc-int)# ", - 1, -}; - -static const struct value_string tchf_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "fr" }, - { GSM48_CMODE_SPEECH_EFR, "efr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static const struct value_string tchh_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "hr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static int config_write_mncc_int(struct vty *vty) -{ - uint16_t meas_port; - char *meas_host; - const char *meas_scenario; - - meas_feed_cfg_get(&meas_host, &meas_port); - meas_scenario = meas_feed_scenario_get(); - - vty_out(vty, "mncc-int%s", VTY_NEWLINE); - vty_out(vty, " default-codec tch-f %s%s", - get_value_string(tchf_codec_names, mncc_int.def_codec[0]), - VTY_NEWLINE); - vty_out(vty, " default-codec tch-h %s%s", - get_value_string(tchh_codec_names, mncc_int.def_codec[1]), - VTY_NEWLINE); - if (meas_port) - vty_out(vty, " meas-feed destination %s %u%s", - meas_host, meas_port, VTY_NEWLINE); - if (strlen(meas_scenario) > 0) - vty_out(vty, " meas-feed scenario %s%s", - meas_scenario, VTY_NEWLINE); - - - return CMD_SUCCESS; -} - -DEFUN(mnccint_def_codec_f, - mnccint_def_codec_f_cmd, - "default-codec tch-f (fr|efr|amr)", - "Set default codec\n" "Codec for TCH/F\n" - "Full-Rate\n" "Enhanced Full-Rate\n" "Adaptive Multi-Rate\n") -{ - mncc_int.def_codec[0] = get_string_value(tchf_codec_names, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(mnccint_def_codec_h, - mnccint_def_codec_h_cmd, - "default-codec tch-h (hr|amr)", - "Set default codec\n" "Codec for TCH/H\n" - "Half-Rate\n" "Adaptive Multi-Rate\n") -{ - mncc_int.def_codec[1] = get_string_value(tchh_codec_names, argv[0]); - - return CMD_SUCCESS; -} - -#define OBSOLETE_MSG "Obsolete\n" -/* this is just for backwards compatibility as the sms code moved into - * libosmocore and old config files no longer parse... */ -DEFUN_DEPRECATED(log_level_sms, log_level_sms_cmd, - "logging level sms (everything|debug|info|notice|error|fatal)", - ".HIDDEN\n" OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG - OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG) -{ - vty_out(vty, "%% 'logging level sms' is now called 'logging level " - "lsms', please update your config %s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -#define MEAS_STR "Measurement export related\n" -DEFUN(mnccint_meas_feed, mnccint_meas_feed_cmd, - "meas-feed destination ADDR <0-65535>", - MEAS_STR "destination\n" "address or hostname\n" "port number\n") -{ - int rc; - - rc = meas_feed_cfg_set(argv[0], atoi(argv[1])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(meas_feed_scenario, meas_feed_scenario_cmd, - "meas-feed scenario NAME", - MEAS_STR "scenario\n" "Name up to 31 characters included in report\n") -{ - meas_feed_scenario_set(argv[0]); - - return CMD_SUCCESS; -} - - -DEFUN(logging_fltr_imsi, - logging_fltr_imsi_cmd, - "logging filter imsi IMSI", - LOGGING_STR FILTER_STR - "Filter log messages by IMSI\n" "IMSI to be used as filter\n") -{ - struct vlr_subscr *vlr_subscr; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct log_target *tgt = osmo_log_vty2tgt(vty); - const char *imsi = argv[0]; - - if (!tgt) - return CMD_WARNING; - - vlr_subscr = vlr_subscr_find_by_imsi(gsmnet->vlr, imsi); - - if (!vlr_subscr) { - vty_out(vty, "%%no subscriber with IMSI(%s)%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - log_set_filter_vlr_subscr(tgt, vlr_subscr); - return CMD_SUCCESS; -} - -static struct cmd_node hlr_node = { - HLR_NODE, - "%s(config-hlr)# ", - 1, -}; - -DEFUN(cfg_hlr, cfg_hlr_cmd, - "hlr", "Configure connection to the HLR") -{ - vty->node = HLR_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_hlr_remote_ip, cfg_hlr_remote_ip_cmd, "remote-ip A.B.C.D", - "Remote GSUP address of the HLR\n" - "Remote GSUP address (default: " MSC_HLR_REMOTE_IP_DEFAULT ")") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - talloc_free((void*)gsmnet->gsup_server_addr_str); - gsmnet->gsup_server_addr_str = talloc_strdup(gsmnet, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_hlr_remote_port, cfg_hlr_remote_port_cmd, "remote-port <1-65535>", - "Remote GSUP port of the HLR\n" - "Remote GSUP port (default: " OSMO_STRINGIFY(MSC_HLR_REMOTE_PORT_DEFAULT) ")") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->gsup_server_port = atoi(argv[0]); - return CMD_SUCCESS; -} - -static int config_write_hlr(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "hlr%s", VTY_NEWLINE); - vty_out(vty, " remote-ip %s%s", - gsmnet->gsup_server_addr_str, VTY_NEWLINE); - vty_out(vty, " remote-port %u%s", - gsmnet->gsup_server_port, VTY_NEWLINE); - return CMD_SUCCESS; -} - -int bsc_vty_init_extra(void) -{ - osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); - - install_element_ve(&show_subscr_cmd); - install_element_ve(&show_subscr_cache_cmd); - - install_element_ve(&sms_send_pend_cmd); - - install_element_ve(&subscriber_create_cmd); - install_element_ve(&subscriber_send_sms_cmd); - install_element_ve(&subscriber_silent_sms_cmd); - install_element_ve(&subscriber_silent_call_start_cmd); - install_element_ve(&subscriber_silent_call_stop_cmd); - install_element_ve(&subscriber_ussd_notify_cmd); - install_element_ve(&subscriber_mstest_close_cmd); - install_element_ve(&subscriber_mstest_open_cmd); - install_element_ve(&subscriber_update_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_smsqueue_cmd); - install_element_ve(&logging_fltr_imsi_cmd); - - install_element(ENABLE_NODE, &ena_subscr_expire_cmd); - install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd); - install_element(ENABLE_NODE, &smsqueue_trigger_cmd); - install_element(ENABLE_NODE, &smsqueue_max_cmd); - install_element(ENABLE_NODE, &smsqueue_clear_cmd); - install_element(ENABLE_NODE, &smsqueue_fail_cmd); - install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd); - install_element(ENABLE_NODE, &meas_feed_scenario_cmd); - - install_element(CONFIG_NODE, &cfg_mncc_int_cmd); - install_node(&mncc_int_node, config_write_mncc_int); - vty_install_default(MNCC_INT_NODE); - install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd); - install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd); - install_element(MNCC_INT_NODE, &mnccint_meas_feed_cmd); - install_element(MNCC_INT_NODE, &meas_feed_scenario_cmd); - - install_element(CFG_LOG_NODE, &log_level_sms_cmd); - install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); - - install_element(CONFIG_NODE, &cfg_hlr_cmd); - install_node(&hlr_node, config_write_hlr); - install_element(HLR_NODE, &cfg_hlr_remote_ip_cmd); - install_element(HLR_NODE, &cfg_hlr_remote_port_cmd); - - return 0; -} |