pespin submitted this change.

View Change

Approvals: Jenkins Builder: Verified pespin: Looks good to me, approved laforge: Looks good to me, but someone else must approve daniel: Looks good to me, but someone else must approve
Introduce initial route status support

Mark route avail/unavail based on DAVA/DUNA from SG.

Related: OS#6754
Change-Id: Icb4f78f8da9de5a611cb6311722bf3acf1a68fc5
---
M src/ss7.c
M src/ss7_combined_linkset.c
M src/ss7_combined_linkset.h
M src/ss7_internal.h
M src/ss7_route.c
M src/ss7_route.h
M src/ss7_route_table.c
M src/ss7_route_table.h
M src/ss7_vty.c
M src/xua_snm.c
M tests/vty/osmo_stp_route_prio.vty
11 files changed, 178 insertions(+), 20 deletions(-)

diff --git a/src/ss7.c b/src/ss7.c
index 902479d..e2f2ca9 100644
--- a/src/ss7.c
+++ b/src/ss7.c
@@ -89,13 +89,18 @@
return pc_fmt->component_len[0] + pc_fmt->component_len[1] + pc_fmt->component_len[2];
}

+/* Obtain a full-width mask matching a single DPC in this ss7_instance. */
+uint32_t ss7_pc_full_mask(const struct osmo_ss7_pc_fmt *pc_fmt)
+{
+ return (1 << osmo_ss7_pc_width(pc_fmt)) - 1;
+}
+
/* truncate pc or mask to maximum permitted length. This solves
* callers specifying arbitrary large masks which then evade duplicate
* detection with longer mask lengths */
uint32_t osmo_ss7_pc_normalize(const struct osmo_ss7_pc_fmt *pc_fmt, uint32_t pc)
{
- uint32_t mask = (1 << osmo_ss7_pc_width(pc_fmt))-1;
- return pc & mask;
+ return pc & ss7_pc_full_mask(pc_fmt);
}

