pespin has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-sgsn/+/40988?usp=email )


Change subject: Introduce iu_rnc FSM
......................................................................

Introduce iu_rnc FSM

This FSM is similar to the already existing ran_peer_fsm in osmo-msc,
which already had better logic around SCCP and RANAP state handling.
Similarly, osmo-sgsn's struct ranap_iu_rnc maps to osmo-msc's struct ran_peer.

With this FSM we can currently track the RANAP link state towards a given
RNC peer:
* Reject (RANAP Error Indication) all UE-related messages until a RANAP
  RESET from RNC is received first.
* Tear down all subsriber connections whenever the RANAP peer sends us a
  RESET message.
* Tear down all subscriber connections whenever the SCCP link towards
  RNC becomes unavailable.
* Send a RESET towards RNC peer once the SCCP link towrdards it becomes
  available again.

This commit only implements so far the Rx path of the FSM, ie. when
receiving events/messages from a peer over SCCP and pushing them locally
up the stack (RANAP). The Tx side will be implemented in a follow-up
commit, which will allow discarding messages if the lower layers towards
a given RNC are known to be down.

Change-Id: I18b7803500163e78ff6a684095194174b0fb6ee1
---
M include/osmocom/sgsn/Makefile.am
M include/osmocom/sgsn/gprs_ranap.h
M include/osmocom/sgsn/iu_rnc.h
A include/osmocom/sgsn/iu_rnc_fsm.h
M include/osmocom/sgsn/sccp.h
M include/osmocom/sgsn/sgsn.h
M src/sgsn/Makefile.am
M src/sgsn/gprs_ranap.c
M src/sgsn/iu_rnc.c
A src/sgsn/iu_rnc_fsm.c
M src/sgsn/sccp.c
M src/sgsn/sgsn.c
M src/sgsn/sgsn_vty.c
M tests/gprs_routing_area/Makefile.am
M tests/osmo-sgsn_test-nodes.vty
M tests/sgsn/Makefile.am
16 files changed, 593 insertions(+), 95 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-sgsn refs/changes/88/40988/1

diff --git a/include/osmocom/sgsn/Makefile.am b/include/osmocom/sgsn/Makefile.am
index 342b568..9c50618 100644
--- a/include/osmocom/sgsn/Makefile.am
+++ b/include/osmocom/sgsn/Makefile.am
@@ -29,6 +29,7 @@
        gtp_mme.h \
        iu_client.h \
        iu_rnc.h \
+       iu_rnc_fsm.h \
        mmctx.h \
        pdpctx.h \
        sccp.h \
diff --git a/include/osmocom/sgsn/gprs_ranap.h 
b/include/osmocom/sgsn/gprs_ranap.h
index 3c4f593..62914ba 100644
--- a/include/osmocom/sgsn/gprs_ranap.h
+++ b/include/osmocom/sgsn/gprs_ranap.h
@@ -13,6 +13,15 @@
 struct sgsn_mm_ctx;
 struct sgsn_pdp_ctx;

+/* struct RANAP_GlobalRNC_ID with a coupled buffer where .buf points to.
+ * Used to easily generate a struct RANAP_GlobalRNC_ID to encode,
+ * see sgsn_ranap_iu_grnc_id_compose(). */
+struct iu_grnc_id {
+       uint8_t plmn_buf[3];
+       struct RANAP_GlobalRNC_ID grnc_id;
+};
+int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct 
osmo_rnc_id *src);
+
 int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum 
ranap_iu_event_type type, void *data);

 int sgsn_ranap_iu_tx(struct msgb *msg, uint8_t sapi);
@@ -40,6 +49,12 @@
                               const struct osmo_sccp_addr *dst_addr,
                               const RANAP_Cause_t *cause);

+void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
+                                    uint32_t conn_id,
+                                    const ranap_message *message);
+void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const 
ranap_message *message);
+
+/* Entry points from rx SCCP: */
 int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups,
                            const struct osmo_scu_unitdata_param *ud_prim,
                            const uint8_t *data, size_t len);
diff --git a/include/osmocom/sgsn/iu_rnc.h b/include/osmocom/sgsn/iu_rnc.h
index 9bdb559..b0e5be3 100644
--- a/include/osmocom/sgsn/iu_rnc.h
+++ b/include/osmocom/sgsn/iu_rnc.h
@@ -4,6 +4,7 @@

 #include <osmocom/core/defs.h>
 #include <osmocom/core/linuxlist.h>
+#include <osmocom/core/fsm.h>
 #include <osmocom/gsm/gsm48.h>
 #include <osmocom/iuh/common.h>
 #include <osmocom/sigtran/sccp_sap.h>
@@ -26,6 +27,7 @@
        struct osmo_rnc_id rnc_id;
        struct sgsn_sccp_user_iups *scu_iups;
        struct osmo_sccp_addr sccp_addr;
+       struct osmo_fsm_inst *fi;

        /* A list of struct iu_lac_rac_entry */
        struct llist_head lac_rac_list;
@@ -34,6 +36,15 @@
 struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
                                           struct sgsn_sccp_user_iups *scu_iups,
                                           const struct osmo_sccp_addr *addr);
+
+struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr 
*rnc_sccp_addr);
+
 void iu_rnc_update_rai_seen(struct ranap_iu_rnc *rnc, const struct 
osmo_routing_area_id *rai);

 void iu_rnc_discard_all_ue_ctx(struct ranap_iu_rnc *rnc);
