diff --git a/lib/diameter/gy/message.h b/lib/diameter/gy/message.h index 2356738f3..5880ca59c 100644 --- a/lib/diameter/gy/message.h +++ b/lib/diameter/gy/message.h @@ -40,8 +40,10 @@ extern "C" { #define OGS_DIAM_GY_AVP_CODE_CC_REQUEST_TYPE (416) #define OGS_DIAM_GY_AVP_CODE_CC_TIME (420) #define OGS_DIAM_GY_AVP_CODE_CC_TOTAL_OCTETS (421) +#define OGS_DIAM_GY_AVP_CODE_FINAL_UNIT_INDICATION (430) #define OGS_DIAM_GY_AVP_CODE_GRANTED_SERVICE_UNIT (431) #define OGS_DIAM_GY_AVP_CODE_VALIDITY_TIME (448) +#define OGS_DIAM_GY_AVP_CODE_FINAL_UNIT_ACTION (449) #define OGS_DIAM_GY_AVP_CODE_MULTIPLE_SERVICES_CREDIT_CONTROL (456) #define OGS_DIAM_GY_AVP_CODE_SUPPORTED_FEATURES (628) #define OGS_DIAM_GY_AVP_CODE_TIME_QUOTA_THRESHOLD (868) @@ -155,6 +157,14 @@ typedef struct ogs_diam_gy_service_unit_s { uint64_t cc_output_octets; } ogs_diam_gy_service_unit_t; +typedef struct gs_diam_gy_final_unit_s { + bool cc_final_action_present; +#define OGS_DIAM_GY_FINAL_UNIT_ACTION_TERMINATE 0 +#define OGS_DIAM_GY_FINAL_UNIT_ACTION_REDIRECT 1 +#define OGS_DIAM_GY_FINAL_UNIT_ACTION_REDIRECT_ACCESS 2 + int32_t cc_final_action; +} ogs_diam_gy_final_unit_t; + typedef struct ogs_diam_gy_message_s { #define OGS_DIAM_GY_CMD_CODE_CREDIT_CONTROL 272 #define OGS_DIAM_GY_CMD_RE_AUTH 258 @@ -187,6 +197,7 @@ typedef struct ogs_diam_gy_message_s { uint32_t time_threshold; uint32_t volume_threshold; ogs_diam_gy_service_unit_t granted; + ogs_diam_gy_final_unit_t final; uint32_t result_code; uint32_t *err; } cca; diff --git a/src/smf/context.h b/src/smf/context.h index 06b9535fd..62d0ee559 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -357,6 +357,9 @@ typedef struct smf_sess_s { uint64_t dl_octets; ogs_time_t duration; uint32_t reporting_reason; /* OGS_DIAM_GY_REPORTING_REASON_* */ + /* Whether Gy Final-Unit-Indication was received. + * Triggers session release upon Rx of next PFCP Report Req */ + bool final_unit; /* Snapshot of measurement when last report was sent: */ struct { uint64_t ul_octets; diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index 7ec051e6f..b42ddbd9c 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -727,6 +727,7 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) ogs_pfcp_xact_t *pfcp_xact = NULL; ogs_pfcp_message_t *pfcp_message = NULL; + uint8_t pfcp_cause; ogs_diam_gy_message_t *gy_message = NULL; uint32_t diam_err; @@ -838,6 +839,14 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) OGS_FSM_TRAN(s, smf_gsm_state_epc_session_will_release); break; + case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: + pfcp_cause = smf_n4_handle_session_report_request(sess, pfcp_xact, + &pfcp_message->pfcp_session_report_request); + if (pfcp_cause != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) { + OGS_FSM_TRAN(s, smf_gsm_state_wait_pfcp_deletion); + } + break; + default: ogs_error("cannot handle PFCP message type[%d]", pfcp_message->h.type); diff --git a/src/smf/gy-handler.c b/src/smf/gy-handler.c index 14ef79d86..51c40ab51 100644 --- a/src/smf/gy-handler.c +++ b/src/smf/gy-handler.c @@ -164,6 +164,7 @@ uint32_t smf_gy_handle_cca_initial_request( /* Configure based on what we received from OCS: */ urr_update_time(sess, bearer->urr, gy_message); urr_update_volume(sess, bearer->urr, gy_message); + sess->gy.final_unit = gy_message->cca.final.cc_final_action_present; /* Associate acconting URR each direction PDR: */ ogs_pfcp_pdr_associate_urr(bearer->ul_pdr, bearer->urr); @@ -221,6 +222,7 @@ uint32_t smf_gy_handle_cca_update_request( urr_update_time(sess, urr, gy_message); urr_update_volume(sess, urr, gy_message); + sess->gy.final_unit = gy_message->cca.final.cc_final_action_present; /* Associate accounting URR each direction PDR: */ ogs_pfcp_pdr_associate_urr(bearer->ul_pdr, urr); ogs_pfcp_pdr_associate_urr(bearer->dl_pdr, urr); diff --git a/src/smf/gy-path.c b/src/smf/gy-path.c index 882787b1b..52a2a0225 100644 --- a/src/smf/gy-path.c +++ b/src/smf/gy-path.c @@ -48,6 +48,8 @@ static ogs_thread_mutex_t sess_state_mutex; static int decode_granted_service_unit( ogs_diam_gy_service_unit_t *su, struct avp *avpch1, int *perror); +static int decode_final_unit_indication( + ogs_diam_gy_final_unit_t *fu, struct avp *avpch1, int *perror); static void smf_gy_cca_cb(void *data, struct msg **msg); static __inline__ struct sess_state *new_state(os0_t sid) @@ -1149,6 +1151,11 @@ static void smf_gy_cca_cb(void *data, struct msg **msg) case OGS_DIAM_GY_AVP_CODE_VOLUME_QUOTA_THRESHOLD: gy_message->cca.volume_threshold = hdr->avp_value->u32; break; + case OGS_DIAM_GY_AVP_CODE_FINAL_UNIT_INDICATION: + rv = decode_final_unit_indication( + &gy_message->cca.final, avpch1, &error); + ogs_assert(rv == OGS_OK); + break; default: ogs_warn("Not supported(%d)", hdr->avp_code); break; @@ -1464,3 +1471,42 @@ static int decode_granted_service_unit( return OGS_OK; } + +static int decode_final_unit_indication( + ogs_diam_gy_final_unit_t *fu, struct avp *avpch1, int *perror) +{ + int ret = 0, error = 0; + struct avp *avpch2; + struct avp_hdr *hdr; + + ogs_assert(fu); + ogs_assert(avpch1); + memset(fu, 0, sizeof(*fu)); + + ret = fd_msg_browse(avpch1, MSG_BRW_FIRST_CHILD, &avpch2, NULL); + ogs_assert(ret == 0); + while (avpch2) { + ret = fd_msg_avp_hdr(avpch2, &hdr); + ogs_assert(ret == 0); + switch (hdr->avp_code) { + case OGS_DIAM_GY_AVP_CODE_FINAL_UNIT_ACTION: + fu->cc_final_action_present = true; + fu->cc_final_action = hdr->avp_value->i32; + break; + /* TODO: + case OGS_DIAM_GY_AVP_CODE_REDIRECT_SERVER: + case OGS_DIAM_GY_AVP_CODE_FILTER_ID: + case OGS_DIAM_GY_AVP_CODE_RESTRICTION_FILTER_RULE: + */ + default: + ogs_error("Not implemented(%d)", hdr->avp_code); + break; + } + fd_msg_browse(avpch2, MSG_BRW_NEXT, &avpch2, NULL); + } + + if (perror) + *perror = error; + + return OGS_OK; +} diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index db1f91c2f..ec1a63f9b 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -1147,7 +1147,9 @@ uint8_t smf_epc_n4_handle_session_deletion_response( return OGS_PFCP_CAUSE_REQUEST_ACCEPTED; } -void smf_n4_handle_session_report_request( +/* Returns OGS_PFCP_CAUSE_REQUEST_ACCEPTED on success, + * other cause value on failure */ +uint8_t smf_n4_handle_session_report_request( smf_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, ogs_pfcp_session_report_request_t *pfcp_req) { @@ -1185,7 +1187,7 @@ void smf_n4_handle_session_report_request( ogs_pfcp_send_error_message(pfcp_xact, 0, OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, cause_value, 0); - return; + return cause_value; } ogs_assert(sess); @@ -1225,7 +1227,7 @@ void smf_n4_handle_session_report_request( ogs_pfcp_send_error_message(pfcp_xact, 0, OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED, 0); - return; + return OGS_PFCP_CAUSE_SERVICE_NOT_SUPPORTED; } if (qfi) { @@ -1235,7 +1237,7 @@ void smf_n4_handle_session_report_request( ogs_pfcp_send_error_message(pfcp_xact, 0, OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); - return; + return OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; } } } else { @@ -1260,7 +1262,7 @@ void smf_n4_handle_session_report_request( ogs_pfcp_send_error_message(pfcp_xact, 0, OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); - return; + return OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND; } switch (sess->up_cnx_state) { @@ -1332,14 +1334,21 @@ void smf_n4_handle_session_report_request( sess->gy.reporting_reason = smf_pfcp_urr_usage_report_trigger2diam_gy_reporting_reason(&rep_trig); } - switch(smf_use_gy_iface()) { + switch (smf_use_gy_iface()) { case 1: - smf_gy_send_ccr(sess, pfcp_xact, - OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST); + if (!sess->gy.final_unit) { + smf_gy_send_ccr(sess, pfcp_xact, + OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST); + } else { + ogs_debug("[%s:%s] Rx PFCP report after Gy Final Unit Indication", + smf_ue->imsi_bcd, sess->session.name); + /* This effectively triggers session release: */ + cause_value = OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE; + } break; case -1: ogs_error("No Gy Diameter Peer"); - /* TODO: terminate connection */ + cause_value = OGS_PFCP_CAUSE_NO_RESOURCES_AVAILABLE; break; /* default: continue below */ } @@ -1379,4 +1388,5 @@ void smf_n4_handle_session_report_request( 0)); } } + return cause_value; } diff --git a/src/smf/n4-handler.h b/src/smf/n4-handler.h index 76a257d12..b5e89886e 100644 --- a/src/smf/n4-handler.h +++ b/src/smf/n4-handler.h @@ -47,7 +47,7 @@ uint8_t smf_epc_n4_handle_session_deletion_response( smf_sess_t *sess, ogs_pfcp_xact_t *xact, ogs_pfcp_session_deletion_response_t *rsp); -void smf_n4_handle_session_report_request( +uint8_t smf_n4_handle_session_report_request( smf_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, ogs_pfcp_session_report_request_t *pfcp_req); diff --git a/src/smf/pfcp-sm.c b/src/smf/pfcp-sm.c index bf0235842..2fe69d1f3 100644 --- a/src/smf/pfcp-sm.c +++ b/src/smf/pfcp-sm.c @@ -370,8 +370,14 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) case OGS_PFCP_SESSION_REPORT_REQUEST_TYPE: if (!message->h.seid_presence) ogs_error("No SEID"); - smf_n4_handle_session_report_request( - sess, xact, &message->pfcp_session_report_request); + if (!sess) { + ogs_error("No Session"); + ogs_pfcp_send_error_message(xact, 0, + OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + break; + } + ogs_fsm_dispatch(&sess->sm, e); break; default: