From 54a10efea079d37083f6db07969e8e8be3b68bfe Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 9 Feb 2018 00:09:16 +0100 Subject: [PATCH] Don't answer to BSC-originated RESET with another RESET If the BSC is contacting us for the first time and sending a BSSMAP RESET, then we should simply ACK that and transition into the "connected" state, where connection-oriented and connectionless procedures are permitted. This patch is a bit large for such a seemingly simple behavioural change, but the existing data model didn't permit a more straight-forward implementation. Change-Id: Ie67e7ed20a6c42afe99bafef96d85a4e083dd057 Closes: OS#2914 --- include/osmocom/msc/a_iface.h | 2 + include/osmocom/msc/a_iface_bssap.h | 6 +- src/libmsc/a_iface.c | 145 +++++++++++++++++++--------- src/libmsc/a_iface_bssap.c | 23 +++-- 4 files changed, 120 insertions(+), 56 deletions(-) diff --git a/include/osmocom/msc/a_iface.h b/include/osmocom/msc/a_iface.h index 74172984d..466e70b94 100644 --- a/include/osmocom/msc/a_iface.h +++ b/include/osmocom/msc/a_iface.h @@ -73,6 +73,8 @@ int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn); * (Helper function for a_iface_bssap.c) */ void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr); +void a_start_reset(struct bsc_context *bsc_ctx, bool already_connected); + /* Delete info of a closed connection from the active connection list * (Helper function for a_iface_bssap.c) */ void a_delete_bsc_con(uint32_t conn_id); diff --git a/include/osmocom/msc/a_iface_bssap.h b/include/osmocom/msc/a_iface_bssap.h index 79b839074..d4b67e3ec 100644 --- a/include/osmocom/msc/a_iface_bssap.h +++ b/include/osmocom/msc/a_iface_bssap.h @@ -20,17 +20,17 @@ #pragma once +#include + /* Note: The structs and functions presented in this header file are intended * to be used only by a_iface.c. */ /* A structure to hold tha most basic information about a sigtran connection * we use this struct internally here to pass connection data around */ struct a_conn_info { - struct osmo_sccp_addr *msc_addr; - struct osmo_sccp_addr *bsc_addr; + struct bsc_context *bsc; uint32_t conn_id; struct gsm_network *network; - struct a_reset_ctx *reset; }; /* Receive incoming connection less data messages via sccp */ diff --git a/src/libmsc/a_iface.c b/src/libmsc/a_iface.c index 16df46da2..600dbb82c 100644 --- a/src/libmsc/a_iface.c +++ b/src/libmsc/a_iface.c @@ -65,6 +65,7 @@ static struct gsm_network *gsm_network = NULL; struct bsc_conn { struct llist_head list; uint32_t conn_id; /* Connection identifier */ + struct bsc_context *bsc; }; /* Internal list with connections we currently maintain. This @@ -72,7 +73,7 @@ struct bsc_conn { 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) +static void record_bsc_con(const void *ctx, struct bsc_context *bsc, uint32_t conn_id) { struct bsc_conn *conn; @@ -80,6 +81,7 @@ static void record_bsc_con(const void *ctx, uint32_t conn_id) OSMO_ASSERT(conn); conn->conn_id = conn_id; + conn->bsc = bsc; llist_add_tail(&conn->list, &active_connections); } @@ -99,23 +101,32 @@ void a_delete_bsc_con(uint32_t conn_id) } } -/* Check if a specified connection id has an active SCCP connection */ -static bool check_connection_active(uint32_t conn_id) +/* Find a specified connection id */ +static struct bsc_conn *find_bsc_con(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 conn; } } - return false; + return NULL; } -/* 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) +/* Check if a specified connection id has an active SCCP connection */ +static bool check_connection_active(uint32_t conn_id) +{ + if (find_bsc_con(conn_id)) + return true; + else + return false; +} + +/* Get the context for a specific calling (BSC) address */ +static struct bsc_context *get_bsc_context_by_sccp_addr(const struct osmo_sccp_addr *addr) { struct bsc_context *bsc_ctx; struct osmo_ss7_instance *ss7; @@ -125,7 +136,7 @@ static struct a_reset_ctx *get_reset_ctx_by_sccp_addr(const struct osmo_sccp_add 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; + return bsc_ctx; } ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance); @@ -452,20 +463,11 @@ static void a_reset_cb(const void *priv) } /* 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) +static struct bsc_context *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; - char bsc_name[32]; - - 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); @@ -479,9 +481,34 @@ static void add_bsc(const struct osmo_sccp_addr *msc_addr, const struct osmo_scc bsc_ctx->sccp_user = scu; llist_add_tail(&bsc_ctx->list, &gsm_network->a.bscs); + return bsc_ctx; +} + +/* start the BSSMAP RESET fsm */ +void a_start_reset(struct bsc_context *bsc_ctx, bool already_connected) +{ + char bsc_name[32]; + OSMO_ASSERT(bsc_ctx->reset == NULL); /* Start reset procedure to make the new connection active */ - snprintf(bsc_name, sizeof(bsc_name), "bsc-%i", bsc_addr->pc); - bsc_ctx->reset = a_reset_alloc(bsc_ctx, bsc_name, a_reset_cb, bsc_ctx, false); + snprintf(bsc_name, sizeof(bsc_name), "bsc-%i", bsc_ctx->bsc_addr.pc); + bsc_ctx->reset = a_reset_alloc(bsc_ctx, bsc_name, a_reset_cb, bsc_ctx, already_connected); +} + +/* determine if given msg is a BSSMAP RESET (true) or not (false) */ +static bool bssmap_is_reset(struct msgb *msg) +{ + struct bssmap_header *bs = (struct bssmap_header *)msgb_l2(msg); + + if (msgb_l2len(msg) < sizeof(*bs)) + return false; + + if (bs->type != BSSAP_MSG_BSS_MANAGEMENT) + return false; + + if (msg->l2h[sizeof(*bs)] == BSS_MAP_MSG_RESET) + return true; + + return false; } /* Callback function, called by the SSCP stack when data arrives */ @@ -491,39 +518,53 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph; int rc = 0; struct a_conn_info a_conn_info; + struct bsc_conn *bsc_con; + 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); + a_conn_info.bsc = get_bsc_context_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); + if (!a_conn_info.bsc) { + /* We haven't heard from this BSC before, allocate it */ + a_conn_info.bsc = add_bsc(&scu_prim->u.connect.called_addr, + &scu_prim->u.connect.calling_addr, scu); + a_start_reset(a_conn_info.bsc, false); + } else { + /* This BSC is already known to us, check if we have been through reset yet */ + if (a_reset_conn_ready(a_conn_info.bsc->reset) == false) { + LOGP(DMSC, LOGL_NOTICE, "Refusing N-CONNECT.ind(%u, %s), BSC not reset yet\n", + scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = osmo_sccp_tx_disconn(scu, a_conn_info.conn_id, &a_conn_info.bsc->msc_addr, + SCCP_RETURN_CAUSE_UNQUALIFIED); + break; + } - 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 = a_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, a_conn_info.bsc, scu_prim->u.connect.conn_id); } - - 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 = a_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 */ + bsc_con = find_bsc_con(scu_prim->u.data.conn_id); + if (!bsc_con) { + LOGP(DMSC, LOGL_ERROR, "N-DATA.ind(%u, %s) for unknown conn_id\n", + scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + break; + } a_conn_info.conn_id = scu_prim->u.data.conn_id; + a_conn_info.bsc = bsc_con->bsc; 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))); a_sccp_rx_dt(scu, &a_conn_info, oph->msg); @@ -531,10 +572,26 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) 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); + a_conn_info.bsc = get_bsc_context_by_sccp_addr(&scu_prim->u.unitdata.calling_addr); + if (!a_conn_info.bsc) { + /* We haven't heard from this BSC before, allocate it */ + a_conn_info.bsc = add_bsc(&scu_prim->u.unitdata.called_addr, + &scu_prim->u.unitdata.calling_addr, scu); + /* if this not an inbound RESET, trigger an outbound RESET */ + if (!bssmap_is_reset(oph->msg)) { + LOGP(DMSC, LOGL_NOTICE, "Ignoring N-UNITDATA.ind(%s), BSC not reset yet\n", + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + a_start_reset(a_conn_info.bsc, false); + break; + } + } else { + /* This BSC is already known to us, check if we have been through reset yet */ + if (a_reset_conn_ready(a_conn_info.bsc->reset) == false) { + LOGP(DMSC, LOGL_NOTICE, "Ignoring N-UNITDATA.ind(%s), BSC not reset yet\n", + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + break; + } + } DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); a_sccp_rx_udt(scu, &a_conn_info, oph->msg); break; diff --git a/src/libmsc/a_iface_bssap.c b/src/libmsc/a_iface_bssap.c index fc95dfba7..7f0350bf0 100644 --- a/src/libmsc/a_iface_bssap.c +++ b/src/libmsc/a_iface_bssap.c @@ -66,7 +66,7 @@ static struct gsm_subscriber_connection *subscr_conn_allocate_a(const struct a_c /* 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)); + memcpy(&conn->a.bsc_addr, &a_conn_info->bsc->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"); @@ -111,11 +111,15 @@ static void bssmap_rx_reset(struct osmo_sccp_user *scu, const struct a_conn_info 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()); + osmo_sccp_addr_name(ss7, &a_conn_info->bsc->bsc_addr)); + osmo_sccp_tx_unitdata_msg(scu, &a_conn_info->bsc->msc_addr, &a_conn_info->bsc->bsc_addr, + gsm0808_create_reset_ack()); /* Make sure all orphand subscriber connections will be cleard */ - a_clear_all(scu, a_conn_info->bsc_addr); + a_clear_all(scu, &a_conn_info->bsc->bsc_addr); + + if (!a_conn_info->bsc->reset) + a_start_reset(a_conn_info->bsc, true); msgb_free(msg); } @@ -131,17 +135,18 @@ static void bssmap_rx_reset_ack(const struct osmo_sccp_user *scu, const struct a ss7 = osmo_ss7_instance_find(network->a.cs7_instance); OSMO_ASSERT(ss7); - if (a_conn_info->reset == NULL) { + if (a_conn_info->bsc->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)); + osmo_sccp_addr_name(ss7, &a_conn_info->bsc->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)); + LOGP(DMSC, LOGL_NOTICE, "Received RESET ACK from BSC %s\n", + osmo_sccp_addr_name(ss7, &a_conn_info->bsc->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); + a_reset_ack_confirm(a_conn_info->bsc->reset); fail: msgb_free(msg); @@ -261,7 +266,7 @@ static int bssmap_rx_clear_complete(struct osmo_sccp_user *scu, const struct a_c 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); + NULL, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); /* Remove the record from the list with active connections. */ a_delete_bsc_con(a_conn_info->conn_id);