diff options
author | Philipp Maier <pmaier@sysmocom.de> | 2017-04-12 15:26:04 +0200 |
---|---|---|
committer | Neels Hofmeyr <nhofmeyr@sysmocom.de> | 2017-06-18 17:50:08 +0200 |
commit | 0923fc6707ea35776ad8c37a6d490ac3408dd274 (patch) | |
tree | 0da3805e1f1d8d5f56167d24dc3a058f81aec768 /openbsc/src/osmo-bsc/osmo_bsc_sigtran.c | |
parent | 22593b2cef43bceafe7727f893be4657f949489f (diff) |
WIP: Integrate AoIP into OsmoBSC
Diffstat (limited to 'openbsc/src/osmo-bsc/osmo_bsc_sigtran.c')
-rw-r--r-- | openbsc/src/osmo-bsc/osmo_bsc_sigtran.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sigtran.c b/openbsc/src/osmo-bsc/osmo_bsc_sigtran.c new file mode 100644 index 000000000..b95ec9ddc --- /dev/null +++ b/openbsc/src/osmo-bsc/osmo_bsc_sigtran.c @@ -0,0 +1,340 @@ +/* (C) 2017 by Sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Philipp Maier + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#include <osmocom/core/utils.h> +#include <osmocom/core/logging.h> +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sccp/sccp_types.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/gsm/gsm0808.h> +#include <osmocom/core/msgb.h> +#include <openbsc/bsc_msc_data.h> +#include <openbsc/debug.h> +#include <openbsc/osmo_bsc.h> +#include <openbsc/osmo_bsc_grace.h> +#include <openbsc/osmo_bsc_sigtran.h> + +/* A pointer to a list with all involved MSCs + * (a copy of the pointer location submitted with osmo_bsc_sigtran_init() */ +static struct llist_head *msc_list; + +#define RESET_INTERVAL 1 /* sek */ +#define SCCP_MSG_MAXSIZE 1024 + +static LLIST_HEAD(active_connections); + +/* Helper function to Check if the given connection id is already assigned */ +static struct osmo_bsc_sccp_con *get_bsc_conn_by_conn_id(int conn_id) +{ + conn_id &= 0xFFFFFF; + struct osmo_bsc_sccp_con *bsc_con; + + llist_for_each_entry(bsc_con, &active_connections, entry) { + if (bsc_con->conn_id == conn_id) + return bsc_con; + } + + return NULL; +} + +/* Pick a free connection id */ +static int pick_free_conn_id(struct bsc_msc_data *msc) +{ + int conn_id = msc->msc_con->conn_id_counter; + int i; + + for (i = 0; i < 0xFFFFFF; i++) { + conn_id++; + conn_id &= 0xFFFFFF; + if (get_bsc_conn_by_conn_id(conn_id) == false) { + msc->msc_con->conn_id_counter = conn_id; + return conn_id; + } + } + + return -1; +} + +/* Timer callback to handle the MSC reset procedure */ +static void msc_reset_timer_cb(void *data) +{ + /* FIXME: Probably the reset procedure should be moved to osmo_bsc_bssap.c, + * since it is clearly in the BSSAP's area of accountability */ + struct bsc_msc_data *msc = (struct bsc_msc_data *)data; + struct msgb *msg = NULL; + + /* Stop sending RESET messages as soon as the + * connection is marked as connected. */ + if (msc->msc_con->reset_ack) + return; + + LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC No.: %i\n", msc->nr); + + msg = gsm0808_create_reset(); + osmo_sccp_tx_unitdata_msg(msc->msc_con->sccp_user, &msc->msc_con->g_calling_addr, + &msc->msc_con->g_called_addr, msg); + + osmo_timer_schedule(&msc->msc_con->msc_reset_timer, RESET_INTERVAL, 0); +} + +/* Perform MSC reset procedure */ +static void msc_reset(struct bsc_msc_data *msc) +{ + LOGP(DMSC, LOGL_NOTICE, "Starting RESET for MSC No.: %i\n", msc->nr); + msc->msc_con->msc_reset_timer.cb = msc_reset_timer_cb; + msc->msc_con->msc_reset_timer.data = msc; + msc->msc_con->reset_ack = false; + osmo_timer_schedule(&msc->msc_con->msc_reset_timer, RESET_INTERVAL, 0); +} + +/* Find an MSC by its sigtran point code */ +static struct bsc_msc_data *get_msc_by_addr(struct osmo_sccp_addr *calling_addr) +{ + struct bsc_msc_data *msc; + llist_for_each_entry(msc, msc_list, entry) { + if (memcmp(calling_addr, &msc->msc_con->g_called_addr, sizeof(*calling_addr)) == 0) + return msc; + } + + LOGP(DMSC, LOGL_ERROR, "Unable to find MSC data under address: %s\n", osmo_sccp_addr_dump(calling_addr)); + return NULL; +} + +/* Send data to MSC, use the connection id which MSC it is */ +static int handle_data_from_msc(int conn_id, struct msgb *msg) +{ + struct osmo_bsc_sccp_con *bsc_con = get_bsc_conn_by_conn_id(conn_id); + int rc = -EINVAL; + + if (bsc_con) { + msg->l3h = msgb_l2(msg); + rc = bsc_handle_dt1(bsc_con, msg, msgb_l2len(msg)); + } else + LOGP(DMSC, LOGL_NOTICE, "incoming data from unknown connection id: %i\n", conn_id); + + return rc; +} + +/* Sent unitdata to MSC, use the point code to determine which MSC it is */ +static int handle_unitdata_from_msc(struct osmo_sccp_addr *calling_addr, struct msgb *msg) +{ + struct bsc_msc_data *msc = get_msc_by_addr(calling_addr); + int rc = -EINVAL; + + if (msc) { + msg->l3h = msgb_l2(msg); + rc = bsc_handle_udt(msc, msg, msgb_l2len(msg)); + } else + LOGP(DMSC, LOGL_NOTICE, "incoming unitdata data from unknown remote address: %s\n", + osmo_sccp_addr_dump(calling_addr)); + + return rc; +} + +/* Callback function, called by the SSCP stack when data arrives */ +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) +{ + struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; + struct osmo_sccp_user *scu = _scu; + int rc = 0; + + switch (OSMO_PRIM_HDR(&scu_prim->oph)) { + case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): + /* Handle inbound UNITDATA */ + DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_unitdata_from_msc(&scu_prim->u.unitdata.calling_addr, oph->msg); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): + /* Handle (Reject) inbound connections */ + DEBUGP(DMSC, "N-CONNECT.ind(X->%u)\n", scu_prim->u.connect.conn_id); + LOGP(DMSC, LOGL_DEBUG, "Rejecting inbound SCCP connection...\n"); + rc = osmo_sccp_tx_disconn(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, 0); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): + /* Handle outbound connection confirmation */ + if (msgb_l2len(oph->msg) > 0) { + DEBUGP(DMSC, "N-CONNECT.cnf(%u, %s)\n", scu_prim->u.connect.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_data_from_msc(scu_prim->u.connect.conn_id, oph->msg); + } else + DEBUGP(DRANAP, "N-CONNECT.cnf(%u)\n", scu_prim->u.connect.conn_id); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): + /* Handle incoming connection oriented data */ + DEBUGP(DMSC, "N-DATA.ind(%u, %s)\n", scu_prim->u.data.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg); + break; + + case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): + /* indication of disconnect */ + if (msgb_l2len(oph->msg) > 0) { + DEBUGP(DMSC, "N-DISCONNECT.ind(%u, %s)\n", scu_prim->u.disconnect.conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + handle_data_from_msc(scu_prim->u.disconnect.conn_id, oph->msg); + } else + DEBUGP(DRANAP, "N-DISCONNECT.ind(%u)\n", scu_prim->u.disconnect.conn_id); + + rc = osmo_bsc_sigtran_del_conn(get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id)); + break; + + default: + LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation); + break; + } + + msgb_free(oph->msg); + return rc; +} + +/* Allocate resources to make a new connection oriented sigtran connection + * (not the connection ittself!) */ +enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, struct bsc_msc_data *msc) +{ + struct osmo_bsc_sccp_con *bsc_con; + int conn_id; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msc); + + LOGP(DMSC, LOGL_NOTICE, "Initalizing resources for new SIGTRAN connection to MSC No.: %i...\n", msc->nr); + + /* This should not trigger */ + if (!msc || !msc->msc_con->is_authenticated) { + LOGP(DMSC, LOGL_ERROR, "How did this happen? MSC is not connected. Dropping.\n"); + return BSC_CON_REJECT_NO_LINK; + } + + if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { + LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); + return BSC_CON_REJECT_RF_GRACE; + } + + bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); + if (!bsc_con) { + LOGP(DMSC, LOGL_ERROR, "Failed to allocate new SIGTRAN connection.\n"); + return BSC_CON_NO_MEM; + } + + bsc_con->msc = msc; + bsc_con->conn = conn; + llist_add_tail(&bsc_con->entry, &active_connections); + conn->sccp_con = bsc_con; + + /* Pick a free connection id */ + conn_id = pick_free_conn_id(msc); + if (conn_id < 0) + return BSC_CON_REJECT_NO_LINK; + bsc_con->conn_id = conn_id; + + LOGP(DMSC, LOGL_NOTICE, "Allocated new connection id: %i\n", conn_id); + + return BSC_CON_SUCCESS; +} + +/* Open a new connection oriented sigtran connection */ +int osmo_bsc_sigtran_open_conn(struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + struct bsc_msc_data *msc = conn->msc; + int conn_id = conn->conn_id; + int rc; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + + LOGP(DMSC, LOGL_NOTICE, "Opening new SIGTRAN connection (id=%i) to MSC No.: %i...\n", conn_id, msc->nr); + + rc = osmo_sccp_tx_conn_req_msg(msc->msc_con->sccp_user, conn_id, &msc->msc_con->g_calling_addr, + &msc->msc_con->g_called_addr, msg); + + return rc; +} + +/* Send data to MSC */ +int osmo_bsc_sigtran_send(struct osmo_bsc_sccp_con *conn, struct msgb *msg) +{ + int conn_id; + int rc; + struct bsc_msc_data *msc; + + OSMO_ASSERT(conn); + OSMO_ASSERT(msg); + + msc = conn->msc; + conn_id = conn->conn_id; + + LOGP(DMSC, LOGL_DEBUG, "Sending connection (id=%i) oriented data to MSC No.: %i...\n", conn_id, msc->nr); + rc = osmo_sccp_tx_data_msg(msc->msc_con->sccp_user, conn_id, msg); + + return rc; +} + +/* Delete a connection from the list with open connections + * (called by osmo_bsc_api.c on failing open connections and + * locally, when a connection is closed by the MSC */ +int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *sccp) +{ + if (!sccp) + return 0; + + if (sccp->conn) + LOGP(DMSC, LOGL_ERROR, "sccp connection not cleared -- forcefully clearing it now!\n"); + + llist_del(&sccp->entry); + talloc_free(sccp); + + return 0; +} + +/* Initalize osmo sigtran backhaul */ +int osmo_bsc_sigtran_init(struct llist_head *mscs) +{ + /* FIXME: Remove hardcoded IP-Addresses */ + /* FIXME: Use STP! */ + struct bsc_msc_data *msc; + char msc_name[256]; + + OSMO_ASSERT(mscs); + + osmo_ss7_init(); + + msc_list = mscs; + + llist_for_each_entry(msc, msc_list, entry) { + snprintf(msc_name, sizeof(msc_name), "MSC No.: %u", msc->nr); + LOGP(DMSC, LOGL_NOTICE, "Initalizing SCCP connection to %s\n", msc_name); + + /* SCCP Protocol stack */ + msc->msc_con->sccp = + osmo_sccp_simple_client(NULL, msc_name, msc->msc_con->g_calling_addr.pc, + OSMO_SS7_ASP_PROT_M3UA, 0, NULL, M3UA_PORT, "127.0.0.2"); + msc->msc_con->sccp_user = + osmo_sccp_user_bind(msc->msc_con->sccp, msc_name, sccp_sap_up, SCCP_SSN_BSSAP); + + /* Start MSC reset procedure */ + msc_reset(msc); + } + + return 0; +} |