+
+#define LOG_RNC_CAT(IU_RNC, subsys, loglevel, fmt, args ...) \
+       LOGPFSMSL((IU_RNC)->fi, subsys, loglevel, fmt, ## args)
+
+#define LOG_RNC(IU_RNC, loglevel, fmt, args ...) \
+       LOG_RNC_CAT(IU_RNC, DRANAP, loglevel, fmt, ## args)
diff --git a/include/osmocom/sgsn/iu_rnc_fsm.h 
b/include/osmocom/sgsn/iu_rnc_fsm.h
new file mode 100644
index 0000000..d9be576
--- /dev/null
+++ b/include/osmocom/sgsn/iu_rnc_fsm.h
@@ -0,0 +1,36 @@
+#include <stdint.h>
+
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/ranap/ranap_ies_defs.h>
+
+struct ranap_iu_rnc;
+
+enum iu_rnc_state {
+       IU_RNC_ST_WAIT_RX_RESET = 0,
+       IU_RNC_ST_WAIT_RX_RESET_ACK,
+       IU_RNC_ST_READY,
+       IU_RNC_ST_DISCARDING,
+};
+
+struct iu_rnc_ev_msg_up_co_initial_ctx {
+       struct ranap_iu_rnc *rnc;
+       uint32_t conn_id;
+       ranap_message message;
+};
+
+struct iu_rnc_ev_msg_up_co_ctx {
+       struct ranap_ue_conn_ctx *ue_ctx;
+       ranap_message message;
+};
+
+enum iu_rnc_event {
+       IU_RNC_EV_MSG_UP_CO_INITIAL, /* struct iu_rnc_ev_msg_up_co_initial_ctx* 
*/
+       IU_RNC_EV_MSG_UP_CO, /* struct iu_rnc_ev_msg_up_co_ctx* */
+       IU_RNC_EV_RX_RESET, /* no param */
+       IU_RNC_EV_RX_RESET_ACK, /* no param */
+       IU_RNC_EV_AVAILABLE,
+       IU_RNC_EV_UNAVAILABLE
+};
+
+extern struct osmo_fsm iu_rnc_fsm;
diff --git a/include/osmocom/sgsn/sccp.h b/include/osmocom/sgsn/sccp.h
index 67388a4..0c05f15 100644
--- a/include/osmocom/sgsn/sccp.h
+++ b/include/osmocom/sgsn/sccp.h
@@ -23,6 +23,7 @@
 #include <osmocom/sigtran/sccp_sap.h>

 struct sgsn_instance;
+struct ranap_ue_conn_ctx;

 struct sgsn_sccp_user_iups {
        struct sgsn_instance *sgsn; /* backpointer */
diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h
index 7d323ed..8fc97b0 100644
--- a/include/osmocom/sgsn/sgsn.h
+++ b/include/osmocom/sgsn/sgsn.h
@@ -180,6 +180,7 @@
 #endif /* if BUILD_IU */
 };

+extern struct osmo_tdef sgsn_T_defs[];
 extern struct sgsn_instance *sgsn;
 extern void *tall_sgsn_ctx;

diff --git a/src/sgsn/Makefile.am b/src/sgsn/Makefile.am
index fdad7dc..e64fe94 100644
--- a/src/sgsn/Makefile.am
+++ b/src/sgsn/Makefile.am
@@ -99,6 +99,7 @@
        gprs_ranap.c \
        iu_client.c \
        iu_rnc.c \
+       iu_rnc_fsm.c \
        sccp.c \
        $(NULL)

diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c
index d05cc7b..df9cc9e 100644
--- a/src/sgsn/gprs_ranap.c
+++ b/src/sgsn/gprs_ranap.c
@@ -29,6 +29,7 @@

 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/tdef.h>
+#include <osmocom/gsm/gsm23003.h>
 #include <osmocom/gprs/gprs_msgb.h>

 #include <osmocom/ranap/ranap_common.h>
@@ -49,17 +50,13 @@
 #include <osmocom/sgsn/gtp_ggsn.h>
 #include <osmocom/sgsn/gtp.h>
 #include <osmocom/sgsn/iu_rnc.h>
+#include <osmocom/sgsn/iu_rnc_fsm.h>
 #include <osmocom/sgsn/pdpctx.h>
 #include <osmocom/sgsn/mmctx.h>

 /* Parsed global RNC id. See also struct RANAP_GlobalRNC_ID, and note that the
  * PLMN identity is a BCD representation of the MCC and MNC.
  * See iu_grnc_id_parse(). */
-struct iu_grnc_id {
-       struct osmo_plmn_id plmn;
-       uint16_t rnc_id;
-};
-
 static int iu_grnc_id_parse(struct osmo_rnc_id *dst, const struct 
RANAP_GlobalRNC_ID *src)
 {
        /* The size is coming from arbitrary sender, check it gracefully */
@@ -73,18 +70,15 @@
        return 0;
 }

-#if 0
 /* not used at present */
-static int iu_grnc_id_compose(struct iu_grnc_id *src, struct 
RANAP_GlobalRNC_ID *dst)
+int sgsn_ranap_iu_grnc_id_compose(struct iu_grnc_id *dst, const struct 
osmo_rnc_id *src)
 {
-       /* The caller must ensure proper size */
-       OSMO_ASSERT(dst->pLMNidentity.size == 3);
-       gsm48_mcc_mnc_to_bcd(&dst->pLMNidentity.buf[0],
-                            src->mcc, src->mnc);
-       dst->rNC_ID = src->rnc_id;
+       dst->grnc_id.pLMNidentity.buf = &dst->plmn_buf[0];
+       dst->grnc_id.pLMNidentity.size = 3;
+       osmo_plmn_to_bcd(dst->grnc_id.pLMNidentity.buf, &src->plmn);
+       dst->grnc_id.rNC_ID = src->rnc_id;
        return 0;
 }
-#endif

 /* Callback for RAB assignment response */
 static int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, 
RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies)
@@ -385,8 +379,7 @@
        osmo_timer_schedule(&ctx->release_timeout, timeout, 0);
 }

