mirror of
https://github.com/open5gs/open5gs.git
synced 2026-04-26 10:30:41 +00:00
Fix DNN Operator-Identifier format and refactor OI parsing for HR roaming interop
Align full-DNN construction with 3GPP TS 23.003 §9.1.2 by switching the Operator Identifier format from "5gc.mncXXX.mccYYY.3gppnetwork.org" to "mncXXX.mccYYY.gprs". Introduce new helper utilities to extract and build OI (Operator Identifier) from both PLMN-ID and FQDN, and replace the legacy `ogs_home_network_domain_from_fqdn()` usage in AMF/SMF/PCF paths. This resolves DNN misalignment in vSMF–hSMF PDU Session Create that caused interop issues with external 5G core vendors during HR roaming. Includes updates across AMF/SMF/PCF, unit tests, and supporting helpers. Issues: #4096
This commit is contained in:
parent
731ecc4e1b
commit
782a97efe9
11 changed files with 102 additions and 99 deletions
|
|
@ -61,6 +61,8 @@
|
|||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -420,3 +420,22 @@ char *ogs_trimcharacter(char *str, char to_remove)
|
|||
return ogs_right_trimcharacter(
|
||||
ogs_left_trimcharacter(str, to_remove), to_remove);
|
||||
}
|
||||
|
||||
char *ogs_strrstr(const char *haystack, const char *needle)
|
||||
{
|
||||
char *result = NULL;
|
||||
char *p = (char *)haystack;
|
||||
|
||||
if (!haystack || !needle)
|
||||
return NULL;
|
||||
|
||||
if (*needle == '\0')
|
||||
return (char *)haystack;
|
||||
|
||||
while ((p = strstr(p, needle)) != NULL) {
|
||||
result = p;
|
||||
p++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ char *ogs_left_trimcharacter(char *str, char to_remove);
|
|||
char *ogs_right_trimcharacter(char *str, char to_remove);
|
||||
char *ogs_trimcharacter(char *str, char to_remove);
|
||||
|
||||
char *ogs_strrstr(const char *haystack, const char *needle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -129,8 +129,9 @@ char *ogs_plmn_id_to_string(const ogs_plmn_id_t *plmn_id, char *buf)
|
|||
}
|
||||
|
||||
#define FQDN_3GPPNETWORK_ORG ".3gppnetwork.org"
|
||||
#define FQDN_5GC_MNC "5gc.mnc"
|
||||
#define FQDN_GPRS ".gprs"
|
||||
#define FQDN_MCC ".mcc"
|
||||
#define FQDN_MNC ".mnc"
|
||||
|
||||
char *ogs_serving_network_name_from_plmn_id(const ogs_plmn_id_t *plmn_id)
|
||||
{
|
||||
|
|
@ -165,78 +166,65 @@ char *ogs_nssf_fqdn_from_plmn_id(const ogs_plmn_id_t *plmn_id)
|
|||
ogs_plmn_id_mnc(plmn_id), ogs_plmn_id_mcc(plmn_id));
|
||||
}
|
||||
|
||||
char *ogs_home_network_domain_from_fqdn(char *fqdn)
|
||||
char *ogs_dnn_oi_from_plmn_id(const ogs_plmn_id_t *plmn_id)
|
||||
{
|
||||
char *p = NULL;
|
||||
return ogs_msprintf("mnc%03d.mcc%03d" FQDN_GPRS,
|
||||
ogs_plmn_id_mnc(plmn_id), ogs_plmn_id_mcc(plmn_id));
|
||||
}
|
||||
|
||||
char *ogs_dnn_oi_from_fqdn(char *fqdn)
|
||||
{
|
||||
char *mnc_pos = NULL;
|
||||
|
||||
ogs_assert(fqdn);
|
||||
|
||||
if (strlen(fqdn) <
|
||||
strlen(FQDN_5GC_MNC "XXX" FQDN_MCC "XXX" FQDN_3GPPNETWORK_ORG)) {
|
||||
/* Find ".mnc" from right side */
|
||||
mnc_pos = ogs_strrstr(fqdn, FQDN_MNC);
|
||||
if (!mnc_pos)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = fqdn + strlen(fqdn);
|
||||
if (strncmp(p - strlen(FQDN_3GPPNETWORK_ORG),
|
||||
FQDN_3GPPNETWORK_ORG, strlen(FQDN_3GPPNETWORK_ORG)) != 0) {
|
||||
/* Ensure minimum required length for parsing */
|
||||
if ((mnc_pos + strlen(FQDN_MNC) + 3 + strlen(FQDN_MCC) + 3) >
|
||||
fqdn + strlen(fqdn))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p -= (strlen(FQDN_3GPPNETWORK_ORG) + 3);
|
||||
if (strncmp(p - strlen(FQDN_MCC),
|
||||
FQDN_MCC, strlen(FQDN_MCC)) != 0) {
|
||||
/* Validate that ".mnc" is followed by 3 digits */
|
||||
if (!isdigit(mnc_pos[4]) ||
|
||||
!isdigit(mnc_pos[5]) ||
|
||||
!isdigit(mnc_pos[6]))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p -= (strlen(FQDN_MCC) + 3);
|
||||
if (strncmp(p - strlen(FQDN_5GC_MNC),
|
||||
FQDN_5GC_MNC, strlen(FQDN_5GC_MNC)) != 0) {
|
||||
/* Check format ".mcc" after MNC */
|
||||
if (strncmp(mnc_pos + 7, FQDN_MCC, strlen(FQDN_MCC)) != 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return p - strlen(FQDN_5GC_MNC);
|
||||
/* Validate MCC digits */
|
||||
if (!isdigit(mnc_pos[11]) ||
|
||||
!isdigit(mnc_pos[12]) ||
|
||||
!isdigit(mnc_pos[13]))
|
||||
return NULL;
|
||||
|
||||
return mnc_pos+1; /* caller will parse MNC, MCC from here */
|
||||
}
|
||||
|
||||
uint16_t ogs_plmn_id_mcc_from_fqdn(char *fqdn)
|
||||
{
|
||||
char mcc[4];
|
||||
char *p = NULL;
|
||||
|
||||
ogs_assert(fqdn);
|
||||
|
||||
p = ogs_home_network_domain_from_fqdn(fqdn);
|
||||
if (p == NULL) {
|
||||
char *p = ogs_dnn_oi_from_fqdn(fqdn);
|
||||
if (!p) {
|
||||
ogs_error("Invalid FQDN [%d:%s]", (int)strlen(fqdn), fqdn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += strlen(FQDN_5GC_MNC) + 3 + strlen(FQDN_MCC);
|
||||
|
||||
memcpy(mcc, p, 3);
|
||||
mcc[3] = 0;
|
||||
|
||||
return atoi(mcc);
|
||||
return (uint16_t)atoi(p + 10); /* after ".mcc" */
|
||||
}
|
||||
|
||||
uint16_t ogs_plmn_id_mnc_from_fqdn(char *fqdn)
|
||||
{
|
||||
char mnc[4];
|
||||
char *p = NULL;
|
||||
|
||||
ogs_assert(fqdn);
|
||||
|
||||
p = ogs_home_network_domain_from_fqdn(fqdn);
|
||||
if (p == NULL) {
|
||||
char *p = ogs_dnn_oi_from_fqdn(fqdn);
|
||||
if (!p) {
|
||||
ogs_error("Invalid FQDN [%d:%s]", (int)strlen(fqdn), fqdn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p += strlen(FQDN_5GC_MNC);
|
||||
|
||||
memcpy(mnc, p, 3);
|
||||
mnc[3] = 0;
|
||||
|
||||
return atoi(mnc);
|
||||
return (uint16_t)atoi(p + 3); /* after "mnc" */
|
||||
}
|
||||
|
||||
uint32_t ogs_amf_id_hexdump(const ogs_amf_id_t *amf_id)
|
||||
|
|
|
|||
|
|
@ -246,7 +246,8 @@ char *ogs_home_network_domain_from_plmn_id(const ogs_plmn_id_t *plmn_id);
|
|||
char *ogs_epc_domain_from_plmn_id(const ogs_plmn_id_t *plmn_id);
|
||||
char *ogs_nrf_fqdn_from_plmn_id(const ogs_plmn_id_t *plmn_id);
|
||||
char *ogs_nssf_fqdn_from_plmn_id(const ogs_plmn_id_t *plmn_id);
|
||||
char *ogs_home_network_domain_from_fqdn(char *fqdn);
|
||||
char *ogs_dnn_oi_from_plmn_id(const ogs_plmn_id_t *plmn_id);
|
||||
char *ogs_dnn_oi_from_fqdn(char *fqdn);
|
||||
uint16_t ogs_plmn_id_mnc_from_fqdn(char *fqdn);
|
||||
uint16_t ogs_plmn_id_mcc_from_fqdn(char *fqdn);
|
||||
|
||||
|
|
|
|||
|
|
@ -2907,7 +2907,7 @@ bool ogs_sbi_fqdn_in_vplmn(char *fqdn)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (ogs_home_network_domain_from_fqdn(fqdn) == NULL) {
|
||||
if (ogs_dnn_oi_from_fqdn(fqdn) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,15 +95,13 @@ ogs_sbi_request_t *amf_nsmf_pdusession_build_create_sm_context(
|
|||
* is absent, the serving core network operator shall be assumed.
|
||||
*/
|
||||
if (ogs_sbi_plmn_id_in_vplmn(&amf_ue->home_plmn_id) == true) {
|
||||
char *home_network_domain =
|
||||
ogs_home_network_domain_from_plmn_id(&amf_ue->home_plmn_id);
|
||||
ogs_assert(home_network_domain);
|
||||
char *dnn_oi = ogs_dnn_oi_from_plmn_id(&amf_ue->home_plmn_id);
|
||||
ogs_assert(dnn_oi);
|
||||
|
||||
SmContextCreateData.dnn =
|
||||
ogs_msprintf("%s.%s", sess->dnn, home_network_domain);
|
||||
SmContextCreateData.dnn = ogs_msprintf("%s.%s", sess->dnn, dnn_oi);
|
||||
ogs_assert(SmContextCreateData.dnn);
|
||||
|
||||
ogs_free(home_network_domain);
|
||||
ogs_free(dnn_oi);
|
||||
|
||||
} else {
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
|
|||
uint16_t fqdn_port = 0;
|
||||
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
|
||||
|
||||
char *home_network_domain = NULL;
|
||||
char *dnn_oi = NULL;
|
||||
|
||||
ogs_assert(sess);
|
||||
pcf_ue_sm = pcf_ue_sm_find_by_id(sess->pcf_ue_sm_id);
|
||||
|
|
@ -369,22 +369,20 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
|
|||
* The DNN of the PDU session, a full DNN with both the Network Identifier
|
||||
* and Operator Identifier, or a DNN with the Network Identifier only
|
||||
*/
|
||||
home_network_domain = ogs_home_network_domain_from_fqdn(
|
||||
SmPolicyContextData->dnn);
|
||||
dnn_oi = ogs_dnn_oi_from_fqdn(SmPolicyContextData->dnn);
|
||||
|
||||
if (home_network_domain) {
|
||||
char dnn_network_identifer[OGS_MAX_DNN_LEN+1];
|
||||
if (dnn_oi) {
|
||||
char dnn_ni[OGS_MAX_DNN_LEN+1];
|
||||
uint16_t mcc = 0, mnc = 0;
|
||||
|
||||
ogs_assert(home_network_domain > SmPolicyContextData->dnn);
|
||||
ogs_assert(dnn_oi > SmPolicyContextData->dnn);
|
||||
|
||||
ogs_cpystrn(dnn_network_identifer, SmPolicyContextData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN,
|
||||
home_network_domain - SmPolicyContextData->dnn));
|
||||
ogs_cpystrn(dnn_ni, SmPolicyContextData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN, dnn_oi - SmPolicyContextData->dnn));
|
||||
|
||||
if (sess->dnn)
|
||||
ogs_free(sess->dnn);
|
||||
sess->dnn = ogs_strdup(dnn_network_identifer);
|
||||
sess->dnn = ogs_strdup(dnn_ni);
|
||||
ogs_assert(sess->dnn);
|
||||
|
||||
if (sess->full_dnn)
|
||||
|
|
|
|||
|
|
@ -115,17 +115,16 @@ ogs_sbi_request_t *smf_npcf_smpolicycontrol_build_create(
|
|||
* and Operator Identifier, or a DNN with the Network Identifier only
|
||||
*/
|
||||
if (ogs_sbi_supi_in_vplmn(smf_ue->supi) == true) {
|
||||
char *home_network_domain = NULL;
|
||||
char *dnn_oi = NULL;
|
||||
|
||||
home_network_domain =
|
||||
ogs_home_network_domain_from_plmn_id(&sess->home_plmn_id);
|
||||
ogs_assert(home_network_domain);
|
||||
dnn_oi = ogs_dnn_oi_from_plmn_id(&sess->home_plmn_id);
|
||||
ogs_assert(dnn_oi);
|
||||
|
||||
SmPolicyContextData.dnn =
|
||||
ogs_msprintf("%s.%s", sess->session.name, home_network_domain);
|
||||
ogs_msprintf("%s.%s", sess->session.name, dnn_oi);
|
||||
ogs_assert(SmPolicyContextData.dnn);
|
||||
|
||||
ogs_free(home_network_domain);
|
||||
ogs_free(dnn_oi);
|
||||
|
||||
} else {
|
||||
SmPolicyContextData.dnn = ogs_strdup(sess->session.name);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ bool smf_nsmf_handle_create_sm_context(
|
|||
char *fqdn = NULL;
|
||||
uint16_t fqdn_port = 0;
|
||||
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
|
||||
char *home_network_domain = NULL;
|
||||
char *dnn_oi = NULL;
|
||||
|
||||
OpenAPI_sm_context_create_data_t *SmContextCreateData = NULL;
|
||||
OpenAPI_nr_location_t *NrLocation = NULL;
|
||||
|
|
@ -353,22 +353,20 @@ bool smf_nsmf_handle_create_sm_context(
|
|||
* the full DNN in LBO and non-roaming scenarios. If the Operator Identifier
|
||||
* is absent, the serving core network operator shall be assumed.
|
||||
*/
|
||||
home_network_domain =
|
||||
ogs_home_network_domain_from_fqdn(SmContextCreateData->dnn);
|
||||
dnn_oi = ogs_dnn_oi_from_fqdn(SmContextCreateData->dnn);
|
||||
|
||||
if (home_network_domain) {
|
||||
char dnn_network_identifer[OGS_MAX_DNN_LEN+1];
|
||||
if (dnn_oi) {
|
||||
char dnn_ni[OGS_MAX_DNN_LEN+1];
|
||||
uint16_t mcc = 0, mnc = 0;
|
||||
|
||||
ogs_assert(home_network_domain > SmContextCreateData->dnn);
|
||||
ogs_assert(dnn_oi > SmContextCreateData->dnn);
|
||||
|
||||
ogs_cpystrn(dnn_network_identifer, SmContextCreateData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN,
|
||||
home_network_domain - SmContextCreateData->dnn));
|
||||
ogs_cpystrn(dnn_ni, SmContextCreateData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN, dnn_oi - SmContextCreateData->dnn));
|
||||
|
||||
if (sess->session.name)
|
||||
ogs_free(sess->session.name);
|
||||
sess->session.name = ogs_strdup(dnn_network_identifer);
|
||||
sess->session.name = ogs_strdup(dnn_ni);
|
||||
ogs_assert(sess->session.name);
|
||||
|
||||
if (sess->full_dnn)
|
||||
|
|
@ -1255,7 +1253,7 @@ bool smf_nsmf_handle_create_data_in_hsmf(
|
|||
char *fqdn = NULL;
|
||||
uint16_t fqdn_port = 0;
|
||||
ogs_sockaddr_t *addr = NULL, *addr6 = NULL;
|
||||
char *home_network_domain = NULL;
|
||||
char *dnn_oi = NULL;
|
||||
|
||||
OpenAPI_pdu_session_create_data_t *PduSessionCreateData = NULL;
|
||||
OpenAPI_nr_location_t *NrLocation = NULL;
|
||||
|
|
@ -1325,21 +1323,19 @@ bool smf_nsmf_handle_create_data_in_hsmf(
|
|||
return false;
|
||||
}
|
||||
|
||||
home_network_domain =
|
||||
ogs_home_network_domain_from_fqdn(PduSessionCreateData->dnn);
|
||||
dnn_oi = ogs_dnn_oi_from_fqdn(PduSessionCreateData->dnn);
|
||||
|
||||
if (home_network_domain) {
|
||||
char dnn_network_identifer[OGS_MAX_DNN_LEN+1];
|
||||
if (dnn_oi) {
|
||||
char dnn_ni[OGS_MAX_DNN_LEN+1];
|
||||
|
||||
ogs_assert(home_network_domain > PduSessionCreateData->dnn);
|
||||
ogs_assert(dnn_oi > PduSessionCreateData->dnn);
|
||||
|
||||
ogs_cpystrn(dnn_network_identifer, PduSessionCreateData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN,
|
||||
home_network_domain - PduSessionCreateData->dnn));
|
||||
ogs_cpystrn(dnn_ni, PduSessionCreateData->dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN, dnn_oi - PduSessionCreateData->dnn));
|
||||
|
||||
if (sess->session.name)
|
||||
ogs_free(sess->session.name);
|
||||
sess->session.name = ogs_strdup(dnn_network_identifer);
|
||||
sess->session.name = ogs_strdup(dnn_ni);
|
||||
ogs_assert(sess->session.name);
|
||||
|
||||
if (sess->full_dnn)
|
||||
|
|
|
|||
|
|
@ -49,29 +49,29 @@ static void proto_message_test1(abts_case *tc, void *data)
|
|||
|
||||
static void proto_message_test2(abts_case *tc, void *data)
|
||||
{
|
||||
char *home_network_domain = NULL;
|
||||
char *dnn_oi = NULL;
|
||||
char *full_dnn = NULL;
|
||||
char dnn_ni[OGS_MAX_DNN_LEN+1];
|
||||
ogs_plmn_id_t plmn_id1, plmn_id2;
|
||||
|
||||
ogs_plmn_id_build(&plmn_id1, 456, 123, 3);
|
||||
home_network_domain = ogs_home_network_domain_from_plmn_id(&plmn_id1);
|
||||
dnn_oi = ogs_dnn_oi_from_plmn_id(&plmn_id1);
|
||||
ABTS_STR_EQUAL(tc,
|
||||
"5gc.mnc123.mcc456.3gppnetwork.org", home_network_domain);
|
||||
full_dnn = ogs_msprintf("internet.realm.%s", home_network_domain);
|
||||
"mnc123.mcc456.gprs", dnn_oi);
|
||||
full_dnn = ogs_msprintf("internet.realm.%s", dnn_oi);
|
||||
ABTS_STR_EQUAL(tc,
|
||||
"internet.realm.5gc.mnc123.mcc456.3gppnetwork.org", full_dnn);
|
||||
"internet.realm.mnc123.mcc456.gprs", full_dnn);
|
||||
ABTS_STR_EQUAL(tc,
|
||||
home_network_domain, ogs_home_network_domain_from_fqdn(full_dnn));
|
||||
dnn_oi, ogs_dnn_oi_from_fqdn(full_dnn));
|
||||
|
||||
ogs_cpystrn(dnn_ni, full_dnn,
|
||||
ogs_min(OGS_MAX_DNN_LEN,
|
||||
ogs_home_network_domain_from_fqdn(full_dnn) - full_dnn));
|
||||
ogs_dnn_oi_from_fqdn(full_dnn) - full_dnn));
|
||||
ABTS_STR_EQUAL(tc, "internet.realm", dnn_ni);
|
||||
|
||||
ABTS_INT_EQUAL(tc, 456, ogs_plmn_id_mcc_from_fqdn(full_dnn));
|
||||
ABTS_INT_EQUAL(tc, 123, ogs_plmn_id_mnc_from_fqdn(full_dnn));
|
||||
ogs_free(home_network_domain);
|
||||
ogs_free(dnn_oi);
|
||||
ogs_free(full_dnn);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue