Add a default layer manager using RKM to register PC with SG This "default layer manager" can optionally be used by a xUA ASP. It will handle the xUA Layer Manager (xlm) primitives and use them to behave as follows:
* bring the ASP into state "INACTIVE" * see if the SG can match our connection (based on IP address + port information) to a statically configured ASP configuration with associated AS(s). If yes, it will send us a NOTIFY message with AS-INACTIVE. * if the above doesn't work, try to dynamically register a routing key using RKM for the point code that was locally confiured on the ASP/client. If that works, the SG will now have created ASP and AS objects as well as a routing key and be able to serve us, sending the NOTIFY with the AS-INACTIVE state. * After either of the two above, we will attempt to transition into ASP-ACTIVE. The SG should send us an AS-ACTIVE notification in return * if anything fails, abort and disconnect the SCTP connection, restart related FSMs and start from scratch Change-Id: I78d4623dd213b5c59007a026a6cc3cfe5c04af50 --- M include/osmocom/sigtran/osmo_ss7.h M include/osmocom/sigtran/sigtran_sap.h M src/Makefile.am M src/osmo_ss7.c M src/sccp_sap.c M src/sccp_user.c M src/xua_asp_fsm.c A src/xua_default_lm_fsm.c M src/xua_internal.h M src/xua_rkm.c 10 files changed, 604 insertions(+), 14 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/83/2283/2 diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h index dff206d..c3a81bb 100644 --- a/include/osmocom/sigtran/osmo_ss7.h +++ b/include/osmocom/sigtran/osmo_ss7.h @@ -345,7 +345,8 @@ bool asp_id_present; /* Layer Manager to which we talk */ - struct osmo_xua_layer_manager *lm; + const struct osmo_xua_layer_manager *lm; + void *lm_priv; /*! Were we dynamically allocated */ bool dyn_allocated; @@ -372,6 +373,7 @@ void osmo_ss7_asp_destroy(struct osmo_ss7_asp *asp); int osmo_ss7_asp_send(struct osmo_ss7_asp *asp, struct msgb *msg); int osmo_ss7_asp_restart(struct osmo_ss7_asp *asp); +int osmo_ss7_asp_use_default_lm(struct osmo_ss7_asp *asp, int log_level); #define LOGPASP(asp, subsys, level, fmt, args ...) \ LOGP(subsys, level, "asp-%s: " fmt, (asp)->cfg.name, ## args) diff --git a/include/osmocom/sigtran/sigtran_sap.h b/include/osmocom/sigtran/sigtran_sap.h index 80cfefc..87504c8 100644 --- a/include/osmocom/sigtran/sigtran_sap.h +++ b/include/osmocom/sigtran/sigtran_sap.h @@ -51,10 +51,16 @@ /* routing key */ struct osmo_ss7_routing_key key; enum osmo_ss7_as_traffic_mode traf_mode; + + /* Status: Confirm only */ + uint32_t status; }; struct osmo_xlm_prim_rk_dereg { uint32_t route_ctx; + + /* Status: Confirm only */ + uint32_t status; }; struct osmo_xlm_prim { @@ -62,9 +68,14 @@ union { struct osmo_xlm_prim_notify notify; struct osmo_xlm_prim_error error; + struct osmo_xlm_prim_rk_reg rk_reg; + struct osmo_xlm_prim_rk_dereg rk_dereg; } u; }; #define msgb_xlm_prim(msg) ((struct osmo_xlm_prim *)(msg)->l1h) char *osmo_xlm_prim_name(struct osmo_prim_hdr *oph); + +/* XUA LM-SAP towards stack */ +int osmo_xlm_sap_down(struct osmo_ss7_asp *asp, struct osmo_prim_hdr *oph); diff --git a/src/Makefile.am b/src/Makefile.am index 7867282..f9b87b0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,7 +28,7 @@ libosmo_sigtran_la_SOURCES = sccp_sap.c sua.c m3ua.c xua_msg.c sccp_helpers.c \ sccp2sua.c sccp_scrc.c sccp_sclc.c sccp_scoc.c \ - sccp_user.c xua_rkm.c \ + sccp_user.c xua_rkm.c xua_default_lm_fsm.c \ osmo_ss7.c osmo_ss7_hmrt.c xua_asp_fsm.c xua_as_fsm.c libosmo_sigtran_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -export-symbols-regex '^osmo_' libosmo_sigtran_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMONETIF_LIBS) $(LIBSCTP_LIBS) diff --git a/src/osmo_ss7.c b/src/osmo_ss7.c index 7ed216a..e66414c 100644 --- a/src/osmo_ss7.c +++ b/src/osmo_ss7.c @@ -1635,6 +1635,7 @@ osmo_fsm_register(&sccp_scoc_fsm); osmo_fsm_register(&xua_as_fsm); osmo_fsm_register(&xua_asp_fsm); + osmo_fsm_register(&xua_default_lm_fsm); ss7_initialized = true; return 0; } diff --git a/src/sccp_sap.c b/src/sccp_sap.c index 2211f71..d4580ae 100644 --- a/src/sccp_sap.c +++ b/src/sccp_sap.c @@ -45,11 +45,43 @@ { const char *name = get_value_string(osmo_scu_prim_names, oph->primitive); - prim_name_buf[0] = '\0'; - strncpy(prim_name_buf, name, sizeof(prim_name_buf)-1); - prim_name_buf[sizeof(prim_name_buf)-1] = '\0'; - name = get_value_string(osmo_prim_op_names, oph->operation); - strncat(prim_name_buf, name, sizeof(prim_name_buf)-strlen(prim_name_buf)-2); + snprintf(prim_name_buf, sizeof(prim_name_buf), "%s.%s", name, + get_value_string(osmo_prim_op_names, oph->operation)); + + return prim_name_buf; +} + + +#include <osmocom/sigtran/sigtran_sap.h> + +const struct value_string osmo_xlm_prim_names[] = { + { OSMO_XLM_PRIM_M_SCTP_ESTABLISH, "M-SCTP_ESTABLISH" }, + { OSMO_XLM_PRIM_M_SCTP_RELEASE, "M-SCTP_RELEASE" }, + { OSMO_XLM_PRIM_M_SCTP_RESTART, "M-SCTP_RESTART" }, + { OSMO_XLM_PRIM_M_SCTP_STATUS, "M-SCTP_STATUS" }, + { OSMO_XLM_PRIM_M_ASP_STATUS, "M-ASP_STATUS" }, + { OSMO_XLM_PRIM_M_AS_STATUS, "M-AS_STATUS" }, + { OSMO_XLM_PRIM_M_NOTIFY, "M-NOTIFY" }, + { OSMO_XLM_PRIM_M_ERROR, "M-ERROR" }, + { OSMO_XLM_PRIM_M_ASP_UP, "M-ASP_UP" }, + { OSMO_XLM_PRIM_M_ASP_DOWN, "M-ASP_DOWN" }, + { OSMO_XLM_PRIM_M_ASP_ACTIVE, "M-ASP_ACTIVE" }, + { OSMO_XLM_PRIM_M_ASP_INACTIVE, "M-ASP_INACTIVE" }, + { OSMO_XLM_PRIM_M_AS_ACTIVE, "M-AS_ACTIVE" }, + { OSMO_XLM_PRIM_M_AS_INACTIVE, "M-AS_INACTIVE" }, + { OSMO_XLM_PRIM_M_AS_DOWN, "M-AS_DOWN" }, + /* optional as per spec, not implemented yet */ + { OSMO_XLM_PRIM_M_RK_REG, "M-RK_REG" }, + { OSMO_XLM_PRIM_M_RK_DEREG, "M-RK_DEREG" }, + { 0, NULL }, +}; + +char *osmo_xlm_prim_name(struct osmo_prim_hdr *oph) +{ + const char *name = get_value_string(osmo_xlm_prim_names, oph->primitive); + + snprintf(prim_name_buf, sizeof(prim_name_buf), "%s.%s", name, + get_value_string(osmo_prim_op_names, oph->operation)); return prim_name_buf; } diff --git a/src/sccp_user.c b/src/sccp_user.c index 01a0638..51cc6b1 100644 --- a/src/sccp_user.c +++ b/src/sccp_user.c @@ -274,6 +274,7 @@ asp->cfg.local.host = talloc_strdup(asp, local_ip); asp->cfg.remote.host = talloc_strdup(asp, remote_ip); osmo_ss7_as_add_asp(as, asp_name); + osmo_ss7_asp_use_default_lm(asp, LOGL_DEBUG); talloc_free(asp_name); osmo_ss7_asp_restart(asp); diff --git a/src/xua_asp_fsm.c b/src/xua_asp_fsm.c index 2830334..59887a4 100644 --- a/src/xua_asp_fsm.c +++ b/src/xua_asp_fsm.c @@ -100,10 +100,14 @@ /* Send a XUA LM Primitive to the XUA Layer Manager (LM) */ void xua_asp_send_xlm_prim(struct osmo_ss7_asp *asp, struct osmo_xlm_prim *prim) { - struct osmo_xua_layer_manager *lm = asp->lm; + const struct osmo_xua_layer_manager *lm = asp->lm; if (lm && lm->prim_cb) lm->prim_cb(&prim->oph, asp); + else { + LOGPASP(asp, DLSS7, LOGL_DEBUG, "No Layer Manager, dropping %s\n", + osmo_xlm_prim_name(&prim->oph)); + } msgb_free(prim->oph.msg); } @@ -334,10 +338,11 @@ ENSURE_ASP_OR_IPSP(fi, event); osmo_fsm_inst_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0); /* inform layer manager */ - send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_UP, - PRIM_OP_CONFIRM); - /* FIXME: This hack should be in layer manager? */ - osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL); + send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_UP, PRIM_OP_CONFIRM); + /* This hack should be in layer manager, but let's try + * to be smart in case there is no layer manager */ + if (!asp->lm) + osmo_fsm_inst_dispatch(fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL); break; case XUA_ASP_E_ASPSM_ASPUP: /* only if role SG */ diff --git a/src/xua_default_lm_fsm.c b/src/xua_default_lm_fsm.c new file mode 100644 index 0000000..fc9ba3c --- /dev/null +++ b/src/xua_default_lm_fsm.c @@ -0,0 +1,383 @@ +/* Default XUA Layer Manager */ +/* (C) 2017 by Harald Welte <lafo...@gnumonks.org> + * All Rights Reserved + * + * 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/>. + */ + +/* The idea of this default Layer Manager is as follows: + * - we wait until a SCTP connection is established + * - we issue the ASP-UP request and wait for the ASP being in UP state + * - we wait if we receive a M-NOTIFY indication about any AS in this ASP + * - if that's not received, we use RKM to register a routing context + * for our locally configured ASP and expect a positive registration + * result as well as a NOTIFY indication about AS-ACTIVE afterwards. + */ + +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/core/logging.h> +#include <osmocom/sigtran/osmo_ss7.h> +#include <osmocom/sigtran/sigtran_sap.h> +#include <osmocom/sigtran/protocol/m3ua.h> + +#include "xua_internal.h" +#include "xua_asp_fsm.h" + +#define S(x) (1 << (x)) + +enum lm_state { + /* idle state, SCTP not connected */ + S_IDLE, + /* we're waiting for the ASP-UP to be confirmed */ + S_WAIT_ASP_UP, + /* we are waiting for any NOTIFY about an AS in this ASP */ + S_WAIT_NOTIFY, + /* we've sent a RK REG REQ and wait for the result */ + S_RKM_REG, + /* all systems up, we're communicating */ + S_ACTIVE, +}; + +enum lm_event { + LM_E_SCTP_EST_IND, + LM_E_ASP_UP_CONF, + LM_E_NOTIFY_IND, + LM_E_AS_INACTIVE_IND, + LM_E_AS_ACTIVE_IND, + LM_E_AS_STATUS_IND, + LM_E_RKM_REG_CONF, + LM_E_SCTP_DISC_IND, +}; + +static const struct value_string lm_event_names[] = { + { LM_E_SCTP_EST_IND, "SCTP-ESTABLISH.ind" }, + { LM_E_ASP_UP_CONF, "ASP-UP.conf" }, + { LM_E_NOTIFY_IND, "NOTIFY.ind" }, + { LM_E_AS_INACTIVE_IND, "AS-INACTIVE.ind" }, + { LM_E_AS_ACTIVE_IND, "AS-ACTIVE.ind" }, + { LM_E_AS_STATUS_IND, "AS-STATUS.ind" }, + { LM_E_RKM_REG_CONF, "RKM_REG.conf" }, + { LM_E_SCTP_DISC_IND, "SCTP-RELEASE.ind" }, + { 0, NULL } +}; + +enum lm_timer { + T_WAIT_ASP_UP, + T_WAIT_NOTIFY, + T_WAIT_NOTIFY_RKM, + T_WAIT_RK_REG_RESP, +}; + +struct lm_fsm_priv { + struct osmo_ss7_asp *asp; +}; + +static struct osmo_ss7_as *find_first_as_in_asp(struct osmo_ss7_asp *asp) +{ + struct osmo_ss7_as *as; + + llist_for_each_entry(as, &asp->inst->as_list, list) { + unsigned int i; + for (i = 0; i < ARRAY_SIZE(as->cfg.asps); i++) { + if (as->cfg.asps[i] == asp) + return as; + } + } + + return NULL; +} + +/* handle an incoming RKM registration response */ +static int handle_reg_conf(struct osmo_fsm_inst *fi, uint32_t l_rk_id, uint32_t rctx) +{ + struct lm_fsm_priv *lmp = fi->priv; + struct osmo_ss7_asp *asp = lmp->asp; + struct osmo_ss7_as *as; + + /* update the application server with the routing context as + * allocated/registered by the SG */ + as = osmo_ss7_as_find_by_l_rk_id(asp->inst, l_rk_id); + if (!as) { + LOGPFSM(fi, "RKM Result for unknown l_rk_id %u\n"); + return -EINVAL; + } + as->cfg.routing_key.context = rctx; + + return 0; +} + +static void restart_asp(struct osmo_fsm_inst *fi) +{ + struct lm_fsm_priv *lmp = fi->priv; + struct osmo_ss7_asp *asp = lmp->asp; + int log_level = fi->log_level; + + osmo_ss7_asp_restart(asp); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + osmo_ss7_asp_use_default_lm(asp, log_level); +} + + +static void lm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct lm_fsm_priv *lmp = fi->priv; + + switch (event) { + case LM_E_SCTP_EST_IND: + /* Try to transition to ASP-UP, wait for 20s */ + osmo_fsm_inst_state_chg(fi, S_WAIT_ASP_UP, 20, T_WAIT_ASP_UP); + osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_UP_REQ, NULL); + break; + } +} + +static void lm_wait_asp_up(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case LM_E_ASP_UP_CONF: + /* ASP is sup, wait for some time if any NOTIFY + * indications about AS in this ASP are received */ + osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 2, T_WAIT_NOTIFY); + break; + } +} + + +static int lm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct lm_fsm_priv *lmp = fi->priv; + struct osmo_xlm_prim *prim; + struct osmo_ss7_as *as; + + switch (fi->T) { + case T_WAIT_ASP_UP: + /* we have been waiting for the ASP to come up, but it + * failed to do so */ + restart_asp(fi); + break; + case T_WAIT_NOTIFY: + /* No AS has reported via NOTIFY that is was + * (statically) configured at the SG for this ASP, so + * let's dynamically register */ + osmo_fsm_inst_state_chg(fi, S_RKM_REG, 10, T_WAIT_RK_REG_RESP); + prim = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_REQUEST); + as = find_first_as_in_asp(lmp->asp); + if (!as) { + LOGPFSML(fi, LOGL_ERROR, "Unable to find AS!\n"); + restart_asp(fi); + return 0; + } + /* Fill in settings from first AS (TODO: multiple AS support) */ + prim->u.rk_reg.key = as->cfg.routing_key; + osmo_xlm_sap_down(lmp->asp, &prim->oph); + break; + case T_WAIT_NOTIFY_RKM: + /* No AS has reported via NOTIFY even after dynamic RKM + * configuration */ + restart_asp(fi); + break; + case T_WAIT_RK_REG_RESP: + /* timeout of registration of routing key */ + restart_asp(fi); + break; + } + return 0; +} + +static void lm_wait_notify(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct lm_fsm_priv *lmp = fi->priv; + struct osmo_xlm_prim *oxp = data; + + switch (event) { + case LM_E_NOTIFY_IND: + OSMO_ASSERT(oxp->oph.primitive == OSMO_XLM_PRIM_M_NOTIFY); + OSMO_ASSERT(oxp->oph.operation == PRIM_OP_INDICATION); + if (oxp->u.notify.status_type == M3UA_NOTIFY_T_STATCHG && + (oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_INACT || + oxp->u.notify.status_info == M3UA_NOTIFY_I_AS_PEND)) { + osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0); + osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL); + } + break; + case LM_E_AS_INACTIVE_IND: + /* we now know that an AS is associated with this ASP at + * the SG, and that this AS is currently inactive */ + /* request the ASP to go into active state (which + * hopefully will bring the AS to active, too) */ + osmo_fsm_inst_state_chg(fi, S_ACTIVE, 0, 0); + osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL); + break; + } +}; + +static void lm_rkm_reg(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_xlm_prim *oxp; + int rc; + + switch (event) { + case LM_E_RKM_REG_CONF: + oxp = data; + if (oxp->u.rk_reg.status != M3UA_RKM_REG_SUCCESS) { + LOGPFSML(fi, LOGL_NOTICE, "Received RKM_REG_RSP with negative result\n"); + restart_asp(fi); + } else { + rc = handle_reg_conf(fi, oxp->u.rk_reg.key.l_rk_id, oxp->u.rk_reg.key.context); + if (rc < 0) + restart_asp(fi); + /* RKM registration was successful, we can + * transition to WAIT_NOTIFY state and assume + * that an NOTIFY/AS-INACTIVE arrives within 20 + * seconds */ + osmo_fsm_inst_state_chg(fi, S_WAIT_NOTIFY, 20, T_WAIT_NOTIFY_RKM); + } + break; + } +} + +static void lm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct lm_fsm_priv *lmp = fi->priv; + struct osmo_xlm_prim *oxp; + + switch (event) { + case LM_E_AS_INACTIVE_IND: + /* request the ASP to go into active state */ + osmo_fsm_inst_dispatch(lmp->asp->fi, XUA_ASP_E_M_ASP_ACTIVE_REQ, NULL); + break; + case LM_E_NOTIFY_IND: + oxp = data; + OSMO_ASSERT(oxp->oph.primitive == OSMO_XLM_PRIM_M_NOTIFY); + OSMO_ASSERT(oxp->oph.operation == PRIM_OP_INDICATION); + if (oxp->u.notify.status_type == M3UA_NOTIFY_T_STATCHG && + oxp->u.notify.status_info != M3UA_NOTIFY_I_AS_ACT) + restart_asp(fi); + break; + } +} + +static void lm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case LM_E_SCTP_DISC_IND: + restart_asp(fi); + break; + } +} + +static const struct osmo_fsm_state lm_states[] = { + [S_IDLE] = { + .in_event_mask = S(LM_E_SCTP_EST_IND), + .out_state_mask = S(S_WAIT_ASP_UP), + .name = "IDLE", + .action = lm_idle, + }, + [S_WAIT_ASP_UP] = { + .in_event_mask = S(LM_E_ASP_UP_CONF), + .out_state_mask = S(S_WAIT_NOTIFY), + .name = "WAIT_ASP_UP", + .action = lm_wait_asp_up, + }, + [S_WAIT_NOTIFY] = { + .in_event_mask = S(LM_E_AS_INACTIVE_IND) | S(LM_E_NOTIFY_IND), + .out_state_mask = S(S_RKM_REG) | S(S_ACTIVE), + .name = "WAIT_NOTIFY", + .action = lm_wait_notify, + }, + [S_RKM_REG] = { + .in_event_mask = S(LM_E_RKM_REG_CONF), + .out_state_mask = S(S_WAIT_NOTIFY), + .name = "RKM_REG", + .action = lm_rkm_reg, + }, + [S_ACTIVE] = { + .in_event_mask = S(LM_E_AS_INACTIVE_IND) | S(LM_E_NOTIFY_IND), + .name = "ACTIVE", + .action = lm_active, + }, +}; + +/* Map from incoming XLM SAP primitives towards FSM events */ +static const struct osmo_prim_event_map lm_event_map[] = { + { XUA_SAP_LM, OSMO_XLM_PRIM_M_SCTP_ESTABLISH, PRIM_OP_INDICATION, LM_E_SCTP_EST_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_SCTP_RELEASE, PRIM_OP_INDICATION, LM_E_SCTP_DISC_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_ASP_UP, PRIM_OP_CONFIRM, LM_E_ASP_UP_CONF }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_AS_STATUS, PRIM_OP_INDICATION, LM_E_AS_STATUS_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_NOTIFY, PRIM_OP_INDICATION, LM_E_NOTIFY_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_AS_INACTIVE, PRIM_OP_INDICATION, LM_E_AS_INACTIVE_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_AS_ACTIVE, PRIM_OP_INDICATION, LM_E_AS_ACTIVE_IND }, + { XUA_SAP_LM, OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_CONFIRM, LM_E_RKM_REG_CONF }, + { 0, 0, 0, OSMO_NO_EVENT }, +}; + + +struct osmo_fsm xua_default_lm_fsm = { + .name = "xua_default_lm", + .states = lm_states, + .num_states = ARRAY_SIZE(lm_states), + .timer_cb = lm_timer_cb, + .event_names = lm_event_names, + .allstate_event_mask = S(LM_E_SCTP_DISC_IND), + .allstate_action = lm_allstate, + .log_subsys = DLSS7, +}; + + +/* layer manager primitive call-back function, registered osmo_ss7 */ +static int default_lm_prim_cb(struct osmo_prim_hdr *oph, void *_asp) +{ + struct osmo_ss7_asp *asp = _asp; + struct osmo_fsm_inst *fi = asp->lm_priv; + uint32_t event = osmo_event_for_prim(oph, lm_event_map); + char *prim_name = osmo_xlm_prim_name(oph); + + LOGPFSM(fi, "Received primitive %s\n", prim_name); + + if (event == OSMO_NO_EVENT) { + LOGPFSML(fi, LOGL_NOTICE, "Ignoring primitive %s\n", prim_name); + return 0; + } + + osmo_fsm_inst_dispatch(fi, event, oph); + + return 0; +} + +static const struct osmo_xua_layer_manager default_layer_manager = { + .prim_cb = default_lm_prim_cb, +}; + +int osmo_ss7_asp_use_default_lm(struct osmo_ss7_asp *asp, int log_level) +{ + struct lm_fsm_priv *lmp; + struct osmo_fsm_inst *fi; + + fi = osmo_fsm_inst_alloc(&xua_default_lm_fsm, asp, NULL, log_level, asp->cfg.name); + + lmp = talloc_zero(fi, struct lm_fsm_priv); + if (!lmp) { + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); + return -ENOMEM; + } + lmp->asp = asp; + fi->priv = lmp; + + asp->lm = &default_layer_manager; + asp->lm_priv = fi; + + return 0; +} diff --git a/src/xua_internal.h b/src/xua_internal.h index 171756b..468b7e4 100644 --- a/src/xua_internal.h +++ b/src/xua_internal.h @@ -56,5 +56,6 @@ enum osmo_xlm_prim_type prim_type, enum osmo_prim_operation op); +extern struct osmo_fsm xua_default_lm_fsm; extern const struct value_string m3ua_rkm_reg_status_vals[]; extern const struct value_string m3ua_rkm_dereg_status_vals[]; diff --git a/src/xua_rkm.c b/src/xua_rkm.c index ad6c880..0d576a7 100644 --- a/src/xua_rkm.c +++ b/src/xua_rkm.c @@ -102,6 +102,41 @@ return msg->tail - old_tail; } +static void xua_rkm_send_reg_req(struct osmo_ss7_asp *asp, + const struct osmo_ss7_routing_key *rkey, + enum osmo_ss7_as_traffic_mode traf_mode) +{ + struct msgb *msg = m3ua_msgb_alloc(__func__); + int tmod = osmo_ss7_tmode_to_xua(traf_mode); + + /* One individual Registration Request according to Chapter 3.6.1 */ + msgb_put_u16(msg, M3UA_IEI_ROUT_KEY); /* outer IEI */ + msgb_put_u16(msg, 32 + 4); /* outer length */ + /* nested IEIs */ + msgb_t16l16vp_put_u32(msg, M3UA_IEI_LOC_RKEY_ID, rkey->l_rk_id); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, rkey->context); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_TRAF_MODE_TYP, tmod); + msgb_t16l16vp_put_u32(msg, M3UA_IEI_DEST_PC, rkey->pc); + + msgb_push_m3ua_hdr(msg, M3UA_MSGC_RKM, M3UA_RKM_REG_REQ); + + osmo_ss7_asp_send(asp, msg); +} + +static void xua_rkm_send_dereg_req(struct osmo_ss7_asp *asp, uint32_t route_ctx) +{ + struct msgb *msg = m3ua_msgb_alloc(__func__); + + /* One individual De-Registration Request according to Chapter 3.6.3 */ + msgb_t16l16vp_put_u32(msg, M3UA_IEI_ROUTE_CTX, route_ctx); + + msgb_push_m3ua_hdr(msg, M3UA_MSGC_RKM, M3UA_RKM_DEREG_REQ); + + osmo_ss7_asp_send(asp, msg); +} + + + /* handle a single registration request IE (nested IEs in 'innner' */ static int handle_rkey_reg(struct osmo_ss7_asp *asp, struct xua_msg *inner, struct msgb *resp) @@ -277,16 +312,110 @@ return 0; } +/* handle a single registration response IE (nested IEs in 'inner' */ +static int handle_rkey_reg_resp(struct osmo_ss7_asp *asp, struct xua_msg *inner) +{ + struct osmo_xlm_prim *oxp; + + if (!xua_msg_find_tag(inner, M3UA_IEI_LOC_RKEY_ID) || + !xua_msg_find_tag(inner, M3UA_IEI_REG_STATUS) || + !xua_msg_find_tag(inner, M3UA_IEI_ROUTE_CTX)) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Missing Inner IE in REG RESP\n"); + /* FIXME: ERROR to peer */ + return -1; + } + + oxp = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_CONFIRM); + if (!oxp) + return -1; + + oxp->u.rk_reg.key.l_rk_id = xua_msg_get_u32(inner, M3UA_IEI_LOC_RKEY_ID); + oxp->u.rk_reg.key.context = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX); + oxp->u.rk_reg.status = xua_msg_get_u32(inner, M3UA_IEI_REG_STATUS); + + LOGPASP(asp, DLSS7, LOGL_INFO, "Received RKM REG RES rctx=%u status=%s\n", + oxp->u.rk_reg.key.context, + get_value_string(m3ua_rkm_reg_status_vals, oxp->u.rk_reg.status)); + + /* Send primitive to LM */ + xua_asp_send_xlm_prim(asp, oxp); + + return 0; +} + /* receive a registration response (ASP role) */ static int m3ua_rx_rkm_reg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua) { - /* TODO */ + struct xua_msg_part *part; + struct xua_msg *inner = NULL; + + llist_for_each_entry(part, &xua->headers, entry) { + /* skip other IEs and/or short REG_RES IEs */ + if (part->tag != M3UA_IEI_REG_RESULT || part->len < 24) + continue; + + /* we leave the above loop at the first valid + * registration result (we only support one AS per ASP + * for now) */ + inner = xua_from_nested(part); + if (!inner) + continue; + + handle_rkey_reg_resp(asp, inner); + } + return 0; +} + +/* handle a single deregistration response IE (nested IEs in 'inner' */ +static int handle_rkey_dereg_resp(struct osmo_ss7_asp *asp, struct xua_msg *inner) +{ + struct osmo_xlm_prim *oxp; + + if (!xua_msg_find_tag(inner, M3UA_IEI_DEREG_STATUS) || + !xua_msg_find_tag(inner, M3UA_IEI_ROUTE_CTX)) { + LOGPASP(asp, DLSS7, LOGL_NOTICE, "Missing Inner IE in DEREG RESP\n"); + /* FIXME: ERROR to peer */ + return -1; + } + + oxp = xua_xlm_prim_alloc(OSMO_XLM_PRIM_M_RK_DEREG, PRIM_OP_CONFIRM); + if (!oxp) + return -1; + + oxp->u.rk_dereg.route_ctx = xua_msg_get_u32(inner, M3UA_IEI_ROUTE_CTX); + oxp->u.rk_dereg.status = xua_msg_get_u32(inner, M3UA_IEI_DEREG_STATUS); + + LOGPASP(asp, DLSS7, LOGL_INFO, "Received RKM DEREG RES rctx=%u status=%s\n", + oxp->u.rk_reg.key.context, + get_value_string(m3ua_rkm_dereg_status_vals, oxp->u.rk_dereg.status)); + + /* Send primitive to LM */ + xua_asp_send_xlm_prim(asp, oxp); + + return 0; } /* receive a deregistration response (ASP role) */ static int m3ua_rx_rkm_dereg_rsp(struct osmo_ss7_asp *asp, struct xua_msg *xua) { - /* TODO */ + struct xua_msg_part *part; + struct xua_msg *inner = NULL; + + llist_for_each_entry(part, &xua->headers, entry) { + /* skip other IEs and/or short REG_RES IEs */ + if (part->tag != M3UA_IEI_DEREG_RESULT || part->len < 16) + continue; + + /* we leave the above loop at the first valid + * registration result (we only support one AS per ASP + * for now) */ + inner = xua_from_nested(part); + if (!inner) + continue; + + handle_rkey_dereg_resp(asp, inner); + } + return 0; } /* process an incoming RKM message in xua format */ @@ -321,3 +450,28 @@ } return rc; } + +int osmo_xlm_sap_down(struct osmo_ss7_asp *asp, struct osmo_prim_hdr *oph) +{ + struct osmo_xlm_prim *prim = (struct osmo_xlm_prim *) oph; + + LOGPASP(asp, DLSS7, LOGL_DEBUG, "Received XUA Layer Manager Primitive: %s)\n", + osmo_xlm_prim_name(&prim->oph)); + + switch (OSMO_PRIM_HDR(&prim->oph)) { + case OSMO_PRIM(OSMO_XLM_PRIM_M_RK_REG, PRIM_OP_REQUEST): + /* Layer Manager asks us to send a Routing Key Reg Request */ + xua_rkm_send_reg_req(asp, &prim->u.rk_reg.key, prim->u.rk_reg.traf_mode); + break; + case OSMO_PRIM(OSMO_XLM_PRIM_M_RK_DEREG, PRIM_OP_REQUEST): + /* Layer Manager asks us to send a Routing Key De-Reg Request */ + xua_rkm_send_dereg_req(asp, prim->u.rk_dereg.route_ctx); + break; + default: + LOGPASP(asp, DLSS7, LOGL_ERROR, "Unknown XUA Layer Manager Primitive: %s\n", + osmo_xlm_prim_name(&prim->oph)); + break; + } + + return 0; +} -- To view, visit https://gerrit.osmocom.org/2283 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: newpatchset Gerrit-Change-Id: I78d4623dd213b5c59007a026a6cc3cfe5c04af50 Gerrit-PatchSet: 2 Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Owner: Harald Welte <lafo...@gnumonks.org> Gerrit-Reviewer: Jenkins Builder