aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmsc')
-rw-r--r--src/libmsc/Makefile.am75
-rw-r--r--src/libmsc/a_iface.c591
-rw-r--r--src/libmsc/a_iface_bssap.c716
-rw-r--r--src/libmsc/auth.c42
-rw-r--r--src/libmsc/ctrl_commands.c88
-rw-r--r--src/libmsc/db.c1007
-rw-r--r--src/libmsc/gsm_04_08.c3493
-rw-r--r--src/libmsc/gsm_04_11.c1189
-rw-r--r--src/libmsc/gsm_04_80.c155
-rw-r--r--src/libmsc/gsm_subscriber.c190
-rw-r--r--src/libmsc/iu_dummy.c93
-rw-r--r--src/libmsc/iucs.c189
-rw-r--r--src/libmsc/iucs_ranap.c104
-rw-r--r--src/libmsc/meas_feed.c168
-rw-r--r--src/libmsc/meas_feed.h12
-rw-r--r--src/libmsc/mncc.c107
-rw-r--r--src/libmsc/mncc_builtin.c383
-rw-r--r--src/libmsc/mncc_sock.c317
-rw-r--r--src/libmsc/msc_ifaces.c423
-rw-r--r--src/libmsc/msc_vty.c162
-rw-r--r--src/libmsc/osmo_msc.c387
-rw-r--r--src/libmsc/rrlp.c104
-rw-r--r--src/libmsc/silent_call.c165
-rw-r--r--src/libmsc/smpp_openbsc.c794
-rw-r--r--src/libmsc/smpp_smsc.c1037
-rw-r--r--src/libmsc/smpp_smsc.h167
-rw-r--r--src/libmsc/smpp_utils.c62
-rw-r--r--src/libmsc/smpp_vty.c612
-rw-r--r--src/libmsc/sms_queue.c578
-rw-r--r--src/libmsc/subscr_conn.c359
-rw-r--r--src/libmsc/transaction.c221
-rw-r--r--src/libmsc/ussd.c103
-rw-r--r--src/libmsc/vty_interface_layer3.c979
33 files changed, 0 insertions, 15072 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, &quoted);
- 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(&notify, 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(&notify.notify, gh->data);
-
- return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, &notify);
-}
-
-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/iu_dummy.c b/src/libmsc/iu_dummy.c
deleted file mode 100644
index e9d335e2e..000000000
--- a/src/libmsc/iu_dummy.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/* Trivial switch-off of external Iu dependencies,
- * allowing to run full unit tests even when built without Iu support. */
-
-/*
- * (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 "../../bscconfig.h"
-#ifndef BUILD_IU
-
-#include <openbsc/iu_dummy.h>
-
-#include <osmocom/core/logging.h>
-#include <osmocom/vty/logging.h>
-#include <osmocom/core/msgb.h>
-
-struct msgb;
-struct ranap_ue_conn_ctx;
-struct gsm_auth_tuple;
-struct RANAP_Cause;
-struct osmo_auth_vector;
-
-int ranap_iu_tx(struct msgb *msg, uint8_t sapi)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx() dummy called, NOT transmitting %d bytes: %s\n",
- msg->len, osmo_hexdump(msg->data, msg->len));
- return 0;
-}
-
-int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec,
- int send_ck)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_sec_mode_cmd() dummy called, NOT transmitting Security Mode Command\n");
- return 0;
-}
-
-int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_page_cs() dummy called, NOT paging\n");
- return 23;
-}
-
-int ranap_iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_page_ps() dummy called, NOT paging\n");
- return 0;
-}
-
-struct msgb *ranap_new_msg_rab_assign_voice(uint8_t rab_id, uint32_t rtp_ip,
- uint16_t rtp_port,
- bool use_x213_nsap)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "ranap_new_msg_rab_assign_voice() dummy called, NOT composing RAB Assignment\n");
- return NULL;
-}
-
-int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_rab_act() dummy called, NOT activating RAB\n");
- return 0;
-}
-
-int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_common_id() dummy called, NOT sending CommonID\n");
- return 0;
-}
-
-int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause)
-{
- LOGP(DLGLOBAL, LOGL_INFO, "iu_tx_release() dummy called, NOT sending Release\n");
- return 0;
-}
-
-#endif
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;
-}