lists.osmocom.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
November
October
September
August
July
June
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
List overview
Download
gerrit-log
September 2025
----- 2025 -----
November 2025
October 2025
September 2025
August 2025
July 2025
June 2025
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
gerrit-log@lists.osmocom.org
1 participants
1216 discussions
Start a n
N
ew thread
[L] Change in osmo-sgsn[master]: Introduce iu_rnc FSM
by lynxis lazus
03 Sep '25
03 Sep '25
lynxis lazus has submitted this change. (
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. Related: OS#3403 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, 591 insertions(+), 95 deletions(-) Approvals: Jenkins Builder: Verified osmith: Looks good to me, but someone else must approve lynxis lazus: Looks good to me, approved 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 <info(a)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..b4bfb2c 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)" }, @@ -116,6 +116,7 @@ /* non spec timers */ { .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)" }, {} }; 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: merged Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I18b7803500163e78ff6a684095194174b0fb6ee1 Gerrit-Change-Number: 40988 Gerrit-PatchSet: 4 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
1
0
0
0
[XS] Change in osmo-sgsn[master]: iu_rnc: Constify function params
by lynxis lazus
03 Sep '25
03 Sep '25
lynxis lazus has submitted this change. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40959?usp=email
) ( 2 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: iu_rnc: Constify function params ...................................................................... iu_rnc: Constify function params Change-Id: I288bc21d24aa5abd3d9bd97e796ac9f8590290bd --- M include/osmocom/sgsn/iu_rnc.h M src/sgsn/iu_rnc.c 2 files changed, 4 insertions(+), 4 deletions(-) Approvals: Jenkins Builder: Verified fixeria: Looks good to me, approved lynxis lazus: Looks good to me, approved diff --git a/include/osmocom/sgsn/iu_rnc.h b/include/osmocom/sgsn/iu_rnc.h index 345ee9c..895e922 100644 --- a/include/osmocom/sgsn/iu_rnc.h +++ b/include/osmocom/sgsn/iu_rnc.h @@ -32,4 +32,4 @@ struct ranap_iu_rnc *iu_rnc_register(struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai, - struct osmo_sccp_addr *addr); + const struct osmo_sccp_addr *addr); diff --git a/src/sgsn/iu_rnc.c b/src/sgsn/iu_rnc.c index 0854f99..970f628 100644 --- a/src/sgsn/iu_rnc.c +++ b/src/sgsn/iu_rnc.c @@ -43,7 +43,7 @@ #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 osmo_sccp_addr *addr) +static struct ranap_iu_rnc *iu_rnc_alloc(const struct osmo_rnc_id *rnc_id, const struct osmo_sccp_addr *addr) { struct ranap_iu_rnc *rnc = talloc_zero(sgsn, struct ranap_iu_rnc); OSMO_ASSERT(rnc); @@ -98,7 +98,7 @@ return NULL; } -static bool same_sccp_addr(struct osmo_sccp_addr *a, struct osmo_sccp_addr *b) +static bool same_sccp_addr(const struct osmo_sccp_addr *a, const struct osmo_sccp_addr *b) { char buf[256]; osmo_strlcpy(buf, osmo_sccp_addr_dump(a), sizeof(buf)); @@ -125,7 +125,7 @@ struct ranap_iu_rnc *iu_rnc_register(struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai, - struct osmo_sccp_addr *addr) + const struct osmo_sccp_addr *addr) { struct ranap_iu_rnc *rnc; struct ranap_iu_rnc *old_rnc; -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/40959?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I288bc21d24aa5abd3d9bd97e796ac9f8590290bd Gerrit-Change-Number: 40959 Gerrit-PatchSet: 3 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
1
0
0
0
[M] Change in osmo-sgsn[master]: sccp: Introduce helper func sgsn_scu_iups_tx_data_req()
by lynxis lazus
03 Sep '25
03 Sep '25
lynxis lazus has submitted this change. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40958?usp=email
) ( 2 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: sccp: Introduce helper func sgsn_scu_iups_tx_data_req() ...................................................................... sccp: Introduce helper func sgsn_scu_iups_tx_data_req() This avoid code deduplication plus moving SCCP SAP logic into sccp.c. Change-Id: Id34f86b4f5e40ddceac4066ec18ebc60529cafab --- M include/osmocom/sgsn/gprs_ranap.h M include/osmocom/sgsn/sccp.h M src/sgsn/gprs_ranap.c M src/sgsn/sccp.c 4 files changed, 34 insertions(+), 52 deletions(-) Approvals: lynxis lazus: Looks good to me, approved Jenkins Builder: Verified diff --git a/include/osmocom/sgsn/gprs_ranap.h b/include/osmocom/sgsn/gprs_ranap.h index 52a70b2..8a6c189 100644 --- a/include/osmocom/sgsn/gprs_ranap.h +++ b/include/osmocom/sgsn/gprs_ranap.h @@ -16,7 +16,6 @@ int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp); int ranap_iu_tx(struct msgb *msg, uint8_t sapi); -int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg); int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id); int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, int send_ck, int new_key); diff --git a/include/osmocom/sgsn/sccp.h b/include/osmocom/sgsn/sccp.h index a75c6aa..53c7a89 100644 --- a/include/osmocom/sgsn/sccp.h +++ b/include/osmocom/sgsn/sccp.h @@ -18,6 +18,8 @@ */ #pragma once +#include <stdint.h> + #include <osmocom/sigtran/sccp_sap.h> struct sgsn_instance; @@ -34,6 +36,8 @@ struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp); void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups); +int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg); + int sgsn_sccp_init(struct sgsn_instance *sgsn); void sgsn_sccp_release(struct sgsn_instance *sgsn); diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c index 8433765..376fcac 100644 --- a/src/sgsn/gprs_ranap.c +++ b/src/sgsn/gprs_ranap.c @@ -331,23 +331,7 @@ msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); - msg->l2h = msg->data; - return ranap_iu_rab_act(uectx, msg); -} - -int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg) -{ - struct osmo_scu_prim *prim; - - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ue_ctx->conn_id; - osmo_prim_init(&prim->oph, - SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, - msg); - return osmo_sccp_user_sap_down(ue_ctx->scu_iups->scu, &prim->oph); + return sgsn_scu_iups_tx_data_req(uectx->scu_iups, uectx->conn_id, msg); } int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id) @@ -359,38 +343,23 @@ int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, int send_ck, int new_key) { - struct osmo_scu_prim *prim; struct msgb *msg; /* create RANAP message */ msg = ranap_new_msg_sec_mod_cmd(vec->ik, send_ck ? vec->ck : NULL, new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old); - msg->l2h = msg->data; - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); + return sgsn_scu_iups_tx_data_req(uectx->scu_iups, uectx->conn_id, msg); } int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi) { struct msgb *msg; - struct osmo_scu_prim *prim; LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n", uectx->conn_id); msg = ranap_new_msg_common_id(imsi); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); + return sgsn_scu_iups_tx_data_req(uectx->scu_iups, uectx->conn_id, msg); } /* Send a paging command down a given SCCP User. tmsi and paging_cause are @@ -413,7 +382,6 @@ { struct ranap_ue_conn_ctx *uectx = msg_nas->dst; struct msgb *msg; - struct osmo_scu_prim *prim; if (!uectx) { LOGP(DRANAP, LOGL_ERROR, @@ -426,22 +394,16 @@ msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas)); msgb_free(msg_nas); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); + + return sgsn_scu_iups_tx_data_req(uectx->scu_iups, uectx->conn_id, msg); } /* Send Iu Release for the given UE connection. * If cause is NULL, Normal Release cause is sent, otherwise * the provided cause. */ -int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause) +int ranap_iu_tx_release(struct ranap_ue_conn_ctx *uectx, const struct RANAP_Cause *cause) { struct msgb *msg; - struct osmo_scu_prim *prim; static const struct RANAP_Cause default_cause = { .present = RANAP_Cause_PR_nAS, .choice.radioNetwork = RANAP_CauseNAS_normal_release, @@ -451,13 +413,7 @@ cause = &default_cause; msg = ranap_new_msg_iu_rel_cmd(cause); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ctx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(ctx->scu_iups->scu, &prim->oph); + return sgsn_scu_iups_tx_data_req(uectx->scu_iups, uectx->conn_id, msg); } void ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx, diff --git a/src/sgsn/sccp.c b/src/sgsn/sccp.c index 68f0fea..291222f 100644 --- a/src/sgsn/sccp.c +++ b/src/sgsn/sccp.c @@ -77,6 +77,29 @@ talloc_free(scu_iups); } +/* wrap RANAP message in SCCP N-DATA.req + * ranap_msg becomes owned by the callee. */ +int sgsn_scu_iups_tx_data_req(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, struct msgb *ranap_msg) +{ + struct osmo_scu_prim *prim; + int rc; + + if (!scu_iups) { + LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u): no SCCP User\n", conn_id); + return -1; + } + + ranap_msg->l2h = ranap_msg->data; + prim = (struct osmo_scu_prim *)msgb_push(ranap_msg, sizeof(*prim)); + osmo_prim_init(&prim->oph, SCCP_SAP_USER, OSMO_SCU_PRIM_N_DATA, PRIM_OP_REQUEST, ranap_msg); + prim->u.data.conn_id = conn_id; + + rc = osmo_sccp_user_sap_down(scu_iups->scu, &prim->oph); + if (rc) + LOGP(DSUA, LOGL_ERROR, "Failed to send SCCP N-DATA.req(%u)\n", conn_id); + return rc; +} + static struct ranap_ue_conn_ctx *ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id) { struct ranap_ue_conn_ctx *ctx; -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/40958?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: Id34f86b4f5e40ddceac4066ec18ebc60529cafab Gerrit-Change-Number: 40958 Gerrit-PatchSet: 3 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu>
1
0
0
0
[XL] Change in osmo-sgsn[master]: Split most of iu_client.{c,h} into sccp.{c,h} and iu_rnc.{c,h}
by lynxis lazus
03 Sep '25
03 Sep '25
lynxis lazus has submitted this change. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40956?usp=email
) Change subject: Split most of iu_client.{c,h} into sccp.{c,h} and iu_rnc.{c,h} ...................................................................... Split most of iu_client.{c,h} into sccp.{c,h} and iu_rnc.{c,h} * iu_rnc is easily spotted as an object class in the data domain which can be put into its own file, handling its specific logic and data. * SCCP code can also be moved into own specific file, which eases self-containment of the layer logic and data. * As a result, lots of RANAP Rx/Tx code in iu_client.{c,h} can also be moved to the already existing gprs_ranap.{c,h}. All these changes improve a lot readibility of code and makes it a lot easier to improve/extend the different layers/objects in future patches. Change-Id: I4c792ae665720460b5a954b5c7cbfe5acbc37473 --- M include/osmocom/sgsn/Makefile.am M include/osmocom/sgsn/gprs_ranap.h M include/osmocom/sgsn/iu_client.h A include/osmocom/sgsn/iu_rnc.h A include/osmocom/sgsn/sccp.h M include/osmocom/sgsn/sgsn.h M src/sgsn/Makefile.am M src/sgsn/gprs_gmm_attach.c M src/sgsn/gprs_ranap.c M src/sgsn/iu_client.c A src/sgsn/iu_rnc.c A src/sgsn/sccp.c M src/sgsn/sgsn.c M src/sgsn/sgsn_main.c M tests/gprs_routing_area/Makefile.am M tests/sgsn/Makefile.am 16 files changed, 1,302 insertions(+), 1,055 deletions(-) Approvals: Jenkins Builder: Verified osmith: Looks good to me, approved diff --git a/include/osmocom/sgsn/Makefile.am b/include/osmocom/sgsn/Makefile.am index 6119640..342b568 100644 --- a/include/osmocom/sgsn/Makefile.am +++ b/include/osmocom/sgsn/Makefile.am @@ -28,8 +28,10 @@ gtp_ggsn.h \ gtp_mme.h \ iu_client.h \ + iu_rnc.h \ mmctx.h \ pdpctx.h \ + sccp.h \ sgsn.h \ sgsn_rim.h \ signal.h \ diff --git a/include/osmocom/sgsn/gprs_ranap.h b/include/osmocom/sgsn/gprs_ranap.h index b75bba2..52a70b2 100644 --- a/include/osmocom/sgsn/gprs_ranap.h +++ b/include/osmocom/sgsn/gprs_ranap.h @@ -6,6 +6,7 @@ #include <osmocom/ranap/ranap_ies_defs.h> #include <osmocom/ranap/ranap_msg_factory.h> #include <osmocom/sgsn/iu_client.h> +#include <osmocom/sgsn/sccp.h> struct sgsn_mm_ctx; struct sgsn_pdp_ctx; @@ -14,6 +15,24 @@ int sgsn_ranap_iu_event(struct ranap_ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data); int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp); +int ranap_iu_tx(struct msgb *msg, uint8_t sapi); +int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg); +int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id); +int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, + int send_ck, int new_key); +int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *ue_ctx, const char *imsi); +int ranap_iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr, + const char *imsi, const uint32_t *tmsi, + bool is_ps, uint32_t paging_cause); + +int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause); +/* Transmit a Iu Release Command and submit event RANAP_IU_EVENT_IU_RELEASE upon + * Release Complete or timeout. Caller is responsible to free the context and + * closing the SCCP connection (ranap_iu_free_ue) upon recieval of the event. */ +void ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx, + const struct RANAP_Cause *cause, + int timeout); + /* free the Iu UE context */ void sgsn_ranap_iu_free(struct sgsn_mm_ctx *ctx); @@ -21,6 +40,12 @@ void sgsn_ranap_iu_release_free(struct sgsn_mm_ctx *ctx, const struct RANAP_Cause *cause); +int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups, struct osmo_scu_unitdata_param *ud_prim, + uint8_t *data, size_t len); +int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_sccp_addr *rem_sccp_addr, + uint32_t conn_id, uint8_t *data, size_t len); +int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, uint8_t *data, size_t len); + #else /* ifndef BUILD_IU */ inline static void sgsn_ranap_iu_free(void *ctx) {}; inline static void sgsn_ranap_iu_release_free(void *ctx, void *cause) {}; diff --git a/include/osmocom/sgsn/iu_client.h b/include/osmocom/sgsn/iu_client.h index 8277886..0dedba4 100644 --- a/include/osmocom/sgsn/iu_client.h +++ b/include/osmocom/sgsn/iu_client.h @@ -14,13 +14,13 @@ struct osmo_auth_vector; struct RANAP_RAB_SetupOrModifiedItemIEs_s; -struct RANAP_Cause; struct ranap_iu_rnc; struct ranap_ue_conn_ctx { - struct llist_head list; + struct llist_head list; /* item in sgsn_sccp->ue_conn_ctx_list */ struct ranap_iu_rnc *rnc; + struct sgsn_sccp_user_iups *scu_iups; uint32_t conn_id; int integrity_active; struct gprs_ra_id ra_id; @@ -70,10 +70,12 @@ typedef int (*ranap_iu_rab_ass_resp_cb_t)(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id, struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies); -int ranap_iu_init(void *ctx, int log_subsystem, const char *sccp_user_name, struct osmo_sccp_instance *sccp, - ranap_iu_recv_cb_t iu_recv_cb, ranap_iu_event_cb_t iu_event_cb); +int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx, + enum ranap_iu_event_type type, + void *data); -int ranap_iu_tx(struct msgb *msg, uint8_t sapi); + +int ranap_iu_init(void *ctx); int ranap_iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac) OSMO_DEPRECATED("Use ranap_iu_page_cs2 instead"); @@ -84,19 +86,8 @@ int ranap_iu_page_cs2(const char *imsi, const uint32_t *tmsi, const struct osmo_location_area_id *lai); int ranap_iu_page_ps2(const char *imsi, const uint32_t *ptmsi, const struct osmo_routing_area_id *rai); -int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg); -int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id); -int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, - int send_ck, int new_key); -int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *ue_ctx, const char *imsi); -int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause); -/* Transmit a Iu Release Command and submit event RANAP_IU_EVENT_IU_RELEASE upon - * Release Complete or timeout. Caller is responsible to free the context and - * closing the SCCP connection (ranap_iu_free_ue) upon recieval of the event. */ -void ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx, - const struct RANAP_Cause *cause, - int timeout); +struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id); /* freeing the UE will release all resources * This will close the SCCP connection connected to the UE */ diff --git a/include/osmocom/sgsn/iu_rnc.h b/include/osmocom/sgsn/iu_rnc.h new file mode 100644 index 0000000..345ee9c --- /dev/null +++ b/include/osmocom/sgsn/iu_rnc.h @@ -0,0 +1,35 @@ +#pragma once + +#include <stdbool.h> + +#include <osmocom/core/defs.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/gsm/gsm48.h> +#include <osmocom/iuh/common.h> +#include <osmocom/sigtran/sccp_sap.h> + +struct iu_lac_rac_entry { + struct llist_head entry; + struct osmo_routing_area_id rai; +}; + +/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has + * called us and is currently reachable at the given osmo_sccp_addr. So, when we + * know a LAC for a subscriber, we can page it at the RNC matching that LAC or + * RAC. An HNB-GW typically presents itself as if it were a single RNC, even + * though it may have several RNCs in hNodeBs connected to it. Those will then + * share the same RNC id, which they actually receive and adopt from the HNB-GW + * in the HNBAP HNB REGISTER ACCEPT message. */ +struct ranap_iu_rnc { + struct llist_head entry; + + struct osmo_rnc_id rnc_id; + struct osmo_sccp_addr sccp_addr; + + /* A list of struct iu_lac_rac_entry */ + struct llist_head lac_rac_list; +}; + +struct ranap_iu_rnc *iu_rnc_register(struct osmo_rnc_id *rnc_id, + const struct osmo_routing_area_id *rai, + struct osmo_sccp_addr *addr); diff --git a/include/osmocom/sgsn/sccp.h b/include/osmocom/sgsn/sccp.h new file mode 100644 index 0000000..a75c6aa --- /dev/null +++ b/include/osmocom/sgsn/sccp.h @@ -0,0 +1,39 @@ +/* SCCP Handling */ +/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)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/
>. + * + */ +#pragma once + +#include <osmocom/sigtran/sccp_sap.h> + +struct sgsn_instance; + +struct sgsn_sccp_user_iups { + struct sgsn_instance *sgsn; /* backpointer */ + struct osmo_sccp_instance *sccp; /* backpointer */ + struct osmo_sccp_user *scu; /* IuPS */ + struct osmo_sccp_addr local_sccp_addr; + struct llist_head ue_conn_ctx_list; /* list of "struct ranap_ue_conn_ctx" */ + struct llist_head ue_conn_sccp_addr_list; /* list of "struct iu_new_ctx_entry" */ +}; + +struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp); +void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups); + +int sgsn_sccp_init(struct sgsn_instance *sgsn); +void sgsn_sccp_release(struct sgsn_instance *sgsn); + diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h index 09eb22c..7d323ed 100644 --- a/include/osmocom/sgsn/sgsn.h +++ b/include/osmocom/sgsn/sgsn.h @@ -1,6 +1,7 @@ #ifndef _SGSN_H #define _SGSN_H +#include "config.h" #include <osmocom/core/msgb.h> #include <osmocom/core/select.h> @@ -14,9 +15,8 @@ #include <osmocom/gsupclient/gsup_client.h> #include <osmocom/sgsn/common.h> -#include "../../config.h" - #if BUILD_IU +#include <osmocom/sigtran/sccp_sap.h> #include <osmocom/sgsn/iu_client.h> #endif @@ -27,6 +27,9 @@ #define SGSN_ERROR_CAUSE_NONE (-1) +/* This rac will be used internally. RAC with 0xff will be rejected */ +#define OSMO_RESERVED_RAC 0xff + enum sgsn_auth_policy { SGSN_AUTH_POLICY_OPEN, SGSN_AUTH_POLICY_CLOSED, @@ -162,8 +165,19 @@ struct llist_head mme_list; /* list of struct sgsn_mme_ctx */ struct llist_head mm_list; /* list of struct sgsn_mm_ctx */ struct llist_head pdp_list; /* list of struct sgsn_pdp_ctx */ +#if BUILD_IU + struct llist_head rnc_list; /* list of struct ranap_iu_rnc */ +#endif /* if BUILD_IU */ struct ctrl_handle *ctrlh; + +#if BUILD_IU + /* SCCP (Iu) */ + struct { + struct osmo_sccp_instance *sccp; + struct sgsn_sccp_user_iups *scu_iups; + } sccp; +#endif /* if BUILD_IU */ }; extern struct sgsn_instance *sgsn; diff --git a/src/sgsn/Makefile.am b/src/sgsn/Makefile.am index d944247..fdad7dc 100644 --- a/src/sgsn/Makefile.am +++ b/src/sgsn/Makefile.am @@ -97,6 +97,9 @@ osmo_sgsn_SOURCES += \ gprs_mm_state_iu_fsm.c \ gprs_ranap.c \ - iu_client.c + iu_client.c \ + iu_rnc.c \ + sccp.c \ + $(NULL) endif diff --git a/src/sgsn/gprs_gmm_attach.c b/src/sgsn/gprs_gmm_attach.c index 6331992..839ad83 100644 --- a/src/sgsn/gprs_gmm_attach.c +++ b/src/sgsn/gprs_gmm_attach.c @@ -1,3 +1,5 @@ +#include "config.h" + #include <osmocom/core/tdef.h> #include <osmocom/crypt/utran_cipher.h> @@ -7,6 +9,7 @@ #include <osmocom/sgsn/debug.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/mmctx.h> +#include <osmocom/sgsn/gprs_ranap.h> #include <osmocom/sgsn/sgsn.h> #define X(s) (1 << (s)) diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c index a7f8c5a..8433765 100644 --- a/src/sgsn/gprs_ranap.c +++ b/src/sgsn/gprs_ranap.c @@ -22,6 +22,9 @@ */ #include "config.h" + +#include <asn1c/asn1helpers.h> + #include <osmocom/gtp/gtp.h> #include <osmocom/core/rate_ctr.h> @@ -29,7 +32,11 @@ #include <osmocom/gprs/gprs_msgb.h> #include <osmocom/ranap/ranap_common.h> +#include <osmocom/ranap/ranap_common_cn.h> +#include <osmocom/ranap/ranap_ies_defs.h> +#include <osmocom/ranap/ranap_msg_factory.h> #include <osmocom/ranap/iu_helpers.h> +#include <osmocom/sigtran/sccp_helpers.h> #include <osmocom/sgsn/gprs_gmm.h> #include <osmocom/sgsn/gprs_sm.h> @@ -41,9 +48,55 @@ #include <osmocom/sgsn/gprs_routing_area.h> #include <osmocom/sgsn/gtp_ggsn.h> #include <osmocom/sgsn/gtp.h> +#include <osmocom/sgsn/iu_rnc.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, struct RANAP_GlobalRNC_ID *src) +{ + /* The size is coming from arbitrary sender, check it gracefully */ + if (src->pLMNidentity.size != 3) { + LOGP(DRANAP, LOGL_ERROR, "Invalid PLMN Identity size: should be 3, is %d\n", + src->pLMNidentity.size); + return -1; + } + osmo_plmn_from_bcd(&src->pLMNidentity.buf[0], &dst->plmn); + dst->rnc_id = (uint16_t)src->rNC_ID; + return 0; +} + +#if 0 +/* not used at present */ +static int iu_grnc_id_compose(struct iu_grnc_id *src, struct RANAP_GlobalRNC_ID *dst) +{ + /* 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; + return 0; +} +#endif + +struct ranap_iu_event_sccp_new_ue_context { + struct sgsn_sccp_user_iups *scu_iups; + struct osmo_sccp_addr rem_sccp_addr; + uint32_t conn_id; +}; + +struct ranap_iu_event_cl_msg_ind { + struct sgsn_sccp_user_iups *scu_iups; + struct osmo_scu_unitdata_param *ud_prim; +}; + /* Send RAB activation requests for all PDP contexts */ void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) { @@ -281,3 +334,462 @@ msg->l2h = msg->data; return ranap_iu_rab_act(uectx, msg); } + +int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg) +{ + struct osmo_scu_prim *prim; + + /* wrap RANAP message in SCCP N-DATA.req */ + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = ue_ctx->conn_id; + osmo_prim_init(&prim->oph, + SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, + msg); + return osmo_sccp_user_sap_down(ue_ctx->scu_iups->scu, &prim->oph); +} + +int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id) +{ + /* FIXME */ + return -1; +} + +int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, + int send_ck, int new_key) +{ + struct osmo_scu_prim *prim; + struct msgb *msg; + + /* create RANAP message */ + msg = ranap_new_msg_sec_mod_cmd(vec->ik, send_ck ? vec->ck : NULL, + new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old); + msg->l2h = msg->data; + /* wrap RANAP message in SCCP N-DATA.req */ + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = uectx->conn_id; + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, msg); + return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); +} + +int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi) +{ + struct msgb *msg; + struct osmo_scu_prim *prim; + + LOGP(DRANAP, LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n", + uectx->conn_id); + + msg = ranap_new_msg_common_id(imsi); + msg->l2h = msg->data; + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = uectx->conn_id; + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, msg); + return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); +} + +/* Send a paging command down a given SCCP User. tmsi and paging_cause are + * optional and may be passed NULL and 0, respectively, to disable their use. + * See enum RANAP_PagingCause. + * + * If TMSI is given, the IMSI is not sent over the air interface. Nevertheless, + * the IMSI is still required for resolution in the HNB-GW and/or(?) RNC. */ +int ranap_iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr, + const char *imsi, const uint32_t *tmsi, + bool is_ps, uint32_t paging_cause) +{ + struct msgb *msg; + msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps ? 1 : 0, paging_cause); + msg->l2h = msg->data; + return osmo_sccp_tx_unitdata_msg(sgsn->sccp.scu_iups->scu, &sgsn->sccp.scu_iups->local_sccp_addr, called_addr, msg); +} + +int ranap_iu_tx(struct msgb *msg_nas, uint8_t sapi) +{ + struct ranap_ue_conn_ctx *uectx = msg_nas->dst; + struct msgb *msg; + struct osmo_scu_prim *prim; + + if (!uectx) { + LOGP(DRANAP, LOGL_ERROR, + "Discarding to-be-transmitted L3 Message as RANAP DT with unset dst SCCP conn_id!\n"); + return -ENOTCONN; + } + + LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SCCP conn_id %u)\n", + uectx->conn_id); + + msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas)); + msgb_free(msg_nas); + msg->l2h = msg->data; + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = uectx->conn_id; + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, msg); + return osmo_sccp_user_sap_down(uectx->scu_iups->scu, &prim->oph); +} + +/* Send Iu Release for the given UE connection. + * If cause is NULL, Normal Release cause is sent, otherwise + * the provided cause. */ +int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause) +{ + struct msgb *msg; + struct osmo_scu_prim *prim; + static const struct RANAP_Cause default_cause = { + .present = RANAP_Cause_PR_nAS, + .choice.radioNetwork = RANAP_CauseNAS_normal_release, + }; + + if (!cause) + cause = &default_cause; + + msg = ranap_new_msg_iu_rel_cmd(cause); + msg->l2h = msg->data; + prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); + prim->u.data.conn_id = ctx->conn_id; + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_DATA, + PRIM_OP_REQUEST, msg); + return osmo_sccp_user_sap_down(ctx->scu_iups->scu, &prim->oph); +} + +void ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx, + const struct RANAP_Cause *cause, + int timeout) +{ + ctx->notification = false; + ctx->free_on_release = true; + int ret = ranap_iu_tx_release(ctx, cause); + /* On Tx failure, trigger timeout immediately, as the response will never arrive */ + if (ret) + timeout = 0; + + osmo_timer_schedule(&ctx->release_timeout, timeout, 0); +} + +static int ranap_handle_co_initial_ue(void *ctx, RANAP_InitialUE_MessageIEs_t *ies) +{ + struct ranap_iu_event_sccp_new_ue_context *new_ctx = ctx; + struct gprs_ra_id ra_id = {}; + struct osmo_routing_area_id ra_id2 = {}; + struct osmo_rnc_id rnc_id = {}; + 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"); + return -1; + } + + if (ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT) { + ra_id.rac = asn1str_to_u8(&ies->rac); + if (ra_id.rac == OSMO_RESERVED_RAC) { + LOGP(DRANAP, LOGL_ERROR, + "Rejecting RNC with invalid/internally used RAC 0x%02x\n", ra_id.rac); + return -1; + } + } else { + ra_id.rac = OSMO_RESERVED_RAC; + } + + if (iu_grnc_id_parse(&rnc_id, &ies->globalRNC_ID) != 0) { + LOGP(DRANAP, LOGL_ERROR, + "Failed to parse RANAP Global-RNC-ID IE\n"); + return -1; + } + + sai = asn1str_to_u16(&ies->sai.sAC); + msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); + memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); + + 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_register(&rnc_id, &ra_id2, &new_ctx->rem_sccp_addr); + + ue = ue_conn_ctx_alloc(rnc, new_ctx->scu_iups, new_ctx->conn_id); + OSMO_ASSERT(ue); + ue->ra_id = ra_id; + + /* Feed into the MM layer */ + msg->dst = ue; + gsm0408_gprs_rcvmsg_iu(msg, &ra_id, &sai); + + msgb_free(msg); + + return 0; +} + +static void cn_ranap_handle_co_initial(void *ctx, ranap_message *message) +{ + int rc; + + LOGP(DRANAP, LOGL_NOTICE, "handle_co_initial(dir=%u, proc=%u)\n", message->direction, message->procedureCode); + + if (message->direction != RANAP_RANAP_PDU_PR_initiatingMessage + || message->procedureCode != RANAP_ProcedureCode_id_InitialUE_Message) { + LOGP(DRANAP, LOGL_ERROR, "Expected direction 'InitiatingMessage'," + " procedureCode 'InitialUE_Message', instead got %u and %u\n", + message->direction, message->procedureCode); + rc = -1; + } else + rc = ranap_handle_co_initial_ue(ctx, &message->msg.initialUE_MessageIEs); + + if (rc) { + LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); + /* TODO handling of the error? */ + } +} + +int sgsn_ranap_iu_rx_co_initial_msg(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_sccp_addr *rem_sccp_addr, uint32_t conn_id, uint8_t *data, size_t len) +{ + struct ranap_iu_event_sccp_new_ue_context new_ctx = { + .scu_iups = scu_iups, + .conn_id = conn_id, + .rem_sccp_addr = *rem_sccp_addr, + }; + return ranap_cn_rx_co(cn_ranap_handle_co_initial, &new_ctx, data, len); +} + +static int ranap_handle_co_dt(void *ctx, RANAP_DirectTransferIEs_t *ies) +{ + struct gprs_ra_id _ra_id, *ra_id = NULL; + uint16_t _sai, *sai = NULL; + struct msgb *msg = msgb_alloc(256, "RANAP->NAS"); + + if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) { + if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) { + LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n"); + return -1; + } + ra_id = &_ra_id; + if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT) + _ra_id.rac = asn1str_to_u8(&ies->rac); + + if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) { + _sai = asn1str_to_u16(&ies->sai.sAC); + sai = &_sai; + } + } + + msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); + memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); + + /* Feed into the MM/CC/SMS-CP layer */ + msg->dst = ctx; + gsm0408_gprs_rcvmsg_iu(msg, ra_id, sai); + + msgb_free(msg); + + return 0; +} + +static int ranap_handle_co_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies) +{ + if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) + LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n", + ranap_cause_str(&ies->cause)); + else + LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n"); + + return 0; +} + +static int ranap_handle_co_iu_rel_req(struct ranap_ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) +{ + LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n"); + ranap_iu_tx_release(ctx, &ies->cause); + return 0; +} + +static int ranap_handle_co_rab_ass_resp(struct ranap_ue_conn_ctx *ctx, RANAP_RAB_AssignmentResponseIEs_t *ies) +{ + int rc = -1; + + LOGP(DRANAP, LOGL_INFO, + "Rx RAB Assignment Response for UE conn_id %u\n", ctx->conn_id); + if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) { + /* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */ + RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0]; + RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies; + + rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value); + if (rc) { + LOGP(DRANAP, LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n"); + return rc; + } + + rc = global_iu_event(ctx, RANAP_IU_EVENT_RAB_ASSIGN, &setup_ies); + + ranap_free_rab_setupormodifieditemies(&setup_ies); + } + /* FIXME: handle RAB Ass failure? */ + + return rc; +} + +/* Entry point for connection-oriented RANAP message */ +static void cn_ranap_handle_co(void *ctx, ranap_message *message) +{ + int rc; + + LOGP(DRANAP, LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode); + + switch (message->direction) { + case RANAP_RANAP_PDU_PR_initiatingMessage: + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_InitialUE_Message: + LOGP(DRANAP, LOGL_ERROR, "Got InitialUE_Message but this is not a new conn\n"); + rc = -1; + break; + case RANAP_ProcedureCode_id_DirectTransfer: + rc = ranap_handle_co_dt(ctx, &message->msg.directTransferIEs); + break; + case RANAP_ProcedureCode_id_ErrorIndication: + rc = ranap_handle_co_err_ind(ctx, &message->msg.errorIndicationIEs); + break; + case RANAP_ProcedureCode_id_Iu_ReleaseRequest: + /* Iu Release Request */ + rc = ranap_handle_co_iu_rel_req(ctx, &message->msg.iu_ReleaseRequestIEs); + break; + default: + LOGP(DRANAP, LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n", + message->procedureCode); + rc = -1; + break; + } + break; + case RANAP_RANAP_PDU_PR_successfulOutcome: + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_SecurityModeControl: + /* Security Mode Complete */ + rc = global_iu_event(ctx, RANAP_IU_EVENT_SECURITY_MODE_COMPLETE, NULL); + break; + case RANAP_ProcedureCode_id_Iu_Release: + /* Iu Release Complete */ + rc = global_iu_event(ctx, RANAP_IU_EVENT_IU_RELEASE, NULL); + if (rc) { + LOGP(DRANAP, LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n", + rc); + } + break; + default: + LOGP(DRANAP, LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n", + message->procedureCode); + rc = -1; + break; + } + break; + case RANAP_RANAP_PDU_PR_outcome: + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_RAB_Assignment: + /* RAB Assignment Response */ + rc = ranap_handle_co_rab_ass_resp(ctx, &message->msg.raB_AssignmentResponseIEs); + break; + default: + LOGP(DRANAP, LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n", + message->procedureCode); + rc = -1; + break; + } + break; + case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: + default: + LOGP(DRANAP, LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n", + message->procedureCode); + rc = -1; + break; + } + + if (rc) { + LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); + /* TODO handling of the error? */ + } +} + +int sgsn_ranap_iu_rx_co_msg(struct ranap_ue_conn_ctx *ue_ctx, uint8_t *data, size_t len) +{ + return ranap_cn_rx_co(cn_ranap_handle_co, ue_ctx, data, len); +} + +static int ranap_handle_cl_reset_req(struct ranap_iu_event_cl_msg_ind *cl_msg_ind, RANAP_ResetIEs_t *ies) +{ + RANAP_GlobalRNC_ID_t *grnc_id = NULL; + struct msgb *resp; + + /* FIXME: verify ies.cN_DomainIndicator */ + + if (ies->presenceMask & RESETIES_RANAP_GLOBALRNC_ID_PRESENT) + grnc_id = &ies->globalRNC_ID; + + /* send reset response */ + resp = ranap_new_msg_reset_ack(ies->cN_DomainIndicator, grnc_id); + if (!resp) + return -ENOMEM; + resp->l2h = resp->data; + return osmo_sccp_tx_unitdata_msg(cl_msg_ind->scu_iups->scu, &cl_msg_ind->scu_iups->local_sccp_addr, &cl_msg_ind->ud_prim->calling_addr, resp); +} + +static int ranap_handle_cl_err_ind(struct ranap_iu_event_cl_msg_ind *cl_msg_ind, RANAP_ErrorIndicationIEs_t *ies) +{ + if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) + LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n", + ranap_cause_str(&ies->cause)); + else + LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n"); + + return 0; +} + +/* Entry point for connection-less RANAP message */ +static void cn_ranap_handle_cl(void *ctx, ranap_message *message) +{ + struct ranap_iu_event_cl_msg_ind *cl_msg_ind = ctx; + int rc; + + switch (message->direction) { + case RANAP_RANAP_PDU_PR_initiatingMessage: + switch (message->procedureCode) { + case RANAP_ProcedureCode_id_Reset: + /* received reset.req, send reset.resp */ + rc = ranap_handle_cl_reset_req(cl_msg_ind, &message->msg.resetIEs); + break; + case RANAP_ProcedureCode_id_ErrorIndication: + rc = ranap_handle_cl_err_ind(cl_msg_ind, &message->msg.errorIndicationIEs); + break; + default: + rc = -1; + break; + } + break; + case RANAP_RANAP_PDU_PR_successfulOutcome: + case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: + case RANAP_RANAP_PDU_PR_outcome: + default: + rc = -1; + break; + } + + if (rc) { + LOGP(DRANAP, LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); + /* TODO handling of the error? */ + } +} + +int sgsn_ranap_iu_rx_cl_msg(struct sgsn_sccp_user_iups *scu_iups, struct osmo_scu_unitdata_param *ud_prim, uint8_t *data, size_t len) +{ + struct ranap_iu_event_cl_msg_ind cl_msg_ind = { + .scu_iups = scu_iups, + .ud_prim = ud_prim, + }; + return ranap_cn_rx_cl(cn_ranap_handle_cl, &cl_msg_ind, data, len); +} diff --git a/src/sgsn/iu_client.c b/src/sgsn/iu_client.c index 1736163..1f36129 100644 --- a/src/sgsn/iu_client.c +++ b/src/sgsn/iu_client.c @@ -18,6 +18,8 @@ * */ +#include "config.h" + #include <stdint.h> #include <unistd.h> #include <stdlib.h> @@ -25,10 +27,7 @@ #include <string.h> #include <stdbool.h> -#include <asn1c/asn1helpers.h> - #include <osmocom/gsm/protocol/gsm_04_08_gprs.h> -#include <osmocom/sgsn/iu_client.h> #include <osmocom/core/logging.h> #include <osmocom/crypt/auth.h> @@ -36,71 +35,19 @@ #include <osmocom/sigtran/sccp_sap.h> #include <osmocom/sigtran/sccp_helpers.h> #include <osmocom/sccp/sccp_types.h> -#include <osmocom/ranap/ranap_common_cn.h> -#include <osmocom/ranap/ranap_ies_defs.h> -#include <osmocom/ranap/ranap_msg_factory.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; -}; - -struct iu_lac_rac_entry { - struct llist_head entry; - - struct osmo_routing_area_id rai; -}; - -/* Entry to cache conn_id <-> sccp_addr mapping in case we receive an empty CR */ -struct iu_new_ctx_entry { - struct llist_head list; - - uint32_t conn_id; - struct osmo_sccp_addr sccp_addr; -}; - -/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has - * called us and is currently reachable at the given osmo_sccp_addr. So, when we - * know a LAC for a subscriber, we can page it at the RNC matching that LAC or - * RAC. An HNB-GW typically presents itself as if it were a single RNC, even - * though it may have several RNCs in hNodeBs connected to it. Those will then - * share the same RNC id, which they actually receive and adopt from the HNB-GW - * in the HNBAP HNB REGISTER ACCEPT message. */ -struct ranap_iu_rnc { - struct llist_head entry; - - struct osmo_rnc_id rnc_id; - struct osmo_sccp_addr sccp_addr; - - /* A list of struct iu_lac_rac_entry */ - struct llist_head lac_rac_list; -}; - -static void *talloc_iu_ctx; - -static ranap_iu_recv_cb_t global_iu_recv_cb = NULL; -static ranap_iu_event_cb_t global_iu_event_cb = NULL; -static int iu_log_subsystem = 0; +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_ranap.h> +#include <osmocom/sgsn/iu_client.h> +#include <osmocom/sgsn/iu_rnc.h> +#include <osmocom/sgsn/sccp.h> +#include <osmocom/sgsn/sgsn.h> #define LOGPIU(level, fmt, args...) \ - LOGP(iu_log_subsystem, level, fmt, ## args) + LOGP(DRANAP, level, fmt, ## args) #define LOGPIUC(level, fmt, args...) \ - LOGPC(iu_log_subsystem, level, fmt, ## args) - -static LLIST_HEAD(ue_conn_sccp_addr_list); -static LLIST_HEAD(ue_conn_ctx_list); -static LLIST_HEAD(rnc_list); - -static struct osmo_sccp_instance *g_sccp; -static struct osmo_sccp_user *g_scu; -static struct osmo_sccp_addr g_local_sccp_addr; - -/* This rac will be used internally. RAC with 0xff will be rejected */ -#define OSMO_RESERVED_RAC 0xff + LOGPC(DRANAP, level, fmt, ## args) const struct value_string iu_client_event_type_names[] = { OSMO_VALUE_STRING(RANAP_IU_EVENT_RAB_ASSIGN), @@ -111,37 +58,17 @@ { 0, NULL } }; -static int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx, - enum ranap_iu_event_type type, - void *data) +int global_iu_event(struct ranap_ue_conn_ctx *ue_ctx, + enum ranap_iu_event_type type, + void *data) { - if (!global_iu_event_cb) - return 0; if (ue_ctx && !ue_ctx->notification) return 0; LOGPIU(LOGL_DEBUG, "Submit Iu event to upper layer: %s\n", iu_client_event_type_str(type)); - return global_iu_event_cb(ue_ctx, type, data); -} - -static void global_iu_event_new_area(const struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai) -{ - struct ranap_iu_event_new_area new_area = (struct ranap_iu_event_new_area) { - .rnc_id = rnc_id, - .cell_type = RANAP_IU_NEW_RAC - }; - - if (rai->rac == OSMO_RESERVED_RAC) { - new_area.cell_type = RANAP_IU_NEW_LAC; - new_area.u.lai = &rai->lac; - } else { - new_area.cell_type = RANAP_IU_NEW_RAC; - new_area.u.rai = rai; - } - - global_iu_event(NULL, RANAP_IU_EVENT_NEW_AREA, &new_area); + return sgsn_ranap_iu_event(ue_ctx, type, data); } @@ -151,119 +78,37 @@ global_iu_event(ctx, RANAP_IU_EVENT_IU_RELEASE, NULL); } -static struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, uint32_t conn_id) +struct ranap_ue_conn_ctx *ue_conn_ctx_alloc(struct ranap_iu_rnc *rnc, struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id) { - struct ranap_ue_conn_ctx *ctx = talloc_zero(talloc_iu_ctx, struct ranap_ue_conn_ctx); + struct ranap_ue_conn_ctx *ctx = talloc_zero(sgsn, struct ranap_ue_conn_ctx); ctx->rnc = rnc; + ctx->scu_iups = scu_iups; ctx->conn_id = conn_id; ctx->notification = true; ctx->free_on_release = false; osmo_timer_setup(&ctx->release_timeout, ue_conn_ctx_release_timeout_cb, ctx); - llist_add(&ctx->list, &ue_conn_ctx_list); + llist_add(&ctx->list, &scu_iups->ue_conn_ctx_list); return ctx; } -static struct ranap_ue_conn_ctx *ue_conn_ctx_find(uint32_t conn_id) -{ - struct ranap_ue_conn_ctx *ctx; - - llist_for_each_entry(ctx, &ue_conn_ctx_list, list) { - if (ctx->conn_id == conn_id) - return ctx; - } - return NULL; -} - void ranap_iu_free_ue(struct ranap_ue_conn_ctx *ue_ctx) { if (!ue_ctx) return; osmo_timer_del(&ue_ctx->release_timeout); - osmo_sccp_tx_disconn(g_scu, ue_ctx->conn_id, NULL, 0); + osmo_sccp_tx_disconn(ue_ctx->scu_iups->scu, ue_ctx->conn_id, NULL, 0); llist_del(&ue_ctx->list); talloc_free(ue_ctx); } -static void ue_conn_sccp_addr_add(uint32_t conn_id, const struct osmo_sccp_addr *calling_addr) -{ - struct iu_new_ctx_entry *entry = talloc_zero(talloc_iu_ctx, struct iu_new_ctx_entry); - - entry->conn_id = conn_id; - entry->sccp_addr = *calling_addr; - - llist_add(&entry->list, &ue_conn_sccp_addr_list); -} - -static const struct osmo_sccp_addr *ue_conn_sccp_addr_find(uint32_t conn_id) -{ - struct iu_new_ctx_entry *entry; - llist_for_each_entry(entry, &ue_conn_sccp_addr_list, list) { - if (entry->conn_id == conn_id) - return &entry->sccp_addr; - } - return NULL; -} - -static void ue_conn_sccp_addr_del(uint32_t conn_id) -{ - struct iu_new_ctx_entry *entry; - llist_for_each_entry(entry, &ue_conn_sccp_addr_list, list) { - if (entry->conn_id == conn_id) { - llist_del(&entry->list); - talloc_free(entry); - return; - } - } -} - -static struct ranap_iu_rnc *iu_rnc_alloc(const struct osmo_rnc_id *rnc_id, struct osmo_sccp_addr *addr) -{ - struct ranap_iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct ranap_iu_rnc); - OSMO_ASSERT(rnc); - - INIT_LLIST_HEAD(&rnc->lac_rac_list); - - rnc->rnc_id = *rnc_id; - rnc->sccp_addr = *addr; - llist_add(&rnc->entry, &rnc_list); - - LOGPIU(LOGL_NOTICE, "New RNC %s at %s\n", osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(addr)); - - return rnc; -} - -/* Find a match for the given LAC (and RAC). For CS, pass rac as 0. - * If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the - * match if a match is found. Return true if a match is found. */ -static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre, - const struct osmo_routing_area_id *ra_id) -{ - struct ranap_iu_rnc *r; - struct iu_lac_rac_entry *e; - - if (rnc) - *rnc = NULL; - if (lre) - *lre = NULL; - - llist_for_each_entry(r, &rnc_list, entry) { - llist_for_each_entry(e, &r->lac_rac_list, entry) { - if (!osmo_rai_cmp(&e->rai, ra_id)) { - if (rnc) - *rnc = r; - if (lre) - *lre = e; - return true; - } - } - } - return false; -} +/*********************************************************************** + * Paging + ***********************************************************************/ /* legacy, do a first match with ignoring PLMN */ static bool iu_rnc_lac_rac_find_legacy(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre, @@ -277,7 +122,7 @@ if (lre) *lre = NULL; - llist_for_each_entry(r, &rnc_list, entry) { + llist_for_each_entry(r, &sgsn->rnc_list, entry) { llist_for_each_entry(e, &r->lac_rac_list, entry) { if (e->rai.lac.lac == lac && e->rai.rac == rac) { if (rnc) @@ -291,546 +136,6 @@ return false; } -static struct ranap_iu_rnc *iu_rnc_id_find(struct osmo_rnc_id *rnc_id) -{ - struct ranap_iu_rnc *rnc; - llist_for_each_entry(rnc, &rnc_list, entry) { - if (!osmo_rnc_id_cmp(&rnc->rnc_id, rnc_id)) - return rnc; - } - return NULL; -} - -static bool same_sccp_addr(struct osmo_sccp_addr *a, struct osmo_sccp_addr *b) -{ - char buf[256]; - osmo_strlcpy(buf, osmo_sccp_addr_dump(a), sizeof(buf)); - return !strcmp(buf, osmo_sccp_addr_dump(b)); -} - -static struct ranap_iu_rnc *iu_rnc_register(struct osmo_rnc_id *rnc_id, - const struct osmo_routing_area_id *rai, - struct osmo_sccp_addr *addr) -{ - struct ranap_iu_rnc *rnc; - struct ranap_iu_rnc *old_rnc; - struct iu_lac_rac_entry *lre; - - /* Make sure we know this rnc_id and that this SCCP address is in our records */ - rnc = iu_rnc_id_find(rnc_id); - - if (rnc) { - if (!same_sccp_addr(&rnc->sccp_addr, addr)) { - LOGPIU(LOGL_NOTICE, "RNC %s changed its SCCP addr to %s (LAC/RAC %s)\n", - osmo_rnc_id_name(rnc_id), osmo_sccp_addr_dump(addr), osmo_rai_name2(rai)); - rnc->sccp_addr = *addr; - } - } else - rnc = iu_rnc_alloc(rnc_id, addr); - - /* Detect whether the LAC,RAC is already recorded in another RNC */ - iu_rnc_lac_rac_find(&old_rnc, &lre, rai); - - if (old_rnc && old_rnc != rnc) { - /* LAC, RAC already exists in a different RNC */ - LOGPIU(LOGL_NOTICE, "LAC/RAC %s moved from RNC %s %s", - osmo_rai_name2(rai), - osmo_rnc_id_name(&old_rnc->rnc_id), osmo_sccp_addr_dump(&old_rnc->sccp_addr)); - LOGPIUC(LOGL_NOTICE, " to RNC %s %s\n", - osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(&rnc->sccp_addr)); - - llist_del(&lre->entry); - llist_add(&lre->entry, &rnc->lac_rac_list); - global_iu_event_new_area(rnc_id, rai); - } else if (!old_rnc) { - /* LAC, RAC not recorded yet */ - LOGPIU(LOGL_NOTICE, "RNC %s: new LAC/RAC %s\n", osmo_rnc_id_name(rnc_id), osmo_rai_name2(rai)); - lre = talloc_zero(rnc, struct iu_lac_rac_entry); - lre->rai = *rai; - llist_add(&lre->entry, &rnc->lac_rac_list); - global_iu_event_new_area(rnc_id, rai); - } - /* else, LAC,RAC already recorded with the current RNC. */ - - return rnc; -} - -/*********************************************************************** - * RANAP handling - ***********************************************************************/ - -int ranap_iu_rab_act(struct ranap_ue_conn_ctx *ue_ctx, struct msgb *msg) -{ - struct osmo_scu_prim *prim; - - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ue_ctx->conn_id; - osmo_prim_init(&prim->oph, - SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, - msg); - return osmo_sccp_user_sap_down(g_scu, &prim->oph); -} - -int ranap_iu_rab_deact(struct ranap_ue_conn_ctx *ue_ctx, uint8_t rab_id) -{ - /* FIXME */ - return -1; -} - -int ranap_iu_tx_sec_mode_cmd(struct ranap_ue_conn_ctx *uectx, struct osmo_auth_vector *vec, - int send_ck, int new_key) -{ - struct osmo_scu_prim *prim; - struct msgb *msg; - - /* create RANAP message */ - msg = ranap_new_msg_sec_mod_cmd(vec->ik, send_ck ? vec->ck : NULL, - new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old); - msg->l2h = msg->data; - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(g_scu, &prim->oph); -} - -int ranap_iu_tx_common_id(struct ranap_ue_conn_ctx *uectx, const char *imsi) -{ - struct msgb *msg; - struct osmo_scu_prim *prim; - - LOGPIU(LOGL_INFO, "Transmitting RANAP CommonID (SCCP conn_id %u)\n", - uectx->conn_id); - - msg = ranap_new_msg_common_id(imsi); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(g_scu, &prim->oph); -} - -static int iu_grnc_id_parse(struct osmo_rnc_id *dst, struct RANAP_GlobalRNC_ID *src) -{ - /* The size is coming from arbitrary sender, check it gracefully */ - if (src->pLMNidentity.size != 3) { - LOGPIU(LOGL_ERROR, "Invalid PLMN Identity size:" - " should be 3, is %d\n", src->pLMNidentity.size); - return -1; - } - osmo_plmn_from_bcd(&src->pLMNidentity.buf[0], &dst->plmn); - dst->rnc_id = (uint16_t)src->rNC_ID; - return 0; -} - -#if 0 -/* not used at present */ -static int iu_grnc_id_compose(struct iu_grnc_id *src, struct RANAP_GlobalRNC_ID *dst) -{ - /* 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; - return 0; -} -#endif - -struct new_ue_conn_ctx { - struct osmo_sccp_addr sccp_addr; - uint32_t conn_id; -}; - -static int ranap_handle_co_initial_ue(void *ctx, RANAP_InitialUE_MessageIEs_t *ies) -{ - struct new_ue_conn_ctx *new_ctx = ctx; - struct gprs_ra_id ra_id = {}; - struct osmo_routing_area_id ra_id2 = {}; - struct osmo_rnc_id rnc_id = {}; - 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) { - LOGPIU(LOGL_ERROR, "Failed to parse RANAP LAI IE\n"); - return -1; - } - - if (ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT) { - ra_id.rac = asn1str_to_u8(&ies->rac); - if (ra_id.rac == OSMO_RESERVED_RAC) { - LOGPIU(LOGL_ERROR, - "Rejecting RNC with invalid/internally used RAC 0x%02x\n", ra_id.rac); - return -1; - } - } else { - ra_id.rac = OSMO_RESERVED_RAC; - } - - if (iu_grnc_id_parse(&rnc_id, &ies->globalRNC_ID) != 0) { - LOGPIU(LOGL_ERROR, - "Failed to parse RANAP Global-RNC-ID IE\n"); - return -1; - } - - sai = asn1str_to_u16(&ies->sai.sAC); - msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); - memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); - - 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_register(&rnc_id, &ra_id2, &new_ctx->sccp_addr); - - ue = ue_conn_ctx_alloc(rnc, new_ctx->conn_id); - OSMO_ASSERT(ue); - ue->ra_id = ra_id; - - /* Feed into the MM layer */ - msg->dst = ue; - global_iu_recv_cb(msg, &ra_id, &sai); - - msgb_free(msg); - - return 0; -} - -static int ranap_handle_co_dt(void *ctx, RANAP_DirectTransferIEs_t *ies) -{ - struct gprs_ra_id _ra_id, *ra_id = NULL; - uint16_t _sai, *sai = NULL; - struct msgb *msg = msgb_alloc(256, "RANAP->NAS"); - - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) { - if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) { - LOGPIU(LOGL_ERROR, "Failed to parse RANAP LAI IE\n"); - return -1; - } - ra_id = &_ra_id; - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT) - _ra_id.rac = asn1str_to_u8(&ies->rac); - - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) { - _sai = asn1str_to_u16(&ies->sai.sAC); - sai = &_sai; - } - } - - msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); - memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); - - /* Feed into the MM/CC/SMS-CP layer */ - msg->dst = ctx; - global_iu_recv_cb(msg, ra_id, sai); - - msgb_free(msg); - - return 0; -} - -static int ranap_handle_co_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies) -{ - if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) - LOGPIU(LOGL_ERROR, "Rx Error Indication (%s)\n", - ranap_cause_str(&ies->cause)); - else - LOGPIU(LOGL_ERROR, "Rx Error Indication\n"); - - return 0; -} - -int ranap_iu_tx(struct msgb *msg_nas, uint8_t sapi) -{ - struct ranap_ue_conn_ctx *uectx = msg_nas->dst; - struct msgb *msg; - struct osmo_scu_prim *prim; - - if (!uectx) { - LOGPIU(LOGL_ERROR, "Discarding to-be-transmitted L3 Message as RANAP DT with unset dst SCCP conn_id!\n"); - return -ENOTCONN; - } - - LOGPIU(LOGL_INFO, "Transmitting L3 Message as RANAP DT (SCCP conn_id %u)\n", - uectx->conn_id); - - msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas)); - msgb_free(msg_nas); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(g_scu, &prim->oph); -} - -/* Send Iu Release for the given UE connection. - * If cause is NULL, Normal Release cause is sent, otherwise - * the provided cause. */ -int ranap_iu_tx_release(struct ranap_ue_conn_ctx *ctx, const struct RANAP_Cause *cause) -{ - struct msgb *msg; - struct osmo_scu_prim *prim; - static const struct RANAP_Cause default_cause = { - .present = RANAP_Cause_PR_nAS, - .choice.radioNetwork = RANAP_CauseNAS_normal_release, - }; - - if (!cause) - cause = &default_cause; - - msg = ranap_new_msg_iu_rel_cmd(cause); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ctx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - return osmo_sccp_user_sap_down(g_scu, &prim->oph); -} - -void ranap_iu_tx_release_free(struct ranap_ue_conn_ctx *ctx, - const struct RANAP_Cause *cause, - int timeout) -{ - ctx->notification = false; - ctx->free_on_release = true; - int ret = ranap_iu_tx_release(ctx, cause); - /* On Tx failure, trigger timeout immediately, as the response will never arrive */ - if (ret) - timeout = 0; - - osmo_timer_schedule(&ctx->release_timeout, timeout, 0); -} - -static int ranap_handle_co_iu_rel_req(struct ranap_ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) -{ - LOGPIU(LOGL_INFO, "Received Iu Release Request, Sending Release Command\n"); - ranap_iu_tx_release(ctx, &ies->cause); - return 0; -} - -static int ranap_handle_co_rab_ass_resp(struct ranap_ue_conn_ctx *ctx, RANAP_RAB_AssignmentResponseIEs_t *ies) -{ - int rc = -1; - - LOGPIU(LOGL_INFO, - "Rx RAB Assignment Response for UE conn_id %u\n", ctx->conn_id); - if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) { - /* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */ - RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0]; - RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies; - - rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value); - if (rc) { - LOGPIU(LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n"); - return rc; - } - - rc = global_iu_event(ctx, RANAP_IU_EVENT_RAB_ASSIGN, &setup_ies); - - ranap_free_rab_setupormodifieditemies(&setup_ies); - } - /* FIXME: handle RAB Ass failure? */ - - return rc; -} - -static void cn_ranap_handle_co_initial(void *ctx, ranap_message *message) -{ - int rc; - - LOGPIU(LOGL_NOTICE, "handle_co_initial(dir=%u, proc=%u)\n", message->direction, message->procedureCode); - - if (message->direction != RANAP_RANAP_PDU_PR_initiatingMessage - || message->procedureCode != RANAP_ProcedureCode_id_InitialUE_Message) { - LOGPIU(LOGL_ERROR, "Expected direction 'InitiatingMessage'," - " procedureCode 'InitialUE_Message', instead got %u and %u\n", - message->direction, message->procedureCode); - rc = -1; - } else - rc = ranap_handle_co_initial_ue(ctx, &message->msg.initialUE_MessageIEs); - - if (rc) { - LOGPIU(LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); - /* TODO handling of the error? */ - } -} - -/* Entry point for connection-oriented RANAP message */ -static void cn_ranap_handle_co(void *ctx, ranap_message *message) -{ - int rc; - - LOGPIU(LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode); - - switch (message->direction) { - case RANAP_RANAP_PDU_PR_initiatingMessage: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_InitialUE_Message: - LOGPIU(LOGL_ERROR, "Got InitialUE_Message but this is not a new conn\n"); - rc = -1; - break; - case RANAP_ProcedureCode_id_DirectTransfer: - rc = ranap_handle_co_dt(ctx, &message->msg.directTransferIEs); - break; - case RANAP_ProcedureCode_id_ErrorIndication: - rc = ranap_handle_co_err_ind(ctx, &message->msg.errorIndicationIEs); - break; - case RANAP_ProcedureCode_id_Iu_ReleaseRequest: - /* Iu Release Request */ - rc = ranap_handle_co_iu_rel_req(ctx, &message->msg.iu_ReleaseRequestIEs); - break; - default: - LOGPIU(LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_successfulOutcome: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_SecurityModeControl: - /* Security Mode Complete */ - rc = global_iu_event(ctx, RANAP_IU_EVENT_SECURITY_MODE_COMPLETE, NULL); - break; - case RANAP_ProcedureCode_id_Iu_Release: - /* Iu Release Complete */ - rc = global_iu_event(ctx, RANAP_IU_EVENT_IU_RELEASE, NULL); - if (rc) { - LOGPIU(LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n", - rc); - } - break; - default: - LOGPIU(LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_outcome: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_RAB_Assignment: - /* RAB Assignment Response */ - rc = ranap_handle_co_rab_ass_resp(ctx, &message->msg.raB_AssignmentResponseIEs); - break; - default: - LOGPIU(LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: - default: - LOGPIU(LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - - if (rc) { - LOGPIU(LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); - /* TODO handling of the error? */ - } -} - -static int ranap_handle_cl_reset_req(void *ctx, RANAP_ResetIEs_t *ies) -{ - struct osmo_scu_prim *prim = (struct osmo_scu_prim *) ctx; - struct osmo_scu_unitdata_param *ud_prim = &prim->u.unitdata; - RANAP_GlobalRNC_ID_t *grnc_id = NULL; - struct msgb *resp; - - OSMO_ASSERT(prim->oph.primitive == OSMO_SCU_PRIM_N_UNITDATA); - - /* FIXME: verify ies.cN_DomainIndicator */ - - if (ies->presenceMask & RESETIES_RANAP_GLOBALRNC_ID_PRESENT) - grnc_id = &ies->globalRNC_ID; - - /* send reset response */ - resp = ranap_new_msg_reset_ack(ies->cN_DomainIndicator, grnc_id); - if (!resp) - return -ENOMEM; - resp->l2h = resp->data; - return osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, &ud_prim->calling_addr, resp); -} - -static int ranap_handle_cl_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies) -{ - if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) - LOGPIU(LOGL_ERROR, "Rx Error Indication (%s)\n", - ranap_cause_str(&ies->cause)); - else - LOGPIU(LOGL_ERROR, "Rx Error Indication\n"); - - return 0; -} - -/* Entry point for connection-less RANAP message */ -static void cn_ranap_handle_cl(void *ctx, ranap_message *message) -{ - int rc; - - switch (message->direction) { - case RANAP_RANAP_PDU_PR_initiatingMessage: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_Reset: - /* received reset.req, send reset.resp */ - rc = ranap_handle_cl_reset_req(ctx, &message->msg.resetIEs); - break; - case RANAP_ProcedureCode_id_ErrorIndication: - rc = ranap_handle_cl_err_ind(ctx, &message->msg.errorIndicationIEs); - break; - default: - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_successfulOutcome: - case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: - case RANAP_RANAP_PDU_PR_outcome: - default: - rc = -1; - break; - } - - if (rc) { - LOGPIU(LOGL_ERROR, "Error in %s (%d)\n", __func__, rc); - /* TODO handling of the error? */ - } -} - -/*********************************************************************** - * Paging - ***********************************************************************/ - -/* Send a paging command down a given SCCP User. tmsi and paging_cause are - * optional and may be passed NULL and 0, respectively, to disable their use. - * See enum RANAP_PagingCause. - * - * If TMSI is given, the IMSI is not sent over the air interface. Nevertheless, - * the IMSI is still required for resolution in the HNB-GW and/or(?) RNC. */ -static int iu_tx_paging_cmd(struct osmo_sccp_addr *called_addr, - const char *imsi, const uint32_t *tmsi, - bool is_ps, uint32_t paging_cause) -{ - struct msgb *msg; - msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps ? 1 : 0, paging_cause); - msg->l2h = msg->data; - return osmo_sccp_tx_unitdata_msg(g_scu, &g_local_sccp_addr, called_addr, msg); -} - static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptmsi, uint16_t lac, uint8_t rac, bool is_ps) { @@ -841,7 +146,7 @@ iu_rnc_lac_rac_find_legacy(&rnc, NULL, lac, rac); if (rnc) { - if (iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptmsi, is_ps, 0) == 0) { + if (ranap_iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi_or_ptmsi, is_ps, 0) == 0) { log_msg = "Paging"; log_level = LOGL_DEBUG; paged = 1; @@ -896,12 +201,12 @@ int rc = 0; /* find all RNCs which are serving this LA */ - llist_for_each_entry(rnc, &rnc_list, entry) { + llist_for_each_entry(rnc, &sgsn->rnc_list, entry) { llist_for_each_entry(entry, &rnc->lac_rac_list, entry) { if (osmo_lai_cmp(&entry->rai.lac, lai)) continue; - rc = iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi, false, 0); + rc = ranap_iu_tx_paging_cmd(&rnc->sccp_addr, imsi, tmsi, false, 0); if (rc > 0) { LOGPIU(LOGL_ERROR, "IuCS: Failed to tx Paging RNC %s for LAC %s for IMSI %s / TMSI %08x", osmo_rnc_id_name(&rnc->rnc_id), @@ -942,12 +247,12 @@ int rc = 0; /* find all RNCs which are serving this RAC */ - llist_for_each_entry(rnc, &rnc_list, entry) { + llist_for_each_entry(rnc, &sgsn->rnc_list, entry) { llist_for_each_entry(entry, &rnc->lac_rac_list, entry) { if (osmo_rai_cmp(&entry->rai, rai)) continue; - rc = iu_tx_paging_cmd(&rnc->sccp_addr, imsi, ptmsi, true, 0); + rc = ranap_iu_tx_paging_cmd(&rnc->sccp_addr, imsi, ptmsi, true, 0); if (rc > 0) { LOGPIU(LOGL_ERROR, "IuPS: Failed to tx Paging RNC %s for RAC %s for IMSI %s / P-TMSI %08x", osmo_rnc_id_name(&rnc->rnc_id), @@ -975,301 +280,8 @@ * ***********************************************************************/ -int tx_unitdata(struct osmo_sccp_user *scu); -int tx_conn_req(struct osmo_sccp_user *scu, uint32_t conn_id); - -struct osmo_prim_hdr *make_conn_req(uint32_t conn_id); -struct osmo_prim_hdr *make_dt1_req(uint32_t conn_id, const uint8_t *data, unsigned int len); - -static struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param) +int ranap_iu_init(void *ctx) { - struct msgb *msg = msgb_alloc(1024, "conn_resp"); - struct osmo_scu_prim *prim; - - prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_CONNECT, - PRIM_OP_RESPONSE, msg); - memcpy(&prim->u.connect, param, sizeof(prim->u.connect)); - return &prim->oph; -} - -static void ue_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue) -{ - uint32_t conn_id = ue->conn_id; - global_iu_event(ue, RANAP_IU_EVENT_LINK_INVALIDATED, NULL); - - /* A RANAP_IU_EVENT_LINK_INVALIDATED, can lead to a free */ - ue = ue_conn_ctx_find(conn_id); - if (!ue) - return; - if (ue->free_on_release) - ranap_iu_free_ue(ue); -} - -static void handle_notice_ind(struct osmo_ss7_instance *cs7, const struct osmo_scu_notice_param *ni) -{ - struct ranap_iu_rnc *rnc; - - LOGPIU(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); - - switch (ni->cause) { - case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION: - case SCCP_RETURN_CAUSE_NETWORK_CONGESTION: - /* Transient failures (hopefully), keep going. */ - return; - default: - break; - } - - /* Messages are not arriving to RNC. Signal to user that all related ue_ctx are invalid. */ - llist_for_each_entry(rnc, &rnc_list, entry) { - struct ranap_ue_conn_ctx *ue_ctx, *ue_ctx_tmp; - if (osmo_sccp_addr_ri_cmp(&rnc->sccp_addr, &ni->calling_addr)) - continue; - LOGPIU(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); - llist_for_each_entry_safe(ue_ctx, ue_ctx_tmp, &ue_conn_ctx_list, list) { - if (ue_ctx->rnc != rnc) - continue; - ue_ctx_link_invalidated_free(ue_ctx); - } - /* TODO: ideally we'd have some event to submit to upper - * layer to inform about peer availability change... */ - } -} - -static void handle_pcstate_ind(struct osmo_ss7_instance *cs7, const struct osmo_scu_pcstate_param *pcst) -{ - struct osmo_sccp_addr rem_addr; - struct ranap_iu_rnc *rnc; - bool connected; - bool disconnected; - - LOGPIU(LOGL_DEBUG, "N-PCSTATE ind: affected_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_sccp_make_addr_pc_ssn(&rem_addr, pcst->affected_pc, OSMO_SCCP_SSN_RANAP); - - /* See if this marks the point code to have become available, or to have been lost. - * - * I want to detect two events: - * - connection event (both indicators say PC is reachable). - * - disconnection event (at least one indicator says the PC is not reachable). - * - * There are two separate incoming indicators with various possible values -- the incoming events can be: - * - * - neither connection nor disconnection indicated -- just indicating congestion - * connected == false, disconnected == false --> do nothing. - * - both incoming values indicate that we are connected - * --> trigger connected - * - both indicate we are disconnected - * --> trigger disconnected - * - one value indicates 'connected', the other indicates 'disconnected' - * --> trigger disconnected - * - * Congestion could imply that we're connected, but it does not indicate - * that a PC's reachability changed, so no need to trigger on that. - */ - connected = false; - disconnected = false; - - switch (pcst->sp_status) { - case OSMO_SCCP_SP_S_ACCESSIBLE: - connected = true; - break; - case OSMO_SCCP_SP_S_INACCESSIBLE: - disconnected = true; - break; - default: - case OSMO_SCCP_SP_S_CONGESTED: - /* Neither connecting nor disconnecting */ - break; - } - - switch (pcst->remote_sccp_status) { - case OSMO_SCCP_REM_SCCP_S_AVAILABLE: - if (!disconnected) - connected = true; - break; - case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN: - case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED: - case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE: - disconnected = true; - connected = false; - break; - default: - case OSMO_SCCP_REM_SCCP_S_CONGESTED: - /* Neither connecting nor disconnecting */ - break; - } - - if (disconnected) { - /* A previously usable RNC has disconnected. Signal to user that all related ue_ctx are invalid. */ - llist_for_each_entry(rnc, &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; - LOGPIU(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, &ue_conn_ctx_list, list) { - if (ue_ctx->rnc != rnc) - continue; - ue_ctx_link_invalidated_free(ue_ctx); - } - /* TODO: ideally we'd have some event to submit to upper - * layer to inform about peer availability change... */ - } - } else if (connected) { - llist_for_each_entry(rnc, &rnc_list, entry) { - if (osmo_sccp_addr_cmp(&rnc->sccp_addr, &rem_addr, OSMO_SCCP_ADDR_T_SSN | OSMO_SCCP_ADDR_T_PC)) - continue; - LOGPIU(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... */ - } - } -} - -static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) -{ - struct osmo_sccp_user *scu = _scu; - struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; - struct osmo_sccp_instance *sccp = osmo_sccp_get_sccp(scu); - struct osmo_prim_hdr *resp = NULL; - int rc = -1; - struct ranap_ue_conn_ctx *ue; - struct new_ue_conn_ctx new_ctx = {}; - uint32_t conn_id; - - LOGPIU(LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); - - switch (OSMO_PRIM_HDR(oph)) { - case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): - /* confirmation of outbound connection */ - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): - /* indication of new inbound connection request*/ - conn_id = prim->u.connect.conn_id; - LOGPIU(LOGL_DEBUG, "N-CONNECT.ind(X->%u)\n", conn_id); - - new_ctx.sccp_addr = prim->u.connect.calling_addr; - new_ctx.conn_id = conn_id; - - /* first ensure the local SCCP socket is ACTIVE */ - resp = make_conn_resp(&prim->u.connect); - osmo_sccp_user_sap_down(scu, resp); - /* then handle the RANAP payload */ - if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */ - !msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) { - LOGPIU(LOGL_DEBUG, - "Received N-CONNECT.ind without data\n"); - ue_conn_sccp_addr_add(conn_id, &prim->u.connect.calling_addr); - } else { - rc = ranap_cn_rx_co(cn_ranap_handle_co_initial, &new_ctx, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - } - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): - /* indication of disconnect */ - conn_id = prim->u.disconnect.conn_id; - LOGPIU(LOGL_DEBUG, "N-DISCONNECT.ind(%u)\n", conn_id); - - ue_conn_sccp_addr_del(conn_id); - ue = ue_conn_ctx_find(conn_id); - if (!ue) - break; - - rc = 0; - if (msgb_l2len(oph->msg) > 0) - rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - - /* A Iu Release event might be used to free the UE in cn_ranap_handle_co. */ - ue = ue_conn_ctx_find(conn_id); - if (!ue) - break; - ue_ctx_link_invalidated_free(ue); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): - /* connection-oriented data received */ - conn_id = prim->u.data.conn_id; - LOGPIU(LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", conn_id, - osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - - /* resolve UE context */ - ue = ue_conn_ctx_find(conn_id); - if (!ue) { - /* Could be an InitialUE-Message after an empty CR, recreate new_ctx */ - const struct osmo_sccp_addr *sccp_addr = ue_conn_sccp_addr_find(conn_id); - if (!sccp_addr) { - LOGPIU(LOGL_NOTICE, - "N-DATA.ind for unknown conn_id (%u)\n", conn_id); - break; - } - new_ctx.conn_id = conn_id; - new_ctx.sccp_addr = *sccp_addr; - ue_conn_sccp_addr_del(conn_id); - rc = ranap_cn_rx_co(cn_ranap_handle_co_initial, &new_ctx, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - } - - rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): - /* connection-less data received */ - LOGPIU(LOGL_DEBUG, "N-UNITDATA.ind(%s)\n", - osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - rc = ranap_cn_rx_cl(cn_ranap_handle_cl, prim, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION): - LOGPIU(LOGL_DEBUG, "N-NOTICE.ind(%s)\n", - osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - handle_notice_ind(osmo_sccp_get_ss7(sccp), &prim->u.notice); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION): - handle_pcstate_ind(osmo_sccp_get_ss7(sccp), &prim->u.pcstate); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_STATE, PRIM_OP_INDICATION): - LOGPIU(LOGL_DEBUG, "SCCP-User-SAP: Ignoring %s.%s\n", - osmo_scu_prim_type_name(oph->primitive), - get_value_string(osmo_prim_op_names, oph->operation)); - break; - default: - break; - } - - msgb_free(oph->msg); - return rc; -} - -int ranap_iu_init(void *ctx, int log_subsystem, const char *sccp_user_name, struct osmo_sccp_instance *sccp, - ranap_iu_recv_cb_t iu_recv_cb, ranap_iu_event_cb_t iu_event_cb) -{ - iu_log_subsystem = log_subsystem; - talloc_iu_ctx = talloc_named_const(ctx, 1, "iu"); - talloc_asn1_ctx = talloc_named_const(talloc_iu_ctx, 1, "asn1"); - - global_iu_recv_cb = iu_recv_cb; - global_iu_event_cb = iu_event_cb; - g_sccp = sccp; - osmo_sccp_local_addr_by_instance(&g_local_sccp_addr, sccp, OSMO_SCCP_SSN_RANAP); - g_scu = osmo_sccp_user_bind(g_sccp, sccp_user_name, sccp_sap_up, OSMO_SCCP_SSN_RANAP); - + talloc_asn1_ctx = talloc_named_const(ctx, 1, "asn1"); return 0; } diff --git a/src/sgsn/iu_rnc.c b/src/sgsn/iu_rnc.c new file mode 100644 index 0000000..0854f99 --- /dev/null +++ b/src/sgsn/iu_rnc.c @@ -0,0 +1,172 @@ +/* A remote RNC (Radio Network Controller), connected over IuPS */ + +/* (C) 2016-2017 by sysmocom s.f.m.c. GmbH <info(a)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/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/core/logging.h> +#include <osmocom/crypt/auth.h> +#include <osmocom/gprs/gprs_msgb.h> +#include <osmocom/sigtran/sccp_sap.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/sccp/sccp_types.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/gprs_ranap.h> +#include <osmocom/sgsn/iu_client.h> +#include <osmocom/sgsn/iu_rnc.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 osmo_sccp_addr *addr) +{ + struct ranap_iu_rnc *rnc = talloc_zero(sgsn, struct ranap_iu_rnc); + OSMO_ASSERT(rnc); + + INIT_LLIST_HEAD(&rnc->lac_rac_list); + + rnc->rnc_id = *rnc_id; + rnc->sccp_addr = *addr; + 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)); + + return rnc; +} + +/* Find a match for the given LAC (and RAC). For CS, pass rac as 0. + * If rnc and lre pointers are not NULL, *rnc / *lre are set to NULL if no match is found, or to the + * match if a match is found. Return true if a match is found. */ +static bool iu_rnc_lac_rac_find(struct ranap_iu_rnc **rnc, struct iu_lac_rac_entry **lre, + const struct osmo_routing_area_id *ra_id) +{ + struct ranap_iu_rnc *r; + struct iu_lac_rac_entry *e; + + if (rnc) + *rnc = NULL; + if (lre) + *lre = NULL; + + llist_for_each_entry(r, &sgsn->rnc_list, entry) { + llist_for_each_entry(e, &r->lac_rac_list, entry) { + if (!osmo_rai_cmp(&e->rai, ra_id)) { + if (rnc) + *rnc = r; + if (lre) + *lre = e; + return true; + } + } + } + return false; +} + +static struct ranap_iu_rnc *iu_rnc_id_find(struct osmo_rnc_id *rnc_id) +{ + struct ranap_iu_rnc *rnc; + llist_for_each_entry(rnc, &sgsn->rnc_list, entry) { + if (!osmo_rnc_id_cmp(&rnc->rnc_id, rnc_id)) + return rnc; + } + return NULL; +} + +static bool same_sccp_addr(struct osmo_sccp_addr *a, struct osmo_sccp_addr *b) +{ + char buf[256]; + osmo_strlcpy(buf, osmo_sccp_addr_dump(a), sizeof(buf)); + return !strcmp(buf, osmo_sccp_addr_dump(b)); +} + +static void global_iu_event_new_area(const struct osmo_rnc_id *rnc_id, const struct osmo_routing_area_id *rai) +{ + struct ranap_iu_event_new_area new_area = (struct ranap_iu_event_new_area) { + .rnc_id = rnc_id, + .cell_type = RANAP_IU_NEW_RAC + }; + + if (rai->rac == OSMO_RESERVED_RAC) { + new_area.cell_type = RANAP_IU_NEW_LAC; + new_area.u.lai = &rai->lac; + } else { + new_area.cell_type = RANAP_IU_NEW_RAC; + new_area.u.rai = rai; + } + + global_iu_event(NULL, RANAP_IU_EVENT_NEW_AREA, &new_area); +} + +struct ranap_iu_rnc *iu_rnc_register(struct osmo_rnc_id *rnc_id, + const struct osmo_routing_area_id *rai, + struct osmo_sccp_addr *addr) +{ + struct ranap_iu_rnc *rnc; + struct ranap_iu_rnc *old_rnc; + struct iu_lac_rac_entry *lre; + + /* Make sure we know this rnc_id and that this SCCP address is in our records */ + rnc = iu_rnc_id_find(rnc_id); + + if (rnc) { + if (!same_sccp_addr(&rnc->sccp_addr, addr)) { + LOGP(DRANAP, LOGL_NOTICE, "RNC %s changed its SCCP addr to %s (LAC/RAC %s)\n", + osmo_rnc_id_name(rnc_id), osmo_sccp_addr_dump(addr), osmo_rai_name2(rai)); + rnc->sccp_addr = *addr; + } + } else + rnc = iu_rnc_alloc(rnc_id, addr); + + /* Detect whether the LAC,RAC is already recorded in another RNC */ + iu_rnc_lac_rac_find(&old_rnc, &lre, rai); + + if (old_rnc && old_rnc != rnc) { + /* LAC, RAC already exists in a different RNC */ + LOGP(DRANAP, LOGL_NOTICE, "LAC/RAC %s moved from RNC %s %s", + osmo_rai_name2(rai), + osmo_rnc_id_name(&old_rnc->rnc_id), osmo_sccp_addr_dump(&old_rnc->sccp_addr)); + LOGPC(DRANAP, LOGL_NOTICE, " to RNC %s %s\n", + osmo_rnc_id_name(&rnc->rnc_id), osmo_sccp_addr_dump(&rnc->sccp_addr)); + + llist_del(&lre->entry); + llist_add(&lre->entry, &rnc->lac_rac_list); + global_iu_event_new_area(rnc_id, rai); + } else if (!old_rnc) { + /* LAC, RAC not recorded yet */ + LOGP(DRANAP, LOGL_NOTICE, "RNC %s: new LAC/RAC %s\n", + osmo_rnc_id_name(rnc_id), osmo_rai_name2(rai)); + lre = talloc_zero(rnc, struct iu_lac_rac_entry); + lre->rai = *rai; + llist_add(&lre->entry, &rnc->lac_rac_list); + global_iu_event_new_area(rnc_id, rai); + } + /* else, LAC,RAC already recorded with the current RNC. */ + + return rnc; +} diff --git a/src/sgsn/sccp.c b/src/sgsn/sccp.c new file mode 100644 index 0000000..68f0fea --- /dev/null +++ b/src/sgsn/sccp.c @@ -0,0 +1,432 @@ +/* SCCP Handling */ +/* (C) 2025 by sysmocom - s.f.m.c. GmbH <info(a)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/sigtran/sccp_sap.h> +#include <osmocom/sigtran/sccp_helpers.h> +#include <osmocom/sccp/sccp_types.h> + +#include <osmocom/sgsn/debug.h> +#include <osmocom/sgsn/iu_client.h> +#include <osmocom/sgsn/iu_rnc.h> +#include <osmocom/sgsn/gprs_ranap.h> +#include <osmocom/sgsn/sccp.h> +#include <osmocom/sgsn/sgsn.h> + +/* Entry to cache conn_id <-> sccp_addr mapping in case we receive an empty CR */ +struct iu_new_ctx_entry { + struct llist_head list; + + uint32_t conn_id; + struct osmo_sccp_addr sccp_addr; +}; + +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu); + +struct sgsn_sccp_user_iups *sgsn_scu_iups_inst_alloc(struct sgsn_instance *sgsn, struct osmo_sccp_instance *sccp) +{ + struct sgsn_sccp_user_iups *scu_iups; + + scu_iups = talloc_zero(sgsn, struct sgsn_sccp_user_iups); + OSMO_ASSERT(scu_iups); + + scu_iups->sgsn = sgsn; + scu_iups->sccp = sccp; + + INIT_LLIST_HEAD(&scu_iups->ue_conn_ctx_list); + INIT_LLIST_HEAD(&scu_iups->ue_conn_sccp_addr_list); + + osmo_sccp_local_addr_by_instance(&scu_iups->local_sccp_addr, scu_iups->sccp, OSMO_SCCP_SSN_RANAP); + scu_iups->scu = osmo_sccp_user_bind(scu_iups->sccp, "OsmoSGSN-IuPS", sccp_sap_up, OSMO_SCCP_SSN_RANAP); + osmo_sccp_user_set_priv(scu_iups->scu, scu_iups); + + return scu_iups; +} + +void sgsn_scu_iups_free(struct sgsn_sccp_user_iups *scu_iups) +{ + if (!scu_iups) + return; + + if (scu_iups->scu) + osmo_sccp_user_unbind(scu_iups->scu); + talloc_free(scu_iups); +} + +static struct ranap_ue_conn_ctx *ue_conn_ctx_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id) +{ + struct ranap_ue_conn_ctx *ctx; + + llist_for_each_entry(ctx, &scu_iups->ue_conn_ctx_list, list) { + if (ctx->conn_id == conn_id) + return ctx; + } + return NULL; +} + +static void ue_conn_sccp_addr_add(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id, const struct osmo_sccp_addr *calling_addr) +{ + struct iu_new_ctx_entry *entry = talloc_zero(scu_iups, struct iu_new_ctx_entry); + + entry->conn_id = conn_id; + entry->sccp_addr = *calling_addr; + + llist_add(&entry->list, &scu_iups->ue_conn_sccp_addr_list); +} + +static const struct osmo_sccp_addr *ue_conn_sccp_addr_find(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id) +{ + struct iu_new_ctx_entry *entry; + llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) { + if (entry->conn_id == conn_id) + return &entry->sccp_addr; + } + return NULL; +} + +static void ue_conn_sccp_addr_del(struct sgsn_sccp_user_iups *scu_iups, uint32_t conn_id) +{ + struct iu_new_ctx_entry *entry; + llist_for_each_entry(entry, &scu_iups->ue_conn_sccp_addr_list, list) { + if (entry->conn_id == conn_id) { + llist_del(&entry->list); + talloc_free(entry); + return; + } + } +} + +static void ue_ctx_link_invalidated_free(struct ranap_ue_conn_ctx *ue) +{ + uint32_t conn_id = ue->conn_id; + struct sgsn_sccp_user_iups *scu_iups = ue->scu_iups; + + global_iu_event(ue, RANAP_IU_EVENT_LINK_INVALIDATED, NULL); + + /* A RANAP_IU_EVENT_LINK_INVALIDATED, can lead to a free */ + ue = ue_conn_ctx_find(scu_iups, conn_id); + if (!ue) + return; + if (ue->free_on_release) + ranap_iu_free_ue(ue); +} + +static void handle_notice_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_notice_param *ni) +{ + 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); + + switch (ni->cause) { + case SCCP_RETURN_CAUSE_SUBSYSTEM_CONGESTION: + case SCCP_RETURN_CAUSE_NETWORK_CONGESTION: + /* Transient failures (hopefully), keep going. */ + return; + default: + 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) { + struct ranap_ue_conn_ctx *ue_ctx, *ue_ctx_tmp; + 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); + llist_for_each_entry_safe(ue_ctx, ue_ctx_tmp, &scu_iups->ue_conn_ctx_list, list) { + if (ue_ctx->rnc != rnc) + continue; + ue_ctx_link_invalidated_free(ue_ctx); + } + /* TODO: ideally we'd have some event to submit to upper + * layer to inform about peer availability change... */ + } +} + +static void handle_pcstate_ind(struct sgsn_sccp_user_iups *scu_iups, const struct osmo_scu_pcstate_param *pcst) +{ + struct osmo_ss7_instance *cs7 = osmo_sccp_get_ss7(scu_iups->sccp); + struct osmo_sccp_addr rem_addr; + struct ranap_iu_rnc *rnc; + bool connected; + bool disconnected; + + LOGP(DSUA, LOGL_DEBUG, "N-PCSTATE ind: affected_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_sccp_make_addr_pc_ssn(&rem_addr, pcst->affected_pc, OSMO_SCCP_SSN_RANAP); + + /* See if this marks the point code to have become available, or to have been lost. + * + * I want to detect two events: + * - connection event (both indicators say PC is reachable). + * - disconnection event (at least one indicator says the PC is not reachable). + * + * There are two separate incoming indicators with various possible values -- the incoming events can be: + * + * - neither connection nor disconnection indicated -- just indicating congestion + * connected == false, disconnected == false --> do nothing. + * - both incoming values indicate that we are connected + * --> trigger connected + * - both indicate we are disconnected + * --> trigger disconnected + * - one value indicates 'connected', the other indicates 'disconnected' + * --> trigger disconnected + * + * Congestion could imply that we're connected, but it does not indicate + * that a PC's reachability changed, so no need to trigger on that. + */ + connected = false; + disconnected = false; + + switch (pcst->sp_status) { + case OSMO_SCCP_SP_S_ACCESSIBLE: + connected = true; + break; + case OSMO_SCCP_SP_S_INACCESSIBLE: + disconnected = true; + break; + default: + case OSMO_SCCP_SP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + switch (pcst->remote_sccp_status) { + case OSMO_SCCP_REM_SCCP_S_AVAILABLE: + if (!disconnected) + connected = true; + break; + case OSMO_SCCP_REM_SCCP_S_UNAVAILABLE_UNKNOWN: + case OSMO_SCCP_REM_SCCP_S_UNEQUIPPED: + case OSMO_SCCP_REM_SCCP_S_INACCESSIBLE: + disconnected = true; + connected = false; + break; + default: + case OSMO_SCCP_REM_SCCP_S_CONGESTED: + /* Neither connecting nor disconnecting */ + break; + } + + 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_ctx_link_invalidated_free(ue_ctx); + } + /* TODO: ideally we'd have some event to submit to upper + * layer to inform about peer availability change... */ + } + } 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... */ + } + } +} + +static struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param) +{ + struct msgb *msg = msgb_alloc(1024, "conn_resp"); + struct osmo_scu_prim *prim; + + prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); + osmo_prim_init(&prim->oph, SCCP_SAP_USER, + OSMO_SCU_PRIM_N_CONNECT, + PRIM_OP_RESPONSE, msg); + memcpy(&prim->u.connect, param, sizeof(prim->u.connect)); + return &prim->oph; +} + +static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu) +{ + struct osmo_sccp_user *scu = _scu; + struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; + struct sgsn_sccp_user_iups *scu_iups = osmo_sccp_user_get_priv(scu); + struct osmo_prim_hdr *resp = NULL; + int rc = -1; + struct ranap_ue_conn_ctx *ue; + uint32_t conn_id; + + LOGP(DSUA, LOGL_DEBUG, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); + + switch (OSMO_PRIM_HDR(oph)) { + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): + /* confirmation of outbound connection */ + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): + /* indication of new inbound connection request*/ + conn_id = prim->u.connect.conn_id; + LOGP(DSUA, LOGL_DEBUG, "N-CONNECT.ind(X->%u)\n", conn_id); + + /* first ensure the local SCCP socket is ACTIVE */ + resp = make_conn_resp(&prim->u.connect); + osmo_sccp_user_sap_down(scu, resp); + /* then handle the RANAP payload */ + if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */ + !msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) { + LOGP(DSUA, LOGL_DEBUG, + "Received N-CONNECT.ind without data\n"); + ue_conn_sccp_addr_add(scu_iups, conn_id, &prim->u.connect.calling_addr); + } else { + rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &prim->u.connect.calling_addr, + conn_id, + msgb_l2(oph->msg), msgb_l2len(oph->msg)); + } + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): + /* indication of disconnect */ + conn_id = prim->u.disconnect.conn_id; + LOGP(DSUA, LOGL_DEBUG, "N-DISCONNECT.ind(%u)\n", conn_id); + + ue_conn_sccp_addr_del(scu_iups, conn_id); + ue = ue_conn_ctx_find(scu_iups, conn_id); + if (!ue) + break; + + rc = 0; + if (msgb_l2len(oph->msg) > 0) + rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + + /* A Iu Release event might be used to free the UE in cn_ranap_handle_co(). */ + ue = ue_conn_ctx_find(scu_iups, conn_id); + if (!ue) + break; + ue_ctx_link_invalidated_free(ue); + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): + /* connection-oriented data received */ + conn_id = prim->u.data.conn_id; + LOGP(DSUA, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n", conn_id, + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + + /* resolve UE context */ + ue = ue_conn_ctx_find(scu_iups, conn_id); + if (!ue) { + /* Could be an InitialUE-Message after an empty CR, recreate new_ctx */ + const struct osmo_sccp_addr *sccp_addr = ue_conn_sccp_addr_find(scu_iups, conn_id); + if (!sccp_addr) { + LOGP(DSUA, LOGL_NOTICE, + "N-DATA.ind for unknown conn_id (%u)\n", conn_id); + break; + } + /* Hold copy of address before deleting it: */ + struct osmo_sccp_addr rem_sccp_addr = *sccp_addr; + ue_conn_sccp_addr_del(scu_iups, conn_id); + rc = sgsn_ranap_iu_rx_co_initial_msg(scu_iups, &rem_sccp_addr, conn_id, + msgb_l2(oph->msg), msgb_l2len(oph->msg)); + break; + } + rc = sgsn_ranap_iu_rx_co_msg(ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): + /* connection-less data received */ + LOGP(DSUA, LOGL_DEBUG, "N-UNITDATA.ind(%s)\n", + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + rc = sgsn_ranap_iu_rx_cl_msg(scu_iups, &prim->u.unitdata, msgb_l2(oph->msg), msgb_l2len(oph->msg)); + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_NOTICE, PRIM_OP_INDICATION): + LOGP(DSUA, LOGL_DEBUG, "N-NOTICE.ind(%s)\n", + osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); + handle_notice_ind(scu_iups, &prim->u.notice); + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_PCSTATE, PRIM_OP_INDICATION): + handle_pcstate_ind(scu_iups, &prim->u.pcstate); + break; + case OSMO_PRIM(OSMO_SCU_PRIM_N_STATE, PRIM_OP_INDICATION): + LOGP(DSUA, LOGL_DEBUG, "SCCP-User-SAP: Ignoring %s.%s\n", + osmo_scu_prim_type_name(oph->primitive), + get_value_string(osmo_prim_op_names, oph->operation)); + break; + default: + break; + } + + msgb_free(oph->msg); + return rc; +} + +int sgsn_sccp_init(struct sgsn_instance *sgi) +{ + /* Note that these are mostly defaults and can be overridden from the VTY */ + sgi->sccp.sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx, + sgi->cfg.iu.cs7_instance, + "OsmoSGSN", + (23 << 3) + 4, + OSMO_SS7_ASP_PROT_M3UA, + 0, "localhost", + 0, "localhost"); + if (!sgi->sccp.sccp) { + LOGP(DGPRS, LOGL_ERROR, "Setting up SCCP instance on cs7 instance %d failed!\n", + sgi->cfg.iu.cs7_instance); + return -EINVAL; + } + osmo_sccp_set_priv(sgi->sccp.sccp, sgsn); + + sgi->sccp.scu_iups = sgsn_scu_iups_inst_alloc(sgsn, sgi->sccp.sccp); + OSMO_ASSERT(sgi->sccp.scu_iups); + + return 0; +} + +void sgsn_sccp_release(struct sgsn_instance *sgi) +{ + sgsn_scu_iups_free(sgi->sccp.scu_iups); + sgi->sccp.scu_iups = NULL; + if (sgi->sccp.sccp) { + osmo_sccp_instance_destroy(sgi->sccp.sccp); + sgi->sccp.sccp = NULL; + } +} diff --git a/src/sgsn/sgsn.c b/src/sgsn/sgsn.c index 4ebba26..c78997e 100644 --- a/src/sgsn/sgsn.c +++ b/src/sgsn/sgsn.c @@ -21,6 +21,8 @@ #include <stdint.h> +#include "config.h" + #include <osmocom/core/linuxlist.h> #include <osmocom/core/talloc.h> #include <osmocom/core/timer.h> @@ -58,6 +60,9 @@ #include <osmocom/sgsn/gtp.h> #include <osmocom/sgsn/pdpctx.h> #include <osmocom/sgsn/gprs_routing_area.h> +#if BUILD_IU +#include <osmocom/sgsn/sccp.h> +#endif /* #if BUILD_IU */ #include <time.h> @@ -152,6 +157,9 @@ static int sgsn_instance_talloc_destructor(struct sgsn_instance *sgi) { sgsn_cdr_release(sgi); +#if BUILD_IU + sgsn_sccp_release(sgi); +#endif /* #if BUILD_IU */ osmo_timer_del(&sgi->llme_timer); rate_ctr_group_free(sgi->rate_ctrs); return 0; @@ -184,6 +192,9 @@ INIT_LLIST_HEAD(&inst->mme_list); INIT_LLIST_HEAD(&inst->mm_list); INIT_LLIST_HEAD(&inst->pdp_list); +#if BUILD_IU + INIT_LLIST_HEAD(&inst->rnc_list); +#endif /* #if BUILD_IU */ osmo_timer_setup(&inst->llme_timer, sgsn_llme_check_cb, NULL); osmo_timer_schedule(&inst->llme_timer, GPRS_LLME_CHECK_TICK, 0); @@ -218,5 +229,13 @@ LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN\n"); return rc; } + +#if BUILD_IU + rc = sgsn_sccp_init(sgsn); + if (rc < 0) { + LOGP(DGPRS, LOGL_FATAL, "Cannot set up SGSN SCCP layer\n"); + return rc; + } +#endif /* #if BUILD_IU */ return 0; } diff --git a/src/sgsn/sgsn_main.c b/src/sgsn/sgsn_main.c index bebb7ae..7489790 100644 --- a/src/sgsn/sgsn_main.c +++ b/src/sgsn/sgsn_main.c @@ -372,9 +372,6 @@ int main(int argc, char **argv) { int rc; -#if BUILD_IU - struct osmo_sccp_instance *sccp; -#endif srand(time(NULL)); tall_sgsn_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); @@ -477,20 +474,7 @@ } #if BUILD_IU - /* Note that these are mostly defaults and can be overriden from the VTY */ - sccp = osmo_sccp_simple_client_on_ss7_id(tall_sgsn_ctx, - sgsn->cfg.iu.cs7_instance, - "OsmoSGSN", - (23 << 3) + 4, - OSMO_SS7_ASP_PROT_M3UA, - 0, "localhost", - 0, "localhost"); - if (!sccp) { - printf("Setting up SCCP client failed.\n"); - return 8; - } - - ranap_iu_init(tall_sgsn_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event); + ranap_iu_init(tall_sgsn_ctx); #endif if (daemonize) { diff --git a/tests/gprs_routing_area/Makefile.am b/tests/gprs_routing_area/Makefile.am index 68bf0f9..ea2779a 100644 --- a/tests/gprs_routing_area/Makefile.am +++ b/tests/gprs_routing_area/Makefile.am @@ -88,6 +88,8 @@ $(top_builddir)/src/sgsn/gprs_ranap.o \ $(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/sccp.o \ $(LIBOSMORANAP_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBASN1C_LIBS) \ diff --git a/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am index e671736..8bf78e7 100644 --- a/tests/sgsn/Makefile.am +++ b/tests/sgsn/Makefile.am @@ -102,6 +102,8 @@ $(top_builddir)/src/sgsn/gprs_ranap.o \ $(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/sccp.o \ $(LIBOSMORANAP_LIBS) \ $(LIBOSMOSIGTRAN_LIBS) \ $(LIBASN1C_LIBS) \ -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/40956?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: merged Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I4c792ae665720460b5a954b5c7cbfe5acbc37473 Gerrit-Change-Number: 40956 Gerrit-PatchSet: 5 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
1
0
0
0
[L] Change in osmo-sgsn[master]: ranap: Take into account RNC availability during paging
by lynxis lazus
03 Sep '25
03 Sep '25
Attention is currently required from: fixeria, pespin. lynxis lazus has posted comments on this change by pespin. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/41001?usp=email
) Change subject: ranap: Take into account RNC availability during paging ...................................................................... Patch Set 1: Code-Review+2 (1 comment) Patchset: PS1: a ttcn3 test case would be great! -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/41001?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I24e5446bcf4c958028577230b231960acea9e5b9 Gerrit-Change-Number: 41001 Gerrit-PatchSet: 1 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 14:08:11 +0000 Gerrit-HasComments: Yes Gerrit-Has-Labels: Yes
1
0
0
0
[M] Change in osmo-sgsn[master]: stats: Introduce stats sgsn.iu_peers.{total,active}
by lynxis lazus
03 Sep '25
03 Sep '25
Attention is currently required from: fixeria, pespin. lynxis lazus has posted comments on this change by pespin. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40990?usp=email
) Change subject: stats: Introduce stats sgsn.iu_peers.{total,active} ...................................................................... Patch Set 2: Code-Review+2 -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/40990?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I51b5227d92027f1251dc4debbbf59737e7c1a9ba Gerrit-Change-Number: 40990 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 14:02:53 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1
0
0
0
[L] Change in osmo-sgsn[master]: Introduce iu_rnc FSM
by lynxis lazus
03 Sep '25
03 Sep '25
Attention is currently required from: fixeria, pespin. lynxis lazus has posted comments on this change by pespin. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40988?usp=email
) Change subject: Introduce iu_rnc FSM ...................................................................... Patch Set 4: Code-Review+2 -- 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: comment Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: I18b7803500163e78ff6a684095194174b0fb6ee1 Gerrit-Change-Number: 40988 Gerrit-PatchSet: 4 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 14:01:31 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1
0
0
0
[M] Change in ...osmo-s1gw[master]: s1ap_proxy: remove eNB-ID from erab_uid()
by pespin
03 Sep '25
03 Sep '25
Attention is currently required from: fixeria. pespin has posted comments on this change by fixeria. (
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40994?usp=email
) Change subject: s1ap_proxy: remove eNB-ID from erab_uid() ...................................................................... Patch Set 3: Code-Review+1 -- To view, visit
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/40994?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I7277d19080795600252306dbfa0c733f996e026e Gerrit-Change-Number: 40994 Gerrit-PatchSet: 3 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 13:46:56 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1
0
0
0
[M] Change in osmo-sgsn[master]: iu_rnc: Introduce helper API iu_rnc_discard_all_ue_ctx()
by lynxis lazus
03 Sep '25
03 Sep '25
Attention is currently required from: fixeria, pespin. lynxis lazus has posted comments on this change by pespin. (
https://gerrit.osmocom.org/c/osmo-sgsn/+/40987?usp=email
) Change subject: iu_rnc: Introduce helper API iu_rnc_discard_all_ue_ctx() ...................................................................... Patch Set 2: Code-Review+2 -- To view, visit
https://gerrit.osmocom.org/c/osmo-sgsn/+/40987?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment Gerrit-Project: osmo-sgsn Gerrit-Branch: master Gerrit-Change-Id: Ie3a4732a85a69d115dd5756bfa64b296e9e5edd2 Gerrit-Change-Number: 40987 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: lynxis lazus <lynxis(a)fe80.eu> Gerrit-Reviewer: osmith <osmith(a)sysmocom.de> Gerrit-Attention: pespin <pespin(a)sysmocom.de> Gerrit-Attention: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 13:46:23 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1
0
0
0
[XS] Change in ...osmo-s1gw[master]: s1ap_proxy: add type hints for #proxy_state.erabs
by fixeria
03 Sep '25
03 Sep '25
fixeria has posted comments on this change by fixeria. (
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41003?usp=email
) Change subject: s1ap_proxy: add type hints for #proxy_state.erabs ...................................................................... Patch Set 2: Code-Review+2 -- To view, visit
https://gerrit.osmocom.org/c/erlang/osmo-s1gw/+/41003?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings?usp=email
Gerrit-MessageType: comment Gerrit-Project: erlang/osmo-s1gw Gerrit-Branch: master Gerrit-Change-Id: I08e0339bf401067eaf8ace32da619d5016fe98d7 Gerrit-Change-Number: 41003 Gerrit-PatchSet: 2 Gerrit-Owner: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de> Gerrit-Reviewer: jolly <andreas(a)eversberg.eu> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Comment-Date: Wed, 03 Sep 2025 13:43:51 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes
1
0
0
0
← Newer
1
...
104
105
106
107
108
109
110
...
122
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
Results per page:
10
25
50
100
200