pespin has uploaded this change for review. (
https://gerrit.osmocom.org/c/libosmo-sigtran/+/40488?usp=email )
Change subject: WIP: m3ua: Make sure MTP-PAUSE.ind is sent to SCCP when AS goes down
......................................................................
WIP: m3ua: Make sure MTP-PAUSE.ind is sent to SCCP when AS goes down
Whenever an SCTP link goes down, it may affect current set of accessible
DPCs if any of the routes (AS) using that ASP become inactive.
The approach presented in this patch is to, based on existing route
table, figure out which DPCs may have been accessible before the AS went
down, and then figure out which of those are not reachable anymore, then
send MTP-PAUSE.ind for those.
In turn, SCCP will trigger N-PCSTATE.ind towards its users.
Change-Id: I0f28b049045fbea93fe91c99251e7aaa40c014e5
---
M src/xua_as_fsm.c
M src/xua_snm.c
2 files changed, 173 insertions(+), 11 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-sigtran refs/changes/88/40488/1
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
index 3e117ee..1247db9 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -22,6 +22,7 @@
#include "xua_msg.h"
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/protocol/m3ua.h>
+#include <sys/types.h>
#include "ss7_as.h"
#include "ss7_asp.h"
@@ -413,12 +414,147 @@
}
}
+
+#include "ss7_combined_linkset.h"
+enum pc_map_item_state {
+ DPC_UNSET = 0,
+ DPC_ACTIVE,
+ DPC_LOST
+};
+struct pc_map {
+ uint8_t v[16384]; /* 2^14, 14-bit PCs */
+};
+
+
+static void pcmap_fill_pc_mask(struct osmo_ss7_instance *ss7, struct pc_map *pcmap,
uint32_t pc, uint32_t mask, enum pc_map_item_state val)
+{
+ unsigned int width = osmo_ss7_pc_width(&ss7->cfg.pc_fmt);
+ uint32_t maskbits = ~mask & ((1 << width)-1);
+ uint32_t fullpc;
+
+ LOGP(DLSS7, LOGL_NOTICE, "PESPIN: FILL PCMAP PC=%u mask=0x%08x width=%u,
maskbits=0x%08x\n", pc, mask, width, maskbits);
+
+ for (fullpc = (pc & maskbits); fullpc <= (pc | maskbits); fullpc++) {
+ const enum pc_map_item_state prev_val = pcmap->v[fullpc];
+ /* if an ACTIVE route is found for the DPC with a lower prio, it will be used, so
it's not lost */
+ if (prev_val == DPC_UNSET || val == DPC_ACTIVE)
+ pcmap->v[fullpc] = val;
+ }
+}
+
+
+static int pcmap_find_chunk_start(const struct pc_map *pcmap, unsigned int start, enum
pc_map_item_state val)
+{
+ unsigned int i = start;
+ while (i < ARRAY_SIZE(pcmap->v)) {
+ if (pcmap->v[i] == val)
+ return i;
+ i++;
+ }
+ return -1;
+}
+static int pcmap_find_chunk_end(const struct pc_map *pcmap, unsigned int start, enum
pc_map_item_state val)
+{
+ unsigned int i = start;
+ while (i < ARRAY_SIZE(pcmap->v)) {
+ if (pcmap->v[i] != val)
+ return i;
+ i++;
+ }
+ return i;
+}
+
+static void as_gen_dpc_lost_bv(const struct osmo_ss7_as *as, struct pc_map *pcmap)
+{
+ struct osmo_ss7_instance *ss7 = as->inst;
+ struct osmo_ss7_route_table *rtbl = ss7->rtable_system;
+ struct osmo_ss7_combined_linkset *clset;
+
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ struct osmo_ss7_route *rt;
+ bool as_match = false;
+ bool active = false;
+ /* If AS is involved and none of the combined linkset routes is working, add it: */
+ llist_for_each_entry(rt, &clset->routes, list) {
+ if (rt->dest.as == as)
+ as_match = true;
+ else if (ss7_route_is_available(rt)) {
+ active = true;
+ break;
+ }
+ }
+ if (active)
+ pcmap_fill_pc_mask(ss7, pcmap, clset->cfg.pc, clset->cfg.mask, DPC_ACTIVE);
+ else if (as_match)
+ pcmap_fill_pc_mask(ss7, pcmap, clset->cfg.pc, clset->cfg.mask, DPC_LOST);
+ }
+}
+
+static unsigned int fill_chunk(uint32_t *aff_pc, uint32_t start_pc, unsigned int num_pc,
unsigned int width)
+{
+ if (num_pc == 1) { /* base case */
+ aff_pc[0] = htonl(start_pc & 0xffffff); /* mask 0 */
+ return 1;
+ }
+
+ /* See if we have a whole contiguous block: */
+ for (unsigned int lvl = width; lvl > 0; lvl--) {
+ LOGP(DLSS7, LOGL_NOTICE, "PESPIN: fill_chunk() lvl=%u %u %u\n", lvl,
(start_pc & ((1 << lvl) - 1)), num_pc == (0x01 << (lvl + 1)));
+
+ if ((start_pc & ((1 << lvl)-1)) == 0 &&
+ num_pc == (0x01 << lvl)) {
+ aff_pc[0] = htonl(lvl << 24 | (start_pc & 0xffffff));
+ return 1;
+ }
+ }
+
+ LOGP(DLSS7, LOGL_NOTICE, "PESPIN: fill_chunk(START_PC=%u num_pc=%u width=%u) NOT
CONTIGUOUS!\n", start_pc, num_pc, width);
+
+ aff_pc[0] = htonl(start_pc & 0xffffff); /* mask 0 */
+ return 1 + fill_chunk(aff_pc, start_pc + 1, num_pc - 1, width);
+}
+
+static unsigned int as_became_unavailable_calc_affected_pc(const struct osmo_ss7_as *as,
+ uint32_t *aff_pc, unsigned int num_aff_pc)
+{
+ struct osmo_ss7_instance *ss7 = as->inst;
+ const uint32_t max_num_aff_pc = num_aff_pc;
+
+ struct pc_map *pcmap = talloc_zero(NULL, struct pc_map);
+ as_gen_dpc_lost_bv(as, pcmap);
+
+ num_aff_pc = 0;
+ unsigned int last_pos = 0;
+ int pos = 0;
+
+ while ((pos = pcmap_find_chunk_start(pcmap, last_pos, DPC_LOST)) >= 0) {
+ unsigned int until = pcmap_find_chunk_end(pcmap, pos, DPC_LOST);
+ if (until - pos > max_num_aff_pc) {
+ LOGPAS(as, DLSS7, LOGL_ERROR, "Not enough space in aff_pc!\n");
+ num_aff_pc += fill_chunk(&aff_pc[num_aff_pc], pos, max_num_aff_pc - num_aff_pc,
osmo_ss7_pc_width(&ss7->cfg.pc_fmt));
+ return num_aff_pc;
+ }
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "PESPIN: FILL CHUNK PC=%u num_pcs=%u\n", pos,
until - pos);
+ num_aff_pc += fill_chunk(&aff_pc[num_aff_pc], pos, until - pos,
osmo_ss7_pc_width(&ss7->cfg.pc_fmt));
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "PESPIN: FILL CHUNK total num_aff_pc=%u\n",
num_aff_pc);
+ last_pos = until + 1;
+ }
+
+ return num_aff_pc;
+}
+
+/* TODO: move to header file: */
+void xua_snm_pc_available_to_sccp(struct osmo_sccp_instance *sccp,
+ const uint32_t *aff_pc, unsigned int num_aff_pc,
+ bool available);
+
/* onenter call-back responsible of transmitting NTFY to all ASPs in
* case of AS state changes */
static void xua_as_fsm_onenter(struct osmo_fsm_inst *fi, uint32_t old_state)
{
struct xua_as_fsm_priv *xafp = (struct xua_as_fsm_priv *) fi->priv;
struct osmo_ss7_as *as = xafp->as;
+ struct osmo_ss7_instance *s7i = as->inst;
struct osmo_xlm_prim_notify npar;
switch (fi->state) {
@@ -456,15 +592,38 @@
as_notify_all_asp(xafp->as, &npar);
- /* only if we are the SG, we must start broadcasting availability information
- * to everyone else */
- if (get_local_role(xafp->as) == OSMO_SS7_ASP_ROLE_SG) {
+ bool became_available = (old_state != XUA_AS_S_ACTIVE && fi->state ==
XUA_AS_S_ACTIVE);
+ bool became_unavailable = (old_state == XUA_AS_S_ACTIVE && fi->state !=
XUA_AS_S_ACTIVE);
+ int role = get_local_role(xafp->as);
+
+ switch (role) {
+ case OSMO_SS7_ASP_ROLE_ASP:
+ if (s7i->sccp) {
+ if (became_available) {
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "PESPIN: AS BECAME AVAILABLE!\n");
+ /* TODO */
+ } else if (became_unavailable) {
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "PESPIN: AS BECAME UNAVAILABLE!\n");
+ uint32_t *aff_pc = talloc_size(NULL, 16384); /* 2^14, 14-bit PCs */
+ uint32_t num_aff_pc = 16384;
+ num_aff_pc = as_became_unavailable_calc_affected_pc(as, aff_pc, num_aff_pc);
+ xua_snm_pc_available_to_sccp(s7i->sccp, aff_pc, num_aff_pc, became_available);
+ talloc_free(aff_pc);
+ }
+ }
+ break;
+ case OSMO_SS7_ASP_ROLE_SG:
+ /* only if we are the SG, we must start broadcasting availability information
+ * to everyone else */
/* advertise availability of the routing key to others */
- uint32_t aff_pc = htonl(as->cfg.routing_key.pc);
- if (old_state != XUA_AS_S_ACTIVE && fi->state == XUA_AS_S_ACTIVE)
- xua_snm_pc_available(as, &aff_pc, 1, NULL, true);
- else if (old_state == XUA_AS_S_ACTIVE && fi->state != XUA_AS_S_ACTIVE)
- xua_snm_pc_available(as, &aff_pc, 1, NULL, false);
+ if (became_available || became_unavailable) {
+ uint32_t aff_pc = htonl(as->cfg.routing_key.pc);
+ xua_snm_pc_available(as, &aff_pc, 1, NULL, became_available);
+ }
+ break;
+ case OSMO_SS7_ASP_ROLE_IPSP:
+ /* TODO */
+ break;
}
};
diff --git a/src/xua_snm.c b/src/xua_snm.c
index 079cd96..2693306 100644
--- a/src/xua_snm.c
+++ b/src/xua_snm.c
@@ -117,9 +117,9 @@
}
/* generate MTP-PAUSE / MTP-RESUME towards local SCCP users */
-static void xua_snm_pc_available_to_sccp(struct osmo_sccp_instance *sccp,
- const uint32_t *aff_pc, unsigned int num_aff_pc,
- bool available)
+void xua_snm_pc_available_to_sccp(struct osmo_sccp_instance *sccp,
+ const uint32_t *aff_pc, unsigned int num_aff_pc,
+ bool available)
{
int i;
for (i = 0; i < num_aff_pc; i++) {
@@ -129,6 +129,8 @@
uint32_t pc = _aff_pc & 0xffffff;
uint8_t mask = _aff_pc >> 24;
+ LOGPSCI(sccp, LOGL_ERROR, "PESPIN: mtp_pause pc=%u mask=%u available=%u\n",
pc, mask, available);
+
if (!mask) {
if (available)
sccp_scmg_rx_mtp_resume(sccp, pc);
@@ -140,6 +142,7 @@
uint32_t maskbits = (1 << mask) - 1;
uint32_t fullpc;
for (fullpc = (pc & ~maskbits); fullpc <= (pc | maskbits); fullpc++) {
+ LOGPSCI(sccp, LOGL_ERROR, "PESPIN: mtp_pause fullpc=%u available=%u\n",
fullpc, available);
if (available)
sccp_scmg_rx_mtp_resume(sccp, fullpc);
else
--
To view, visit
https://gerrit.osmocom.org/c/libosmo-sigtran/+/40488?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: newchange
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I0f28b049045fbea93fe91c99251e7aaa40c014e5
Gerrit-Change-Number: 40488
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>