[SMF] Initial implementation of Final-Unit-Indication

Only "Terminate" action is implemented so far, and it will be used
regardless of the action provided by the OCS.
This commit is contained in:
Pau Espin 2024-04-04 16:05:40 +02:00 committed by Sukchan Lee
parent bbdfca29bf
commit b30604b289
8 changed files with 99 additions and 12 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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: