[SMF] Introduce optional Gy interface support (#1479)

The use of the Gy interface (SMF acting as CTF towards an OCS node) is
mandated through configuration file. Default value "enable: auto" will
only make use of it in case a Diameter peer announcing support for the
Credit-Control Application is found.

Upon subscriber session creation, and after auth check over Gx, the SMF
will create a Gy session with the OCS and only after that step the SMF
will accept the session back to the subscriber.
The OCS may then grant some traffic volumes/time and ask to be notified
back with updated measurements.
In order to get the measurements, the SMF relies on PFCP URR configured
to the UPF through Session Repoort Request messages.
When closing the subscriber session, the SMF will also terminate the Gy
session at the OCS.

So far only some specifics parts of the Gy interface as well as the PFCP
side are implemented. Those should be enough to at least have
volume/time thresholds granted by the OCS, which then will be able to
track subsriber resource use.

This patch doesn't implement the OCS side of the Gy interface, that's
left as a future exercise. The interface was tested using an OCS
emulator implemented in TTCN-3 [1]

[1] https://cgit.osmocom.org/osmo-ttcn3-hacks/
This commit is contained in:
Pau Espin 2022-04-09 01:26:28 +02:00 committed by GitHub
parent 7455424d29
commit 2be12903cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1759 additions and 10 deletions

View File

@ -233,6 +233,18 @@ logger:
# - 127.0.0.1
# - ::1
#
# <CTF>
#
# o Gy interface parameters towards OCS.
# o enabled:
# o auto: Default. Use Gy only if OCS available among Diameter peers
# o yes: Use Gy always; reject subscribers if no OCS available among Diameter peers
# o no: Don't use Gy interface if there is an OCS available
#
# ctf:
# enabled: auto|yes|no
#
#
# <SMF Selection - 5G Core only>
# 1. SMF sends SmfInfo(S-NSSAI, DNN, TAI) to the NRF
# 2. NRF responds to AMF with SmfInfo during NF-Discovery.
@ -385,6 +397,8 @@ smf:
- 2001:4860:4860::8888
- 2001:4860:4860::8844
mtu: 1400
ctf:
enabled: auto
freeDiameter: @sysconfdir@/freeDiameter/smf.conf
#

View File

@ -637,6 +637,51 @@ void ogs_pfcp_build_create_urr(
}
}
void ogs_pfcp_build_update_urr(
ogs_pfcp_tlv_update_urr_t *message, int i, ogs_pfcp_urr_t *urr, uint64_t modify_flags)
{
ogs_assert(message);
ogs_assert(urr);
/* No change requested, skip. */
if (!(modify_flags & (OGS_PFCP_MODIFY_URR_MEAS_METHOD|
OGS_PFCP_MODIFY_URR_REPORT_TRIGGER|
OGS_PFCP_MODIFY_URR_VOLUME_THRESH|
OGS_PFCP_MODIFY_URR_TIME_THRESH)))
return;
/* Change request: Send only changed IEs */
message->presence = 1;
message->urr_id.presence = 1;
message->urr_id.u32 = urr->id;
if (modify_flags & OGS_PFCP_MODIFY_URR_MEAS_METHOD) {
message->measurement_method.presence = 1;
message->measurement_method.u8 = urr->meas_method;
}
if (modify_flags & OGS_PFCP_MODIFY_URR_REPORT_TRIGGER) {
message->reporting_triggers.presence = 1;
message->reporting_triggers.u24 = (urr->rep_triggers.reptri_5 << 16)
| (urr->rep_triggers.reptri_6 << 8)
| (urr->rep_triggers.reptri_7);
}
if (modify_flags & OGS_PFCP_MODIFY_URR_VOLUME_THRESH) {
if (urr->vol_threshold.flags) {
message->volume_threshold.presence = 1;
ogs_pfcp_build_volume(
&message->volume_threshold, &urr->vol_threshold,
&urrbuf[i].vol_threshold, sizeof(urrbuf[i].vol_threshold));
}
}
if (modify_flags & OGS_PFCP_MODIFY_URR_TIME_THRESH) {
if (urr->time_threshold) {
message->time_threshold.presence = 1;
message->time_threshold.u32 = urr->time_threshold;
}
}
}
static struct {
char mbr[OGS_PFCP_BITRATE_LEN];
char gbr[OGS_PFCP_BITRATE_LEN];

View File

@ -59,6 +59,8 @@ void ogs_pfcp_build_update_qer(
void ogs_pfcp_build_create_urr(
ogs_pfcp_tlv_create_urr_t *message, int i, ogs_pfcp_urr_t *urr);
void ogs_pfcp_build_update_urr(
ogs_pfcp_tlv_update_urr_t *message, int i, ogs_pfcp_urr_t *urr, uint64_t modify_flags);
void ogs_pfcp_build_create_bar(
ogs_pfcp_tlv_create_bar_t *message, ogs_pfcp_bar_t *bar);

View File

@ -97,6 +97,11 @@ typedef struct ogs_pfcp_xact_s {
#define OGS_PFCP_MODIFY_XN_HANDOVER ((uint64_t)1<<21)
#define OGS_PFCP_MODIFY_N2_HANDOVER ((uint64_t)1<<22)
#define OGS_PFCP_MODIFY_HANDOVER_CANCEL ((uint64_t)1<<23)
#define OGS_PFCP_MODIFY_URR ((uint64_t)1<<24) /* type of trigger */
#define OGS_PFCP_MODIFY_URR_MEAS_METHOD ((uint64_t)1<<25)
#define OGS_PFCP_MODIFY_URR_REPORT_TRIGGER ((uint64_t)1<<26)
#define OGS_PFCP_MODIFY_URR_VOLUME_THRESH ((uint64_t)1<<27)
#define OGS_PFCP_MODIFY_URR_TIME_THRESH ((uint64_t)1<<28)
uint64_t modify_flags;

View File

@ -38,6 +38,27 @@ static int num_of_smf_sess = 0;
static void stats_add_smf_session(void);
static void stats_remove_smf_session(void);
int smf_ctf_config_init(smf_ctf_config_t *ctf_config)
{
ctf_config->enabled = SMF_CTF_ENABLED_AUTO;
return OGS_OK;
}
/* Shall Gy session be used according to policy and state? 1: yes, 0: no, -1: reject */
int smf_use_gy_iface()
{
switch (smf_self()->ctf_config.enabled) {
case SMF_CTF_ENABLED_AUTO:
return ogs_diam_app_connected(OGS_DIAM_GY_APPLICATION_ID) ? 1 : 0;
case SMF_CTF_ENABLED_YES:
return ogs_diam_app_connected(OGS_DIAM_GY_APPLICATION_ID) ? 1 : -1;
case SMF_CTF_ENABLED_NO:
return 0;
default:
return -1;
}
}
void smf_context_init(void)
{
ogs_assert(context_initialized == 0);
@ -47,6 +68,7 @@ void smf_context_init(void)
/* Initialize SMF context */
memset(&self, 0, sizeof(smf_context_t));
smf_ctf_config_init(&self.ctf_config);
self.diam_config = &g_diam_conf;
ogs_log_install_domain(&__ogs_ngap_domain, "ngap", ogs_core()->log.level);
@ -331,6 +353,32 @@ int smf_context_parse_config(void)
ogs_warn("unknown key `%s`", fd_key);
}
}
} else if (!strcmp(smf_key, "ctf")) {
ogs_yaml_iter_t ctf_iter;
yaml_node_t *node =
yaml_document_get_node(document, smf_iter.pair->value);
ogs_assert(node);
ogs_assert(node->type == YAML_MAPPING_NODE);
ogs_yaml_iter_recurse(&smf_iter, &ctf_iter);
while (ogs_yaml_iter_next(&ctf_iter)) {
const char *ctf_key = ogs_yaml_iter_key(&ctf_iter);
ogs_assert(ctf_key);
if (!strcmp(ctf_key, "enabled")) {
yaml_node_t *ctf_node =
yaml_document_get_node(document, ctf_iter.pair->value);
ogs_assert(ctf_node->type == YAML_SCALAR_NODE);
const char* enabled = ogs_yaml_iter_value(&ctf_iter);
if (!strcmp(enabled, "auto"))
self.ctf_config.enabled = SMF_CTF_ENABLED_AUTO;
else if (!strcmp(enabled, "yes"))
self.ctf_config.enabled = SMF_CTF_ENABLED_YES;
else if (!strcmp(enabled, "no"))
self.ctf_config.enabled = SMF_CTF_ENABLED_NO;
else
ogs_warn("unknown 'enabled' value `%s`", enabled);
} else
ogs_warn("unknown key `%s`", ctf_key);
}
} else if (!strcmp(smf_key, "gtpc")) {
/* handle config in gtp library */
} else if (!strcmp(smf_key, "gtpu")) {

View File

@ -24,6 +24,7 @@
#include "ogs-gtp.h"
#include "ogs-diameter-gx.h"
#include "ogs-diameter-gy.h"
#include "ogs-diameter-rx.h"
#include "ogs-diameter-s6b.h"
#include "ogs-pfcp.h"
@ -50,7 +51,20 @@ extern int __gsm_log_domain;
#undef OGS_LOG_DOMAIN
#define OGS_LOG_DOMAIN __smf_log_domain
typedef enum {
SMF_CTF_ENABLED_AUTO = 0,
SMF_CTF_ENABLED_YES,
SMF_CTF_ENABLED_NO,
} smf_ctf_enabled_mode;
typedef struct smf_ctf_config_s {
smf_ctf_enabled_mode enabled;
} smf_ctf_config_t;
int smf_ctf_config_init(smf_ctf_config_t *ctf_config);
typedef struct smf_context_s {
smf_ctf_config_t ctf_config;
const char* diam_conf_path; /* SMF Diameter conf path */
ogs_diam_config_t *diam_config; /* SMF Diameter config */
@ -217,6 +231,7 @@ typedef struct smf_sess_s {
ogs_ip_t gnb_n3_ip; /* gNB-N3 IPv4/IPv6 */
char *gx_sid; /* Gx Session ID */
char *gy_sid; /* Gx Session ID */
char *s6b_sid; /* S6b Session ID */
OGS_POOL(pf_precedence_pool, uint8_t);
@ -291,6 +306,18 @@ typedef struct smf_sess_s {
uint8_t nsapi;
} gtp1; /* GTPv1C specific fields */
struct {
uint64_t ul_octets;
uint64_t dl_octets;
ogs_time_t duration;
/* Snapshot of measurement when last report was sent: */
struct {
uint64_t ul_octets;
uint64_t dl_octets;
ogs_time_t duration;
} last_report;
} gy;
struct {
ogs_nas_extended_protocol_configuration_options_t ue_pco;
} nas; /* Saved from NAS-5GS */
@ -358,6 +385,8 @@ smf_context_t *smf_self(void);
int smf_context_parse_config(void);
int smf_use_gy_iface(void);
smf_ue_t *smf_ue_add_by_supi(char *supi);
smf_ue_t *smf_ue_add_by_imsi(uint8_t *imsi, int imsi_len);
void smf_ue_remove(smf_ue_t *smf_ue);

View File

@ -68,6 +68,8 @@ const char *smf_event_get_name(smf_event_t *e)
return "SMF_EVT_GN_MESSAGE";
case SMF_EVT_GX_MESSAGE:
return "SMF_EVT_GX_MESSAGE";
case SMF_EVT_GY_MESSAGE:
return "SMF_EVT_GY_MESSAGE";
case SMF_EVT_N4_MESSAGE:
return "SMF_EVT_N4_MESSAGE";
case SMF_EVT_N4_TIMER:

View File

@ -32,6 +32,7 @@ typedef struct ogs_pfcp_node_s ogs_pfcp_node_t;
typedef struct ogs_pfcp_xact_s ogs_pfcp_xact_t;
typedef struct ogs_pfcp_message_s ogs_pfcp_message_t;
typedef struct ogs_diam_gx_message_s ogs_diam_gx_message_t;
typedef struct ogs_diam_gy_message_s ogs_diam_gy_message_t;
typedef struct smf_sess_s smf_sess_t;
typedef struct smf_upf_s smf_upf_t;
typedef struct ogs_sbi_request_s ogs_sbi_request_t;
@ -48,6 +49,7 @@ typedef enum {
SMF_EVT_S5C_MESSAGE,
SMF_EVT_GN_MESSAGE,
SMF_EVT_GX_MESSAGE,
SMF_EVT_GY_MESSAGE,
SMF_EVT_N4_MESSAGE,
SMF_EVT_N4_TIMER,
@ -79,7 +81,10 @@ typedef struct smf_event_s {
ogs_pfcp_xact_t *pfcp_xact;
ogs_pfcp_message_t *pfcp_message;
ogs_diam_gx_message_t *gx_message;
union {
ogs_diam_gx_message_t *gx_message;
ogs_diam_gy_message_t *gy_message;
};
struct {
ogs_sbi_request_t *request;

View File

@ -36,6 +36,9 @@ int smf_fd_init(void)
ogs_assert(rv == 0);
rv = smf_gx_init();
ogs_assert(rv == OGS_OK);
rv = smf_gy_init();
ogs_assert(rv == OGS_OK);
rv = ogs_diam_rx_init();

View File

@ -33,11 +33,15 @@ void smf_fd_final(void);
int smf_gx_init(void);
void smf_gx_final(void);
int smf_gy_init(void);
void smf_gy_final(void);
int smf_s6b_init(void);
void smf_s6b_final(void);
void smf_gx_send_ccr(smf_sess_t *sess, ogs_gtp_xact_t *xact,
uint32_t cc_request_type);
void smf_gy_send_ccr(smf_sess_t *sess, void *xact,
uint32_t cc_request_type);
void smf_s6b_send_aar(smf_sess_t *sess, ogs_gtp_xact_t *xact);
void smf_s6b_send_str(smf_sess_t *sess, ogs_gtp_xact_t *xact, uint32_t cause);
@ -47,4 +51,3 @@ void smf_s6b_send_str(smf_sess_t *sess, ogs_gtp_xact_t *xact, uint32_t cause);
#endif
#endif /* SMF_FD_PATH_H */

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This file is part of Open5GS.
*
@ -20,6 +21,7 @@
#include "context.h"
#include "gtp-path.h"
#include "pfcp-path.h"
#include "fd-path.h"
#include "gx-handler.h"
#include "binding.h"
@ -271,8 +273,29 @@ void smf_gx_handle_cca_initial_request(
ogs_pfcp_pdr_associate_qer(ul_pdr, qer);
}
ogs_assert(OGS_OK ==
smf_epc_pfcp_send_session_establishment_request(sess, gtp_xact));
switch(smf_use_gy_iface()) {
case 1:
/* Gy is available, set up session for the bearer before accepting it towards the UE */
smf_gy_send_ccr(sess, gtp_xact,
OGS_DIAM_GY_CC_REQUEST_TYPE_INITIAL_REQUEST);
return;
case 0:
/* Not using Gy, jump directly to PFCP Session Establishment Request */
ogs_assert(OGS_OK ==
smf_epc_pfcp_send_session_establishment_request(sess, gtp_xact));
return;
case -1:
ogs_error("No Gy Diameter Peer");
if (gtp_xact->gtp_version == 1)
ogs_gtp1_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP1_CREATE_PDP_CONTEXT_RESPONSE_TYPE,
OGS_GTP1_CAUSE_NO_RESOURCES_AVAILABLE);
else
ogs_gtp2_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE,
OGS_GTP_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER);
return;
}
}
void smf_gx_handle_cca_termination_request(

260
src/smf/gy-handler.c Normal file
View File

@ -0,0 +1,260 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This file is part of Open5GS.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "context.h"
#include "gtp-path.h"
#include "pfcp-path.h"
#include "gy-handler.h"
#include "binding.h"
static uint8_t gtp_cause_from_diameter(
const uint32_t *dia_err, const uint32_t *dia_exp_err)
{
if (dia_exp_err) {
}
if (dia_err) {
switch (*dia_err) {
case OGS_DIAM_UNKNOWN_SESSION_ID:
return OGS_GTP_CAUSE_APN_ACCESS_DENIED_NO_SUBSCRIPTION;
}
}
ogs_error("Unexpected Diameter Result Code %d/%d, defaulting to severe "
"network failure",
dia_err ? *dia_err : -1, dia_exp_err ? *dia_exp_err : -1);
return OGS_GTP_CAUSE_UE_NOT_AUTHORISED_BY_OCS_OR_EXTERNAL_AAA_SERVER;
}
static void urr_enable_total_volume_threshold(smf_sess_t *sess, ogs_pfcp_urr_t *urr,
uint64_t total_volume_threshold)
{
ogs_debug("Adding CC Grant total_octets=%" PRIu64, total_volume_threshold);
urr->meas_method |= OGS_PFCP_MEASUREMENT_METHOD_VOLUME;
urr->rep_triggers.volume_threshold = 1;
urr->vol_threshold.tovol = 1;
urr->vol_threshold.total_volume = total_volume_threshold;
if (sess->pfcp_node->up_function_features.mnop)
urr->meas_info.mnop = 1;
}
static void urr_disable_total_volume_threshold(ogs_pfcp_urr_t *urr)
{
urr->meas_method &= ~OGS_PFCP_MEASUREMENT_METHOD_VOLUME;
urr->rep_triggers.volume_threshold = 0;
urr->vol_threshold.tovol = 0;
urr->vol_threshold.total_volume = 0;
}
static void urr_update_total_volume_threshold(smf_sess_t *sess, ogs_pfcp_urr_t *urr, ogs_diam_gy_message_t *gy_message)
{
if (gy_message->cca.granted.cc_total_octets_present)
urr_enable_total_volume_threshold(sess, urr, gy_message->cca.granted.cc_total_octets);
else
urr_disable_total_volume_threshold(urr);
}
static void urr_update_time_threshold(ogs_pfcp_urr_t *urr, ogs_diam_gy_message_t *gy_message)
{
uint32_t time_threshold;
if (gy_message->cca.validity_time && (gy_message->cca.granted.cc_time_present && gy_message->cca.granted.cc_time > 0))
time_threshold = (gy_message->cca.validity_time <= gy_message->cca.granted.cc_time) ?
gy_message->cca.validity_time : gy_message->cca.granted.cc_time;
else if (gy_message->cca.validity_time)
time_threshold = gy_message->cca.validity_time;
else if (gy_message->cca.granted.cc_time_present && gy_message->cca.granted.cc_time > 0)
time_threshold = gy_message->cca.granted.cc_time;
else
time_threshold = 0;
if (time_threshold) {
ogs_debug("Adding CC Grant time=%" PRIu32, time_threshold);
urr->meas_method |= OGS_PFCP_MEASUREMENT_METHOD_DURATION;
urr->rep_triggers.time_threshold = 1;
urr->time_threshold = time_threshold;
urr->meas_info.istm = 1;
} else {
urr->meas_method &= ~OGS_PFCP_MEASUREMENT_METHOD_DURATION;
urr->rep_triggers.time_threshold = 0;
urr->time_threshold = 0;
}
}
void smf_gy_handle_cca_initial_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_gtp_xact_t *gtp_xact)
{
smf_bearer_t *bearer;
ogs_assert(sess);
ogs_assert(gy_message);
ogs_assert(gtp_xact);
ogs_debug("[Gy CCA Initial]");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->smf_n4_teid);
if (gy_message->result_code != ER_DIAMETER_SUCCESS) {
uint8_t cause_value = gtp_cause_from_diameter(
gy_message->err, gy_message->exp_err);
if (gtp_xact->gtp_version == 1)
ogs_gtp1_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP1_CREATE_PDP_CONTEXT_RESPONSE_TYPE, cause_value);
else
ogs_gtp_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0,
OGS_GTP_CREATE_SESSION_RESPONSE_TYPE, cause_value);
return;
}
bearer = smf_default_bearer_in_sess(sess);
ogs_assert(bearer);
if (!bearer->urr)
bearer->urr = ogs_pfcp_urr_add(&sess->pfcp);
ogs_assert(bearer->urr);
/* Configure based on what we received from OCS: */
urr_update_time_threshold(bearer->urr, gy_message);
urr_update_total_volume_threshold(sess, bearer->urr, gy_message);
/* Associate acconting URR each direction PDR: */
ogs_pfcp_pdr_associate_urr(bearer->ul_pdr, bearer->urr);
ogs_pfcp_pdr_associate_urr(bearer->dl_pdr, bearer->urr);
ogs_assert(OGS_OK ==
smf_epc_pfcp_send_session_establishment_request(sess, gtp_xact));
}
void smf_gy_handle_cca_update_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_pfcp_xact_t *pfcp_xact)
{
ogs_pfcp_urr_t *urr = NULL;
smf_bearer_t *bearer;
int rv;
uint64_t modify_flags = 0;
ogs_pfcp_measurement_method_t prev_meas_method;
ogs_pfcp_reporting_triggers_t prev_rep_triggers;
ogs_pfcp_volume_threshold_t prev_vol_threshold;
ogs_pfcp_time_threshold_t prev_time_threshold;
ogs_assert(sess);
ogs_assert(gy_message);
ogs_assert(pfcp_xact);
ogs_debug("[Gy CCA Update]");
ogs_debug(" SGW_S5C_TEID[0x%x] PGW_S5C_TEID[0x%x]",
sess->sgw_s5c_teid, sess->smf_n4_teid);
if (gy_message->result_code != ER_DIAMETER_SUCCESS) {
ogs_warn("Gy CCA Update Diameter failure: res=%u err=%u",
gy_message->result_code, *gy_message->err);
// TODO: generate new gtp_xact from sess here? */
//ogs_assert(OGS_OK ==
// smf_epc_pfcp_send_session_deletion_request(sess, gtp_xact));
return;
}
bearer = smf_default_bearer_in_sess(sess);
ogs_assert(bearer);
urr = bearer->urr;
ogs_assert(urr);
prev_meas_method = urr->meas_method;
prev_rep_triggers = urr->rep_triggers;
prev_vol_threshold = urr->vol_threshold;
prev_time_threshold = urr->time_threshold;
urr_update_time_threshold(urr, gy_message);
urr_update_total_volume_threshold(sess, urr, gy_message);
ogs_pfcp_pdr_associate_urr(bearer->ul_pdr, urr);
if (urr->meas_method != prev_meas_method)
modify_flags |= OGS_PFCP_MODIFY_URR_MEAS_METHOD;
if (urr->rep_triggers.time_threshold != prev_rep_triggers.time_threshold ||
urr->rep_triggers.volume_threshold != prev_rep_triggers.volume_threshold)
modify_flags |= OGS_PFCP_MODIFY_URR_REPORT_TRIGGER;
if (urr->time_threshold != prev_time_threshold)
modify_flags |= OGS_PFCP_MODIFY_URR_TIME_THRESH;
if (urr->vol_threshold.tovol != prev_vol_threshold.tovol ||
urr->vol_threshold.total_volume != prev_vol_threshold.total_volume)
modify_flags |= OGS_PFCP_MODIFY_URR_VOLUME_THRESH;
/* Send PFCP Session Modification Request if we need to update the params. */
if (modify_flags) {
modify_flags |= OGS_PFCP_MODIFY_URR|OGS_PFCP_MODIFY_UL_ONLY;
rv = smf_epc_pfcp_send_session_modification_request(sess, pfcp_xact,
modify_flags,
OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED,
OGS_GTP1_CAUSE_REACTIACTION_REQUESTED);
ogs_assert(rv == OGS_OK);
}
}
void smf_gy_handle_cca_termination_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_gtp_xact_t *gtp_xact)
{
ogs_assert(sess);
ogs_assert(gy_message);
ogs_assert(gtp_xact);
ogs_debug("[SMF] Delete Session Response");
ogs_debug(" SGW_S5C_TEID[0x%x] SMF_N4_TEID[0x%x]",
sess->sgw_s5c_teid, sess->smf_n4_teid);
if (gtp_xact) {
/*
* 1. MME sends Delete Session Request to SGW/SMF.
* 2. SMF sends Delete Session Response to SGW/MME.
*/
switch (gtp_xact->gtp_version) {
case 1:
ogs_assert(OGS_OK == smf_gtp1_send_delete_pdp_context_response(sess, gtp_xact));
break;
case 2:
ogs_assert(OGS_OK == smf_gtp_send_delete_session_response(sess, gtp_xact));
break;
}
} else {
/*
* 1. SMF sends Delete Bearer Request(DEFAULT BEARER) to SGW/MME.
* 2. MME sends Delete Bearer Response to SGW/SMF.
*
* OR
*
* 1. SMF sends Delete Bearer Request(DEFAULT BEARER) to ePDG.
* 2. ePDG sends Delete Bearer Response(DEFAULT BEARER) to SMF.
*
* Note that the following messages are not processed here.
* - Bearer Resource Command
* - Delete Bearer Request/Response with DEDICATED BEARER.
*/
}
SMF_SESS_CLEAR(sess);
return;
}
void smf_gy_handle_re_auth_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message)
{
/* TODO: find out what to do here */
}

