pespin has uploaded this change for review.

View Change

Implement ASP loadsharing

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, 223 insertions(+), 11 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/libosmo-sigtran refs/changes/08/39408/1
diff --git a/src/osmo_ss7_as.c b/src/osmo_ss7_as.c
index eb655ca..ccf929f 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"
@@ -153,6 +154,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;
@@ -254,6 +263,83 @@
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)) & 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 */
+ aeslse->alt_asp = NULL;
+ return asp;
+ }
+ /* We can transmit over alternative ASP: */
+ 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;
+ 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;
+ 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)
{
@@ -278,7 +364,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;

@@ -287,9 +373,45 @@
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);
+ 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 6b404b3..852aca3 100644
--- a/src/osmo_ss7_vty.c
+++ b/src/osmo_ss7_vty.c
@@ -1916,10 +1916,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")
{
@@ -1930,6 +1929,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",
@@ -1939,6 +1968,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;
}

@@ -2157,9 +2202,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", osmo_ss7_as_traffic_mode_name(as->cfg.mode));
+ 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%s", as->cfg.loadshare.opc_shift, VTY_NEWLINE);
+ }
+ }
+ 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);
@@ -3092,7 +3149,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 504818c..dbeb61e 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 */
@@ -28,6 +29,15 @@
SS7_AS_CTR_TX_MSU_TOTAL,
};

+#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;
@@ -45,6 +55,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;
@@ -64,8 +77,23 @@

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);
+
#define LOGPAS(as, subsys, level, fmt, args ...) \
_LOGSS7((as)->inst, subsys, level, "as-%s: " fmt, (as)->cfg.name, ## args)
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
index 5c2c5e0..e435970 100644
--- a/src/xua_as_fsm.c
+++ b/src/xua_as_fsm.c
@@ -150,7 +150,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 cb24560..dcde514 100644
--- a/tests/vty/osmo_stp_test.vty
+++ b/tests/vty/osmo_stp_test.vty
@@ -344,8 +344,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
@@ -361,6 +363,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 change 39408. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: libosmo-sigtran
Gerrit-Branch: master
Gerrit-Change-Id: I5f47e40b70ed566034cd1533b71e21fc03e94f6c
Gerrit-Change-Number: 39408
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin@sysmocom.de>