pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/39408?usp=email )
(
10 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
)Change subject: ASP loadsharing: Implement based on VTY configuration
......................................................................
ASP loadsharing: Implement based on VTY configuration
Calculate AS Extended SLS (7 bit) out of OPC (12 bits) and SLS (4 bits)
based on VTY configuration.
Assign a normal destination ASP for each AS-eSLS and try to use it
whenever possible; fallback to an alternative ASP when normal ASP is not
available.
Related: SYS#7112
Change-Id: I5f47e40b70ed566034cd1533b71e21fc03e94f6c
---
M src/osmo_ss7_as.c
M src/osmo_ss7_vty.c
M src/ss7_as.h
M src/xua_as_fsm.c
M tests/vty/osmo_stp_test.vty
5 files changed, 247 insertions(+), 11 deletions(-)
Approvals:
laforge: Looks good to me, but someone else must approve
pespin: Looks good to me, approved
osmith: Looks good to me, but someone else must approve
Jenkins Builder: Verified
diff --git a/src/osmo_ss7_as.c b/src/osmo_ss7_as.c
index ab40080..35a2650 100644
--- a/src/osmo_ss7_as.c
+++ b/src/osmo_ss7_as.c
@@ -33,6 +33,7 @@
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
+#include <osmocom/sigtran/mtp_sap.h>
#include "ss7_as.h"
#include "ss7_asp.h"
@@ -198,6 +199,14 @@
LOGPAS(as, DLSS7, LOGL_INFO, "Removing ASP %s from AS\n", asp->cfg.name);
+ /* Remove route from AS-eSLS table: */
+ for (unsigned int i = 0; i < ARRAY_SIZE(as->aesls_table); i++) {
+ if (as->aesls_table[i].normal_asp == asp)
+ as->aesls_table[i].normal_asp = NULL;
+ if (as->aesls_table[i].alt_asp == asp)
+ as->aesls_table[i].alt_asp = NULL;
+ }
+
for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) {
if (as->cfg.asps[i] == asp) {
as->cfg.asps[i] = NULL;
@@ -314,6 +323,106 @@
return asp;
}
+static as_ext_sls_t osmo_ss7_instance_calc_itu_as_ext_sls(const struct osmo_ss7_as *as, uint32_t opc, uint8_t sls)
+{
+ uint16_t opc12;
+ uint8_t opc3;
+ as_ext_sls_t as_ext_sls;
+
+ if (as->cfg.loadshare.opc_sls) {
+ /* Take 12 bits from OPC according to config: */
+ opc12 = (uint16_t)((opc >> as->cfg.loadshare.opc_shift) & 0x3fff);
+
+ /* Derivate 3-bit value from 12-bit value: */
+ opc3 = ((opc12 >> 9) & 0x07) ^
+ ((opc12 >> 6) & 0x07) ^
+ ((opc12 >> 3) & 0x07) ^
+ (opc12 & 0x07);
+ opc3 &= 0x07;
+
+ /* Generate 7 bit AS-extended-SLS: 3-bit OPC + 4 bit SLS: */
+ as_ext_sls = (opc3 << 4) | ((sls) & 0x0f);
+ OSMO_ASSERT(as_ext_sls < NUM_AS_EXT_SLS);
+ } else {
+ as_ext_sls = sls;
+ }
+
+ /* Pick extended-SLS bits according to config: */
+ as_ext_sls = as_ext_sls >> as->cfg.loadshare.sls_shift;
+ return as_ext_sls;
+}
+
+/* ITU Q.704 4.2.1: "current signalling link". Pick available already selected ASP */
+static struct osmo_ss7_asp *current_asp(const struct osmo_ss7_as *as, const struct osmo_ss7_as_esls_entry *aeslse)
+{
+ if (aeslse->normal_asp && osmo_ss7_asp_active(aeslse->normal_asp))
+ return aeslse->normal_asp;
+ if (aeslse->alt_asp && osmo_ss7_asp_active(aeslse->alt_asp))
+ return aeslse->alt_asp;
+ return NULL;
+}
+
+static struct osmo_ss7_asp *ss7_as_select_asp_loadshare(struct osmo_ss7_as *as, const struct osmo_mtp_transfer_param *mtp)
+{
+ as_ext_sls_t as_ext_sls;
+ struct osmo_ss7_asp *asp;
+
+ as_ext_sls = osmo_ss7_instance_calc_itu_as_ext_sls(as, mtp->opc, mtp->sls);
+ struct osmo_ss7_as_esls_entry *aeslse = &as->aesls_table[as_ext_sls];
+
+ /* First check if we have a cached route for this ESLS */
+ asp = current_asp(as, aeslse);
+ if (asp) {
+ if (asp == aeslse->normal_asp) {
+ /* We can transmit over normal ASP.
+ * Clean up alternative ASP since it's not needed anymore */
+ if (aeslse->alt_asp) {
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "Tx Loadshare: OPC=%u=%s,SLS=%u -> eSLS=%u: "
+ "Normal ASP '%s' became available, drop use of Alternative ASP '%s'\n",
+ mtp->opc, osmo_ss7_pointcode_print(as->inst, mtp->opc),
+ mtp->sls, as_ext_sls, asp->cfg.name, aeslse->alt_asp->cfg.name);
+ aeslse->alt_asp = NULL;
+ }
+ LOGPAS(as, DLSS7, LOGL_DEBUG, "Tx Loadshare: OPC=%u=%s,SLS=%u -> eSLS=%u: use Normal ASP '%s'\n",
+ mtp->opc, osmo_ss7_pointcode_print(as->inst, mtp->opc),
+ mtp->sls, as_ext_sls, asp->cfg.name);
+ return asp;
+ }
+ /* We can transmit over alternative ASP: */
+ LOGPAS(as, DLSS7, LOGL_INFO, "Tx Loadshare: OPC=%u=%s,SLS=%u -> eSLS=%u: use Alternative ASP '%s'\n",
+ mtp->opc, osmo_ss7_pointcode_print(as->inst, mtp->opc),
+ mtp->sls, as_ext_sls, asp->cfg.name);
+ return asp;
+ }
+
+ /* No current ASP available, try to find a new current ASP: */
+
+ /* No normal route selected yet: */
+ if (!aeslse->normal_asp) {
+ asp = ss7_as_select_asp_roundrobin(as);
+ /* Either a normal route was selected or none found: */
+ aeslse->normal_asp = asp;
+ if (asp)
+ LOGPAS(as, DLSS7, LOGL_DEBUG, "Tx Loadshare: OPC=%u=%s,SLS=%u -> eSLS=%u: "
+ "picked Normal ASP '%s' round-robin style\n",
+ mtp->opc, osmo_ss7_pointcode_print(as->inst, mtp->opc),
+ mtp->sls, as_ext_sls, asp->cfg.name);
+ return asp;
+ }
+
+ /* Normal ASP unavailable and no alternative ASP (or unavailable too).
+ * start ITU Q.704 section 7 "forced rerouting" procedure: */
+ asp = ss7_as_select_asp_roundrobin(as);
+ if (asp) {
+ aeslse->alt_asp = asp;
+ LOGPAS(as, DLSS7, LOGL_NOTICE, "Tx Loadshare: OPC=%u=%s,SLS=%u -> eSLS=%u: "
+ "Normal ASP '%s' unavailable, picked Alternative ASP '%s' round-robin style\n",
+ mtp->opc, osmo_ss7_pointcode_print(as->inst, mtp->opc),
+ mtp->sls, as_ext_sls, aeslse->normal_asp->cfg.name, asp->cfg.name);
+ }
+ return asp;
+}
+
/* returns NULL if multiple ASPs would need to be selected. */
static struct osmo_ss7_asp *ss7_as_select_asp_broadcast(struct osmo_ss7_as *as)
{
@@ -338,7 +447,7 @@
* This function returns NULL too if multiple ASPs would be selected, ie. AS is
* configured in broadcast mode and more than one ASP is configured.
*/
-struct osmo_ss7_asp *osmo_ss7_as_select_asp(struct osmo_ss7_as *as)
+struct osmo_ss7_asp *ss7_as_select_asp(struct osmo_ss7_as *as, const struct osmo_mtp_transfer_param *mtp)
{
struct osmo_ss7_asp *asp = NULL;
@@ -347,9 +456,46 @@
asp = ss7_as_select_asp_override(as);
break;
case OSMO_SS7_AS_TMOD_LOADSHARE:
- /* TODO: actually use the SLS value to ensure same SLS goes
- * through same ASP. Not strictly required by M3UA RFC, but
- * would fit the overall principle. */
+ asp = ss7_as_select_asp_loadshare(as, mtp);
+ break;
+ case OSMO_SS7_AS_TMOD_ROUNDROBIN:
+ asp = ss7_as_select_asp_roundrobin(as);
+ break;
+ case OSMO_SS7_AS_TMOD_BCAST:
+ return ss7_as_select_asp_broadcast(as);
+ case _NUM_OSMO_SS7_ASP_TMOD:
+ OSMO_ASSERT(false);
+ }
+
+ if (!asp) {
+ LOGPFSM(as->fi, "No selectable ASP in AS\n");
+ return NULL;
+ }
+ return asp;
+}
+/*! Select an AS to transmit a message, according to AS configuration and ASP availability.
+ * \param[in] as Application Server.
+ * \returns asp to send the message to, NULL if no possible asp found
+ *
+ * This function returns NULL too if multiple ASPs would be selected, ie. AS is
+ * configured in broadcast mode and more than one ASP is configured.
+ */
+struct osmo_ss7_asp *osmo_ss7_as_select_asp(struct osmo_ss7_as *as)
+{
+ struct osmo_ss7_asp *asp = NULL;
+ struct osmo_mtp_transfer_param mtp;
+
+ switch (as->cfg.mode) {
+ case OSMO_SS7_AS_TMOD_OVERRIDE:
+ asp = ss7_as_select_asp_override(as);
+ break;
+ case OSMO_SS7_AS_TMOD_LOADSHARE:
+ /* We don't have OPC and SLS information in this API (which is
+ actually only used to route IPA msgs nowadays by osmo-bsc, so we
+ don't care. Use hardcoded value to provide some fallback for this scenario: */
+ mtp = (struct osmo_mtp_transfer_param){0};
+ asp = ss7_as_select_asp_loadshare(as, &mtp);
+ break;
case OSMO_SS7_AS_TMOD_ROUNDROBIN:
asp = ss7_as_select_asp_roundrobin(as);
break;
diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c
index 81ce0ce..83890b8 100644
--- a/src/osmo_ss7_vty.c
+++ b/src/osmo_ss7_vty.c
@@ -2033,10 +2033,9 @@
DEFUN_USRATTR(as_traf_mode, as_traf_mode_cmd,
OSMO_SCCP_LIB_ATTR_RSTRT_ASP,
- "traffic-mode (broadcast | loadshare | roundrobin | override)",
+ "traffic-mode (broadcast | roundrobin | override)",
"Specifies traffic mode of operation of the ASP within the AS\n"
"Broadcast to all ASP within AS\n"
- "Share Load among all ASP within AS\n"
"Round-Robin between all ASP within AS\n"
"Override\n")
{
@@ -2047,6 +2046,36 @@
return CMD_SUCCESS;
}
+DEFUN_USRATTR(as_traf_mode_loadshare, as_traf_mode_loadshare_cmd,
+ OSMO_SCCP_LIB_ATTR_RSTRT_ASP,
+ "traffic-mode loadshare [bindings] [sls] [opc-sls] [opc-shift] [<0-2>]",
+ "Specifies traffic mode of operation of the ASP within the AS\n"
+ "Share Load among all ASP within AS\n"
+ "Configure Loadshare parameters\n"
+ "Configure Loadshare SLS generation parameters\n"
+ "Generate extended SLS with OPC information\n"
+ "Shift OPC bits used during routing decision\n"
+ "How many bits from ITU OPC field (starting from least-significant-bit) to skip (default=0). 6 bits are always used\n"
+ )
+{
+ struct osmo_ss7_as *as = vty->index;
+
+ as->cfg.mode = OSMO_SS7_AS_TMOD_LOADSHARE;
+ as->cfg.mode_set_by_vty = true;
+ if (argc < 3) {
+ as->cfg.loadshare.opc_sls = false;
+ as->cfg.loadshare.opc_shift = 0;
+ return CMD_SUCCESS;
+ }
+ as->cfg.loadshare.opc_sls = true;
+ if (argc < 5) {
+ as->cfg.loadshare.opc_shift = 0;
+ return CMD_SUCCESS;
+ }
+ as->cfg.loadshare.opc_shift = atoi(argv[4]);
+ return CMD_SUCCESS;
+}
+
DEFUN_USRATTR(as_no_traf_mode, as_no_traf_mode_cmd,
OSMO_SCCP_LIB_ATTR_RSTRT_ASP,
"no traffic-mode",
@@ -2056,6 +2085,22 @@
as->cfg.mode = 0;
as->cfg.mode_set_by_vty = false;
+
+ as->cfg.loadshare.sls_shift = 0;
+ as->cfg.loadshare.opc_sls = false;
+ as->cfg.loadshare.opc_shift = 0;
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(as_sls_shift, as_sls_shift_cmd,
+ "sls-shift <0-3>",
+ "Shift SLS bits used during routing decision\n"
+ "How many bits from SLS field (starting from least-significant-bit) to skip\n",
+ CMD_ATTR_IMMEDIATE)
+{
+ struct osmo_ss7_as *as = vty->index;
+ as->cfg.loadshare.sls_shift = atoi(argv[0]);
+
return CMD_SUCCESS;
}
@@ -2274,9 +2319,21 @@
continue;
vty_out(vty, " asp %s%s", asp->cfg.name, VTY_NEWLINE);
}
- if (as->cfg.mode_set_by_vty)
- vty_out(vty, " traffic-mode %s%s",
- osmo_ss7_as_traffic_mode_name(as->cfg.mode), VTY_NEWLINE);
+ if (as->cfg.mode_set_by_vty) {
+ vty_out(vty, " traffic-mode %s%s", osmo_ss7_as_traffic_mode_name(as->cfg.mode), VTY_NEWLINE);
+ if (as->cfg.mode == OSMO_SS7_AS_TMOD_LOADSHARE) {
+ if (as->cfg.loadshare.opc_sls) {
+ vty_out(vty, " bindings sls opc-sls");
+ if (as->cfg.loadshare.opc_shift != 0)
+ vty_out(vty, " opc-shift %u", as->cfg.loadshare.opc_shift);
+ }
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+
+ if (as->cfg.loadshare.sls_shift != 0)
+ vty_out(vty, " sls-shift %u%s", as->cfg.loadshare.sls_shift, VTY_NEWLINE);
+ }
+
if (as->cfg.recovery_timeout_msec != 2000) {
vty_out(vty, " recovery-timeout %u%s",
as->cfg.recovery_timeout_msec, VTY_NEWLINE);
@@ -3218,7 +3275,9 @@
install_lib_element(L_CS7_AS_NODE, &as_asp_cmd);
install_lib_element(L_CS7_AS_NODE, &as_no_asp_cmd);
install_lib_element(L_CS7_AS_NODE, &as_traf_mode_cmd);
+ install_lib_element(L_CS7_AS_NODE, &as_traf_mode_loadshare_cmd);
install_lib_element(L_CS7_AS_NODE, &as_no_traf_mode_cmd);
+ install_lib_element(L_CS7_AS_NODE, &as_sls_shift_cmd);
install_lib_element(L_CS7_AS_NODE, &as_recov_tout_cmd);
install_lib_element(L_CS7_AS_NODE, &as_qos_class_cmd);
install_lib_element(L_CS7_AS_NODE, &as_rout_key_cmd);
diff --git a/src/ss7_as.h b/src/ss7_as.h
index 6a2b649..c4dfdf6 100644
--- a/src/ss7_as.h
+++ b/src/ss7_as.h
@@ -17,6 +17,7 @@
struct osmo_ss7_instance;
struct osmo_ss7_asp;
+struct osmo_mtp_transfer_param;
enum osmo_ss7_as_patch_sccp_mode {
OSMO_SS7_PATCH_NONE, /* no patching of SCCP */
@@ -60,6 +61,15 @@
SS7_AS_CTR_TX_MSU_SLS_15,
};
+#define NUM_AS_EXT_SLS 128
+typedef uint8_t as_ext_sls_t; /* range: 0-127, 7 bit */
+struct osmo_ss7_as_esls_entry {
+ /* ITU Q.704 4.2.1: "normal signallink link" */
+ struct osmo_ss7_asp *normal_asp;
+ /* ITU Q.704 4.2.1: "alternative signallink link" */
+ struct osmo_ss7_asp *alt_asp;
+};
+
struct osmo_ss7_as {
/*! entry in 'ref osmo_ss7_instance.as_list */
struct llist_head list;
@@ -77,6 +87,9 @@
/*! Rate Counter Group */
struct rate_ctr_group *ctrg;
+ /* ASP loadshare: */
+ struct osmo_ss7_as_esls_entry aesls_table[NUM_AS_EXT_SLS];
+
struct {
char *name;
char *description;
@@ -96,9 +109,24 @@
struct osmo_ss7_asp *asps[16];
uint8_t last_asp_idx_sent; /* used for load-sharing traffic mode (round robin implementation) */
+
+ struct {
+ /* How many bits from ITU SLS field (starting from least-significant-bit)
+ * to skip for routing decisions.
+ * range 0-3, defaults to 0, which means take all 4 bits. */
+ uint8_t sls_shift;
+ /* Whether to generate a extended-SLS with OPC information, see opc_shift below. */
+ bool opc_sls;
+ /* How many bits from ITU OPC field (starting from least-significant-bit)
+ * to skip for routing decisions (always takes 12 bits).
+ * range 0-2, defaults to 0, which means take least significant 12 bits. */
+ uint8_t opc_shift;
+ } loadshare;
} cfg;
};
+struct osmo_ss7_asp *ss7_as_select_asp(struct osmo_ss7_as *as, const struct osmo_mtp_transfer_param *mtp);
+
unsigned int osmo_ss7_as_count_asp(const struct osmo_ss7_as *as);
int ss7_as_add_asp(struct osmo_ss7_as *as, struct osmo_ss7_asp *asp);
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
index d0bc019..2034659 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -172,7 +172,7 @@
* strictly required by M3UA RFC, but would fit the overall
* principle. */
case OSMO_SS7_AS_TMOD_ROUNDROBIN:
- asp = osmo_ss7_as_select_asp(as);
+ asp = ss7_as_select_asp(as, &xua->mtp);
break;
case OSMO_SS7_AS_TMOD_BCAST:
return xua_as_transmit_msg_broadcast(as, xua);
diff --git a/tests/vty/osmo_stp_test.vty b/tests/vty/osmo_stp_test.vty
index 200fff8..58a1ee0 100644
--- a/tests/vty/osmo_stp_test.vty
+++ b/tests/vty/osmo_stp_test.vty
@@ -351,8 +351,10 @@
description .TEXT
asp NAME
no asp NAME
- traffic-mode (broadcast | loadshare | roundrobin | override)
+ traffic-mode (broadcast | roundrobin | override)
+ traffic-mode loadshare [bindings] [sls] [opc-sls] [opc-shift] [<0-2>]
no traffic-mode
+ sls-shift <0-3>
recovery-timeout <1-2000>
qos-class <0-7>
routing-key RCONTEXT DPC
@@ -368,6 +370,7 @@
asp Specify that a given ASP is part of this AS
no Negate a command or set its defaults
traffic-mode Specifies traffic mode of operation of the ASP within the AS
+ sls-shift Shift SLS bits used during routing decision
recovery-timeout Specifies the recovery timeout value in milliseconds
qos-class Specity QoS Class of AS
routing-key Define a routing key
--
To view, visit https://gerrit.osmocom.org/c/libosmo-sigtran/+/39408?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I5f47e40b70ed566034cd1533b71e21fc03e94f6c
Gerrit-Change-Number: 39408
Gerrit-PatchSet: 11
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/39689?usp=email )
(
1 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
)Change subject: sccp: Apply SLS on locally-originated transmitted Connection-Oriented messages
......................................................................
sccp: Apply SLS on locally-originated transmitted Connection-Oriented messages
Change-Id: I1956acf631867b12b647d14c10057ded20d7fa2f
---
M src/sccp_scoc.c
1 file changed, 34 insertions(+), 3 deletions(-)
Approvals:
pespin: Looks good to me, approved
osmith: Looks good to me, but someone else must approve
Jenkins Builder: Verified
laforge: Looks good to me, but someone else must approve
diff --git a/src/sccp_scoc.c b/src/sccp_scoc.c
index c11dddf..8222bde 100644
--- a/src/sccp_scoc.c
+++ b/src/sccp_scoc.c
@@ -102,6 +102,11 @@
uint32_t sccp_class;
uint32_t release_cause; /* WAIT_CONN_CONF */
+ /* SLS to be used to transmit all Connection-oriented messages
+ * (ITU-T Q.714 1.1.2.3 Protocol class 2).
+ * SLS is 4 bits, as described in ITU Q.704 Figure 3 */
+ uint8_t tx_co_mtp_sls;
+
struct msgb *opt_data_cache;
/* incoming (true) or outgoing (false) */
@@ -450,6 +455,26 @@
#define INIT_TIMER(x, fn, priv) do { (x)->cb = fn; (x)->data = priv; } while (0)
+/* Generate an SLS to be used for Connection-oriented messages on this SCCP connection.
+ * SLS is 4 bits, as described in ITU Q.704 Figure 3.
+ * ITU-T Q.714 1.1.2.3 Protocol class 2:
+ * "Messages belonging to a given signalling connection shall contain the same
+ * value of the SLS field to ensure sequencing as described in 1.1.2.2"
+ */
+static uint8_t sccp_conn_gen_tx_co_mtp_sls(const struct sccp_connection *conn)
+{
+ /* Implementation: Derive the SLS from conn->conn_id. */
+ const uint32_t id = conn->conn_id;
+ uint8_t sls;
+
+ /* First shrink it to 1 byte: */
+ sls = ((id >> (3*8)) ^ (id >> (2*8)) ^ (id >> (1*8)) ^ (id)) & 0xff;
+ /* Now shrink it 8 -> 4 bits: */
+ sls = ((sls >> 4) ^ sls) & 0x0f;
+
+ return sls;
+}
+
/* allocate + init a SCCP Connection with given ID */
static struct sccp_connection *conn_create_id(struct osmo_sccp_user *user, uint32_t conn_id)
{
@@ -472,6 +497,8 @@
INIT_TIMER(&conn->t_int, int_tmr_cb, conn);
INIT_TIMER(&conn->t_rep_rel, rep_rel_tmr_cb, conn);
+ conn->tx_co_mtp_sls = sccp_conn_gen_tx_co_mtp_sls(conn);
+
/* this might change at runtime, as it is not a constant :/ */
sccp_scoc_fsm.log_subsys = DLSCCP;
@@ -715,6 +742,13 @@
if (!xua)
return NULL;
+ /* amend this with point code information; Many CO msgs
+ * includes neither called nor calling party address! */
+ xua->mtp.dpc = conn->remote_pc;
+
+ /* Apply SLS calculated for the connection (ITU-T Q.714 1.1.2.3). */
+ xua->mtp.sls = conn->tx_co_mtp_sls;
+
switch (msg_type) {
case SUA_CO_CORE: /* Connect Request == SCCP CR */
xua->hdr = XUA_HDR(SUA_MSGC_CO, SUA_CO_CORE);
@@ -835,9 +869,6 @@
if (!xua)
return -ENOMEM;
- /* amend this with point code information; Many CO msgs
- * includes neither called nor calling party address! */
- xua->mtp.dpc = conn->remote_pc;
sccp_scrc_rx_scoc_conn_msg(conn->inst, xua);
xua_msg_free(xua);
return 0;
--
To view, visit https://gerrit.osmocom.org/c/libosmo-sigtran/+/39689?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I1956acf631867b12b647d14c10057ded20d7fa2f
Gerrit-Change-Number: 39689
Gerrit-PatchSet: 2
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/38731?usp=email )
(
13 is the latest approved patch-set.
No files were changed between the latest approved patch-set and the submitted one.
)Change subject: AS loadsharing: Introduce combined_linkset
......................................................................
AS loadsharing: Introduce combined_linkset
A "Combined linkset" is a set of routes for different AS or linksets
whose destination (DPC & mask) and priority are the same.
An SP is expected to load share traffic among linksets/AS of a given
combined linkset, based on DPC/OPC,SLS. This means certain state or
heuristics need to be stored in the combined linkset in order to keep
forwarding messages of the same transaction (SLS) over the same specific
AS/linkset.
This commit introduces the osmo_ss7_combined_linkset object, which
is roughly a set of osmo_ss7_route sharing DPC+MASK+PRIO key, and
it is placed in between the osmo_ss7_route_table and osmo_ss7_route
entries.
User still operates/manages indiviual routes, but those are grouped
internally inside its corresponding combined linkset.
No routing change is implemented in this commit. Code is only tweaked
minimally so far to retrieve the first route in the combined link.
This will allow future work, i.e to:
- Check if data can be sent over the combined link (at least one
AS/linkset is ACTIVE), and otherwise try using a combined link with a
less specific match (shorter prefix mask or/and lower priority).
- Select one of the AS/linksets inserted the combined link, based on
OPC/DPC/SLS of the message.
Related: SYS#7112
Change-Id: I500ec74ba975e3c93071771027e4e5fe6000e6f3
---
M src/Makefile.am
A src/osmo_ss7_combined_linkset.c
M src/osmo_ss7_route.c
M src/osmo_ss7_route_table.c
M src/osmo_ss7_vty.c
A src/ss7_combined_linkset.h
M src/ss7_route.h
M src/ss7_route_table.h
8 files changed, 357 insertions(+), 95 deletions(-)
Approvals:
Jenkins Builder: Verified
osmith: Looks good to me, but someone else must approve
daniel: Looks good to me, but someone else must approve
pespin: Looks good to me, approved
diff --git a/src/Makefile.am b/src/Makefile.am
index ced19f1..f108f06 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,6 +7,7 @@
ss7_as.h \
ss7_asp.h \
ss7_asp_peer.h \
+ ss7_combined_linkset.h \
ss7_instance.h \
ss7_internal.h \
ss7_link.h \
@@ -44,6 +45,7 @@
osmo_ss7_as.c \
osmo_ss7_asp.c \
osmo_ss7_asp_peer.c \
+ osmo_ss7_combined_linkset.c \
osmo_ss7_hmrt.c \
osmo_ss7_instance.c \
osmo_ss7_link.c \
diff --git a/src/osmo_ss7_combined_linkset.c b/src/osmo_ss7_combined_linkset.c
new file mode 100644
index 0000000..2e068b4
--- /dev/null
+++ b/src/osmo_ss7_combined_linkset.c
@@ -0,0 +1,134 @@
+/* (C) 2025 by sysmocom s.f.m.c. GmbH <info(a)sysmocom.de>
+ * All Rights Reserved
+ * Author: Pau Espin Pedrol <pespin(a)sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/sigtran/osmo_ss7.h>
+
+#include "ss7_combined_linkset.h"
+#include "ss7_route.h"
+#include "ss7_route_table.h"
+#include "ss7_internal.h"
+
+/******************************************************************************
+ * SS7 Combined Linkset
+ *
+ * ITU Q.704 4.2.1: "Signalling traffic to be sent to a particular signalling
+ * point in the network is normally routed to one or, in the case of load
+ * sharing between link sets in the international network, two link sets. A load
+ * sharing collection of two or more link sets is called a combined link set"
+ * [...]
+ * "The possible link set (combined link sets) appear in a certain priority
+ * order. The link set (combined link set) having the highest priority is used
+ * whenever it is available."
+ * [...]
+ * "It is defined that the normal link set (combined link set) for traffic to the
+ * concerned destination. The link set (combined link set) which is in use at a
+ * given time is called the current link set (combined link set). The current
+ * link set (combined link set) consists either of the normal link set (combined
+ * link set) or of an alternative link set (combined link set)."
+ *****************************************************************************/
+
+/*! \brief Insert combined_link into its routing table
+ * \param[in] clset Combined link to be inserted into its routing table
+ * \returns 0 on success, negative on error
+ *
+ * A combined link is only really used once it has been inserted into its routing table.
+ *
+ * insert the route in the ordered list of routes. The list is sorted by
+ * mask length, so that the more specific (longer mask) routes are
+ * first, while the less specific routes with shorter masks are last.
+ * Within the same mask length, the routes are ordered by priority.
+ * Hence, the first matching route in a linear iteration is the most
+ * specific match.
+ */
+static void ss7_combined_linkset_insert(struct osmo_ss7_combined_linkset *clset)
+{
+ struct osmo_ss7_route_table *rtbl = clset->rtable;
+ struct osmo_ss7_combined_linkset *it;
+
+ llist_for_each_entry(it, &rtbl->combined_linksets, list) {
+ if (it->cfg.mask == clset->cfg.mask &&
+ it->cfg.priority > clset->cfg.priority) {
+ /* insert before the current entry */
+ llist_add(&clset->list, it->list.prev);
+ return;
+ }
+ if (it->cfg.mask < clset->cfg.mask) {
+ /* insert before the current entry */
+ llist_add(&clset->list, it->list.prev);
+ return;
+ }
+ }
+ /* not added, i.e. no smaller mask length and priority found: we are the
+ * smallest mask and priority and thus should go last */
+ llist_add_tail(&clset->list, &rtbl->combined_linksets);
+}
+
+struct osmo_ss7_combined_linkset *
+ss7_combined_linkset_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask, uint32_t prio)
+{
+ struct osmo_ss7_combined_linkset *clset;
+
+ clset = talloc_zero(rtbl, struct osmo_ss7_combined_linkset);
+ if (!clset)
+ return NULL;
+
+ clset->rtable = rtbl;
+ /* truncate mask to maximum. Let's avoid callers specifying arbitrary large
+ * masks to ensure we don't fail duplicate detection with longer mask lengths */
+ clset->cfg.mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
+ clset->cfg.pc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, pc);
+ clset->cfg.priority = prio;
+ INIT_LLIST_HEAD(&clset->routes);
+
+ ss7_combined_linkset_insert(clset);
+ return clset;
+}
+
+void
+ss7_combined_linkset_free(struct osmo_ss7_combined_linkset *clset)
+{
+ if (!clset)
+ return;
+ llist_del(&clset->list);
+ talloc_free(clset);
+}
+
+void ss7_combined_linkset_add_route(struct osmo_ss7_combined_linkset *clset, struct osmo_ss7_route *rt)
+{
+ llist_add_tail(&rt->list, &clset->routes);
+ clset->num_routes++;
+ rt->clset = clset;
+}
+
+/* clset may end up freed as a result: */
+void ss7_combined_linkset_del_route(struct osmo_ss7_route *rt)
+{
+ struct osmo_ss7_combined_linkset *clset = rt->clset;
+ llist_del(&rt->list);
+ rt->clset = NULL;
+ clset->num_routes--;
+ if (clset->num_routes == 0)
+ ss7_combined_linkset_free(clset);
+}
diff --git a/src/osmo_ss7_route.c b/src/osmo_ss7_route.c
index 2787895..f1fe150 100644
--- a/src/osmo_ss7_route.c
+++ b/src/osmo_ss7_route.c
@@ -26,6 +26,7 @@
#include <osmocom/sigtran/mtp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
+#include "ss7_combined_linkset.h"
#include "ss7_linkset.h"
#include "ss7_as.h"
#include "ss7_asp.h"
@@ -132,34 +133,6 @@
return 0;
}
-/* insert the route in the ordered list of routes. The list is sorted by
- * mask length, so that the more specific (longer mask) routes are
- * first, while the less specific routes with shorter masks are last.
- * Within the same mask length, the routes are ordered by priority.
- * Hence, the first matching route in a linear iteration is the most
- * specific match. */
-static void route_insert_sorted(struct osmo_ss7_route_table *rtbl,
- struct osmo_ss7_route *cmp)
-{
- struct osmo_ss7_route *rt;
-
- llist_for_each_entry(rt, &rtbl->routes, list) {
- if (rt->cfg.mask == cmp->cfg.mask &&
- rt->cfg.priority > cmp->cfg.priority) {
- /* insert before the current entry */
- llist_add(&cmp->list, rt->list.prev);
- return;
- }
- if (rt->cfg.mask < cmp->cfg.mask) {
- /* insert before the current entry */
- llist_add(&cmp->list, rt->list.prev);
- return;
- }
- }
- /* not added, i.e. no smaller mask length and priority found: we are the
- * smallest mask and priority and thus should go last */
- llist_add_tail(&cmp->list, &rtbl->routes);
-}
/*! \brief Insert route into its routing table
* \param[in] rt Route to be inserted into its routing table
@@ -170,7 +143,7 @@
int
ss7_route_insert(struct osmo_ss7_route *rt)
{
- struct osmo_ss7_route *prev_rt;
+ struct osmo_ss7_combined_linkset *clset;
struct osmo_ss7_route_table *rtbl = rt->rtable;
if (ss7_route_inserted(rt)) {
@@ -183,17 +156,24 @@
return -EINVAL;
}
- /* check for duplicates */
- prev_rt = ss7_route_table_find_route_by_dpc_mask(rtbl, rt->cfg.pc, rt->cfg.mask);
- if (prev_rt && !strcmp(prev_rt->cfg.linkset_name, rt->cfg.linkset_name)) {
- LOGSS7(rtbl->inst, LOGL_ERROR,
- "Refusing to create route with existing linkset name: pc=%u=%s mask=0x%x via linkset/AS '%s'\n",
- rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc),
- rt->cfg.mask, rt->cfg.linkset_name);
- return -EADDRINUSE;
+ clset = ss7_route_table_find_combined_linkset(rtbl, rt->cfg.pc, rt->cfg.mask, rt->cfg.priority);
+ if (clset) { /* check for duplicates */
+ struct osmo_ss7_route *prev_rt;
+ llist_for_each_entry(prev_rt, &clset->routes, list) {
+ if (!strcmp(prev_rt->cfg.linkset_name, rt->cfg.linkset_name)) {
+ LOGSS7(rtbl->inst, LOGL_ERROR,
+ "Refusing to create route with existing linkset name: pc=%u=%s mask=0x%x via linkset/AS '%s'\n",
+ rt->cfg.pc, osmo_ss7_pointcode_print(rtbl->inst, rt->cfg.pc),
+ rt->cfg.mask, rt->cfg.linkset_name);
+ return -EADDRINUSE;
+ }
+ }
+ } else {
+ clset = ss7_combined_linkset_alloc(rtbl, rt->cfg.pc, rt->cfg.mask, rt->cfg.priority);
+ OSMO_ASSERT(clset);
}
- route_insert_sorted(rtbl, rt);
+ ss7_combined_linkset_add_route(clset, rt);
return 0;
}
@@ -250,7 +230,7 @@
"Destroying route: pc=%u=%s mask=0x%x via linkset/ASP '%s'\n",
rt->cfg.pc, osmo_ss7_pointcode_print(inst, rt->cfg.pc),
rt->cfg.mask, rt->cfg.linkset_name);
- llist_del(&rt->list);
+ ss7_combined_linkset_del_route(rt);
}
talloc_free(rt);
}
diff --git a/src/osmo_ss7_route_table.c b/src/osmo_ss7_route_table.c
index 03596d0..b230521 100644
--- a/src/osmo_ss7_route_table.c
+++ b/src/osmo_ss7_route_table.c
@@ -24,6 +24,7 @@
#include <osmocom/sigtran/mtp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
+#include "ss7_combined_linkset.h"
#include "ss7_route.h"
#include "ss7_route_table.h"
#include "ss7_internal.h"
@@ -44,7 +45,7 @@
rtbl->inst = inst;
rtbl->cfg.name = talloc_strdup(rtbl, name);
- INIT_LLIST_HEAD(&rtbl->routes);
+ INIT_LLIST_HEAD(&rtbl->combined_linksets);
llist_add_tail(&rtbl->list, &inst->rtable_list);
return rtbl;
}
@@ -77,8 +78,8 @@
ss7_route_table_destroy(struct osmo_ss7_route_table *rtbl)
{
llist_del(&rtbl->list);
- /* routes are allocated as children of route table, will be
- * automatically freed() */
+ /* combined links & routes are allocated as children of route table,
+ * will be automatically freed() */
talloc_free(rtbl);
}
@@ -86,20 +87,18 @@
struct osmo_ss7_route *
ss7_route_table_find_route_by_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc)
{
+ struct osmo_ss7_combined_linkset *clset;
struct osmo_ss7_route *rt;
OSMO_ASSERT(ss7_initialized);
dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
- /* we assume the routes are sorted by mask length, i.e. more
- * specific routes first, and less specific routes with shorter
- * mask later */
- llist_for_each_entry(rt, &rtbl->routes, list) {
- if ((dpc & rt->cfg.mask) == rt->cfg.pc)
- return rt;
- }
- return NULL;
+ clset = ss7_route_table_find_combined_linkset_by_dpc(rtbl, dpc);
+ if (!clset)
+ return NULL;
+ rt = llist_first_entry_or_null(&clset->routes, struct osmo_ss7_route, list);
+ return rt;
}
/*! \brief Find a SS7 route for given destination point code + mask in given table */
@@ -107,40 +106,124 @@
ss7_route_table_find_route_by_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc,
uint32_t mask)
{
+ struct osmo_ss7_combined_linkset *clset;
struct osmo_ss7_route *rt;
OSMO_ASSERT(ss7_initialized);
- mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
- dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
- /* we assume the routes are sorted by mask length, i.e. more
- * specific routes first, and less specific routes with shorter
+ dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
+ mask = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, mask);
+
+ clset = ss7_route_table_find_combined_linkset_by_dpc_mask(rtbl, dpc, mask);
+ if (!clset)
+ return NULL;
+ rt = llist_first_entry_or_null(&clset->routes, struct osmo_ss7_route, list);
+ return rt;
+}
+
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_combined_linkset_by_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc)
+{
+ struct osmo_ss7_combined_linkset *clset;
+
+ OSMO_ASSERT(ss7_initialized);
+
+ dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
+ /* we assume the combined_links are sorted by mask length, i.e. more
+ * specific combined links first, and less specific combined links with shorter
* mask later */
- llist_for_each_entry(rt, &rtbl->routes, list) {
- if (dpc == rt->cfg.pc && mask == rt->cfg.mask)
- return rt;
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ if ((dpc & clset->cfg.mask) != clset->cfg.pc)
+ continue;
+ return clset;
}
return NULL;
}
-/* find any routes pointing to this linkset and remove them */
-void ss7_route_table_del_routes_by_linkset(struct osmo_ss7_route_table *rtbl, struct osmo_ss7_linkset *lset)
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_combined_linkset_by_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc, uint32_t mask)
{
- struct osmo_ss7_route *rt, *rt2;
+ struct osmo_ss7_combined_linkset *clset;
- llist_for_each_entry_safe(rt, rt2, &rtbl->routes, list) {
- if (rt->dest.linkset == lset)
- ss7_route_destroy(rt);
+ OSMO_ASSERT(ss7_initialized);
+
+ dpc = osmo_ss7_pc_normalize(&rtbl->inst->cfg.pc_fmt, dpc);
+ /* we assume the combined_links are sorted by mask length, i.e. more
+ * specific combined links first, and less specific combined links with shorter
+ * mask later */
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ if ((dpc & clset->cfg.mask) != clset->cfg.pc)
+ continue;
+ if (mask != clset->cfg.mask)
+ continue;
+ return clset;
}
+ return NULL;
+}
+
+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)
+{
+ struct osmo_ss7_combined_linkset *clset;
+
+ /* we assume the combined_links are sorted by mask length, i.e. more
+ * specific routes first, and less specific routes with shorter
+ * mask later */
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ if (mask < clset->cfg.mask)
+ break;
+ if (dpc == clset->cfg.pc && mask == clset->cfg.mask) {
+ if (prio > clset->cfg.priority)
+ break;
+ if (prio == clset->cfg.priority)
+ return clset;
+ }
+ }
+ return NULL;
+}
+
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_or_create_combined_linkset(struct osmo_ss7_route_table *rtable, uint32_t pc, uint32_t mask, uint32_t prio)
+{
+ struct osmo_ss7_combined_linkset *clset;
+ clset = ss7_route_table_find_combined_linkset(rtable, pc, mask, prio);
+ if (!clset)
+ clset = ss7_combined_linkset_alloc(rtable, pc, mask, prio);
+ return clset;
}
/* 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)
{
- struct osmo_ss7_route *rt, *rt2;
+ struct osmo_ss7_combined_linkset *clset, *clset2;
- llist_for_each_entry_safe(rt, rt2, &rtbl->routes, list) {
- if (rt->dest.as == as)
- ss7_route_destroy(rt);
+ llist_for_each_entry_safe(clset, clset2, &rtbl->combined_linksets, list) {
+ struct osmo_ss7_route *rt;
+ llist_for_each_entry(rt, &clset->routes, list) {
+ if (rt->dest.as == as) {
+ ss7_route_destroy(rt);
+ /* clset may have been freed here. Same AS can't be twice in a combined
+ * linkset, so simply continue iterating in the upper loop. */
+ break;
+ }
+ }
+ }
+}
+
+/* find any routes pointing to this linkset and remove them */
+void ss7_route_table_del_routes_by_linkset(struct osmo_ss7_route_table *rtbl, struct osmo_ss7_linkset *lset)
+{
+ struct osmo_ss7_combined_linkset *clset, *clset2;
+
+ llist_for_each_entry_safe(clset, clset2, &rtbl->combined_linksets, list) {
+ struct osmo_ss7_route *rt;
+ llist_for_each_entry(rt, &clset->routes, list) {
+ if (rt->dest.linkset == lset) {
+ ss7_route_destroy(rt);
+ /* clset may have been freed here. Same linkset can't be twice in a combined
+ * linkset, so simply continue iterating in the upper loop. */
+ break;
+ }
+ }
}
}
diff --git a/src/osmo_ss7_vty.c b/src/osmo_ss7_vty.c
index f76a69e..2704742 100644
--- a/src/osmo_ss7_vty.c
+++ b/src/osmo_ss7_vty.c
@@ -48,6 +48,7 @@
#include "sccp_internal.h"
#include "ss7_as.h"
#include "ss7_asp.h"
+#include "ss7_combined_linkset.h"
#include "ss7_route.h"
#include "ss7_route_table.h"
#include "ss7_internal.h"
@@ -487,27 +488,31 @@
static void write_one_rtable(struct vty *vty, struct osmo_ss7_route_table *rtable)
{
+ struct osmo_ss7_combined_linkset *clset;
struct osmo_ss7_route *rt;
vty_out(vty, " route-table %s%s", rtable->cfg.name, VTY_NEWLINE);
if (rtable->cfg.description)
vty_out(vty, " description %s%s", rtable->cfg.description, VTY_NEWLINE);
- llist_for_each_entry(rt, &rtable->routes, list) {
- vty_out(vty, " update route %s %s linkset %s",
- osmo_ss7_pointcode_print(rtable->inst, rt->cfg.pc),
- osmo_ss7_pointcode_print2(rtable->inst, rt->cfg.mask),
- rt->cfg.linkset_name);
- if (rt->cfg.priority != OSMO_SS7_ROUTE_PRIO_DEFAULT)
- vty_out(vty, " priority %u", rt->cfg.priority);
- if (rt->cfg.qos_class)
- vty_out(vty, " qos-class %u", rt->cfg.qos_class);
- vty_out(vty, "%s", VTY_NEWLINE);
+ llist_for_each_entry(clset, &rtable->combined_linksets, list) {
+ llist_for_each_entry(rt, &clset->routes, list) {
+ vty_out(vty, " update route %s %s linkset %s",
+ osmo_ss7_pointcode_print(rtable->inst, rt->cfg.pc),
+ osmo_ss7_pointcode_print2(rtable->inst, rt->cfg.mask),
+ rt->cfg.linkset_name);
+ if (rt->cfg.priority != OSMO_SS7_ROUTE_PRIO_DEFAULT)
+ vty_out(vty, " priority %u", rt->cfg.priority);
+ if (rt->cfg.qos_class)
+ vty_out(vty, " qos-class %u", rt->cfg.qos_class);
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
}
}
/* "filter_pc == OSMO_SS7_PC_INVALID" means "show all" */
static void vty_dump_rtable(struct vty *vty, struct osmo_ss7_route_table *rtbl, uint32_t filter_pc)
{
+ struct osmo_ss7_combined_linkset *clset;
struct osmo_ss7_route *rt;
vty_out(vty, "Routing table = %s%s", rtbl->cfg.name, VTY_NEWLINE);
@@ -516,23 +521,25 @@
vty_out(vty, "Destination C Q P Linkset Name Linkset Non-adj Route%s", VTY_NEWLINE);
vty_out(vty, "---------------------- - - - ------------------- ------- ------- -------%s", VTY_NEWLINE);
- llist_for_each_entry(rt, &rtbl->routes, list) {
- if ((filter_pc != OSMO_SS7_PC_INVALID) && ((filter_pc & rt->cfg.mask) != rt->cfg.pc))
- continue; /* Skip routes not matching destination */
+ llist_for_each_entry(clset, &rtbl->combined_linksets, list) {
+ if ((filter_pc != OSMO_SS7_PC_INVALID) && ((filter_pc & clset->cfg.mask) != clset->cfg.pc))
+ continue; /* Skip combined linksets not matching destination */
- bool rt_avail = ss7_route_is_available(rt);
+ llist_for_each_entry(rt, &clset->routes, list) {
+ bool rt_avail = ss7_route_is_available(rt);
- vty_out(vty, "%-16s %-5s %c %c %u %-19s %-7s %-7s %-7s%s",
- osmo_ss7_route_print(rt),
- rt_avail ? "acces" : "INACC",
- ' ',
- '0' + rt->cfg.qos_class,
- rt->cfg.priority,
- rt->cfg.linkset_name,
- rt_avail ? "avail" : "UNAVAIL",
- "?",
- rt_avail ? "avail" : "UNAVAIL",
- VTY_NEWLINE);
+ vty_out(vty, "%-16s %-5s %c %c %u %-19s %-7s %-7s %-7s%s",
+ osmo_ss7_route_print(rt),
+ rt_avail ? "acces" : "INACC",
+ ' ',
+ '0' + rt->cfg.qos_class,
+ rt->cfg.priority,
+ rt->cfg.linkset_name,
+ rt_avail ? "avail" : "UNAVAIL",
+ "?",
+ rt_avail ? "avail" : "UNAVAIL",
+ VTY_NEWLINE);
+ }
}
}
diff --git a/src/ss7_combined_linkset.h b/src/ss7_combined_linkset.h
new file mode 100644
index 0000000..1c3b684
--- /dev/null
+++ b/src/ss7_combined_linkset.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+
+/***********************************************************************
+ * SS7 Combined Linkset
+ * Set of routes with same destination and priority.
+ ***********************************************************************/
+
+struct osmo_ss7_instance;
+struct osmo_ss7_link;
+
+struct osmo_ss7_combined_linkset {
+ /*! member in \ref osmo_ss7_route_table.combined_linksets */
+ struct llist_head list;
+
+ /*! \ref osmo_ss7_route_table to which we belong */
+ struct osmo_ss7_route_table *rtable;
+
+ /*! list of \ref osmo_ss7_route */
+ struct llist_head routes;
+ unsigned int num_routes;
+
+ struct {
+ uint32_t pc;
+ uint32_t mask;
+ /*! lower priority is higher */
+ uint32_t priority;
+ } cfg;
+};
+
+struct osmo_ss7_combined_linkset *
+ss7_combined_linkset_alloc(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask, uint32_t prio);
+void
+ss7_combined_linkset_free(struct osmo_ss7_combined_linkset *clset);
+struct osmo_ss7_linkset *
+ss7_combined_linkset_find_or_create(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask, uint32_t prio);
+
+void
+ss7_combined_linkset_add_route(struct osmo_ss7_combined_linkset *clset, struct osmo_ss7_route *rt);
+void
+ss7_combined_linkset_del_route(struct osmo_ss7_route *rt);
diff --git a/src/ss7_route.h b/src/ss7_route.h
index 0382564..0dcc34e 100644
--- a/src/ss7_route.h
+++ b/src/ss7_route.h
@@ -15,10 +15,12 @@
#define OSMO_SS7_ROUTE_PRIO_DEFAULT 5
struct osmo_ss7_route {
- /*! member in \ref osmo_ss7_route_table.routes */
+ /*! member in \ref osmo_ss7_combined_linkset.routes */
struct llist_head list;
/*! \ref osmo_ss7_route_table to which we belong */
struct osmo_ss7_route_table *rtable;
+ /* Combined linkset this route is part of */
+ struct osmo_ss7_combined_linkset *clset;
struct {
/*! pointer to linkset (destination) of route */
diff --git a/src/ss7_route_table.h b/src/ss7_route_table.h
index 38cff31..c88c742 100644
--- a/src/ss7_route_table.h
+++ b/src/ss7_route_table.h
@@ -14,8 +14,8 @@
struct llist_head list;
/*! \ref osmo_ss7_instance to which we belong */
struct osmo_ss7_instance *inst;
- /*! list of \ref osmo_ss7_route */
- struct llist_head routes;
+ /*! list of \ref osmo_ss7_combined_linksets*/
+ struct llist_head combined_linksets;
struct {
char *name;
@@ -35,5 +35,16 @@
ss7_route_table_find_route_by_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc,
uint32_t mask);
+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);
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_or_create_combined_linkset(struct osmo_ss7_route_table *rtbl, uint32_t pc, uint32_t mask, uint32_t prio);
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_combined_linkset_by_dpc(struct osmo_ss7_route_table *rtbl, uint32_t dpc);
+struct osmo_ss7_combined_linkset *
+ss7_route_table_find_combined_linkset_by_dpc_mask(struct osmo_ss7_route_table *rtbl, uint32_t dpc, uint32_t mask);
+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_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);
--
To view, visit https://gerrit.osmocom.org/c/libosmo-sigtran/+/38731?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I500ec74ba975e3c93071771027e4e5fe6000e6f3
Gerrit-Change-Number: 38731
Gerrit-PatchSet: 14
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann(a)sysmocom.de>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/39391?usp=email )
Change subject: AS loadsharing: Implement AS loadshare, skip unavailable AS/lset when choosing
......................................................................
AS loadsharing: Implement AS loadshare, skip unavailable AS/lset when choosing
Pick normal route containing destination AS/lset to serve a given
func(<OPC,DPC,SLS>)=eSLS on a round-robin base.
If normal route becomes unavailable, pick an alternative route in a
similar way. This alternative route will be used until itself becomes
unavailable or the normal route becomes available again.
Related: SYS#7112
Change-Id: I928fb1ef5db6922f1386a188e3fbf9e70780f25d
---
M src/osmo_ss7_combined_linkset.c
M src/osmo_ss7_hmrt.c
M src/ss7_combined_linkset.h
M src/xua_snm.c
M tests/ss7/ss7_test.c
M tests/vty/osmo_stp_route_prio.vty
6 files changed, 67 insertions(+), 21 deletions(-)
Approvals:
Jenkins Builder: Verified
pespin: Looks good to me, approved
osmith: Looks good to me, but someone else must approve
diff --git a/src/osmo_ss7_combined_linkset.c b/src/osmo_ss7_combined_linkset.c
index 90a54f6..99b0c79 100644
--- a/src/osmo_ss7_combined_linkset.c
+++ b/src/osmo_ss7_combined_linkset.c
@@ -49,6 +49,20 @@
* link set) or of an alternative link set (combined link set)."
*****************************************************************************/
+static inline struct llist_head *_ss7_llist_round_robin(struct llist_head *list, void **state)
+{
+ struct llist_head *e = *state;
+ if (!e || e->next == list)
+ e = list;
+ e = e->next;
+ if (e == list)
+ e = NULL;
+ *state = e;
+ return e;
+}
+#define ss7_llist_round_robin(list, state, struct_type, entry_name) \
+ llist_entry(_ss7_llist_round_robin(list, state), struct_type, entry_name)
+
/*! \brief Insert combined_link into its routing table
* \param[in] clset Combined link to be inserted into its routing table
* \returns 0 on success, negative on error
@@ -135,6 +149,14 @@
clset->esls_table[i].alt_rt = NULL;
}
+ /* Update round robin state */
+ if (rt == clset->last_route_roundrobin) {
+ ss7_llist_round_robin(&clset->routes, &clset->last_route_roundrobin, struct osmo_ss7_route, list);
+ /* If there's only one left, remove state: */
+ if (rt == clset->last_route_roundrobin)
+ clset->last_route_roundrobin = NULL;
+ }
+
llist_del(&rt->list);
rt->clset = NULL;
clset->num_routes--;
@@ -172,6 +194,27 @@
return NULL;
}
+static struct osmo_ss7_route *ss7_combined_linkset_select_route_roundrobin(struct osmo_ss7_combined_linkset *clset)
+{
+ struct osmo_ss7_route *rt;
+ struct osmo_ss7_route *rt_found = NULL;
+ unsigned int i = 0;
+
+ while (i < clset->num_routes) {
+ i++;
+ rt = ss7_llist_round_robin(&clset->routes, &clset->last_route_roundrobin, struct osmo_ss7_route, list);
+ if (ss7_route_is_available(rt)) {
+ rt_found = rt;
+ break;
+ }
+ }
+
+ if (!rt_found)
+ return NULL;
+
+ return rt_found;
+}
+
struct osmo_ss7_route *
ss7_combined_linkset_lookup_route(struct osmo_ss7_combined_linkset *clset, const struct osmo_ss7_route_label *rtlabel)
{
@@ -192,20 +235,20 @@
return rt;
}
- /* TODO: Check if the AS/linkset in rt is actually UP and can be
- * used, otherwise start ITU Q.704 section 7 "forced rerouting" prcoedure:
- * we need to pick a temporary dst (update the esls_table entry) while the
- * original one is DOWN. */
+ /* No current route available, try to find a new current route: */
- /* We need to pick a new AS/linkset from the combined linkset and cache
- * it so it is always used for this eSLS: */
- /* FIXME: for now we simply take the first AS in the combined linksed, to be improved later... */
- rt = llist_first_entry_or_null(&clset->routes, struct osmo_ss7_route, list);
+ /* No normal route selected yet: */
+ if (!eslse->normal_rt) {
+ rt = ss7_combined_linkset_select_route_roundrobin(clset);
+ /* Either a normal route was selected or none found: */
+ eslse->normal_rt = rt;
+ return rt;
+ }
- /* TODO: here we'd need to actually check if dst AS/linkset in the route
- * is actually UP, otherwise pick next one in the roundrobin list... */
+ /* Normal route unavailable and no alternative route (or unavailable too).
+ * start ITU Q.704 section 7 "forced rerouting" procedure: */
+ rt = ss7_combined_linkset_select_route_roundrobin(clset);
if (rt)
- clset->esls_table[esls].normal_rt = rt;
-
+ eslse->alt_rt = rt;
return rt;
}
diff --git a/src/osmo_ss7_hmrt.c b/src/osmo_ss7_hmrt.c
index 1e000b4..72d5adc 100644
--- a/src/osmo_ss7_hmrt.c
+++ b/src/osmo_ss7_hmrt.c
@@ -180,12 +180,6 @@
dpc, osmo_ss7_pointcode_print(inst, dpc), rt_name);
}
- if (osmo_ss7_as_down(as)) {
- LOGP(DLSS7, LOGL_ERROR, "Unable to route HMRT message: the AS %s is down\n",
- as->cfg.name);
- return -ENETDOWN;
- }
-
rate_ctr_inc2(as->ctrg, SS7_AS_CTR_TX_MSU_TOTAL);
switch (as->cfg.proto) {
diff --git a/src/ss7_combined_linkset.h b/src/ss7_combined_linkset.h
index e2f9c9d..b80a007 100644
--- a/src/ss7_combined_linkset.h
+++ b/src/ss7_combined_linkset.h
@@ -33,6 +33,7 @@
/*! list of \ref osmo_ss7_route */
struct llist_head routes;
unsigned int num_routes;
+ void *last_route_roundrobin;
struct {
uint32_t pc;
diff --git a/src/xua_snm.c b/src/xua_snm.c
index acdfe0c..1481178 100644
--- a/src/xua_snm.c
+++ b/src/xua_snm.c
@@ -347,7 +347,7 @@
.sls = 0,
};
- /* FIXME: don't just check for a route; but also check if the route is "active" */
+ /* Check if there's an "active" route available: */
if (ss7_instance_lookup_route(s7i, &rtlabel))
is_available = true;
diff --git a/tests/ss7/ss7_test.c b/tests/ss7/ss7_test.c
index 95fa1b1..f61ee0a 100644
--- a/tests/ss7/ss7_test.c
+++ b/tests/ss7/ss7_test.c
@@ -154,6 +154,7 @@
{
struct osmo_ss7_route_table *rtbl;
struct osmo_ss7_linkset *lset_a, *lset_b;
+ struct osmo_ss7_link *l_a, *l_b;
struct osmo_ss7_route *rt, *rt12, *rtdef;
struct osmo_ss7_route_label route_label;
@@ -173,8 +174,15 @@
lset_a = ss7_linkset_find_or_create(s7i, "a", 100);
OSMO_ASSERT(lset_a);
+ l_a = ss7_link_find_or_create(lset_a, 0);
+ OSMO_ASSERT(l_a);
+ l_a->cfg.adm_state = OSMO_SS7_LS_ENABLED;
+
lset_b = ss7_linkset_find_or_create(s7i, "b", 200);
OSMO_ASSERT(lset_b);
+ l_b = ss7_link_find_or_create(lset_b, 0);
+ OSMO_ASSERT(l_b);
+ l_b->cfg.adm_state = OSMO_SS7_LS_ENABLED;
/* route with full mask */
route_label = RT_LABEL(0, 12, 0);
diff --git a/tests/vty/osmo_stp_route_prio.vty b/tests/vty/osmo_stp_route_prio.vty
index 6f60d9a..2b7cec6 100644
--- a/tests/vty/osmo_stp_route_prio.vty
+++ b/tests/vty/osmo_stp_route_prio.vty
@@ -87,6 +87,6 @@
3.2.1/14 INACC 0 5 as2 UNAVAIL ? UNAVAIL
3.2.1/14 INACC 7 6 as1 UNAVAIL ? UNAVAIL
-OsmoSTP(config-cs7-rt)# ! NOW TEST LOOKUP WORKS AS DESIRED (as3 PICKED):
+OsmoSTP(config-cs7-rt)# ! NOW TEST LOOKUP FAILS AS EXPECTED (all route links are down):
OsmoSTP(config-cs7-rt)# do show cs7 instance 0 route-lookup 3.2.1 from 0.0.1 sls 0
-pc=6161=3.2.1 mask=0x3fff=7.255.7 via AS as3 proto=m3ua
+No route found for label 'OPC=1=0.0.1,DPC=6161=3.2.1,SLS=0'
--
To view, visit https://gerrit.osmocom.org/c/libosmo-sigtran/+/39391?usp=email
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I928fb1ef5db6922f1386a188e3fbf9e70780f25d
Gerrit-Change-Number: 39391
Gerrit-PatchSet: 11
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-CC: laforge <laforge(a)osmocom.org>