-static int ranap_handle_co_initial_ue(struct sgsn_sccp_user_iups *scu_iups,
-                                     const struct osmo_sccp_addr 
*rem_sccp_addr,
+static int ranap_handle_co_initial_ue(struct ranap_iu_rnc *rnc,
                                      uint32_t conn_id,
                                      const RANAP_InitialUE_MessageIEs_t *ies)
 {
@@ -396,7 +389,6 @@
        uint16_t sai;
        struct ranap_ue_conn_ctx *ue;
        struct msgb *msg = msgb_alloc(256, "RANAP->NAS");
-       struct ranap_iu_rnc *rnc;

        if (ranap_parse_lai(&ra_id, &ies->lai) != 0) {
                LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n");
@@ -427,9 +419,7 @@

        gprs_rai_to_osmo(&ra_id2, &ra_id);

-       /* Make sure we know the RNC Id and LAC+RAC coming in on this 
connection. */
-       rnc = iu_rnc_find_or_create(&rnc_id, scu_iups, rem_sccp_addr);
-       OSMO_ASSERT(rnc);
+       /* Make sure we update LAC+RAC coming in on this connection. */
        iu_rnc_update_rai_seen(rnc, &ra_id2);

        ue = ue_conn_ctx_alloc(rnc, conn_id);
@@ -445,10 +435,9 @@
        return 0;
 }

-static void cn_ranap_handle_co_initial(struct sgsn_sccp_user_iups *scu_iups,
-                                      const struct osmo_sccp_addr 
*rem_sccp_addr,
-                                      uint32_t conn_id,
-                                      const ranap_message *message)
+void sgsn_ranap_iu_handle_co_initial(struct ranap_iu_rnc *iu_rnc,
+                                    uint32_t conn_id,
+                                    const ranap_message *message)
 {
        int rc;

@@ -461,7 +450,7 @@
                     message->direction, message->procedureCode);
                rc = -1;
        } else
-               rc = ranap_handle_co_initial_ue(scu_iups, rem_sccp_addr, 
conn_id, &message->msg.initialUE_MessageIEs);
+               rc = ranap_handle_co_initial_ue(iu_rnc, conn_id, 
&message->msg.initialUE_MessageIEs);

        if (rc) {
                LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc);
@@ -474,20 +463,37 @@
                                    uint32_t conn_id,
                                    const uint8_t *data, size_t len)
 {
-       ranap_message message;
+       struct iu_rnc_ev_msg_up_co_initial_ctx ev_ctx = {
+               .conn_id = conn_id,
+       };
+       RANAP_Cause_t cause;
        int rc;

-       rc = ranap_cn_rx_co_decode2(&message, data, len);
+       rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
        if (rc != 0) {
                LOGP(DRANAP, LOGL_ERROR, "Not calling 
cn_ranap_handle_co_initial() due to rc=%d\n", rc);
                goto free_ret;
        }

-       cn_ranap_handle_co_initial(scu_iups, rem_sccp_addr, conn_id, &message);
+       ev_ctx.rnc = iu_rnc_find_by_addr(rem_sccp_addr);
+       if (!ev_ctx.rnc)
+               goto tx_err_ind;

+       rc = osmo_fsm_inst_dispatch(ev_ctx.rnc->fi, 
IU_RNC_EV_MSG_UP_CO_INITIAL, &ev_ctx);
+       if (rc != 0)
+               goto tx_err_ind;
+
+       goto free_ret;
+
+tx_err_ind:
+       cause = (RANAP_Cause_t){
+               .present = RANAP_Cause_PR_protocol,
+               .choice.protocol = 
RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
+       };
+       sgsn_ranap_iu_tx_error_ind(scu_iups, rem_sccp_addr, &cause);
 free_ret:
        /* Free the asn1 structs in message */
-       ranap_cn_rx_co_free(&message);
+       ranap_cn_rx_co_free(&ev_ctx.message);
        return rc;
 }

@@ -569,7 +575,7 @@
 }

 /* Entry point for connection-oriented RANAP message */
