From bc02e48d1ab3df1296ec6d4e5eb65cd95e804ca4 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Thu, 31 Oct 2024 22:20:06 +0900 Subject: [PATCH] [ePDG] Add Node-Identifier IE support in GTPv2 S2b Create-Session-Request for SMF Diameter S6b Routing (#3507) Implement support for Node-Identifier IE in GTPv2 S2b Create-Session-Request to SMF for Diameter S6b integration This patch adds support for processing the Node-Identifier IE within GTPv2 Create-Session-Request messages sent via the S2b interface to the SMF. When the ePDG includes the Node-Identifier IE containing both host and realm of the AAA-Server, the SMF now uses this information to populate the Destination-Realm and Destination-Host AVPs in the Diameter S6b AAR message. This enables seamless integration and allows the SMF to route requests directly to the appropriate AAA-Server, enhancing interoperability in setups where the host and realm data are required by the Diameter network. --- lib/gtp/v2/types.c | 104 ++++++++++++++++++++++++++++++++++++-- lib/gtp/v2/types.h | 14 +++++ src/smf/context.c | 5 ++ src/smf/context.h | 6 +++ src/smf/s5c-handler.c | 28 ++++++++++ src/smf/s6b-path.c | 21 +++++++- tests/non3gpp/s2b-build.c | 13 +++++ 7 files changed, 186 insertions(+), 5 deletions(-) diff --git a/lib/gtp/v2/types.c b/lib/gtp/v2/types.c index 949588ef4..ea8bee3d5 100644 --- a/lib/gtp/v2/types.c +++ b/lib/gtp/v2/types.c @@ -26,13 +26,15 @@ int16_t ogs_gtp2_parse_bearer_qos( ogs_gtp2_bearer_qos_t *bearer_qos, ogs_tlv_octet_t *octet) { - ogs_gtp2_bearer_qos_t *source = (ogs_gtp2_bearer_qos_t *)octet->data; + ogs_gtp2_bearer_qos_t *source = NULL; int16_t size = 0; ogs_assert(bearer_qos); ogs_assert(octet); ogs_assert(octet->len == GTP2_BEARER_QOS_LEN); + source = (ogs_gtp2_bearer_qos_t *)octet->data; + memset(bearer_qos, 0, sizeof(ogs_gtp2_bearer_qos_t)); bearer_qos->pre_emption_capability = source->pre_emption_capability; @@ -201,13 +203,15 @@ uint64_t ogs_gtp2_qos_to_kbps(uint8_t br, uint8_t extended, uint8_t extended2) int16_t ogs_gtp2_parse_flow_qos( ogs_gtp2_flow_qos_t *flow_qos, ogs_tlv_octet_t *octet) { - ogs_gtp2_flow_qos_t *source = (ogs_gtp2_flow_qos_t *)octet->data; + ogs_gtp2_flow_qos_t *source = NULL; int16_t size = 0; ogs_assert(flow_qos); ogs_assert(octet); ogs_assert(octet->len == GTP2_FLOW_QOS_LEN); + source = (ogs_gtp2_flow_qos_t *)octet->data; + memset(flow_qos, 0, sizeof(ogs_gtp2_flow_qos_t)); flow_qos->qci = source->qci; @@ -617,12 +621,14 @@ int16_t ogs_gtp2_build_tft( /* 8.21 User Location Information (ULI) */ int16_t ogs_gtp2_parse_uli(ogs_gtp2_uli_t *uli, ogs_tlv_octet_t *octet) { - ogs_gtp2_uli_t *source = (ogs_gtp2_uli_t *)octet->data; + ogs_gtp2_uli_t *source = NULL; int16_t size = 0; ogs_assert(uli); ogs_assert(octet); + source = (ogs_gtp2_uli_t *)octet->data; + memset(uli, 0, sizeof(ogs_gtp2_uli_t)); uli->flags = source->flags; @@ -796,3 +802,95 @@ int16_t ogs_gtp2_build_uli( return octet->len; } + +int16_t ogs_gtp2_parse_node_identifier( + ogs_gtp2_node_identifier_t *node_identifier, ogs_tlv_octet_t *octet) +{ + int16_t size = 0; + + ogs_assert(node_identifier); + ogs_assert(octet); + + memset(node_identifier, 0, sizeof(ogs_gtp2_node_identifier_t)); + + if (size + sizeof(node_identifier->name_len) > octet->len) { + ogs_error("Invalid TLV length [%d != %d]", size, octet->len); + ogs_log_hexdump(OGS_LOG_ERROR, octet->data, octet->len); + return size; + } + memcpy(&node_identifier->name_len, + (unsigned char *)octet->data + size, + sizeof(node_identifier->name_len)); + size += sizeof(node_identifier->name_len); + + if (size + node_identifier->name_len > octet->len) { + ogs_error("Invalid TLV length [%d != %d]", size, octet->len); + ogs_log_hexdump(OGS_LOG_ERROR, octet->data, octet->len); + return size; + } + node_identifier->name = (char *)octet->data + size; + size += node_identifier->name_len; + + if (size + sizeof(node_identifier->realm_len) > octet->len) { + ogs_error("Invalid TLV length [%d != %d]", size, octet->len); + ogs_log_hexdump(OGS_LOG_ERROR, octet->data, octet->len); + return size; + } + memcpy(&node_identifier->realm_len, + (unsigned char *)octet->data + size, + sizeof(node_identifier->realm_len)); + size += sizeof(node_identifier->realm_len); + + if (size + node_identifier->realm_len > octet->len) { + ogs_error("Invalid TLV length [%d != %d]", size, octet->len); + ogs_log_hexdump(OGS_LOG_ERROR, octet->data, octet->len); + return size; + } + node_identifier->realm = (char *)octet->data + size; + size += node_identifier->realm_len; + + if (size != octet->len) { + ogs_error("Invalid TLV length [%d != %d]", size, octet->len); + ogs_log_hexdump(OGS_LOG_ERROR, octet->data, octet->len); + } + + return size; +} +int16_t ogs_gtp2_build_node_identifier(ogs_tlv_octet_t *octet, + ogs_gtp2_node_identifier_t *node_identifier, void *data, int data_len) +{ + int16_t size = 0; + + ogs_assert(node_identifier); + ogs_assert(octet); + ogs_assert(data); + ogs_assert(data_len); + + octet->data = data; + + ogs_assert(size + sizeof(node_identifier->name_len) <= data_len); + memcpy((unsigned char *)octet->data + size, + &node_identifier->name_len, + sizeof(node_identifier->name_len)); + size += sizeof(node_identifier->name_len); + + ogs_assert(size + node_identifier->name_len <= data_len); + memcpy((unsigned char *)octet->data + size, + node_identifier->name, node_identifier->name_len); + size += node_identifier->name_len; + + ogs_assert(size + sizeof(node_identifier->realm_len) <= data_len); + memcpy((unsigned char *)octet->data + size, + &node_identifier->realm_len, + sizeof(node_identifier->realm_len)); + size += sizeof(node_identifier->realm_len); + + ogs_assert(size + node_identifier->realm_len <= data_len); + memcpy((unsigned char *)octet->data + size, + node_identifier->realm, node_identifier->realm_len); + size += node_identifier->realm_len; + + octet->len = size; + + return octet->len; +} diff --git a/lib/gtp/v2/types.h b/lib/gtp/v2/types.h index a1cc7d574..486eddbfb 100644 --- a/lib/gtp/v2/types.h +++ b/lib/gtp/v2/types.h @@ -571,6 +571,20 @@ ED5(uint8_t spare1:1;, uint8_t pre_emption_capability:1;) } __attribute__ ((packed)) ogs_gtp2_arp_t; +/* 8.107 Node Identifier */ +#define OGS_GTP2_MAX_NODE_IDENTIFIER_LEN (1+OGS_MAX_FQDN_LEN)*2 +typedef struct ogs_gtp2_node_identifier_s { + uint8_t name_len; + char *name; + uint8_t realm_len; + char *realm; +} ogs_gtp2_node_identifier_t; + +int16_t ogs_gtp2_parse_node_identifier( + ogs_gtp2_node_identifier_t *node_identifier, ogs_tlv_octet_t *octet); +int16_t ogs_gtp2_build_node_identifier(ogs_tlv_octet_t *octet, + ogs_gtp2_node_identifier_t *node_identifier, void *data, int data_len); + #ifdef __cplusplus } #endif diff --git a/src/smf/context.c b/src/smf/context.c index b2a0f26ab..bb76b77c0 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1791,6 +1791,11 @@ void smf_sess_remove(smf_sess_t *sess) /* Free SBI object memory */ ogs_sbi_object_free(&sess->sbi); + if (sess->aaa_server_identifier.name) + ogs_free(sess->aaa_server_identifier.name); + if (sess->aaa_server_identifier.realm) + ogs_free(sess->aaa_server_identifier.realm); + smf_bearer_remove_all(sess); ogs_assert(sess->pfcp.bar); diff --git a/src/smf/context.h b/src/smf/context.h index 9b3f571ed..add0d89a8 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -486,6 +486,12 @@ typedef struct smf_sess_s { uint32_t id; } charging; + /* AAA Node Identifier */ + struct { + char *name; + char *realm; + } aaa_server_identifier; + /* Data Forwarding between the CP and UP functions */ ogs_pfcp_pdr_t *cp2up_pdr; ogs_pfcp_pdr_t *up2cp_pdr; diff --git a/src/smf/s5c-handler.c b/src/smf/s5c-handler.c index 7923c55fe..fbba768cc 100644 --- a/src/smf/s5c-handler.c +++ b/src/smf/s5c-handler.c @@ -445,6 +445,34 @@ uint8_t smf_s5c_handle_create_session_request( smf_ue->imeisv, smf_ue->imeisv_len, smf_ue->imeisv_bcd); } + /* Set Node Identifier */ + if (req->_aaa_server_identifier.presence) { + ogs_gtp2_node_identifier_t node_identifier; + decoded = ogs_gtp2_parse_node_identifier( + &node_identifier, &req->_aaa_server_identifier); + if (req->_aaa_server_identifier.len == decoded) { + if (sess->aaa_server_identifier.name) + ogs_free(sess->aaa_server_identifier.name); + sess->aaa_server_identifier.name = ogs_memdup( + node_identifier.name, node_identifier.name_len+1); + ogs_assert(sess->aaa_server_identifier.name); + sess->aaa_server_identifier.name[node_identifier.name_len] = 0; + + if (sess->aaa_server_identifier.realm) + ogs_free(sess->aaa_server_identifier.realm); + sess->aaa_server_identifier.realm = ogs_memdup( + node_identifier.realm, node_identifier.realm_len+1); + ogs_assert(sess->aaa_server_identifier.realm); + sess->aaa_server_identifier.realm[node_identifier.realm_len] = 0; + } else { + ogs_error("Invalid AAA Server Identifier [%d != %d]", + req->_aaa_server_identifier.len, decoded); + ogs_log_hexdump(OGS_LOG_ERROR, + req->_aaa_server_identifier.data, + req->_aaa_server_identifier.len); + } + } + return OGS_GTP2_CAUSE_REQUEST_ACCEPTED; } diff --git a/src/smf/s6b-path.c b/src/smf/s6b-path.c index 53d87b2ce..826209653 100644 --- a/src/smf/s6b-path.c +++ b/src/smf/s6b-path.c @@ -176,11 +176,28 @@ void smf_s6b_send_aar(smf_sess_t *sess, ogs_gtp_xact_t *xact) ret = fd_msg_add_origin(req, 0); ogs_assert(ret == 0); + /* Set the Destination-Host AVP */ + if (sess->aaa_server_identifier.name) { + ret = fd_msg_avp_new(ogs_diam_destination_host, 0, &avp); + ogs_assert(ret == 0); + val.os.data = (unsigned char *)sess->aaa_server_identifier.name; + val.os.len = strlen(sess->aaa_server_identifier.name); + ret = fd_msg_avp_setvalue(avp, &val); + ogs_assert(ret == 0); + ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); + ogs_assert(ret == 0); + } + /* Set the Destination-Realm AVP */ ret = fd_msg_avp_new(ogs_diam_destination_realm, 0, &avp); ogs_assert(ret == 0); - val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); - val.os.len = strlen(fd_g_config->cnf_diamrlm); + if (sess->aaa_server_identifier.realm) { + val.os.data = (unsigned char *)(sess->aaa_server_identifier.realm); + val.os.len = strlen(sess->aaa_server_identifier.realm); + } else { + val.os.data = (unsigned char *)(fd_g_config->cnf_diamrlm); + val.os.len = strlen(fd_g_config->cnf_diamrlm); + } ret = fd_msg_avp_setvalue(avp, &val); ogs_assert(ret == 0); ret = fd_msg_avp_add(req, MSG_BRW_LAST_CHILD, avp); diff --git a/tests/non3gpp/s2b-build.c b/tests/non3gpp/s2b-build.c index bbdb46672..0ca39b5c8 100644 --- a/tests/non3gpp/s2b-build.c +++ b/tests/non3gpp/s2b-build.c @@ -46,6 +46,9 @@ ogs_pkbuf_t *test_s2b_build_create_session_request( ogs_gtp2_indication_t indication; + char node_identifier_buf[OGS_GTP2_MAX_NODE_IDENTIFIER_LEN]; + ogs_gtp2_node_identifier_t node_identifier; + ogs_assert(sess); test_ue = sess->test_ue; ogs_assert(test_ue); @@ -172,6 +175,16 @@ ogs_pkbuf_t *test_s2b_build_create_session_request( (uint8_t *)"\x80\x00\x0d\x00"; req->additional_protocol_configuration_options.len = 4; + memset(&node_identifier, 0, sizeof(ogs_gtp2_node_identifier_t)); + node_identifier.name = "aaa.localdomain"; + node_identifier.name_len = strlen(node_identifier.name); + node_identifier.realm = "localdomain"; + node_identifier.realm_len = strlen(node_identifier.realm); + req->_aaa_server_identifier.presence = 1; + ogs_gtp2_build_node_identifier( + &req->_aaa_server_identifier, &node_identifier, + node_identifier_buf, OGS_GTP2_MAX_NODE_IDENTIFIER_LEN); + gtp_message.h.type = type; return ogs_gtp2_build_msg(>p_message); }