From 1ab26c623fced86d2f0952be4709b3ceadc0fe12 Mon Sep 17 00:00:00 2001 From: Spencer Sevilla Date: Fri, 19 Jan 2024 16:30:12 -0800 Subject: [PATCH] [smf] add timeouts for Diameter (Gx/Gy) messages (#70) --- lib/app/ogs-config.c | 9 +++- lib/app/ogs-config.h | 1 + src/smf/context.h | 3 ++ src/smf/event.h | 3 +- src/smf/gsm-sm.c | 112 +++++++++++++++++++++++++++++++++++++++++++ src/smf/smf-sm.c | 9 ++++ src/smf/timer.c | 24 ++++++++++ src/smf/timer.h | 4 ++ 8 files changed, 163 insertions(+), 2 deletions(-) diff --git a/lib/app/ogs-config.c b/lib/app/ogs-config.c index 2071af9acf..f640e14948 100644 --- a/lib/app/ogs-config.c +++ b/lib/app/ogs-config.c @@ -438,7 +438,7 @@ static int local_conf_prepare(void) local_conf.time.message.sbi_set = false; local_conf.time.message.gtp_set = false; local_conf.time.message.pfcp_set = false; - + local_conf.time.message.diameter_timeout = ogs_time_from_sec(3); #define PFCP_N1_RESPONSE_RETRY_COUNT 3 #define GTP_N3_RESPONSE_RETRY_COUNT 3 @@ -647,6 +647,13 @@ int ogs_app_parse_local_conf(const char *local) local_conf.time.message.gtp.n3_response_rcount = atoi(v); regenerate_all_timer_duration(); } + } else if (!strcmp(msg_key, "diameter_timeout")) { + const char *v = ogs_yaml_iter_value(&msg_iter); + if (v) { + local_conf.time.message.diameter_timeout = + ogs_time_from_msec(atoll(v)); + regenerate_all_timer_duration(); + } } else ogs_warn("unknown key `%s`", msg_key); } diff --git a/lib/app/ogs-config.h b/lib/app/ogs-config.h index 841c2075c3..b49a0b9715 100644 --- a/lib/app/ogs-config.h +++ b/lib/app/ogs-config.h @@ -97,6 +97,7 @@ typedef struct ogs_local_conf_s { ogs_time_t sbi_duration; ogs_time_t gtp_duration; ogs_time_t pfcp_duration; + ogs_time_t diameter_timeout; bool sbi_set; bool gtp_set; bool pfcp_set; diff --git a/src/smf/context.h b/src/smf/context.h index a53504d90c..be7c3ef5b0 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -427,6 +427,9 @@ typedef struct smf_sess_s { ogs_pfcp_node_t *pfcp_node; bool pfcp_established; + ogs_timer_t *timer_gx_cca; + ogs_timer_t *timer_gy_cca; + smf_ue_t *smf_ue; bool n1_released; diff --git a/src/smf/event.h b/src/smf/event.h index 258681c6de..a01e3f361c 100644 --- a/src/smf/event.h +++ b/src/smf/event.h @@ -51,7 +51,8 @@ typedef enum { SMF_EVT_GN_MESSAGE, SMF_EVT_GX_MESSAGE, SMF_EVT_GY_MESSAGE, - + SMF_EVT_DIAMETER_TIMER, + SMF_EVT_N4_MESSAGE, SMF_EVT_N4_TIMER, SMF_EVT_N4_NO_HEARTBEAT, diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index e0a9c8c8a0..6a23f113c8 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -44,12 +44,16 @@ static uint8_t gtp_cause_from_diameter(uint8_t gtp_version, switch (dia_err) { case OGS_DIAM_UNKNOWN_SESSION_ID: return OGS_GTP1_CAUSE_APN_ACCESS_DENIED; + case ER_DIAMETER_UNABLE_TO_DELIVER: + return OGS_GTP1_CAUSE_USER_AUTHENTICATION_FAILED; } break; case 2: switch (dia_err) { case OGS_DIAM_UNKNOWN_SESSION_ID: return OGS_GTP2_CAUSE_APN_ACCESS_DENIED_NO_SUBSCRIPTION; + case ER_DIAMETER_UNABLE_TO_DELIVER: + return OGS_GTP2_CAUSE_REMOTE_PEER_NOT_RESPONDING; } break; } @@ -104,6 +108,10 @@ static bool send_ccr_init_req_gx_gy(smf_sess_t *sess, smf_event_t *e) } sess->sm_data.gx_ccr_init_in_flight = true; + sess->timer_gx_cca = ogs_timer_add(ogs_app()->timer_mgr, + smf_timer_gx_no_cca, e); + ogs_assert(sess->timer_gx_cca); + ogs_timer_start(sess->timer_gx_cca, ogs_local_conf()->time.message.diameter_timeout); smf_gx_send_ccr(sess, e->gtp_xact, OGS_DIAM_GX_CC_REQUEST_TYPE_INITIAL_REQUEST); @@ -111,6 +119,10 @@ static bool send_ccr_init_req_gx_gy(smf_sess_t *sess, smf_event_t *e) /* Gy is available, * set up session for the bearer before accepting it towards the UE */ sess->sm_data.gy_ccr_init_in_flight = true; + sess->timer_gy_cca = ogs_timer_add(ogs_app()->timer_mgr, + smf_timer_gy_no_cca, e); + ogs_assert(sess->timer_gy_cca); + ogs_timer_start(sess->timer_gy_cca, ogs_local_conf()->time.message.diameter_timeout); smf_gy_send_ccr(sess, e->gtp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_INITIAL_REQUEST); } @@ -141,6 +153,9 @@ static bool send_ccr_termination_req_gx_gy_s6b(smf_sess_t *sess, smf_event_t *e) } sess->sm_data.gx_ccr_term_in_flight = true; + sess->timer_gx_cca = ogs_timer_add(ogs_app()->timer_mgr, smf_timer_gx_no_cca, e); + ogs_assert(sess->timer_gx_cca); + ogs_timer_start(sess->timer_gx_cca, ogs_local_conf()->time.message.diameter_timeout); smf_gx_send_ccr(sess, e->gtp_xact, OGS_DIAM_GX_CC_REQUEST_TYPE_TERMINATION_REQUEST); @@ -148,6 +163,10 @@ static bool send_ccr_termination_req_gx_gy_s6b(smf_sess_t *sess, smf_event_t *e) /* Gy is available, * set up session for the bearer before accepting it towards the UE */ sess->sm_data.gy_ccr_term_in_flight = true; + sess->timer_gy_cca = ogs_timer_add(ogs_app()->timer_mgr, + smf_timer_gy_no_cca, e); + ogs_assert(sess->timer_gy_cca); + ogs_timer_start(sess->timer_gy_cca, ogs_local_conf()->time.message.diameter_timeout); smf_gy_send_ccr(sess, e->gtp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_TERMINATION_REQUEST); } @@ -227,6 +246,10 @@ void smf_gsm_state_initial(ogs_fsm_t *s, smf_event_t *e) OGS_FSM_TRAN(s, smf_gsm_state_wait_epc_auth_initial); break; case OGS_GTP2_RAT_TYPE_WLAN: + sess->timer_gx_cca = ogs_timer_add(ogs_app()->timer_mgr, + smf_timer_gx_no_cca, e); + ogs_assert(sess->timer_gx_cca); + ogs_timer_start(sess->timer_gx_cca, ogs_local_conf()->time.message.diameter_timeout); smf_s6b_send_aar(sess, e->gtp_xact); OGS_FSM_TRAN(s, smf_gsm_state_wait_epc_auth_initial); break; @@ -353,6 +376,10 @@ void smf_gsm_state_wait_epc_auth_initial(ogs_fsm_t *s, smf_event_t *e) diam_err = smf_gx_handle_cca_initial_request(sess, gx_message, e->gtp_xact); sess->sm_data.gx_ccr_init_in_flight = false; + ogs_timer_stop(sess->timer_gx_cca); + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + sess->sm_data.gx_cca_init_err = diam_err; goto test_can_proceed; } @@ -372,12 +399,37 @@ void smf_gsm_state_wait_epc_auth_initial(ogs_fsm_t *s, smf_event_t *e) diam_err = smf_gy_handle_cca_initial_request(sess, gy_message, e->gtp_xact); sess->sm_data.gy_ccr_init_in_flight = false; + ogs_timer_stop(sess->timer_gy_cca); + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + sess->sm_data.gy_cca_init_err = diam_err; goto test_can_proceed; } break; } break; + + case SMF_EVT_DIAMETER_TIMER: + switch(e->h.timer_id) { + case SMF_TIMER_GX_CCA: + ogs_error("Gx timeout waiting for CCA Init"); + sess->sm_data.gx_ccr_init_in_flight = false; + ogs_timer_stop(sess->timer_gx_cca); + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + sess->sm_data.gx_cca_init_err = ER_DIAMETER_UNABLE_TO_DELIVER; + goto test_can_proceed; + case SMF_TIMER_GY_CCA: + ogs_error("Gy timeout waiting for CCA Init"); + sess->sm_data.gy_ccr_init_in_flight = false; + ogs_timer_stop(sess->timer_gy_cca); + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + sess->sm_data.gy_cca_init_err = ER_DIAMETER_UNABLE_TO_DELIVER; + goto test_can_proceed; + } + break; } return; @@ -1432,6 +1484,9 @@ void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e) diam_err = smf_gx_handle_cca_termination_request(sess, gx_message, e->gtp_xact); sess->sm_data.gx_ccr_term_in_flight = false; + ogs_timer_stop(sess->timer_gx_cca); + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; sess->sm_data.gx_cca_term_err = diam_err; goto test_can_proceed; } @@ -1451,6 +1506,9 @@ void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e) diam_err = smf_gy_handle_cca_termination_request(sess, gy_message, e->gtp_xact); sess->sm_data.gy_ccr_term_in_flight = false; + ogs_timer_stop(sess->timer_gy_cca); + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; sess->sm_data.gy_cca_term_err = diam_err; goto test_can_proceed; } @@ -1470,6 +1528,27 @@ void smf_gsm_state_wait_epc_auth_release(ogs_fsm_t *s, smf_event_t *e) goto test_can_proceed; } break; + + case SMF_EVT_DIAMETER_TIMER: + switch(e->h.timer_id) { + case SMF_TIMER_GX_CCA: + ogs_error("Gx timeout waiting for CCA Termination"); + sess->sm_data.gx_ccr_term_in_flight = false; + ogs_timer_stop(sess->timer_gx_cca); + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + sess->sm_data.gx_cca_term_err = ER_DIAMETER_UNABLE_TO_DELIVER; + goto test_can_proceed; + case SMF_TIMER_GY_CCA: + ogs_error("Gy timeout waiting for CCA Termination"); + sess->sm_data.gy_ccr_term_in_flight = false; + ogs_timer_stop(sess->timer_gy_cca); + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + sess->sm_data.gy_cca_term_err = ER_DIAMETER_UNABLE_TO_DELIVER; + goto test_can_proceed; + } + break; } return; @@ -1881,6 +1960,15 @@ void smf_gsm_state_epc_session_will_release(ogs_fsm_t *s, smf_event_t *e) switch (e->h.id) { case OGS_FSM_ENTRY_SIG: + if (sess->timer_gx_cca) { + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + } + if (sess->timer_gy_cca) { + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + } + SMF_SESS_CLEAR(sess); break; @@ -1910,6 +1998,15 @@ void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e) switch (e->h.id) { case OGS_FSM_ENTRY_SIG: + if (sess->timer_gx_cca) { + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + } + if (sess->timer_gy_cca) { + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + } + ogs_error("[%s:%d] State machine exception", smf_ue->supi, sess->psi); SMF_SESS_CLEAR(sess); break; @@ -1925,4 +2022,19 @@ void smf_gsm_state_exception(ogs_fsm_t *s, smf_event_t *e) void smf_gsm_state_final(ogs_fsm_t *s, smf_event_t *e) { + smf_sess_t *sess = NULL; + ogs_assert(s); + ogs_assert(e); + + sess = e->sess; + ogs_assert(sess); + + if (sess->timer_gx_cca) { + ogs_timer_delete(sess->timer_gx_cca); + sess->timer_gx_cca = NULL; + } + if (sess->timer_gy_cca) { + ogs_timer_delete(sess->timer_gy_cca); + sess->timer_gy_cca = NULL; + } } diff --git a/src/smf/smf-sm.c b/src/smf/smf-sm.c index c0369fbea4..aa8e362157 100644 --- a/src/smf/smf-sm.c +++ b/src/smf/smf-sm.c @@ -357,6 +357,15 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_free(gy_message); break; + case SMF_EVT_DIAMETER_TIMER: + ogs_assert(e); + sess = e->sess; + if (sess) { + ogs_assert(OGS_FSM_STATE(&sess->sm)); + ogs_fsm_dispatch(&sess->sm, e); + } + break; + case SMF_EVT_S6B_MESSAGE: ogs_assert(e); s6b_message = e->s6b_message; diff --git a/src/smf/timer.c b/src/smf/timer.c index fa2882d75e..d8dfd56211 100644 --- a/src/smf/timer.c +++ b/src/smf/timer.c @@ -54,6 +54,7 @@ static void timer_send_event(int timer_id, void *data) { int rv; smf_event_t *e = NULL; + smf_event_t *old_e = NULL; ogs_assert(data); switch (timer_id) { @@ -64,6 +65,19 @@ static void timer_send_event(int timer_id, void *data) e->h.timer_id = timer_id; e->pfcp_node = data; break; + + case SMF_TIMER_GX_CCA: + case SMF_TIMER_GY_CCA: + old_e = data; + + e = smf_event_new(SMF_EVT_DIAMETER_TIMER); + ogs_assert(e); + e->h.timer_id = timer_id; + e->sess = old_e->sess; + e->gx_message = old_e->gx_message; + e->gtp_xact = old_e->gtp_xact; + break; + default: ogs_fatal("Unknown timer id[%d]", timer_id); ogs_assert_if_reached(); @@ -87,3 +101,13 @@ void smf_timer_pfcp_no_heartbeat(void *data) { timer_send_event(SMF_TIMER_PFCP_NO_HEARTBEAT, data); } + +void smf_timer_gx_no_cca(void *data) +{ + timer_send_event(SMF_TIMER_GX_CCA, data); +} + +void smf_timer_gy_no_cca(void *data) +{ + timer_send_event(SMF_TIMER_GY_CCA, data); +} \ No newline at end of file diff --git a/src/smf/timer.h b/src/smf/timer.h index 9a30701c70..e4c62b2164 100644 --- a/src/smf/timer.h +++ b/src/smf/timer.h @@ -33,6 +33,8 @@ typedef enum { SMF_TIMER_PFCP_ASSOCIATION, SMF_TIMER_PFCP_NO_HEARTBEAT, SMF_TIMER_PFCP_NO_ESTABLISHMENT_RESPONSE, + SMF_TIMER_GX_CCA, + SMF_TIMER_GY_CCA, MAX_NUM_OF_SMF_TIMER, @@ -42,6 +44,8 @@ const char *smf_timer_get_name(int timer_id); void smf_timer_pfcp_association(void *data); void smf_timer_pfcp_no_heartbeat(void *data); +void smf_timer_gx_no_cca(void *data); +void smf_timer_gy_no_cca(void *data); #ifdef __cplusplus }