-static void cn_ranap_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const 
ranap_message *message)
+void sgsn_ranap_iu_handle_co(struct ranap_ue_conn_ctx *ue_ctx, const 
ranap_message *message)
 {
        int rc;

@@ -649,20 +655,34 @@

 int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, const uint8_t 
*data, size_t len)
 {
-       ranap_message message;
+       struct iu_rnc_ev_msg_up_co_ctx ev_ctx = {
+               .ue_ctx = ue_ctx,
+       };
+       RANAP_Cause_t cause;
        int rc;

-       rc = ranap_cn_rx_co_decode2(&message, data, len);
+       rc = ranap_cn_rx_co_decode2(&ev_ctx.message, data, len);
        if (rc != 0) {
                LOGP(DRANAP, LOGL_ERROR, "Not calling cn_ranap_handle_co() due 
to rc=%d\n", rc);
                goto free_ret;
        }

-       cn_ranap_handle_co(ue_ctx, &message);
+       rc = osmo_fsm_inst_dispatch(ue_ctx->rnc->fi, IU_RNC_EV_MSG_UP_CO, 
&ev_ctx);
+       if (rc != 0)
+               goto tx_err_ind;
+
+       goto free_ret;
+
+tx_err_ind:
+       cause = (RANAP_Cause_t){
+               .present = RANAP_Cause_PR_protocol,
+               .choice.protocol = 
RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
+       };
+       sgsn_ranap_iu_tx_error_ind(ue_ctx->rnc->scu_iups, 
&ue_ctx->rnc->sccp_addr, &cause);

 free_ret:
        /* Free the asn1 structs in message */
-       ranap_cn_rx_co_free(&message);
+       ranap_cn_rx_co_free(&ev_ctx.message);
        return rc;
 }

@@ -674,7 +694,7 @@
        RANAP_Cause_t cause;
        struct osmo_rnc_id rnc_id = {};
        struct ranap_iu_rnc *rnc;
-       struct msgb *resp;
+       int rc;

        if (ies->presenceMask & 
ERRORINDICATIONIES_RANAP_CN_DOMAININDICATOR_PRESENT) {
                if (ies->cN_DomainIndicator != 
RANAP_CN_DomainIndicator_ps_domain) {
@@ -701,7 +721,7 @@
        }
        grnc_id = &ies->globalRNC_ID;

-       if (iu_grnc_id_parse(&rnc_id, &ies->globalRNC_ID) != 0) {
+       if (iu_grnc_id_parse(&rnc_id, grnc_id) != 0) {
                LOGP(DRANAP, LOGL_ERROR,
                     "Rx RESET: Failed to parse RANAP Global-RNC-ID IE\n");
                cause = (RANAP_Cause_t){
@@ -713,12 +733,41 @@

        rnc = iu_rnc_find_or_create(&rnc_id, scu_iups, &ud_prim->calling_addr);
        OSMO_ASSERT(rnc);
+       rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET, NULL);
+       if (rc != 0) {
+               cause = (RANAP_Cause_t){
+                       .present = RANAP_Cause_PR_protocol,
+                       .choice.protocol = 
RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
+               };
+               return sgsn_ranap_iu_tx_error_ind(scu_iups, 
&ud_prim->calling_addr, &cause);
+       }
+       return 0;
+}
 
-       /* send reset response */
-       resp = ranap_new_msg_reset_ack(ies->cN_DomainIndicator, grnc_id);
-       if (!resp)
-               return -ENOMEM;
-       return sgsn_ranap_iu_tx_cl(scu_iups, &ud_prim->calling_addr, resp);
+static int ranap_handle_cl_reset_ack(struct sgsn_sccp_user_iups *scu_iups,
+                                    const struct osmo_scu_unitdata_param 
*ud_prim,
+                                    const RANAP_ResetAcknowledgeIEs_t *ies)
+{
+       struct ranap_iu_rnc *rnc;
+       RANAP_Cause_t cause;
+       int rc;
+
+       rnc = iu_rnc_find_by_addr(&ud_prim->calling_addr);
+       if (!rnc)
+               goto tx_err_ind;
+
+       rc = osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_RX_RESET_ACK, NULL);
+       if (rc != 0)
+               goto tx_err_ind;
+
+       return 0;
+
+tx_err_ind:
+       cause = (RANAP_Cause_t){
+               .present = RANAP_Cause_PR_protocol,
+               .choice.protocol = 
RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
+       };
+       return sgsn_ranap_iu_tx_error_ind(scu_iups, &ud_prim->calling_addr, 
&cause);
 }

 static int ranap_handle_cl_err_ind(struct sgsn_sccp_user_iups *scu_iups,
@@ -757,6 +806,15 @@
                }
                break;
        case RANAP_RANAP_PDU_PR_successfulOutcome:
+               switch (message->procedureCode) {
+               case RANAP_ProcedureCode_id_Reset:
+                       rc = ranap_handle_cl_reset_ack(scu_iups, ud_prim, 
&message->msg.resetAcknowledgeIEs);
+                       break;
+               default:
+                       rc = -1;
+                       break;
+               }
+               break;
        case RANAP_RANAP_PDU_PR_unsuccessfulOutcome:
        case RANAP_RANAP_PDU_PR_outcome:
        default:
diff --git a/src/sgsn/iu_rnc.c b/src/sgsn/iu_rnc.c
index 158381c..abe6f61 100644
--- a/src/sgsn/iu_rnc.c
+++ b/src/sgsn/iu_rnc.c
@@ -40,25 +40,45 @@
 #include <osmocom/sgsn/gprs_ranap.h>
 #include <osmocom/sgsn/iu_client.h>
 #include <osmocom/sgsn/iu_rnc.h>
+#include <osmocom/sgsn/iu_rnc_fsm.h>
 #include <osmocom/sgsn/sccp.h>
 #include <osmocom/sgsn/sgsn.h>

 static struct ranap_iu_rnc *iu_rnc_alloc(const struct osmo_rnc_id *rnc_id,
                                         struct sgsn_sccp_user_iups *scu_iups,
-                                        const struct osmo_sccp_addr *addr)
+                                        const struct osmo_sccp_addr 
*rnc_sccp_addr)
 {
-       struct ranap_iu_rnc *rnc = talloc_zero(sgsn, struct ranap_iu_rnc);
+       struct ranap_iu_rnc *rnc;
+       char *addr_str, *pos;
+
+       rnc = talloc_zero(sgsn, struct ranap_iu_rnc);
        OSMO_ASSERT(rnc);

        INIT_LLIST_HEAD(&rnc->lac_rac_list);

        rnc->rnc_id = *rnc_id;
        rnc->scu_iups = scu_iups;
-       rnc->sccp_addr = *addr;
+       rnc->sccp_addr = *rnc_sccp_addr;
+
+       rnc->fi = osmo_fsm_inst_alloc(&iu_rnc_fsm, rnc, rnc, LOGL_INFO, NULL);
+       OSMO_ASSERT(rnc->fi);
+
+       /* Unfortunately, osmo_sccp_inst_addr_name() returns 
"RI=SSN_PC,PC=0.24.1,SSN=BSSAP" but neither commas nor
+        * full-stops are allowed as FSM inst id. Make it 
"RI-SSN_PC:PC-0-24-1:SSN-BSSAP". */
+       addr_str = osmo_sccp_addr_dump(rnc_sccp_addr);
+       for (pos = addr_str; *pos; pos++) {
+               if (*pos == ',')
+                       *pos = ':';
+               else if (*pos == '.' || *pos == '=')
+                       *pos = '-';
+       }
+       osmo_fsm_inst_update_id_f(rnc->fi, "RNC_ID-%s:%s",
+                                 osmo_rnc_id_name(rnc_id), addr_str);
+
        llist_add(&rnc->entry, &sgsn->rnc_list);

        LOGP(DRANAP, LOGL_NOTICE, "New RNC %s at %s\n",
-            osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(addr));
+            osmo_rnc_id_name(&rnc->rnc_id), 
osmo_sccp_addr_dump(rnc_sccp_addr));

        return rnc;
 }
@@ -73,6 +93,17 @@
        return NULL;
 }

+struct ranap_iu_rnc *iu_rnc_find_by_addr(const struct osmo_sccp_addr 
*rnc_sccp_addr)
+{
+       struct ranap_iu_rnc *rnc;
+       llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
+               if (osmo_sccp_addr_ri_cmp(rnc_sccp_addr, &rnc->sccp_addr))
+                       continue;
+               return rnc;
+       }
+       return NULL;
+}
+
 struct ranap_iu_rnc *iu_rnc_find_or_create(const struct osmo_rnc_id *rnc_id,
                                           struct sgsn_sccp_user_iups *scu_iups,
                                           const struct osmo_sccp_addr *addr)
