From 8f8af409743ee03b4a620a21d22facda304f3e68 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Thu, 30 Mar 2023 05:18:53 +0200 Subject: [PATCH] SCCP N-PCSTATE: trigger MSC status on PC availability Related: SYS#6319 Related: Ia1aea4e33230d6a685b72ea5ba20dd9c7d265d44 osmo-ttcn3-hacks Related: Ib4a5330df30a73e744c316898817b2fa3271d75e osmo-ttcn3-hacks Change-Id: I3a0869598b8395601a16d78dbc46eec400c0ea84 --- include/osmocom/bsc/bssmap_reset.h | 1 + src/osmo-bsc/bssmap_reset.c | 6 ++ src/osmo-bsc/osmo_bsc_sigtran.c | 105 ++++++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/include/osmocom/bsc/bssmap_reset.h b/include/osmocom/bsc/bssmap_reset.h index fcd850b8b..c0de13c4d 100644 --- a/include/osmocom/bsc/bssmap_reset.h +++ b/include/osmocom/bsc/bssmap_reset.h @@ -28,4 +28,5 @@ struct bssmap_reset { struct bssmap_reset *bssmap_reset_alloc(void *ctx, const char *label, const struct bssmap_reset_cfg *cfg); bool bssmap_reset_is_conn_ready(const struct bssmap_reset *bssmap_reset); void bssmap_reset_resend_reset(struct bssmap_reset *bssmap_reset); +void bssmap_reset_set_disconnected(struct bssmap_reset *bssmap_reset); void bssmap_reset_term_and_free(struct bssmap_reset *bssmap_reset); diff --git a/src/osmo-bsc/bssmap_reset.c b/src/osmo-bsc/bssmap_reset.c index fa6684d2f..ac304a548 100644 --- a/src/osmo-bsc/bssmap_reset.c +++ b/src/osmo-bsc/bssmap_reset.c @@ -251,6 +251,12 @@ void bssmap_reset_resend_reset(struct bssmap_reset *bssmap_reset) osmo_fsm_inst_state_chg_ms(bssmap_reset->fi, BSSMAP_RESET_ST_DISC, 1, 0); } +void bssmap_reset_set_disconnected(struct bssmap_reset *bssmap_reset) +{ + /* Go to disconnected state, with the normal RESET timeout to re-send RESET. */ + bssmap_reset_fsm_state_chg(bssmap_reset->fi, BSSMAP_RESET_ST_DISC); +} + static __attribute__((constructor)) void bssmap_reset_fsm_init(void) { OSMO_ASSERT(osmo_fsm_register(&bssmap_reset_fsm) == 0); diff --git a/src/osmo-bsc/osmo_bsc_sigtran.c b/src/osmo-bsc/osmo_bsc_sigtran.c index 8837330ec..f4c8f811b 100644 --- a/src/osmo-bsc/osmo_bsc_sigtran.c +++ b/src/osmo-bsc/osmo_bsc_sigtran.c @@ -36,6 +36,7 @@ #include #include #include +#include #include /* A pointer to a list with all involved MSCs @@ -92,7 +93,7 @@ void osmo_bsc_sigtran_tx_reset_ack(const struct bsc_msc_data *msc) &msc->a.msc_addr, msg); } -/* Find an MSC by its sigtran point code */ +/* Find an MSC by its remote SCCP address */ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_addr) { struct osmo_ss7_instance *ss7; @@ -107,6 +108,21 @@ static struct bsc_msc_data *get_msc_by_addr(const struct osmo_sccp_addr *msc_add return NULL; } +/* Find an MSC by its remote sigtran point code on a given cs7 instance. */ +static struct bsc_msc_data *get_msc_by_pc(struct osmo_ss7_instance *cs7, uint32_t pc) +{ + struct bsc_msc_data *msc; + llist_for_each_entry(msc, msc_list, entry) { + if (msc->a.cs7_instance != cs7->cfg.id) + continue; + if ((msc->a.msc_addr.presence & OSMO_SCCP_ADDR_T_PC) == 0) + continue; + if (msc->a.msc_addr.pc == pc) + return msc; + } + return NULL; +} + /* Received data from MSC, use the connection id which MSC it is */ static int handle_data_from_msc(struct gsm_subscriber_connection *conn, struct msgb *msg) { @@ -188,6 +204,89 @@ refuse: return rc; } +static void handle_pcstate_ind(struct osmo_ss7_instance *cs7, const struct osmo_scu_pcstate_param *pcst) +{ + struct bsc_msc_data *msc; + bool connected; + bool disconnected; + + LOGP(DMSC, LOGL_DEBUG, "N-PCSTATE ind: affected_pc=%u sp_status=%d remote_sccp_status=%d\n", + pcst->affected_pc, pcst->sp_status, pcst->remote_sccp_status); + + /* If we don't care about that point-code, ignore PCSTATE. */ + msc = get_msc_by_pc(cs7, pcst->affected_pc); + if (!msc) + return; + + /* See if this marks the point code to have become available, or to have been lost. + * + * I want to detect two events: + * - connection event (both indicators say PC is reachable). + * - disconnection event (at least one indicator says the PC is not reachable). + * + * There are two separate incoming indicators with various possible values -- the incoming events can be: + * + * - neither connection nor disconnection indicated -- just indicating congestion + * connected == false, disconnected == false --> do nothing. + * - both incoming values indicate that we are connected + * --> trigger connected + * - both indicate we are disconnected + * --> trigger disconnected + * - one value indicates 'connected', the other indicates 'disconnected' + * --> trigger disconnected + * + * Congestion could imply that we're connected, but it does not indicate that a PC's reachability changed, so no need to + * trigger on that. + */ + connected = false; + disconnected = false; + + switch (pcst->sp_status) { + case OSMO_SCCP_SP_S_ACCESSIBLE: + connected = true; + break; + case OSMO_SCCP_SP_S_INACCESSIBLE: + disconnected = true; + break; + default: + case OSMO_SCCP_SP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + switch (pcst->remote_sccp_status) { + case OSMO_SCCP_REM_SCCP_S_AVAILABLE: + if (!disconnected) + connected = true; + break; + case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN: + case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED: + case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE: + disconnected = true; + connected = false; + break; + default: + case OSMO_SCCP_REM_SCCP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + if (disconnected && a_reset_conn_ready(msc)) { + LOGP(DMSC, LOGL_NOTICE, + "(msc%d) now unreachable: N-PCSTATE ind: pc=%u sp_status=%d remote_sccp_status=%d\n", + msc->nr, pcst->affected_pc, pcst->sp_status, pcst->remote_sccp_status); + /* A previously usable MSC has disconnected. Kick the BSSMAP back to DISC state. */ + bssmap_reset_set_disconnected(msc->a.bssmap_reset); + } else if (connected && !a_reset_conn_ready(msc)) { + LOGP(DMSC, LOGL_NOTICE, + "(msc%d) now available: N-PCSTATE ind: pc=%u sp_status=%d remote_sccp_status=%d\n", + msc->nr, pcst->affected_pc, pcst->sp_status, pcst->remote_sccp_status); + /* A previously unusable MSC has become reachable. Trigger immediate BSSMAP RESET -- we would resend a + * RESET either way, but we might as well do it now to speed up connecting. */ + bssmap_reset_resend_reset(msc->a.bssmap_reset); + } +} + /* Callback function, called by the SCCP stack when data arrives */ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) { @@ -255,6 +354,10 @@ static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) } break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION): + handle_pcstate_ind(osmo_sccp_get_ss7(sccp), &scu_prim->u.pcstate); + break; + default: LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN operation %s on primitive %u\n", get_value_string(osmo_prim_op_names, oph->operation), oph->primitive);