/***********************************************************************
diff --git a/src/ss7_combined_linkset.c b/src/ss7_combined_linkset.c
index eb19b85..0b6dd3c 100644
--- a/src/ss7_combined_linkset.c
+++ b/src/ss7_combined_linkset.c
@@ -172,6 +172,20 @@
ss7_combined_linkset_free(clset);
}

+/* Whether any route in the combined linkset is available: */
+bool ss7_combined_linkset_is_available(const struct osmo_ss7_combined_linkset *clset)
+{
+ bool avail = false;
+ struct osmo_ss7_route *rt;
+ llist_for_each_entry(rt, &clset->routes, list) {
+ if (ss7_route_is_available(rt)) {
+ avail = true;
+ break;
+ }
+ }
+ return avail;
+}
+
static ext_sls_t osmo_ss7_instance_calc_itu_ext_sls(const struct osmo_ss7_instance *inst, const struct osmo_ss7_route_label *rtlabel)
{
/* Take 6 bits from OPC and DPC according to config: */
diff --git a/src/ss7_combined_linkset.h b/src/ss7_combined_linkset.h
index 432ebd7..e3756b6 100644
--- a/src/ss7_combined_linkset.h
+++ b/src/ss7_combined_linkset.h
@@ -57,6 +57,7 @@
ss7_combined_linkset_del_route(struct osmo_ss7_route *rt);
struct osmo_ss7_route *
ss7_combined_linkset_lookup_route(struct osmo_ss7_combined_linkset *clset, const struct osmo_ss7_route_label *rtlabel);
+bool ss7_combined_linkset_is_available(const struct osmo_ss7_combined_linkset *clset);

#define LOGPCLSET(clset, subsys, level, fmt, args ...) do { \
char _pc_str[MAX_PC_STR_LEN]; \
diff --git a/src/ss7_internal.h b/src/ss7_internal.h
index d931351..cfa092a 100644
--- a/src/ss7_internal.h
+++ b/src/ss7_internal.h
@@ -15,6 +15,8 @@

bool ss7_ipv6_sctp_supported(const char *host, bool bind);

+uint32_t ss7_pc_full_mask(const struct osmo_ss7_pc_fmt *pc_fmt);
+
struct osmo_ss7_asp *ss7_asp_find_by_socket_addr(int fd, int trans_proto);

bool ss7_asp_protocol_check_trans_proto(enum osmo_ss7_asp_protocol proto, int trans_proto);
diff --git a/src/ss7_route.c b/src/ss7_route.c
index ec41a8a..c432c49 100644
--- a/src/ss7_route.c
+++ b/src/ss7_route.c
@@ -38,6 +38,14 @@
* SS7 Routes
***********************************************************************/

+ /* ITU Q.704 3.4 Status of signalling routes */
+const struct value_string ss7_route_status_names[] = {
+ { OSMO_SS7_ROUTE_STATUS_UNAVAILABLE, "unavailable" },
+ { OSMO_SS7_ROUTE_STATUS_AVAILABLE, "available" },
+ { OSMO_SS7_ROUTE_STATUS_RESTRICTED, "restricted" },
+ {}
+};
+
/*! \brief Allocate a route entry
* \param[in] rtbl Routing Table where the route belongs
* \param[in] pc Point Code of the destination of the route
@@ -79,6 +87,7 @@
/* Mark it as not being inserted yet in rtbl */
INIT_LLIST_HEAD(&rt->list);
rt->rtable = rtbl;
+ rt->status = OSMO_SS7_ROUTE_STATUS_AVAILABLE;
/* truncate mask to maximum. Let's avoid callers specifying arbitrary large
* masks to ensure we don't fail duplicate detection with longer mask lengths */
rt->cfg.mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
@@ -374,9 +383,29 @@
bool ss7_route_is_available(const struct osmo_ss7_route *rt)
{
OSMO_ASSERT(rt);
+ if (!ss7_route_dest_is_available(rt))
+ return false;
+ return rt->status == OSMO_SS7_ROUTE_STATUS_AVAILABLE;
+}
+
+bool ss7_route_dest_is_available(const struct osmo_ss7_route *rt)
+{
+ OSMO_ASSERT(rt);
if (rt->dest.as)
return osmo_ss7_as_active(rt->dest.as);
if (rt->dest.linkset)
return ss7_linkset_is_available(rt->dest.linkset);
return false;
}
+
+/* Whether route mask identifies a single DPC. */
+bool ss7_route_is_fully_qualified(const struct osmo_ss7_route *rt)
+{
+ return rt->cfg.mask == ss7_pc_full_mask(&rt->rtable->inst->cfg.pc_fmt);
+}
+
+void ss7_route_update_route_status(struct osmo_ss7_route *rt, enum osmo_ss7_route_status status)
+{
+ LOGPRT(rt, DLSS7, LOGL_NOTICE, "changed to status '%s'\n", ss7_route_status_name(status));
+ rt->status = status;
+}
diff --git a/src/ss7_route.h b/src/ss7_route.h
index 7ba7ba5..2e35d1f 100644
--- a/src/ss7_route.h
+++ b/src/ss7_route.h
@@ -15,6 +15,16 @@

#define OSMO_SS7_ROUTE_PRIO_DEFAULT 5

+/* ITU Q.704 3.4 Status of signalling routes */
+enum osmo_ss7_route_status {
+ OSMO_SS7_ROUTE_STATUS_UNAVAILABLE,
+ OSMO_SS7_ROUTE_STATUS_AVAILABLE,
+ OSMO_SS7_ROUTE_STATUS_RESTRICTED,
+};
+extern const struct value_string ss7_route_status_names[];
+static inline const char *ss7_route_status_name(enum osmo_ss7_route_status val)
+{ return get_value_string(ss7_route_status_names, val); }
+
struct osmo_ss7_route {
/*! member in \ref osmo_ss7_combined_linkset.routes */
struct llist_head list;
@@ -23,6 +33,8 @@
/* Combined linkset this route is part of */
struct osmo_ss7_combined_linkset *clset;

+ enum osmo_ss7_route_status status;
+
struct {
/*! pointer to linkset (destination) of route */
struct osmo_ss7_linkset *linkset;
@@ -59,15 +71,26 @@
int ss7_route_set_linkset(struct osmo_ss7_route *rt, const char *linkset_name);
int ss7_route_insert(struct osmo_ss7_route *rt);

+bool ss7_route_dest_is_available(const struct osmo_ss7_route *rt);
bool ss7_route_is_available(const struct osmo_ss7_route *rt);
+
+bool ss7_route_is_fully_qualified(const struct osmo_ss7_route *rt);
+static inline bool ss7_route_is_summary(const struct osmo_ss7_route *rt)
+{
+ return !ss7_route_is_fully_qualified(rt);
+}
+
+void ss7_route_update_route_status(struct osmo_ss7_route *rt, enum osmo_ss7_route_status status);
+
#define LOGPRT(rt, subsys, level, fmt, args ...) do { \
char _pc_str[MAX_PC_STR_LEN]; \
char _mask_str[MAX_PC_STR_LEN]; \
_LOGSS7((rt)->rtable->inst, subsys, level, \
- "RT(dpc=%u=%s,mask=0x%x=%s,prio=%u,via=%s) " fmt, \
+ "RT(dpc=%u=%s,mask=0x%x=%s,prio=%u,via=%s,st=%s) " fmt, \
(rt)->cfg.pc, osmo_ss7_pointcode_print_buf(_pc_str, MAX_PC_STR_LEN, (rt)->rtable->inst, (rt)->cfg.pc), \
(rt)->cfg.mask, osmo_ss7_pointcode_print_buf(_mask_str, MAX_PC_STR_LEN, (rt)->rtable->inst, (rt)->cfg.mask), \
(rt)->cfg.priority, \
(rt)->cfg.linkset_name ? (rt)->cfg.linkset_name : "", \
+ ss7_route_status_name((rt)->status), \
## args); \
} while (0)
diff --git a/src/ss7_route_table.c b/src/ss7_route_table.c
index c816e36..f216cff 100644
--- a/src/ss7_route_table.c
+++ b/src/ss7_route_table.c
@@ -209,6 +209,25 @@
return clset;
}

+void ss7_route_table_update_route_status_by_as(struct osmo_ss7_route_table *rtbl, enum osmo_ss7_route_status status,
+ const struct osmo_ss7_as *as, uint32_t dpc)
+{
+ struct osmo_ss7_combined_linkset *clset;
+
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ struct osmo_ss7_route *rt;
+ llist_for_each_entry(rt, &clset->routes, list) {
+ if (rt->dest.as != as)
+ continue;
+ if (rt->cfg.pc != dpc)
+ continue;
+ if (ss7_route_is_summary(rt))
+ continue; /* Only interested in fully qualified routes */
+ ss7_route_update_route_status(rt, status);
+ }
+ }
+}
+
/* find any routes pointing to this AS and remove them */
void ss7_route_table_del_routes_by_as(struct osmo_ss7_route_table *rtbl, struct osmo_ss7_as *as)
{
diff --git a/src/ss7_route_table.h b/src/ss7_route_table.h
index 9f27412..1155600 100644
--- a/src/ss7_route_table.h
+++ b/src/ss7_route_table.h
@@ -9,6 +9,7 @@
***********************************************************************/

struct osmo_ss7_instance;
+enum osmo_ss7_route_status;

struct osmo_ss7_route_label {
uint32_t opc;
@@ -54,5 +55,7 @@
struct osmo_ss7_combined_linkset *
ss7_route_table_find_combined_linkset(struct osmo_ss7_route_table *rtbl, uint32_t dpc, uint32_t mask, uint32_t prio);

+void ss7_route_table_update_route_status_by_as(struct osmo_ss7_route_table *rtbl, enum osmo_ss7_route_status status,
+ const struct osmo_ss7_as *as, uint32_t dpc);
void ss7_route_table_del_routes_by_as(struct osmo_ss7_route_table *rtbl, struct osmo_ss7_as *as);
void ss7_route_table_del_routes_by_linkset(struct osmo_ss7_route_table *rtbl, struct osmo_ss7_linkset *lset);
diff --git a/src/ss7_vty.c b/src/ss7_vty.c
index ab9784c..9e9ceaa 100644
--- a/src/ss7_vty.c
+++ b/src/ss7_vty.c
@@ -515,23 +515,48 @@
if ((filter_pc != OSMO_SS7_PC_INVALID) && ((filter_pc & clset->cfg.mask) != clset->cfg.pc))
continue; /* Skip combined linksets not matching destination */

+ bool clset_avail = ss7_combined_linkset_is_available(clset);
llist_for_each_entry(rt, &clset->routes, list) {
- bool rt_avail = ss7_route_is_available(rt);
+ bool dst_avail = ss7_route_dest_is_available(rt);
bool first_rt_in_clset = (rt == llist_first_entry(&clset->routes, struct osmo_ss7_route, list));
+ const char *nonadj_str, *rtavail_str;
/* Print route str only in first rt in combined linkset.
* This allows users to easily determine visually combined
* linksets: */
- const char *rt_str = first_rt_in_clset ? osmo_ss7_route_print(rt) : "";
+ const char *rt_str, *clsetavail_str;
+ if (first_rt_in_clset) {
+ rt_str = osmo_ss7_route_print(rt);
+ clsetavail_str = clset_avail ? "acces" : "INACC";
+ } else {
+ rt_str = "";
+ clsetavail_str = "";
+ }
+ switch (rt->status) {
+ case OSMO_SS7_ROUTE_STATUS_UNAVAILABLE:
+ nonadj_str = "PROHIB";
+ rtavail_str = "UNAVAIL";
+ break;
+ case OSMO_SS7_ROUTE_STATUS_AVAILABLE:
+ nonadj_str = "allowed";
+ rtavail_str = dst_avail ? "avail" : "UNAVAIL";
+ break;
+ case OSMO_SS7_ROUTE_STATUS_RESTRICTED:
+ nonadj_str = "RESTRIC";
+ rtavail_str = dst_avail ? "RESTRIC" : "UNAVAIL";
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
vty_out(vty, "%-16s %-5s %c %c %u %-19s %-7s %-7s %-7s %-3s%s",
rt_str,
- rt_avail ? "acces" : "INACC",
+ clsetavail_str,
' ',
'0' + rt->cfg.qos_class,
rt->cfg.priority,
rt->cfg.linkset_name,
- rt_avail ? "avail" : "UNAVAIL",
- "?",
- rt_avail ? "avail" : "UNAVAIL",
+ dst_avail ? "avail" : "UNAVAIL",
+ nonadj_str,
+ rtavail_str,
rt->cfg.dyn_allocated ? "dyn" : "",
VTY_NEWLINE);
}
diff --git a/src/xua_snm.c b/src/xua_snm.c
index f3c8bb8..42361c9 100644
--- a/src/xua_snm.c
+++ b/src/xua_snm.c
@@ -33,6 +33,7 @@

#include "ss7_as.h"
#include "ss7_asp.h"
+#include "ss7_route.h"
#include "ss7_internal.h"
#include "ss7_route_table.h"
#include "xua_internal.h"
@@ -149,6 +150,39 @@
}
}

+/* Figure 43/Q.704, Figure 44/Q.704 */
+/* RFC4666 1.4.2.5: "maintain a dynamic table of available SGP routes
+ * for the SS7 destinations, taking into account the SS7 destination
+ * availability/restricted/congestion status received from the SGP "*/
+static void xua_snm_srm_pc_available(struct osmo_ss7_as *as,
+ const uint32_t *aff_pc, unsigned int num_aff_pc,
+ bool available)
+{
+ struct osmo_ss7_instance *s7i = as->inst;
+ enum osmo_ss7_route_status new_status;
+
+ new_status = available ? OSMO_SS7_ROUTE_STATUS_AVAILABLE :
+ OSMO_SS7_ROUTE_STATUS_UNAVAILABLE;
+
+ for (unsigned int i = 0; i < num_aff_pc; i++) {
+ /* 32bit "Affected Point Code" consists of a 7-bit mask followed by 14/16/24-bit SS7 PC,
+ * see RFC 4666 3.4.1 */
+ uint32_t _aff_pc = ntohl(aff_pc[i]);
+ uint32_t pc = _aff_pc & 0xffffff;
+ uint8_t mask = _aff_pc >> 24;
+
+ if (!mask) {
+ ss7_route_table_update_route_status_by_as(s7i->rtable_system, new_status, as, pc);
+ } else {
+ /* Update only full DPC routes. */
+ uint32_t maskbits = (1 << mask) - 1;
+ uint32_t fullpc;
+ for (fullpc = (pc & ~maskbits); fullpc <= (pc | maskbits); fullpc++)
+ ss7_route_table_update_route_status_by_as(s7i->rtable_system, new_status, as, fullpc);
+ }
+ }
+}
+
/* advertise availability of point codes (with masks) */
void xua_snm_pc_available(struct osmo_ss7_as *as, const uint32_t *aff_pc,
unsigned int num_aff_pc, const char *info_str, bool available)
@@ -158,6 +192,9 @@
uint32_t rctx[OSMO_SS7_MAX_RCTX_COUNT];
unsigned int num_rctx;

+ xua_snm_srm_pc_available(as, aff_pc, num_aff_pc, available);
+
+
/* inform local users via a MTP-{PAUSE, RESUME} primitive */
if (s7i->sccp)
xua_snm_pc_available_to_sccp(s7i->sccp, aff_pc, num_aff_pc, available);
diff --git a/tests/vty/osmo_stp_route_prio.vty b/tests/vty/osmo_stp_route_prio.vty
index f2bfd1e..d9e3add 100644
--- a/tests/vty/osmo_stp_route_prio.vty
+++ b/tests/vty/osmo_stp_route_prio.vty
@@ -92,9 +92,9 @@

Destination C Q P Linkset Name Linkset Non-adj Route
---------------------- - - - ------------------- ------- ------- -------
-3.2.1/14 INACC 0 2 as3 UNAVAIL ? UNAVAIL
-3.2.1/14 INACC 0 5 as2 UNAVAIL ? UNAVAIL
-3.2.1/14 INACC 7 6 as1 UNAVAIL ? UNAVAIL
+3.2.1/14 INACC 0 2 as3 UNAVAIL allowed UNAVAIL
+3.2.1/14 INACC 0 5 as2 UNAVAIL allowed UNAVAIL
+3.2.1/14 INACC 7 6 as1 UNAVAIL allowed UNAVAIL

OsmoSTP(config-cs7-rt)# ! NOW ADD MORE GENERIC ROUTES (SMALLER BITMASK LENGTH)
OsmoSTP(config-cs7-rt)# update route 3.2.0 7.255.0 linkset as3 priority 3
@@ -122,14 +122,14 @@

Destination C Q P Linkset Name Linkset Non-adj Route
---------------------- - - - ------------------- ------- ------- -------
-3.2.1/14 INACC 0 2 as3 UNAVAIL ? UNAVAIL
-3.2.1/14 INACC 0 5 as2 UNAVAIL ? UNAVAIL
-3.2.1/14 INACC 7 6 as1 UNAVAIL ? UNAVAIL
-3.2.0/11 INACC 0 1 as1 UNAVAIL ? UNAVAIL
- INACC 0 1 as2 UNAVAIL ? UNAVAIL
- INACC 0 1 as3 UNAVAIL ? UNAVAIL
-3.2.0/11 INACC 0 2 as3 UNAVAIL ? UNAVAIL
-3.2.0/11 INACC 0 3 as3 UNAVAIL ? UNAVAIL
+3.2.1/14 INACC 0 2 as3 UNAVAIL allowed UNAVAIL
+3.2.1/14 INACC 0 5 as2 UNAVAIL allowed UNAVAIL
+3.2.1/14 INACC 7 6 as1 UNAVAIL allowed UNAVAIL
+3.2.0/11 INACC 0 1 as1 UNAVAIL allowed UNAVAIL
+ 0 1 as2 UNAVAIL allowed UNAVAIL
+ 0 1 as3 UNAVAIL allowed UNAVAIL
+3.2.0/11 INACC 0 2 as3 UNAVAIL allowed UNAVAIL
+3.2.0/11 INACC 0 3 as3 UNAVAIL allowed UNAVAIL

OsmoSTP(config-cs7-rt)# do show cs7 instance 0 route binding-table 3.2.1


To view, visit change 40556. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: merged
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: Icb4f78f8da9de5a611cb6311722bf3acf1a68fc5
Gerrit-Change-Number: 40556
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann@sysmocom.de>
Gerrit-Reviewer: laforge <laforge@osmocom.org>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-CC: osmith <osmith@sysmocom.de>