diff --git a/src/sgsn/iu_rnc_fsm.c b/src/sgsn/iu_rnc_fsm.c
new file mode 100644
index 0000000..4085586
--- /dev/null
+++ b/src/sgsn/iu_rnc_fsm.c
@@ -0,0 +1,349 @@
+/* A remote RNC (Radio Network Controller) FSM */
+
+/* (C) 2025 by sysmocom s.f.m.c. GmbH <i...@sysmocom.de>
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <osmocom/core/logging.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/tdef.h>
+
+#include <osmocom/sigtran/sccp_helpers.h>
+
+#include <osmocom/sgsn/debug.h>
+#include <osmocom/sgsn/gprs_ranap.h>
+#include <osmocom/sgsn/iu_rnc_fsm.h>
+#include <osmocom/sgsn/iu_rnc.h>
+#include <osmocom/sgsn/sgsn.h>
+
+#define S(x)   (1 << (x))
+
+struct osmo_fsm iu_rnc_fsm;
+
+
+static const struct osmo_tdef_state_timeout iu_rnc_fsm_timeouts[32] = {
+       [IU_RNC_ST_WAIT_RX_RESET_ACK] = { .T = -1002 },
+       [IU_RNC_ST_DISCARDING] = { .T = -1002 },
+};
+
+#define iu_rnc_state_chg(iu_rnc, next_st) \
+       osmo_tdef_fsm_inst_state_chg((iu_rnc)->fi, next_st, 
iu_rnc_fsm_timeouts, sgsn_T_defs, 5)
+
+static const struct value_string iu_rnc_fsm_event_names[] = {
+       OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO_INITIAL),
+       OSMO_VALUE_STRING(IU_RNC_EV_MSG_UP_CO),
+       OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET),
+       OSMO_VALUE_STRING(IU_RNC_EV_RX_RESET_ACK),
+       OSMO_VALUE_STRING(IU_RNC_EV_AVAILABLE),
+       OSMO_VALUE_STRING(IU_RNC_EV_UNAVAILABLE),
+       {}
+};
+
+/* Drop all SCCP connections for this iu_rnc, respond with RESET ACKNOWLEDGE 
and move to READY state. */
+static void iu_rnc_rx_reset(struct ranap_iu_rnc *rnc)
+{
+       struct msgb *reset_ack;
+       struct iu_grnc_id grnc_id;
+       sgsn_ranap_iu_grnc_id_compose(&grnc_id, &rnc->rnc_id);
+
+       iu_rnc_discard_all_ue_ctx(rnc);
+
+       reset_ack = ranap_new_msg_reset_ack(RANAP_CN_DomainIndicator_ps_domain, 
&grnc_id.grnc_id);
+       if (!reset_ack) {
+               LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET ACKNOWLEDGE 
message\n");
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+       }
+       if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset_ack) < 0) 
{
+               LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET ACKNOWLEDGE 
message\n");
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+       }
+
+       LOG_RNC(rnc, LOGL_INFO, "Sent RESET ACKNOWLEDGE\n");
+       iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
+}
+
+static void iu_rnc_reset(struct ranap_iu_rnc *rnc)
+{
+       struct msgb *reset;
+       const RANAP_Cause_t cause = {
+               .present = RANAP_Cause_PR_protocol,
+               .choice = {
+                       .protocol = 
RANAP_CauseProtocol_message_not_compatible_with_receiver_state,
+               },
+       };
+
+       iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET_ACK);
+       iu_rnc_discard_all_ue_ctx(rnc);
+
+       reset = ranap_new_msg_reset(RANAP_CN_DomainIndicator_ps_domain, &cause);
+       if (!reset) {
+               LOG_RNC(rnc, LOGL_ERROR, "Failed to compose RESET message\n");
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+       }
+
+       if (sgsn_ranap_iu_tx_cl(rnc->scu_iups, &rnc->sccp_addr, reset) < 0) {
+               LOG_RNC(rnc, LOGL_ERROR, "Failed to send RESET message\n");
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+       }
+}
+
+static void iu_rnc_st_wait_rx_reset(struct osmo_fsm_inst *fi, uint32_t event, 
void *data)
+{
+       struct ranap_iu_rnc *rnc = fi->priv;
+       switch (event) {
+
+       case IU_RNC_EV_MSG_UP_CO:
+       case IU_RNC_EV_MSG_UP_CO_INITIAL:
+               OSMO_ASSERT(data);
+
+#define LEGACY_BEHAVIOR
+#ifdef LEGACY_BEHAVIOR
+               LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that 
has not done a proper RESET yet."
+                       " Accepting RAN peer implicitly (legacy compat)\n");
+               iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
+               osmo_fsm_inst_dispatch(rnc->fi, event, data);
+               return;
+#else
+               LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that 
has not done a proper RESET yet."
+                            " Disconnecting on incoming message, sending RESET 
to RAN peer.\n");
+               /* No valid RESET procedure has happened here yet. Usually, 
we're expecting the RAN peer (BSC,
+                * RNC) to first send a RESET message before sending Connection 
Oriented messages. So if we're
+                * getting a CO message, likely we've just restarted or 
something. Send a RESET to the peer. */
+
+               /* Make sure the MS / UE properly disconnects. */
+               clear_and_disconnect(rnc, ctx->conn_id);
+
+               iu_rnc_reset(rnc);
+               return;
+#endif
+
+       case IU_RNC_EV_RX_RESET:
+               iu_rnc_rx_reset(rnc);
+               return;
+
+       case IU_RNC_EV_AVAILABLE:
+               /* Send a RESET to the peer. */
+               iu_rnc_reset(rnc);
+               return;
+
+       case IU_RNC_EV_UNAVAILABLE:
+               /* Do nothing, wait for peer to come up again. */
+               return;
+
+       default:
+               LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", 
osmo_fsm_event_name(&iu_rnc_fsm, event));
+               return;
+       }
+}
+
+static void iu_rnc_st_wait_rx_reset_ack(struct osmo_fsm_inst *fi, uint32_t 
event, void *data)
+{
+       struct ranap_iu_rnc *rnc = fi->priv;
+       struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
+       struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
+
+       switch (event) {
+
+       case IU_RNC_EV_RX_RESET_ACK:
+               iu_rnc_state_chg(rnc, IU_RNC_ST_READY);
+               return;
+
+
+       case IU_RNC_EV_MSG_UP_CO_INITIAL:
+               ev_msg_up_co_initial_ctx = data;
+               OSMO_ASSERT(ev_msg_up_co_initial_ctx);
+               LOG_RNC(rnc, LOGL_ERROR, "Receiving CO Initial message on RAN 
peer that has not done a proper RESET yet."
+                            " Disconnecting on incoming message, sending RESET 
to RAN peer.\n");
+               
osmo_sccp_tx_disconn(ev_msg_up_co_initial_ctx->rnc->scu_iups->scu,
+                                    ev_msg_up_co_initial_ctx->conn_id, NULL, 
0);
+               /* No valid RESET procedure has happened here yet. */
+               iu_rnc_reset(rnc);
+               return;
+               return;
+       case IU_RNC_EV_MSG_UP_CO:
+               ev_msg_up_co_ctx = data;
+               OSMO_ASSERT(ev_msg_up_co_ctx);
+               LOG_RNC(rnc, LOGL_ERROR, "Receiving CO message on RAN peer that 
has not done a proper RESET yet."
+                            " Disconnecting on incoming message, sending RESET 
to RAN peer.\n");
+               ue_conn_ctx_link_invalidated_free(ev_msg_up_co_ctx->ue_ctx);
+               /* No valid RESET procedure has happened here yet. */
+               iu_rnc_reset(rnc);
+               return;
+
+       case IU_RNC_EV_RX_RESET:
+               iu_rnc_rx_reset(rnc);
+               return;
+
+       case IU_RNC_EV_AVAILABLE:
+               /* Send a RESET to the peer. */
+               iu_rnc_reset(rnc);
+               return;
+
+       case IU_RNC_EV_UNAVAILABLE:
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+
+       default:
+               LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", 
osmo_fsm_event_name(&iu_rnc_fsm, event));
+               return;
+       }
+}
+
+static void iu_rnc_st_ready(struct osmo_fsm_inst *fi, uint32_t event, void 
*data)
+{
+       struct ranap_iu_rnc *rnc = fi->priv;
+       struct iu_rnc_ev_msg_up_co_initial_ctx *ev_msg_up_co_initial_ctx;
+       struct iu_rnc_ev_msg_up_co_ctx *ev_msg_up_co_ctx;
+
+       switch (event) {
+
+       case IU_RNC_EV_MSG_UP_CO_INITIAL:
+               ev_msg_up_co_initial_ctx = data;
+               OSMO_ASSERT(ev_msg_up_co_initial_ctx);
+               OSMO_ASSERT(ev_msg_up_co_initial_ctx->rnc);
+
+               sgsn_ranap_iu_handle_co_initial(ev_msg_up_co_initial_ctx->rnc,
+                                                     
ev_msg_up_co_initial_ctx->conn_id,
+                                                     
&ev_msg_up_co_initial_ctx->message);
+               return;
+
+       case IU_RNC_EV_MSG_UP_CO:
+               ev_msg_up_co_ctx = data;
+               OSMO_ASSERT(ev_msg_up_co_ctx);
+               OSMO_ASSERT(ev_msg_up_co_ctx->ue_ctx);
+
+               sgsn_ranap_iu_handle_co(ev_msg_up_co_ctx->ue_ctx, 
&ev_msg_up_co_ctx->message);
+               return;
+
+       case IU_RNC_EV_RX_RESET:
+               iu_rnc_rx_reset(rnc);
+               return;
+
+       case IU_RNC_EV_AVAILABLE:
+               /* Do nothing, we were already up. */
+               return;
+
+       case IU_RNC_EV_UNAVAILABLE:
+               iu_rnc_discard_all_ue_ctx(rnc);
+               iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+               return;
+
+       default:
+               LOG_RNC(rnc, LOGL_ERROR, "Unhandled event: %s\n", 
osmo_fsm_event_name(&iu_rnc_fsm, event));
+               return;
+       }
+}
+
+static int iu_rnc_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+       struct ranap_iu_rnc *rnc = fi->priv;
+       iu_rnc_state_chg(rnc, IU_RNC_ST_WAIT_RX_RESET);
+       return 0;
+}
+
+static void iu_rnc_fsm_cleanup(struct osmo_fsm_inst *fi, enum 
osmo_fsm_term_cause cause)
+{
+       struct ranap_iu_rnc *rnc = fi->priv;
+
+       iu_rnc_discard_all_ue_ctx(rnc);
+}
+
+static const struct osmo_fsm_state iu_rnc_fsm_states[] = {
+       [IU_RNC_ST_WAIT_RX_RESET] = {
+               .name = "WAIT_RX_RESET",
+               .action = iu_rnc_st_wait_rx_reset,
+               .in_event_mask = 0
+                       | S(IU_RNC_EV_RX_RESET)
+                       | S(IU_RNC_EV_MSG_UP_CO_INITIAL)
+                       | S(IU_RNC_EV_MSG_UP_CO)
+                       | S(IU_RNC_EV_AVAILABLE)
+                       | S(IU_RNC_EV_UNAVAILABLE)
+                       ,
+               .out_state_mask = 0
+                       | S(IU_RNC_ST_WAIT_RX_RESET)
+                       | S(IU_RNC_ST_WAIT_RX_RESET_ACK)
+                       | S(IU_RNC_ST_READY)
+                       | S(IU_RNC_ST_DISCARDING)
+                       ,
+       },
+       [IU_RNC_ST_WAIT_RX_RESET_ACK] = {
+               .name = "WAIT_RX_RESET_ACK",
+               .action = iu_rnc_st_wait_rx_reset_ack,
+               .in_event_mask = 0
+                       | S(IU_RNC_EV_RX_RESET)
+                       | S(IU_RNC_EV_RX_RESET_ACK)
+                       | S(IU_RNC_EV_MSG_UP_CO_INITIAL)
+                       | S(IU_RNC_EV_MSG_UP_CO)
+                       | S(IU_RNC_EV_AVAILABLE)
+                       | S(IU_RNC_EV_UNAVAILABLE)
+                       ,
+               .out_state_mask = 0
+                       | S(IU_RNC_ST_WAIT_RX_RESET)
+                       | S(IU_RNC_ST_WAIT_RX_RESET_ACK)
+                       | S(IU_RNC_ST_READY)
+                       | S(IU_RNC_ST_DISCARDING)
+                       ,
+       },
+       [IU_RNC_ST_READY] = {
+               .name = "READY",
+               .action = iu_rnc_st_ready,
+               .in_event_mask = 0
+                       | S(IU_RNC_EV_RX_RESET)
+                       | S(IU_RNC_EV_MSG_UP_CO_INITIAL)
+                       | S(IU_RNC_EV_MSG_UP_CO)
+                       | S(IU_RNC_EV_AVAILABLE)
+                       | S(IU_RNC_EV_UNAVAILABLE)
+                       ,
+               .out_state_mask = 0
+                       | S(IU_RNC_ST_WAIT_RX_RESET)
+                       | S(IU_RNC_ST_WAIT_RX_RESET_ACK)
+                       | S(IU_RNC_ST_READY)
+                       | S(IU_RNC_ST_DISCARDING)
+                       ,
+       },
+       [IU_RNC_ST_DISCARDING] = {
+               .name = "DISCARDING",
+       },
+};
+
+struct osmo_fsm iu_rnc_fsm = {
+       .name = "iu_rnc",
+       .states = iu_rnc_fsm_states,
+       .num_states = ARRAY_SIZE(iu_rnc_fsm_states),
+       .log_subsys = DRANAP,
+       .event_names = iu_rnc_fsm_event_names,
+       .timer_cb = iu_rnc_fsm_timer_cb,
+       .cleanup = iu_rnc_fsm_cleanup,
+};
+
+static __attribute__((constructor)) void iu_rnc_init(void)
+{
+       OSMO_ASSERT(osmo_fsm_register(&iu_rnc_fsm) == 0);
+}
diff --git a/src/sgsn/sccp.c b/src/sgsn/sccp.c
index 8b57dd4..83aa19f 100644
--- a/src/sgsn/sccp.c
+++ b/src/sgsn/sccp.c
@@ -33,6 +33,7 @@
 #include <osmocom/sgsn/debug.h>
 #include <osmocom/sgsn/iu_client.h>
 #include <osmocom/sgsn/iu_rnc.h>
