pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/libosmo-sigtran/+/41526?usp=email )
Change subject: Implement ITU Q.704 timer T8 ......................................................................
Implement ITU Q.704 timer T8
Related: OS#6892 Change-Id: I27ebf7327448554a6e4600e45a4ab0343ab846a4 --- M src/mtp3_rtpc.c M src/ss7_instance.c M src/ss7_instance.h M src/ss7_vty.c 4 files changed, 190 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-sigtran refs/changes/26/41526/1
diff --git a/src/mtp3_rtpc.c b/src/mtp3_rtpc.c index 1ce5c25..34d40e7 100644 --- a/src/mtp3_rtpc.c +++ b/src/mtp3_rtpc.c @@ -68,7 +68,15 @@ struct osmo_ss7_route_label rtlabel; struct osmo_ss7_route *rt;
- /* TODO: Start T8 */ + /* Start T8 */ + if (ss7_instance_t8_inaccessible_sp_running(inst, orig_xua->mtp.dpc)) { + /* T8 is running for this SP, inhibit Tx of transfer prohibited */ + LOGSS7(inst, LOGL_DEBUG, "Tx TFP (DUNA) inaccessible SP %u=%s to concerned SP %u=%s: inhibit due to T8\n", + orig_xua->mtp.dpc, osmo_ss7_pointcode_print_buf(buf_orig_dpc, sizeof(buf_orig_dpc), inst, orig_xua->mtp.dpc), + orig_xua->mtp.opc, osmo_ss7_pointcode_print_buf(buf_orig_opc, sizeof(buf_orig_opc), inst, orig_xua->mtp.opc)); + return 0; + } + ss7_instance_t8_inaccessible_sp_start(inst, orig_xua->mtp.dpc);
/* "transfer prohibited RTPC -> HMRT", "To concerned SP or STP". * See also Q.704 13.2 Transfer-prohibited. */ diff --git a/src/ss7_instance.c b/src/ss7_instance.c index f8c78b4..b51f1b9 100644 --- a/src/ss7_instance.c +++ b/src/ss7_instance.c @@ -63,6 +63,25 @@ .ctr_desc = ss7_inst_rcd, };
+const struct osmo_tdef ss7_instance_xua_timer_defaults[SS7_INST_XUA_TIMERS_LEN] = { + { .T = SS7_INST_XUA_T8, .default_val = SS7_INST_XUA_DEFAULT_T8_MSEC, .unit = OSMO_TDEF_MS, + .desc = "T8 - Transfer prohibited inhibition timer (transient solution) (ms)", + .min_val = 800, .max_val = 1200}, + {} +}; + +/* ITU Q.704 16.8 Timers and timer values */ +const struct value_string ss7_instance_xua_timer_names[] = { + { SS7_INST_XUA_T8, "T8" }, + {} +}; + +osmo_static_assert(ARRAY_SIZE(ss7_instance_xua_timer_defaults) == (SS7_INST_XUA_TIMERS_LEN) && + ARRAY_SIZE(ss7_instance_xua_timer_names) == (SS7_INST_XUA_TIMERS_LEN), + assert_ss7_instance_xua_timer_count); + +static void t8_inaccessible_sp_timer_cb(void *_inst); + struct osmo_ss7_instance * ss7_instance_alloc(void *ctx, uint32_t id) { @@ -84,6 +103,13 @@ INIT_LLIST_HEAD(&inst->asp_list); INIT_LLIST_HEAD(&inst->rtable_list); INIT_LLIST_HEAD(&inst->xua_servers); + INIT_LLIST_HEAD(&inst->t8_inaccessible_sp.list); + + osmo_timer_setup(&inst->t8_inaccessible_sp.timer, t8_inaccessible_sp_timer_cb, inst); + + inst->cfg.T_defs_xua = talloc_memdup(inst, ss7_instance_xua_timer_defaults, + sizeof(ss7_instance_xua_timer_defaults)); + osmo_tdefs_reset(inst->cfg.T_defs_xua);
inst->ctrg = rate_ctr_group_alloc(inst, &ss7_inst_rcgd, id); if (!inst->ctrg) { @@ -124,6 +150,9 @@ llist_for_each_entry_safe(lset, lset2, &inst->linksets, list) ss7_linkset_destroy(lset);
+ osmo_timer_del(&inst->t8_inaccessible_sp.timer); + /* Talloc takes care of freeing inst->t8_inaccessible_sp.list and its entries */ + rate_ctr_group_free(inst->ctrg); llist_del(&inst->list); talloc_free(inst); @@ -803,3 +832,61 @@
return NULL; } + + +static void t8_inaccessible_sp_timer_cb(void *_inst) +{ + struct osmo_ss7_instance *inst = _inst; + struct t8_inaccessible_sp_entry *it; + struct timespec ts_now, ts_t8; + char buf_dpc[MAX_PC_STR_LEN]; + + unsigned int t8_msec = osmo_tdef_get(inst->cfg.T_defs_xua, SS7_INST_XUA_T8, OSMO_TDEF_MS, -1); + ts_t8.tv_sec = (t8_msec/1000); + ts_t8.tv_nsec = (t8_msec%1000)*1000*1000; + osmo_clock_gettime(CLOCK_MONOTONIC, &ts_now); + + while ((it = llist_first_entry_or_null(&inst->t8_inaccessible_sp.list, struct t8_inaccessible_sp_entry, entry))) { + struct timespec ts_expire; + timespecadd(&it->ts_started, &ts_t8, &ts_expire); + if (timespeccmp(&ts_expire, &ts_now, >)) { + struct timespec ts_diff; + timespecsub(&ts_expire, &ts_now, &ts_diff); + osmo_timer_schedule(&inst->t8_inaccessible_sp.timer, ts_diff.tv_sec, ts_diff.tv_nsec / 1000); + break; + } + LOGSS7(inst, LOGL_DEBUG, "T8: SP %u=%s: Timeout\n", it->dpc, osmo_ss7_pointcode_print_buf(buf_dpc, sizeof(buf_dpc), inst, it->dpc)); + llist_del(&it->entry); + talloc_free(it); + } +} + +bool ss7_instance_t8_inaccessible_sp_running(const struct osmo_ss7_instance *inst, uint32_t dpc) +{ + struct t8_inaccessible_sp_entry *it; + llist_for_each_entry(it, &inst->t8_inaccessible_sp.list, entry) { + if (it->dpc == dpc) + return true; + } + return false; +} + +void ss7_instance_t8_inaccessible_sp_start(struct osmo_ss7_instance *inst, uint32_t dpc) +{ + struct t8_inaccessible_sp_entry *it; + char buf_dpc[MAX_PC_STR_LEN]; + + LOGSS7(inst, LOGL_DEBUG, "T8: SP %u=%s: Start\n", + dpc, osmo_ss7_pointcode_print_buf(buf_dpc, sizeof(buf_dpc), inst, dpc)); + + OSMO_ASSERT(!ss7_instance_t8_inaccessible_sp_running(inst, dpc)); + + it = talloc_zero(inst, struct t8_inaccessible_sp_entry); + it->dpc = dpc; + osmo_clock_gettime(CLOCK_MONOTONIC, &it->ts_started); + if (llist_empty(&inst->t8_inaccessible_sp.list)) { + unsigned int timeout_msec = osmo_tdef_get(inst->cfg.T_defs_xua, SS7_INST_XUA_T8, OSMO_TDEF_MS, -1); + osmo_timer_schedule(&inst->t8_inaccessible_sp.timer, (timeout_msec/1000), ((timeout_msec%1000)*10)); + } + llist_add_tail(&it->entry, &inst->t8_inaccessible_sp.list); +} diff --git a/src/ss7_instance.h b/src/ss7_instance.h index 3045403..29310f4 100644 --- a/src/ss7_instance.h +++ b/src/ss7_instance.h @@ -3,6 +3,9 @@ #include <stdint.h> #include <stdbool.h> #include <osmocom/core/linuxlist.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/timer_compat.h> #include <osmocom/core/rate_ctr.h>
#include <osmocom/sigtran/protocol/mtp.h> @@ -16,6 +19,17 @@ struct osmo_ss7_route_label; struct osmo_sccp_instance;
+enum ss7_instance_xua_timer { + /* 0 kept unused on purpose since it's handled specially by osmo_fsm */ + SS7_INST_XUA_T8 = 1, /* Q.704 T8 */ + /* This must remain the last item: */ + SS7_INST_XUA_TIMERS_LEN +}; +extern const struct value_string ss7_instance_xua_timer_names[]; +extern const struct osmo_tdef ss7_instance_xua_timer_defaults[SS7_INST_XUA_TIMERS_LEN]; +/* According to SUA RFC3868 Section 8, M3UA RFC4666 Section 4.3.4.1 */ +#define SS7_INST_XUA_DEFAULT_T8_MSEC 1000 + enum ss7_instance_ctr { SS7_INST_CTR_PKT_RX_TOTAL, SS7_INST_CTR_PKT_RX_UNKNOWN, @@ -51,6 +65,13 @@
struct rate_ctr_group *ctrg;
+ /* Q.704 Figure 44 and section 13.2: List to store remote PCs with T8 started */ + struct { + /* list of struct t8_inaccessible_sp_entry, sorted by entry->ts_started */ + struct llist_head list; + struct osmo_timer_list timer; + } t8_inaccessible_sp; + struct { uint32_t id; char *name; @@ -71,6 +92,9 @@ * to skip for routing decisions. * range 0-3, defaults to 0, which means take all 4 bits. */ uint8_t sls_shift; + + /* T_defs defined at instance level: */ + struct osmo_tdef *T_defs_xua; } cfg; };
@@ -85,3 +109,15 @@ LOGP(subsys, level, "%u: " fmt, inst ? (inst)->cfg.id : 0, ## args) #define LOGSS7(inst, level, fmt, args ...) \ _LOGSS7(inst, DLSS7, level, fmt, ## args) + + +/*********************************************************************** + * ITUQ.704 13.2.2: Timer T8 concerning one SP + ***********************************************************************/ +struct t8_inaccessible_sp_entry { + struct llist_head entry; /* item in (struct osmo_ss7_instance)->t8_inaccessible_sp.list */ + uint32_t dpc; /* SP inaccessible */ + struct timespec ts_started; /* Timestamp T8 was started for this SP */ +}; +bool ss7_instance_t8_inaccessible_sp_running(const struct osmo_ss7_instance *inst, uint32_t dpc); +void ss7_instance_t8_inaccessible_sp_start(struct osmo_ss7_instance *inst, uint32_t dpc); diff --git a/src/ss7_vty.c b/src/ss7_vty.c index 98c6073..ff95f4f 100644 --- a/src/ss7_vty.c +++ b/src/ss7_vty.c @@ -279,6 +279,61 @@ return CMD_SUCCESS; }
+/* timer xua <name> <1-999999> + * (cmdstr and doc are dynamically generated from ss7_instance_xua_timer_names.) */ +DEFUN_ATTR(cs7_timer_xua, cs7_timer_xua_cmd, + NULL, NULL, CMD_ATTR_IMMEDIATE) +{ + struct osmo_ss7_instance *inst = vty->index; + enum ss7_instance_xua_timer timer = get_string_value(ss7_instance_xua_timer_names, argv[0]); + + if (timer <= 0 || timer >= SS7_INST_XUA_TIMERS_LEN) { + vty_out(vty, "%% Invalid timer: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + osmo_tdef_set(inst->cfg.T_defs_xua, timer, atoi(argv[1]), OSMO_TDEF_S); + return CMD_SUCCESS; +} + +static void gen_cs7_timer_xua_cmd_strs(struct cmd_element *cmd) +{ + int i; + char *cmd_str = NULL; + char *doc_str = NULL; + + OSMO_ASSERT(cmd->string == NULL); + OSMO_ASSERT(cmd->doc == NULL); + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "timer xua ("); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, + "Configure CS7 Instance default timer values\n" + "Configure CS7 Instance default xua timer values\n"); + + for (i = 0; ss7_instance_xua_timer_names[i].str; i++) { + const struct osmo_tdef *def; + enum ss7_asp_xua_timer timer; + + timer = ss7_instance_xua_timer_names[i].value; + def = osmo_tdef_get_entry((struct osmo_tdef *)&ss7_instance_xua_timer_defaults, timer); + OSMO_ASSERT(def); + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, "%s%s", + i ? "|" : "", + ss7_instance_xua_timer_names[i].str); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, "%s (default: %lu)\n", + def->desc, + def->default_val); + } + + osmo_talloc_asprintf(tall_vty_ctx, cmd_str, ") <1-999999>"); + osmo_talloc_asprintf(tall_vty_ctx, doc_str, + "Timer value, in seconds\n"); + + cmd->string = cmd_str; + cmd->doc = doc_str; +} + static void write_one_cs7(struct vty *vty, struct osmo_ss7_instance *inst, bool show_dyn_config);
static int write_all_cs7(struct vty *vty, bool show_dyn_config) @@ -1482,6 +1537,9 @@ cs7_role = CS7_ROLE_SG; vty_init_shared(ctx);
+ gen_cs7_timer_xua_cmd_strs(&cs7_timer_xua_cmd); + install_lib_element(L_CS7_NODE, &cs7_timer_xua_cmd); + install_node(&rtable_node, NULL); install_lib_element(L_CS7_NODE, &cs7_route_table_cmd); install_lib_element(L_CS7_RTABLE_NODE, &cfg_description_cmd);