libcommon-cs: genralize a interface reset fsm

The AoIP standard also describes an MSC->BSC reset procedure. We
currently do not implement it. However, the fsm that is used to
issue the reset request from the bsc side is not generalized. This
patch generalizes the code in order to be able to use the same
code on the MSC side to perform a reset procedure from there.

Change-Id: I65e20bebb126ba35122ca3a545393d18fcadf5ad
This commit is contained in:
Philipp Maier
2017-06-12 17:40:01 +02:00
committed by Neels Hofmeyr
parent ad228e16cf
commit fef5ee84f2
9 changed files with 273 additions and 24 deletions

View File

@@ -67,7 +67,7 @@ noinst_HEADERS = \
openbscdefines.h \
osmo_bsc.h \
osmo_bsc_grace.h \
osmo_bsc_reset.h \
a_reset.h \
osmo_bsc_rf.h \
osmo_msc.h \
osmo_bsc_sigtran.h \

60
include/openbsc/a_reset.h Normal file
View File

@@ -0,0 +1,60 @@
/* (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/>.
*
*/
#pragma once
/* Reset context data (callbacks, state machine etc...) */
struct a_reset_ctx {
/* FSM instance, which handles the reset procedure */
struct osmo_fsm_inst *fsm;
/* Connection failure counter. When this counter
* reaches a certain threshold, the reset procedure
* will be triggered */
int conn_loss_counter;
/* A human readable name to display in the logs */
char name[256];
/* Callback function to be called when a connection
* failure is detected and a rest must occur */
void (*cb)(void *priv);
/* Privated data for the callback function */
void *priv;
};
/* Create and start state machine which handles the reset/reset-ack procedure */
void a_reset_start(struct a_reset_ctx *reset);
/* Confirm that we sucessfully received a reset acknowlege message */
void a_reset_ack_confirm(struct a_reset_ctx *reset);
/* Report a failed connection */
void a_reset_conn_fail(struct a_reset_ctx *reset);
/* Report a successful connection */
void a_reset_conn_success(struct a_reset_ctx *reset);
/* Check if we have a connection to a specified msc */
bool a_reset_conn_ready(struct a_reset_ctx *reset);

View File

@@ -25,6 +25,7 @@
#include <osmocom/core/write_queue.h>
#include <osmocom/core/timer.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <openbsc/a_reset.h>
#include <netinet/in.h>
@@ -60,8 +61,7 @@ struct bsc_msc_connection {
struct osmo_sccp_user *sccp_user;
struct osmo_sccp_addr g_calling_addr;
struct osmo_sccp_addr g_called_addr;
struct osmo_fsm_inst *fsm_reset;
unsigned int msc_conn_loss_count;
struct a_reset_ctx reset;
int conn_id_counter;
};

View File

@@ -38,11 +38,5 @@ int osmo_bsc_sigtran_send(struct osmo_bsc_sccp_con *conn, struct msgb *msg);
* locally, when a connection is closed by the MSC */
int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *sccp);
/* Send reset to MSC */
void osmo_bsc_sigtran_tx_reset(struct bsc_msc_data *msc);
/* close all open connections */
void osmo_bsc_sigtran_reset(struct bsc_msc_data *msc);
/* Initalize osmo sigtran backhaul */
int osmo_bsc_sigtran_init(struct llist_head *mscs);

View File

@@ -16,5 +16,6 @@ AM_CFLAGS = \
noinst_LIBRARIES = libcommon-cs.a
libcommon_cs_a_SOURCES = \
a_reset.c \
common_cs.c \
common_cs_vty.c

186
src/libcommon-cs/a_reset.c Normal file
View File