+#include <osmocom/sgsn/iu_rnc_fsm.h>
 #include <osmocom/sgsn/gprs_ranap.h>
 #include <osmocom/sgsn/sccp.h>
 #include <osmocom/sgsn/sgsn.h>
@@ -147,10 +148,21 @@
 {
        struct ranap_iu_rnc *rnc;

-       LOGP(DSUA, LOGL_DEBUG, "(calling_addr=%s) N-NOTICE.ind cause=%u='%s' 
importance=%u\n",
-            osmo_sccp_addr_dump(&ni->calling_addr),
-            ni->cause, osmo_sccp_return_cause_name(ni->cause),
-            ni->importance);
+       rnc = iu_rnc_find_by_addr(&ni->calling_addr);
+
+       if (!rnc) {
+               LOGP(DSUA, LOGL_DEBUG,
+                    "(calling_addr=%s) N-NOTICE.ind cause=%u='%s' 
importance=%u didn't match any RNC, ignoring\n",
+                    osmo_sccp_addr_dump(&ni->calling_addr),
+                    ni->cause, osmo_sccp_return_cause_name(ni->cause),
+                    ni->importance);
+               return;
+       }
+
+       LOG_RNC(rnc, LOGL_NOTICE,
+               "N-NOTICE.ind cause=%u='%s' importance=%u\n",
+               ni->cause, osmo_sccp_return_cause_name(ni->cause),
+               ni->importance);

        switch (ni->cause) {
        case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION:
@@ -161,19 +173,8 @@
                break;
        }

-       /* Messages are not arriving to RNC. Signal to user that all related 
ue_ctx are invalid. */
-       llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
-               if (osmo_sccp_addr_ri_cmp(&rnc->sccp_addr, &ni->calling_addr))
-                       continue;
-               LOGP(DSUA, LOGL_NOTICE,
-                    "RNC %s now unreachable: N-NOTICE.ind cause=%u='%s' 
importance=%u\n",
-                    osmo_rnc_id_name(&rnc->rnc_id),
-                    ni->cause, osmo_sccp_return_cause_name(ni->cause),
-                    ni->importance);
-               iu_rnc_discard_all_ue_ctx(rnc);
-               /* TODO: ideally we'd have some event to submit to upper
-                * layer to inform about peer availability change... */
-       }
+       /* Messages are not arriving to rnc. Signal it is unavailable to update 
local state. */
+       osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
 }

 static void handle_pcstate_ind(struct sgsn_sccp_user_iups *scu_iups, const 
struct osmo_scu_pcstate_param *pcst)
@@ -191,6 +192,13 @@

        osmo_sccp_make_addr_pc_ssn(&rem_addr, pcst->affected_pc, 
OSMO_SCCP_SSN_RANAP);