46
src/smf/gy-handler.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* This file is part of Open5GS.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SMF_GY_HANDLER_H
#define SMF_GY_HANDLER_H
#include "context.h"
#ifdef __cplusplus
extern "C" {
#endif
void smf_gy_handle_cca_initial_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_gtp_xact_t *gtp_xact);
void smf_gy_handle_cca_update_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_pfcp_xact_t *gtp_xact);
void smf_gy_handle_cca_termination_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message,
ogs_gtp_xact_t *gtp_xact);
void smf_gy_handle_re_auth_request(
smf_sess_t *sess, ogs_diam_gy_message_t *gy_message);
#ifdef __cplusplus
}
#endif
#endif /* SMF_GY_HANDLER_H */

1129
src/smf/gy-path.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,7 @@ libsmf_sources = files('''
s5c-handler.h
fd-path.h
gx-handler.h
gy-handler.h
pfcp-path.h
n4-build.h
n4-handler.h
@ -82,8 +83,10 @@ libsmf_sources = files('''
s5c-handler.c
fd-path.c
gx-path.c
gy-path.c
s6b-path.c
gx-handler.c
gy-handler.c
pfcp-path.c
n4-build.c
n4-handler.c
@ -113,6 +116,7 @@ libsmf = static_library('smf',
libngap_dep,
libnas_5gs_dep,
libdiameter_gx_dep,
libdiameter_gy_dep,
libdiameter_s6b_dep,
libgtp_dep,
libpfcp_dep],
@ -125,6 +129,7 @@ libsmf_dep = declare_dependency(
libngap_dep,
libnas_5gs_dep,
libdiameter_gx_dep,
libdiameter_gy_dep,
libdiameter_s6b_dep,
libgtp_dep,
libpfcp_dep])