@@ -0,0 +1,186 @@
/* (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/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/fsm.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <openbsc/debug.h>
#include <openbsc/bsc_msc_data.h>
#include <openbsc/osmo_bsc_sigtran.h>
#define RESET_RESEND_INTERVAL 2 /* sec */
#define RESET_RESEND_TIMER_NO 1234 /* FIXME: dig out the real timer number */
#define BAD_CONNECTION_THRESOLD 3 /* connection failures */
enum fsm_states {
ST_DISC, /* Disconnected from remote end */
ST_CONN, /* We have a confirmed connection */
};
static const struct value_string fsm_state_names[] = {
{ST_DISC, "ST_DISC (disconnected)"},
{ST_CONN, "ST_CONN (connected)"},
{0, NULL},
};
enum fsm_evt {
EV_RESET_ACK, /* got reset acknowlegement from remote end */
EV_N_DISCONNECT, /* lost a connection */
EV_N_CONNECT, /* made a successful connection */
};
static const struct value_string fsm_evt_names[] = {
{EV_RESET_ACK, "EV_RESET_ACK"},
{EV_N_DISCONNECT, "EV_N_DISCONNECT"},
{EV_N_CONNECT, "EV_N_CONNECT"},
{0, NULL},
};
/* Disconnected state */
static void fsm_disc_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct a_reset_ctx *reset = (struct a_reset_ctx *)data;
OSMO_ASSERT(reset);
LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name,
get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event));
reset->conn_loss_counter = 0;
osmo_fsm_inst_state_chg(fi, ST_CONN, 0, 0);
}
/* Connected state */
static void fsm_conn_cb(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct a_reset_ctx *reset = (struct a_reset_ctx *)data;
OSMO_ASSERT(reset);
LOGP(DMSC, LOGL_NOTICE, "(%s) fsm-state (msc-reset): %s, fsm-event: %s\n", reset->name,
get_value_string(fsm_state_names, ST_CONN), get_value_string(fsm_evt_names, event));
switch (event) {
case EV_N_DISCONNECT:
if (reset->conn_loss_counter >= BAD_CONNECTION_THRESOLD) {
LOGP(DMSC, LOGL_NOTICE, "(%s) SIGTRAN connection down, reconnecting...\n", reset->name);
osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
} else
reset->conn_loss_counter++;
break;
case EV_N_CONNECT:
reset->conn_loss_counter = 0;
break;
}
}
/* Timer callback to retransmit the reset signal */
static int fsm_reset_ack_timeout_cb(struct osmo_fsm_inst *fi)
{
struct a_reset_ctx *reset = (struct a_reset_ctx *)fi->priv;
LOGP(DMSC, LOGL_NOTICE, "(%s) reset-ack timeout (T%i) in state %s, resending...\n", reset->name, fi->T,
get_value_string(fsm_state_names, fi->state));
reset->cb(reset->priv);
osmo_fsm_inst_state_chg(fi, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
return 0;
}
static struct osmo_fsm_state fsm_states[] = {
[ST_DISC] = {
.in_event_mask = (1 << EV_RESET_ACK),
.out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
.name = "DISC",
.action = fsm_disc_cb,
},
[ST_CONN] = {
.in_event_mask = (1 << EV_N_DISCONNECT) | (1 << EV_N_CONNECT),
.out_state_mask = (1 << ST_DISC) | (1 << ST_CONN),
.name = "CONN",
.action = fsm_conn_cb,
},
};
/* State machine definition */
static struct osmo_fsm fsm = {
.name = "FSM RESET",
.states = fsm_states,
.num_states = ARRAY_SIZE(fsm_states),
.log_subsys = DMSC,
.timer_cb = fsm_reset_ack_timeout_cb,
};
/* Create and start state machine which handles the reset/reset-ack procedure */
void a_reset_start(struct a_reset_ctx *reset)
{
OSMO_ASSERT(reset);
osmo_fsm_register(&fsm);
reset->fsm = osmo_fsm_inst_alloc(&fsm, NULL, NULL, LOGL_DEBUG, "FSM RESET INST");
OSMO_ASSERT(reset->fsm);
reset->fsm->priv = reset;
/* kick off reset-ack sending mechanism */
osmo_fsm_inst_state_chg(reset->fsm, ST_DISC, RESET_RESEND_INTERVAL, RESET_RESEND_TIMER_NO);
}
/* Confirm that we sucessfully received a reset acknowlege message */
void a_reset_ack_confirm(struct a_reset_ctx *reset)
{
OSMO_ASSERT(reset);
OSMO_ASSERT(reset->fsm);
osmo_fsm_inst_dispatch(reset->fsm, EV_RESET_ACK, reset);
}
/* Report a failed connection */
void a_reset_conn_fail(struct a_reset_ctx *reset)
{
OSMO_ASSERT(reset);
OSMO_ASSERT(reset->fsm);
osmo_fsm_inst_dispatch(reset->fsm, EV_N_DISCONNECT, reset);
}
/* Report a successful connection */
void a_reset_conn_success(struct a_reset_ctx *reset)
{
OSMO_ASSERT(reset);
OSMO_ASSERT(reset->fsm);
osmo_fsm_inst_dispatch(reset->fsm, EV_N_CONNECT, reset);
}
/* Check if we have a connection to a specified msc */
bool a_reset_conn_ready(struct a_reset_ctx *reset)
{
OSMO_ASSERT(reset);
OSMO_ASSERT(reset->fsm);
if (reset->fsm->state == ST_CONN)
return true;
return false;
}

View File

@@ -31,7 +31,6 @@ osmo_bsc_SOURCES = \
osmo_bsc_api.c \
osmo_bsc_grace.c \
osmo_bsc_msc.c \
osmo_bsc_reset.c \
osmo_bsc_sigtran.c \
osmo_bsc_filter.c \
osmo_bsc_bssap.c \

View File

@@ -31,7 +31,7 @@
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <openbsc/osmo_bsc_sigtran.h>
#include <openbsc/osmo_bsc_reset.h>
#include <openbsc/a_reset.h>
#include <osmocom/core/byteswap.h>
#define IP_V4_ADDR_LEN 4
@@ -198,7 +198,7 @@ static int bssmap_handle_reset_ack(struct bsc_msc_data *msc,
/* Inform the FSM that controls the RESET/RESET-ACK procedure
* that we have successfully received the reset-ack message */
reset_ack_confirm(msc);
a_reset_ack_confirm(&msc->msc_con->reset);
return 0;
}

View File

@@ -31,7 +31,7 @@
#include <openbsc/osmo_bsc.h>
#include <openbsc/osmo_bsc_grace.h>
#include <openbsc/osmo_bsc_sigtran.h>
#include <openbsc/osmo_bsc_reset.h>
#include <openbsc/a_reset.h>
#include <openbsc/gsm_04_80.h>
/* A pointer to a list with all involved MSCs
@@ -76,7 +76,7 @@ static int pick_free_conn_id(struct bsc_msc_data *msc)
}
/* Send reset to MSC */
void osmo_bsc_sigtran_tx_reset(struct bsc_msc_data *msc)
static void osmo_bsc_sigtran_tx_reset(struct bsc_msc_data *msc)
{
struct msgb *msg;
LOGP(DMSC, LOGL_NOTICE, "Sending RESET to MSC No.: %i\n", msc->nr);
@@ -169,7 +169,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
/* Incoming data is a sign of a vital connection */
bsc_con = get_bsc_conn_by_conn_id(scu_prim->u.disconnect.conn_id);
if (bsc_con)
report_conn_success(bsc_con->msc);
a_reset_conn_success(&bsc_con->msc->msc_con->reset);
rc = handle_data_from_msc(scu_prim->u.data.conn_id, oph->msg);
break;
@@ -189,7 +189,7 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
/* We might have a connectivity problem. Maybe we need to go
* through the reset procedure again? */
if (scu_prim->u.disconnect.cause == 0)
report_conn_fail(bsc_con->msc);
a_reset_conn_fail(&bsc_con->msc->msc_con->reset);
rc = osmo_bsc_sigtran_del_conn(bsc_con);
}
@@ -216,7 +216,7 @@ enum bsc_con osmo_bsc_sigtran_new_conn(struct gsm_subscriber_connection *conn, s
LOGP(DMSC, LOGL_NOTICE, "Initalizing resources for new SIGTRAN connection to MSC No.: %i...\n", msc->nr);
if (sccp_conn_ready(msc) == false) {
if (a_reset_conn_ready(&msc->msc_con->reset) == false) {
LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
return BSC_CON_REJECT_NO_LINK;
}
@@ -261,7 +261,7 @@ int osmo_bsc_sigtran_open_conn(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
msc = conn->msc;
if (sccp_conn_ready(msc) == false) {
if (a_reset_conn_ready(&msc->msc_con->reset) == false) {
LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
return -EINVAL;
}
@@ -288,7 +288,7 @@ int osmo_bsc_sigtran_send(struct osmo_bsc_sccp_con *conn, struct msgb *msg)
msc = conn->msc;
if (sccp_conn_ready(msc) == false) {
if (a_reset_conn_ready(&msc->msc_con->reset) == false) {
LOGP(DMSC, LOGL_ERROR, "MSC is not connected. Dropping.\n");
return -EINVAL;
}
@@ -319,7 +319,7 @@ int osmo_bsc_sigtran_del_conn(struct osmo_bsc_sccp_con *conn)
/* This bahaviour might be caused by a bad connection. Maybe we
* will have to go through the reset procedure again */
report_conn_fail(conn->msc);
a_reset_conn_fail(&conn->msc->msc_con->reset);
}
llist_del(&conn->entry);
@@ -348,20 +348,26 @@ static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con)
}
/* close all open connections */
void osmo_bsc_sigtran_reset(struct bsc_msc_data *msc)
/* Callback function: Close all open connections */
static void osmo_bsc_sigtran_reset_cb(void *priv)
{
struct osmo_bsc_sccp_con *conn;
struct osmo_bsc_sccp_con *conn_temp;
struct bsc_msc_data *msc = (struct bsc_msc_data*) priv;
OSMO_ASSERT(msc);
/* Close all open connections */
llist_for_each_entry_safe(conn, conn_temp, &active_connections, entry) {
if (conn->conn)
gsm0808_clear(conn->conn);
bsc_notify_msc_lost(conn);
osmo_bsc_sigtran_del_conn(conn);
}
msc->msc_con->conn_id_counter = 0;
/* Send reset to MSC */
osmo_bsc_sigtran_tx_reset(msc);
}
/* Initalize osmo sigtran backhaul */
@@ -389,7 +395,10 @@ int osmo_bsc_sigtran_init(struct llist_head *mscs)
osmo_sccp_user_bind(msc->msc_con->sccp, msc_name, sccp_sap_up, SCCP_SSN_BSSAP);
/* Start MSC reset procedure */
start_reset_fsm(msc);
msc->msc_con->reset.priv = msc;
msc->msc_con->reset.cb = osmo_bsc_sigtran_reset_cb;
snprintf(msc->msc_con->reset.name,sizeof(msc->msc_con->reset.name),"MSC No. %u",msc->nr);
a_reset_start(&msc->msc_con->reset);
}
return 0;