+       rnc = iu_rnc_find_by_addr(&rem_addr);
+       if (!rnc) {
+               LOGP(DSUA, LOGL_DEBUG, "No RNC found under pc=%u=s%s\n",
+                    pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 
pcst->affected_pc));
+               return;
+       }
+
        /* See if this marks the point code to have become available, or to 
have been lost.
         *
         * I want to detect two events:
@@ -245,38 +253,19 @@
        }

        if (disconnected) {
-               /* A previously usable RNC has disconnected. Signal to user 
that all related ue_ctx are invalid. */
-               llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
-                       struct ranap_ue_conn_ctx *ue_ctx, *ue_ctx_tmp;
-                       if (osmo_sccp_addr_cmp(&rnc->sccp_addr, &rem_addr, 
OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
-                               continue;
-                       LOGP(DSUA, LOGL_NOTICE,
-                            "RNC %s now unreachable: N-PCSTATE ind: pc=%u=%s 
sp_status=%s remote_sccp_status=%s\n",
-                            osmo_rnc_id_name(&rnc->rnc_id),
-                            pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 
pcst->affected_pc),
-                            osmo_sccp_sp_status_name(pcst->sp_status),
-                            
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
-                       llist_for_each_entry_safe(ue_ctx, ue_ctx_tmp, 
&scu_iups->ue_conn_ctx_list, list) {
-                               if (ue_ctx->rnc != rnc)
-                                       continue;
-                               ue_conn_ctx_link_invalidated_free(ue_ctx);
-                       }
-                       /* TODO: ideally we'd have some event to submit to upper
-                        * layer to inform about peer availability change... */
-               }
+               LOG_RNC(rnc, LOGL_NOTICE,
+                       "now unreachable: N-PCSTATE ind: pc=%u=%s sp_status=%s 
remote_sccp_status=%s\n",
+                       pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 
pcst->affected_pc),
+                       osmo_sccp_sp_status_name(pcst->sp_status),
+                       
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+               osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_UNAVAILABLE, NULL);
        } else if (connected) {
-               llist_for_each_entry(rnc, &sgsn->rnc_list, entry) {
-                       if (osmo_sccp_addr_cmp(&rnc->sccp_addr, &rem_addr, 
OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC))
-                               continue;
-                       LOGP(DSUA, LOGL_NOTICE,
-                            "RNC %s now available: N-PCSTATE ind: pc=%u=%s 
sp_status=%s remote_sccp_status=%s\n",
-                            osmo_rnc_id_name(&rnc->rnc_id),
-                            pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 
pcst->affected_pc),
-                            osmo_sccp_sp_status_name(pcst->sp_status),
-                            
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
-                       /* TODO: ideally we'd have some event to submit to upper
-                        * layer to inform about peer availability change... */
-               }
+               LOG_RNC(rnc, LOGL_NOTICE,
+                       "now available: N-PCSTATE ind: pc=%u=%s sp_status=%s 
remote_sccp_status=%s\n",
+                       pcst->affected_pc, osmo_ss7_pointcode_print(cs7, 
pcst->affected_pc),
+                       osmo_sccp_sp_status_name(pcst->sp_status),
+                       
osmo_sccp_rem_sccp_status_name(pcst->remote_sccp_status));
+               osmo_fsm_inst_dispatch(rnc->fi, IU_RNC_EV_AVAILABLE, NULL);
        }
 }
 
