Review at https://gerrit.osmocom.org/2283
WIP: default layer manager using RKM to register PC with SG Change-Id: I78d4623dd213b5c59007a026a6cc3cfe5c04af50 --- M include/osmocom/sigtran/osmo_ss7.h M include/osmocom/sigtran/sigtran_sap.h M src/Makefile.am M src/sccp_sap.c A src/xua_default_lm_fsm.c M src/xua_rkm.c 6 files changed, 399 insertions(+), 7 deletions(-) git pull ssh://gerrit.osmocom.org:29418/libosmo-sccp refs/changes/83/2283/1 diff --git a/include/osmocom/sigtran/osmo_ss7.h b/include/osmocom/sigtran/osmo_ss7.h index e33a9ad..7ddebae 100644 --- a/include/osmocom/sigtran/osmo_ss7.h +++ b/include/osmocom/sigtran/osmo_ss7.h @@ -343,7 +343,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; struct { char *name; diff --git a/include/osmocom/sigtran/sigtran_sap.h b/include/osmocom/sigtran/sigtran_sap.h index d18aa3d..419498e 100644 --- a/include/osmocom/sigtran/sigtran_sap.h +++ b/include/osmocom/sigtran/sigtran_sap.h @@ -1,6 +1,8 @@ #pragma once #include <osmocom/core/prim.h> +#include <osmocom/sigtran/osmo_ss7.h> + enum osmo_sigtran_sap { SCCP_SAP_USER = _SAP_SS7_BASE, @@ -46,14 +48,31 @@ uint32_t code; }; +struct osmo_xlm_prim_rk_reg { + /* local routing key ID */ + uint32_t l_rk_id; + /* routing key */ + struct osmo_ss7_routing_key key; + enum osmo_ss7_as_traffic_mode traf_mode; +}; + +struct osmo_xlm_prim_rk_dereg { + uint32_t route_ctx; +}; + struct osmo_xlm_prim { struct osmo_prim_hdr oph; 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/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/xua_default_lm_fsm.c b/src/xua_default_lm_fsm.c new file mode 100644 index 0000000..9606841 --- /dev/null +++ b/src/xua_default_lm_fsm.c @@ -0,0 +1,282 @@ +/* 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 + */ + +#include <errno.h> + +#include <osmocom/core/fsm.h> +#include <osmocom/sigtran/sigtran_sap.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_IND, + 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_IND, "ASP-UP.ind" }, + { 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 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 */ + 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_IND: + /* 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; + + switch (fi->T) { + case T_WAIT_ASP_UP: + /* we have been waiting for the ASP to come up, but it + * failed to do so */ + /* FIXME: kill SCTP connection and re-try? */ + 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); + 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 */ + /* FIXME: disconnect/reconnect SCTP? */ + break; + case T_WAIT_RK_REG_RESP: + /* timeout of registration of routing key */ + /* FIXME: disconnect/reconect SCTP? */ + 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; + + switch (event) { + 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) +{ + switch (event) { + case LM_E_RKM_REG_CONF: + /* 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; + + 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; + } +} + +static void lm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case LM_E_SCTP_DISC_IND: + /* FIXME: disconnect/reconnect SCTP? */ + 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_NOTIFY), + .name = "IDLE", + .action = lm_idle, + }, + [S_WAIT_ASP_UP] = { + .in_event_mask = S(LM_E_ASP_UP_IND), + .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), + .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_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, OSMO_NO_EVENT }, +}; + + +struct osmo_fsm 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, +}; + + +/* 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); + + 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 xua_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(&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_rkm.c b/src/xua_rkm.c index 12d59c7..d906db4 100644 --- a/src/xua_rkm.c +++ b/src/xua_rkm.c @@ -75,6 +75,41 @@ return msg->tail - old_tail; } +static void xua_rkm_send_reg_req(struct osmo_ss7_asp *asp, uint32_t l_rk_id, + 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, 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) @@ -294,3 +329,26 @@ } 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): + xua_rkm_send_reg_req(asp, prim->u.rk_reg.l_rk_id, &prim->u.rk_reg.key, + prim->u.rk_reg.traf_mode); + break; + case OSMO_PRIM(OSMO_XLM_PRIM_M_RK_DEREG, PRIM_OP_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)); + } + + return 0; +} -- To view, visit https://gerrit.osmocom.org/2283 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I78d4623dd213b5c59007a026a6cc3cfe5c04af50 Gerrit-PatchSet: 1 Gerrit-Project: libosmo-sccp Gerrit-Branch: master Gerrit-Owner: Harald Welte <lafo...@gnumonks.org>