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

Reply via email to