diff --git a/src/sgsn/sgsn.c b/src/sgsn/sgsn.c
index c78997e..1b02f76 100644
--- a/src/sgsn/sgsn.c
+++ b/src/sgsn/sgsn.c
@@ -61,6 +61,7 @@
 #include <osmocom/sgsn/pdpctx.h>
 #include <osmocom/sgsn/gprs_routing_area.h>
 #if BUILD_IU
+#include <osmocom/sgsn/iu_rnc_fsm.h>
 #include <osmocom/sgsn/sccp.h>
 #endif /* #if BUILD_IU */

@@ -68,8 +69,6 @@

 #define GPRS_LLME_CHECK_TICK 30

-extern struct osmo_tdef sgsn_T_defs[];
-
 static const struct rate_ctr_desc sgsn_ctr_description[] = {
        { "llc:dl_bytes", "Count sent LLC bytes before giving it to the bssgp 
layer" },
        { "llc:ul_bytes", "Count successful received LLC bytes (encrypt & fcs 
correct)" },
diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c
index 9721ee1..7761afc 100644
--- a/src/sgsn/sgsn_vty.c
+++ b/src/sgsn/sgsn_vty.c
@@ -98,7 +98,7 @@

 /* Non spec timer */
 #define NONSPEC_X1001_SECS     5       /* wait for a RANAP Release Complete */
-
+#define RANAP_TRafR_SECS       5       /* wait for a RANAP Release Complete */

 struct osmo_tdef sgsn_T_defs[] = {
        { .T=3312, .default_val=GSM0408_T3312_SECS, .desc="Periodic RA Update 
timer (s)" },
@@ -114,8 +114,11 @@
        { .T=3395, .default_val=GSM0408_T3395_SECS, .desc="Wait for DEACT PDP 
CTX ACK timer (s)" },
        { .T=3397, .default_val=GSM0408_T3397_SECS, .desc="Wait for DEACT AA 
PDP CTX ACK timer (s)" },
        /* non spec timers */
+#if BUILD_IU
        { .T=-1001, .default_val=NONSPEC_X1001_SECS, .desc="RANAP Release 
timeout. Wait for RANAP Release Complete."
                                                           "On expiry release 
Iu connection (s)" },
+       { .T=-1002, .default_val=RANAP_TRafR_SECS, .desc="TRafR, Maximum time 
for Reset procedure in the CN (s)" },
+#endif /* #if BUILD_IU */
        {}
 };

diff --git a/tests/gprs_routing_area/Makefile.am 
b/tests/gprs_routing_area/Makefile.am
index ea2779a..380d946 100644
--- a/tests/gprs_routing_area/Makefile.am
+++ b/tests/gprs_routing_area/Makefile.am
@@ -89,6 +89,7 @@
        $(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
        $(top_builddir)/src/sgsn/iu_client.o \
        $(top_builddir)/src/sgsn/iu_rnc.o \
+       $(top_builddir)/src/sgsn/iu_rnc_fsm.o \
        $(top_builddir)/src/sgsn/sccp.o \
        $(LIBOSMORANAP_LIBS) \
        $(LIBOSMOSIGTRAN_LIBS) \
diff --git a/tests/osmo-sgsn_test-nodes.vty b/tests/osmo-sgsn_test-nodes.vty
index 32ff8e7..3b914c8 100644
--- a/tests/osmo-sgsn_test-nodes.vty
+++ b/tests/osmo-sgsn_test-nodes.vty
@@ -13,6 +13,7 @@
 T3395 = 8 s    Wait for DEACT PDP CTX ACK timer (s) (default: 8 s)
 T3397 = 8 s    Wait for DEACT AA PDP CTX ACK timer (s) (default: 8 s)
 X1001 = 5 s    RANAP Release timeout. Wait for RANAP Release Complete.On 
expiry release Iu connection (s) (default: 5 s)
+X1002 = 5 s    TRafR, Maximum time for Reset procedure in the CN (s) (default: 
5 s)
 OsmoSGSN# configure terminal
 OsmoSGSN(config)# list
 ...
diff --git a/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am
index 8bf78e7..77c1dd7 100644
--- a/tests/sgsn/Makefile.am
+++ b/tests/sgsn/Makefile.am
@@ -103,6 +103,7 @@
        $(top_builddir)/src/sgsn/gprs_mm_state_iu_fsm.o \
        $(top_builddir)/src/sgsn/iu_client.o \
        $(top_builddir)/src/sgsn/iu_rnc.o \
+       $(top_builddir)/src/sgsn/iu_rnc_fsm.o \
        $(top_builddir)/src/sgsn/sccp.o \
        $(LIBOSMORANAP_LIBS) \
        $(LIBOSMOSIGTRAN_LIBS) \

--
To view, visit https://gerrit.osmocom.org/c/osmo-sgsn/+/40988?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: osmo-sgsn
Gerrit-Branch: master
Gerrit-Change-Id: I18b7803500163e78ff6a684095194174b0fb6ee1
Gerrit-Change-Number: 40988
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pes...@sysmocom.de>

Reply via email to