View File

@ -114,6 +114,8 @@ ogs_pkbuf_t *smf_n4_build_session_modification_request(
uint8_t type, smf_sess_t *sess, uint64_t modify_flags)
{
ogs_pfcp_pdr_t *pdr = NULL;
ogs_pfcp_urr_t *urr = NULL;
int i;
ogs_pfcp_message_t pfcp_message;
ogs_pfcp_session_modification_request_t *req = NULL;
@ -201,7 +203,7 @@ ogs_pkbuf_t *smf_n4_build_session_modification_request(
&req->update_far[num_of_update_far],
num_of_update_far, far);
num_of_update_far++;
} else {
} else if (modify_flags == 0) {
ogs_fatal("Invalid modify_flags = %lld",
(long long)modify_flags);
ogs_assert_if_reached();
@ -210,6 +212,13 @@ ogs_pkbuf_t *smf_n4_build_session_modification_request(
}
/* Update URR */
i = 0;
ogs_list_for_each(&sess->pfcp.urr_list, urr) {
ogs_pfcp_build_update_urr(&req->update_urr[i], i, urr, modify_flags);
i++;
}
pfcp_message.h.type = type;
pkbuf = ogs_pfcp_build_msg(&pfcp_message);

View File

@ -26,6 +26,7 @@
#include "binding.h"
#include "sbi-path.h"
#include "ngap-path.h"
#include "fd-path.h"
uint8_t gtp_cause_from_pfcp(uint8_t pfcp_cause, uint8_t gtp_version)
{
@ -927,7 +928,6 @@ void smf_epc_n4_handle_session_modification_response(
if (flags & OGS_PFCP_MODIFY_SESSION) {
/* If smf_epc_pfcp_send_session_modification_request() is called */
} else {
/* If smf_epc_pfcp_send_bearer_modification_request() is called */
bearer = xact->data;
@ -936,9 +936,14 @@ void smf_epc_n4_handle_session_modification_response(
flags = xact->modify_flags;
ogs_assert(flags);
gtp_xact = xact->assoc_xact;
gtp_pti = xact->gtp_pti;
gtp_cause = xact->gtp_cause;
/* OGS_PFCP_MODIFY_URR: Modification Response was originally triggered by
PFCP Session Report Request, xact->assoc_xact is not a gtp_xact. No
need to do anything. */
if (!(flags & OGS_PFCP_MODIFY_URR)) {
gtp_xact = xact->assoc_xact;
gtp_pti = xact->gtp_pti;
gtp_cause = xact->gtp_cause;
}
ogs_pfcp_xact_commit(xact);
@ -1126,6 +1131,8 @@ void smf_epc_n4_handle_session_deletion_response(
uint8_t cause_value = 0;
uint8_t resp_type = 0;
ogs_gtp_xact_t *gtp_xact = NULL;
smf_bearer_t *bearer = NULL;
unsigned int i;
ogs_assert(xact);
ogs_assert(rsp);
@ -1176,6 +1183,38 @@ void smf_epc_n4_handle_session_deletion_response(
ogs_assert(sess);
bearer = smf_default_bearer_in_sess(sess);
for (i = 0; i < OGS_ARRAY_SIZE(rsp->usage_report); i++) {
ogs_pfcp_tlv_usage_report_session_deletion_response_t *use_rep = &rsp->usage_report[i];
uint32_t urr_id;
ogs_pfcp_volume_measurement_t volume;
if (use_rep->presence == 0)
break;
if (use_rep->urr_id.presence == 0)
continue;
urr_id = use_rep->urr_id.u32;
if (!bearer || !bearer->urr || bearer->urr->id != urr_id)
continue;
ogs_pfcp_parse_volume_measurement(&volume, &use_rep->volume_measurement);
if (volume.ulvol)
sess->gy.ul_octets += volume.uplink_volume;
if (volume.dlvol)
sess->gy.dl_octets += volume.downlink_volume;
sess->gy.duration += use_rep->duration_measurement.u32;
}
switch(smf_use_gy_iface()) {
case 1:
/* Gy is available, terminate the Gy session before terminating it towards the UE */
smf_gy_send_ccr(sess, gtp_xact,
OGS_DIAM_GY_CC_REQUEST_TYPE_TERMINATION_REQUEST);
return;
case -1:
ogs_error("No Gy Diameter Peer");
break; /* continue below */
/* default: continue below */
}
if (gtp_xact) {
/*
* 1. MME sends Delete Session Request to SGW/SMF.
@ -1213,11 +1252,13 @@ void smf_n4_handle_session_report_request(
ogs_pfcp_session_report_request_t *pfcp_req)
{
smf_bearer_t *qos_flow = NULL;
smf_bearer_t *bearer = NULL;
ogs_pfcp_pdr_t *pdr = NULL;
ogs_pfcp_report_type_t report_type;
uint8_t cause_value = 0;
uint16_t pdr_id = 0;
unsigned int i;
ogs_assert(pfcp_xact);
ogs_assert(pfcp_req);
@ -1343,8 +1384,42 @@ void smf_n4_handle_session_report_request(
}
}
if (report_type.usage_report) {
bearer = smf_default_bearer_in_sess(sess);
for (i = 0; i < OGS_ARRAY_SIZE(pfcp_req->usage_report); i++) {
ogs_pfcp_tlv_usage_report_session_report_request_t *use_rep = &pfcp_req->usage_report[i];
uint32_t urr_id;
ogs_pfcp_volume_measurement_t volume;
if (use_rep->presence == 0)
break;
if (use_rep->urr_id.presence == 0)
continue;
urr_id = use_rep->urr_id.u32;
if (!bearer || !bearer->urr || bearer->urr->id != urr_id)
continue;
ogs_pfcp_parse_volume_measurement(&volume, &use_rep->volume_measurement);
if (volume.ulvol)
sess->gy.ul_octets += volume.uplink_volume;
if (volume.dlvol)
sess->gy.dl_octets += volume.downlink_volume;
sess->gy.duration += use_rep->duration_measurement.u32;
}
switch(smf_use_gy_iface()) {
case 1:
smf_gy_send_ccr(sess, pfcp_xact, OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST);
break;
case -1:
ogs_error("No Gy Diameter Peer");
/* TODO: terminate connection */
break;
/* default: continue below */
}
}
/* TS 29.244 sec 8.2.21: At least one bit shall be set to "1". Several bits may be set to "1". */
if (report_type.downlink_data_report || report_type.error_indication_report) {
if (report_type.downlink_data_report ||
report_type.error_indication_report ||
report_type.usage_report) {
ogs_assert(OGS_OK ==
smf_pfcp_send_session_report_response(
pfcp_xact, sess, OGS_PFCP_CAUSE_REQUEST_ACCEPTED));

View File

@ -25,6 +25,7 @@
#include "s5c-handler.h"
#include "gn-handler.h"
#include "gx-handler.h"
#include "gy-handler.h"
#include "nnrf-handler.h"
#include "namf-handler.h"
#include "npcf-handler.h"
@ -60,6 +61,7 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_gtp1_message_t gtp1_message;
ogs_diam_gx_message_t *gx_message = NULL;
ogs_diam_gy_message_t *gy_message = NULL;
ogs_pfcp_node_t *pfcp_node = NULL;
ogs_pfcp_xact_t *pfcp_xact = NULL;
@ -269,6 +271,50 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_session_data_free(&gx_message->session_data);
ogs_free(gx_message);
break;
case SMF_EVT_GY_MESSAGE:
ogs_assert(e);
gy_message = e->gy_message;
ogs_assert(gy_message);
sess = e->sess;
ogs_assert(sess);
switch(gy_message->cmd_code) {
case OGS_DIAM_GY_CMD_CODE_CREDIT_CONTROL:
switch(gy_message->cc_request_type) {
case OGS_DIAM_GY_CC_REQUEST_TYPE_INITIAL_REQUEST:
ogs_assert(e->gtp_xact);
smf_gy_handle_cca_initial_request(
sess, gy_message, e->gtp_xact);
break;
case OGS_DIAM_GY_CC_REQUEST_TYPE_UPDATE_REQUEST:
ogs_assert(e->pfcp_xact);
smf_gy_handle_cca_update_request(
sess, gy_message, e->pfcp_xact);
break;
case OGS_DIAM_GY_CC_REQUEST_TYPE_TERMINATION_REQUEST:
ogs_assert(e->gtp_xact);
smf_gy_handle_cca_termination_request(
sess, gy_message, e->gtp_xact);
break;
default:
ogs_error("Not implemented(%d)", gy_message->cc_request_type);
break;
}
break;
case OGS_DIAM_GY_CMD_RE_AUTH:
smf_gy_handle_re_auth_request(sess, gy_message);
break;
default:
ogs_error("Invalid type(%d)", gy_message->cmd_code);
break;
}
ogs_free(gy_message);
break;
case SMF_EVT_N4_MESSAGE:
ogs_assert(e);
recvbuf = e->pkbuf;