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
This commit is contained in:
Harald Welte 2018-02-09 00:09:16 +01:00
parent b6777fb055
commit 54a10efea0
4 changed files with 120 additions and 56 deletions

View File

@ -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);

View File

@ -20,17 +20,17 @@
#pragma once
#include <osmocom/msc/a_iface.h>
/* 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 */

View File

@ -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;

View File

@ -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);