diff --git a/lib/core/ogs-conv.c b/lib/core/ogs-conv.c index 709df4ee4..921556c02 100644 --- a/lib/core/ogs-conv.c +++ b/lib/core/ogs-conv.c @@ -161,6 +161,12 @@ char ogs_from_hex(char ch) return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10; } +char ogs_to_hex(char ch) +{ + static char hex[] = "0123456789ABCDEF"; + return hex[ch & 15]; +} + char *ogs_uint24_to_0string(ogs_uint24_t x) { return ogs_msprintf("%06x", x.v); diff --git a/lib/core/ogs-conv.h b/lib/core/ogs-conv.h index 6065314e0..df092e40c 100644 --- a/lib/core/ogs-conv.h +++ b/lib/core/ogs-conv.h @@ -44,6 +44,7 @@ void *ogs_bcd_to_buffer_reverse_order(const char *in, void *out, int *out_len); void *ogs_buffer_to_bcd(uint8_t *in, int in_len, void *out); char ogs_from_hex(char ch); +char ogs_to_hex(char ch); char *ogs_uint24_to_0string(ogs_uint24_t x); char *ogs_uint28_to_0string(uint32_t x); diff --git a/lib/sbi/context.c b/lib/sbi/context.c index 3e6f04d57..c1bcb4670 100644 --- a/lib/sbi/context.c +++ b/lib/sbi/context.c @@ -1316,6 +1316,62 @@ ogs_sbi_nf_info_t *ogs_sbi_nf_info_find( return NULL; } +bool ogs_sbi_check_smf_info_slice( + ogs_sbi_smf_info_t *smf_info, ogs_s_nssai_t *s_nssai, char *dnn) +{ + int i, j; + + ogs_assert(smf_info); + ogs_assert(s_nssai); + ogs_assert(dnn); + + for (i = 0; i < smf_info->num_of_slice; i++) { + if (s_nssai->sst == smf_info->slice[i].s_nssai.sst && + s_nssai->sd.v == smf_info->slice[i].s_nssai.sd.v) { + + for (j = 0; j < smf_info->slice[i].num_of_dnn; j++) { + if (ogs_strcasecmp(dnn, smf_info->slice[i].dnn[j]) == 0) + return true; + } + } + } + + return false; +} +bool ogs_sbi_check_smf_info_tai( + ogs_sbi_smf_info_t *smf_info, ogs_5gs_tai_t *tai) +{ + int i, j; + + ogs_assert(smf_info); + ogs_assert(tai); + + if (smf_info->num_of_nr_tai == 0 && smf_info->num_of_nr_tai_range == 0) + return true; + + for (i = 0; i < smf_info->num_of_nr_tai; i++) { + if (memcmp(&tai->plmn_id, + &smf_info->nr_tai[i].plmn_id, OGS_PLMN_ID_LEN) == 0) { + if (tai->tac.v == smf_info->nr_tai[i].tac.v) + return true; + } + } + + for (i = 0; i < smf_info->num_of_nr_tai_range; i++) { + if (memcmp(&tai->plmn_id, + &smf_info->nr_tai_range[i].plmn_id, OGS_PLMN_ID_LEN) == 0) { + for (j = 0; j < smf_info->nr_tai_range[i].num_of_tac_range; j++) { + if (tai->tac.v >= smf_info->nr_tai_range[i].start[j].v && + tai->tac.v <= smf_info->nr_tai_range[i].end[j].v) { + return true; + } + } + } + } + + return false; +} + void ogs_sbi_nf_instance_build_default(ogs_sbi_nf_instance_t *nf_instance) { ogs_sbi_server_t *server = NULL; @@ -1547,6 +1603,8 @@ bool ogs_sbi_discovery_option_is_matched( OpenAPI_nf_type_e requester_nf_type, ogs_sbi_discovery_option_t *discovery_option) { + ogs_sbi_nf_info_t *nf_info = NULL; + ogs_assert(nf_instance); ogs_assert(requester_nf_type); ogs_assert(discovery_option); @@ -1581,6 +1639,30 @@ bool ogs_sbi_discovery_option_is_matched( if (exist == false) return false; } + ogs_list_for_each(&nf_instance->nf_info_list, nf_info) { + if (nf_instance->nf_type != nf_info->nf_type) { + ogs_error("Invalid NF-Type [%d:%d]", + nf_instance->nf_type, nf_info->nf_type); + return false; + } + + switch (nf_info->nf_type) { + case OpenAPI_nf_type_SMF: + if (discovery_option->num_of_snssais && discovery_option->dnn && + ogs_sbi_check_smf_info_slice(&nf_info->smf, + &discovery_option->snssais[0], + discovery_option->dnn) == false) + return false; + if (discovery_option->num_of_tai && + ogs_sbi_check_smf_info_tai(&nf_info->smf, + &discovery_option->tai[0]) == false) + return false; + break; + default: + break; + } + } + return true; } diff --git a/lib/sbi/context.h b/lib/sbi/context.h index 68dac7b52..c08add26c 100644 --- a/lib/sbi/context.h +++ b/lib/sbi/context.h @@ -402,6 +402,11 @@ void ogs_sbi_nf_info_remove_all(ogs_list_t *list); ogs_sbi_nf_info_t *ogs_sbi_nf_info_find( ogs_list_t *list, OpenAPI_nf_type_e nf_type); +bool ogs_sbi_check_smf_info_slice( + ogs_sbi_smf_info_t *smf_info, ogs_s_nssai_t *s_nssai, char *dnn); +bool ogs_sbi_check_smf_info_tai( + ogs_sbi_smf_info_t *smf_info, ogs_5gs_tai_t *tai); + void ogs_sbi_nf_instance_build_default(ogs_sbi_nf_instance_t *nf_instance); ogs_sbi_nf_service_t *ogs_sbi_nf_service_build_default( ogs_sbi_nf_instance_t *nf_instance, const char *name); @@ -424,7 +429,9 @@ int ogs_sbi_client_default_port(void); ogs_assert(__nFInstance); \ \ if ((__cTX).nf_instance) { \ - ogs_warn("NF Instance updated [%s]", (__nFInstance)->id); \ + ogs_warn("NF Instance [%s] updated [%s]", \ + OpenAPI_nf_type_ToString((__nFInstance)->nf_type), \ + (__nFInstance)->id); \ ogs_sbi_nf_instance_remove((__cTX).nf_instance); \ } \ \ diff --git a/lib/sbi/conv.c b/lib/sbi/conv.c index f3122a8bc..939f581c2 100644 --- a/lib/sbi/conv.c +++ b/lib/sbi/conv.c @@ -364,10 +364,51 @@ char *ogs_sbi_client_apiroot(ogs_sbi_client_t *client) * Thanks Geek Hideout! * http://www.geekhideout.com/urlcode.shtml */ -static char *url_decode(const char *str) +char *ogs_sbi_url_encode(const char *str) { if (str != NULL) { - char *pstr = (char*)str; + char *pstr = (char *)str; + char *buf = ogs_malloc(strlen(str) * 3 + 1); + char *pbuf = buf; + ogs_assert(buf); + while (*pstr) { + + if (*pstr == '"' || + *pstr == '(' || + *pstr == ')' || + *pstr == ',' || + *pstr == '/' || + *pstr == ':' || + *pstr == ';' || + *pstr == '<' || + *pstr == '=' || + *pstr == '>' || + *pstr == '?' || + *pstr == '@' || + *pstr == '[' || + *pstr == '\\' || + *pstr == ']' || + *pstr == '{' || + *pstr == '}') { + *pbuf++ = '%'; + *pbuf++ = ogs_to_hex(*pstr >> 4); + *pbuf++ = ogs_to_hex(*pstr & 15); + } else + *pbuf++ = *pstr; + + pstr++; + } + *pbuf = '\0'; + return buf; + } else { + return NULL; + } +} + +char *ogs_sbi_url_decode(const char *str) +{ + if (str != NULL) { + char *pstr = (char *)str; char *buf = ogs_malloc(strlen(str) + 1); char *pbuf = buf; ogs_assert(buf); @@ -396,7 +437,7 @@ char *ogs_sbi_parse_uri(char *uri, const char *delim, char **saveptr) { char *item = NULL; - item = url_decode(ogs_strtok_r(uri, delim, saveptr)); + item = ogs_sbi_url_decode(ogs_strtok_r(uri, delim, saveptr)); if (!item) { return NULL; } diff --git a/lib/sbi/conv.h b/lib/sbi/conv.h index 29ccd6b9b..9ee6a3647 100644 --- a/lib/sbi/conv.h +++ b/lib/sbi/conv.h @@ -40,6 +40,9 @@ char *ogs_sbi_server_uri(ogs_sbi_server_t *server, ogs_sbi_header_t *h); char *ogs_sbi_client_apiroot(ogs_sbi_client_t *client); char *ogs_sbi_client_uri(ogs_sbi_client_t *client, ogs_sbi_header_t *h); +char *ogs_sbi_url_encode(const char *str); +char *ogs_sbi_url_decode(const char *str); + char *ogs_sbi_parse_uri(char *uri, const char *delim, char **saveptr); bool ogs_sbi_getaddr_from_uri( diff --git a/lib/sbi/message.c b/lib/sbi/message.c index d698da7f0..970a7faea 100644 --- a/lib/sbi/message.c +++ b/lib/sbi/message.c @@ -388,6 +388,23 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) if (ogs_sbi_self()->discovery_config.no_service_names == false && discovery_option->num_of_service_names) { + /* + * Issues #1730 + * Send NF discovery query with service-names delimited with comma + * + * OpenAPI specification for sending NF discovery query with + * "service-names" parameter is defined as folowing: + * name: service-names + * ... + * style: form + * explode: false + * + * According to OpenAPI specification, this means array items + * should be delimited with a comma character (example: /users?id=3,4,5). + * + * See also https://swagger.io/docs/specification/serialization/ + */ + /* send array items separated by a comma */ char *v = ogs_sbi_discovery_option_build_service_names( discovery_option); @@ -396,11 +413,47 @@ ogs_sbi_request_t *ogs_sbi_build_request(ogs_sbi_message_t *message) OGS_SBI_PARAM_SERVICE_NAMES, v); ogs_free(v); } else { - ogs_warn("invalid service names failed[%d:%s]", + ogs_warn("build failed: service-names[%d:%s]", discovery_option->num_of_service_names, discovery_option->service_names[0]); } } + if (discovery_option->num_of_snssais) { + char *v = ogs_sbi_discovery_option_build_snssais(discovery_option); + if (v) { + /* + * In http.params, the CURL library automatically encodes the URL. + * http.headers implements open5gs to directly encode URLs. + * + * Since it is http.params, the result is sent as is. + */ + ogs_sbi_header_set(request->http.params, + OGS_SBI_PARAM_SNSSAIS, v); + ogs_free(v); + } else { + ogs_error("build failed: snssais(%d)[SST:%d SD:0x%x]", + discovery_option->num_of_snssais, + discovery_option->snssais[0].sst, + discovery_option->snssais[0].sd.v); + } + } + if (discovery_option->dnn) { + ogs_sbi_header_set(request->http.params, + OGS_SBI_PARAM_DNN, discovery_option->dnn); + } + if (discovery_option->num_of_tai) { + char *v = ogs_sbi_discovery_option_build_tai(discovery_option); + if (v) { + ogs_sbi_header_set(request->http.params, OGS_SBI_PARAM_TAI, v); + ogs_free(v); + } else { + ogs_error("build failed: tai(%d)[PLMN_ID:%06x,TAC:%d]", + discovery_option->num_of_tai, + ogs_plmn_id_hexdump( + &discovery_option->tai[0].plmn_id), + discovery_option->tai[0].tac.v); + } + } if (discovery_option->requester_features) { char *v = ogs_uint64_to_string( discovery_option->requester_features); @@ -702,12 +755,47 @@ int ogs_sbi_parse_request( } } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_SERVICE_NAMES)) { + /* + * Issues #1730 + * Send NF discovery query with service-names delimited with comma + * + * OpenAPI specification for sending NF discovery query with + * "service-names" parameter is defined as folowing: + * name: service-names + * ... + * style: form + * explode: false + * + * According to OpenAPI specification, this means array items + * should be delimited with a comma character (example: /users?id=3,4,5). + * + * See also https://swagger.io/docs/specification/serialization/ + */ char *v = ogs_hash_this_val(hi); if (v) { ogs_sbi_discovery_option_parse_service_names( discovery_option, v); discovery_option_presence = true; } + } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_SNSSAIS)) { + char *v = ogs_hash_this_val(hi); + if (v) { + ogs_sbi_discovery_option_parse_snssais(discovery_option, v); + discovery_option_presence = true; + } + } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_DNN)) { + char *v = ogs_hash_this_val(hi); + + if (v) { + ogs_sbi_discovery_option_set_dnn(discovery_option, v); + discovery_option_presence = true; + } + } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_TAI)) { + char *v = ogs_hash_this_val(hi); + if (v) { + ogs_sbi_discovery_option_parse_tai(discovery_option, v); + discovery_option_presence = true; + } } else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_REQUESTER_FEATURES)) { char *v = ogs_hash_this_val(hi); @@ -2682,6 +2770,8 @@ void ogs_sbi_discovery_option_free( ogs_free(discovery_option->target_nf_instance_id); if (discovery_option->requester_nf_instance_id) ogs_free(discovery_option->requester_nf_instance_id); + if (discovery_option->dnn) + ogs_free(discovery_option->dnn); for (i = 0; i < discovery_option->num_of_service_names; i++) ogs_free(discovery_option->service_names[i]); @@ -2712,6 +2802,16 @@ void ogs_sbi_discovery_option_set_requester_nf_instance_id( ogs_strdup(requester_nf_instance_id); ogs_assert(discovery_option->requester_nf_instance_id); } +void ogs_sbi_discovery_option_set_dnn( + ogs_sbi_discovery_option_t *discovery_option, char *dnn) +{ + ogs_assert(discovery_option); + ogs_assert(dnn); + + ogs_assert(!discovery_option->dnn); + discovery_option->dnn = ogs_strdup(dnn); + ogs_assert(discovery_option->dnn); +} void ogs_sbi_discovery_option_add_service_names( ogs_sbi_discovery_option_t *discovery_option, char *service_name) @@ -2743,6 +2843,22 @@ char *ogs_sbi_discovery_option_build_service_names( return NULL;; } + /* + * Issues #1730 + * Send NF discovery query with service-names delimited with comma + * + * OpenAPI specification for sending NF discovery query with + * "service-names" parameter is defined as folowing: + * name: service-names + * ... + * style: form + * explode: false + * + * According to OpenAPI specification, this means array items + * should be delimited with a comma character (example: /users?id=3,4,5). + * + * See also https://swagger.io/docs/specification/serialization/ + */ if (discovery_option->num_of_service_names > 1) { for (i = 1; i < discovery_option->num_of_service_names; i++) service_names = ogs_mstrcatf( @@ -2763,9 +2879,29 @@ void ogs_sbi_discovery_option_parse_service_names( ogs_assert(discovery_option); ogs_assert(service_names); - v = ogs_strdup(service_names); - ogs_assert(v); + v = ogs_sbi_url_decode(service_names); + if (!v) { + ogs_error("ogs_sbi_url_decode() failed : service_names[%s]", + service_names); + return; + } + /* + * Issues #1730 + * Send NF discovery query with service-names delimited with comma + * + * OpenAPI specification for sending NF discovery query with + * "service-names" parameter is defined as folowing: + * name: service-names + * ... + * style: form + * explode: false + * + * According to OpenAPI specification, this means array items + * should be delimited with a comma character (example: /users?id=3,4,5). + * + * See also https://swagger.io/docs/specification/serialization/ + */ token = ogs_strtok_r(v, ",", &saveptr); while (token != NULL) { ogs_sbi_discovery_option_add_service_names(discovery_option, token); @@ -2774,3 +2910,205 @@ void ogs_sbi_discovery_option_parse_service_names( ogs_free(v); } + +void ogs_sbi_discovery_option_add_snssais( + ogs_sbi_discovery_option_t *discovery_option, ogs_s_nssai_t *s_nssai) +{ + ogs_assert(discovery_option); + ogs_assert(s_nssai); + + ogs_assert(discovery_option->num_of_snssais < OGS_MAX_NUM_OF_SLICE); + + memcpy(&discovery_option->snssais[discovery_option->num_of_snssais], + s_nssai, sizeof(ogs_s_nssai_t)); + discovery_option->num_of_snssais++; +} +char *ogs_sbi_discovery_option_build_snssais( + ogs_sbi_discovery_option_t *discovery_option) +{ + cJSON *item = NULL; + char *v = NULL; + int i; + + ogs_assert(discovery_option); + + item = cJSON_CreateArray(); + if (!item) { + ogs_error("cJSON_CreateArray() failed"); + return NULL; + } + + for (i = 0; i < discovery_option->num_of_snssais; i++) { + OpenAPI_snssai_t sNSSAI; + cJSON *snssaiItem = NULL; + + memset(&sNSSAI, 0, sizeof(sNSSAI)); + + sNSSAI.sst = discovery_option->snssais[i].sst; + sNSSAI.sd = ogs_s_nssai_sd_to_string(discovery_option->snssais[i].sd); + + snssaiItem = OpenAPI_snssai_convertToJSON(&sNSSAI); + ogs_assert(snssaiItem); + cJSON_AddItemToArray(item, snssaiItem); + } + + v = cJSON_PrintUnformatted(item); + ogs_expect(v); + cJSON_Delete(item); + + return v; +} +void ogs_sbi_discovery_option_parse_snssais( + ogs_sbi_discovery_option_t *discovery_option, char *snssais) +{ + cJSON *item = NULL; + cJSON *snssaiItem = NULL; + char *v = NULL; + + ogs_assert(discovery_option); + ogs_assert(snssais); + + v = ogs_sbi_url_decode(snssais); + if (!v) { + ogs_error("ogs_sbi_url_decode() failed : snssais[%s]", snssais); + return; + } + + item = cJSON_Parse(v); + if (!item) { + ogs_error("Cannot parse snssais[%s]", snssais); + ogs_free(v); + return; + } + + cJSON_ArrayForEach(snssaiItem, item) { + if (cJSON_IsObject(snssaiItem)) { + OpenAPI_snssai_t *sNSSAI = OpenAPI_snssai_parseFromJSON(snssaiItem); + + if (sNSSAI) { + ogs_s_nssai_t s_nssai; + + s_nssai.sst = sNSSAI->sst; + s_nssai.sd = ogs_s_nssai_sd_from_string(sNSSAI->sd); + + ogs_sbi_discovery_option_add_snssais( + discovery_option, &s_nssai); + + OpenAPI_snssai_free(sNSSAI); + } else { + ogs_error("OpenAPI_snssai_parseFromJSON() failed : snssais[%s]", + snssais); + } + } else { + ogs_error("Invalid cJSON Type in snssias[%s]", snssais); + } + } + cJSON_Delete(item); + + ogs_free(v); +} + +void ogs_sbi_discovery_option_add_tai( + ogs_sbi_discovery_option_t *discovery_option, ogs_5gs_tai_t *tai) +{ + ogs_assert(discovery_option); + ogs_assert(tai); + + ogs_assert(discovery_option->num_of_tai < OGS_MAX_NUM_OF_TAI); + + memcpy(&discovery_option->tai[discovery_option->num_of_tai], + tai, sizeof(ogs_5gs_tai_t)); + discovery_option->num_of_tai++; +} +char *ogs_sbi_discovery_option_build_tai( + ogs_sbi_discovery_option_t *discovery_option) +{ + cJSON *item = NULL; + char *v = NULL; + int i; + + ogs_assert(discovery_option); + + item = cJSON_CreateArray(); + if (!item) { + ogs_error("cJSON_CreateArray() failed"); + return NULL; + } + + for (i = 0; i < discovery_option->num_of_tai; i++) { + OpenAPI_tai_t Tai; + cJSON *taiItem = NULL; + + memset(&Tai, 0, sizeof(Tai)); + + Tai.plmn_id = ogs_sbi_build_plmn_id(&discovery_option->tai[i].plmn_id); + ogs_assert(Tai.plmn_id); + Tai.tac = ogs_uint24_to_0string(discovery_option->tai[i].tac); + ogs_assert(Tai.tac); + + taiItem = OpenAPI_tai_convertToJSON(&Tai); + ogs_assert(taiItem); + cJSON_AddItemToArray(item, taiItem); + + ogs_sbi_free_plmn_id(Tai.plmn_id); + ogs_free(Tai.tac); + } + + v = cJSON_PrintUnformatted(item); + ogs_expect(v); + cJSON_Delete(item); + + return v; +} +void ogs_sbi_discovery_option_parse_tai( + ogs_sbi_discovery_option_t *discovery_option, char *tai) +{ + cJSON *item = NULL; + cJSON *taiItem = NULL; + char *v = NULL; + + ogs_assert(discovery_option); + ogs_assert(tai); + + v = ogs_sbi_url_decode(tai); + if (!v) { + ogs_error("ogs_sbi_url_decode() failed : tai[%s]", tai); + return; + } + + item = cJSON_Parse(v); + if (!item) { + ogs_error("Cannot parse tai[%s]", tai); + ogs_free(v); + return; + } + + cJSON_ArrayForEach(taiItem, item) { + if (cJSON_IsObject(taiItem)) { + OpenAPI_tai_t *Tai = OpenAPI_tai_parseFromJSON(taiItem); + + if (Tai) { + ogs_5gs_tai_t tai; + + memset(&tai, 0, sizeof(tai)); + + if (Tai->plmn_id) + ogs_sbi_parse_plmn_id(&tai.plmn_id, Tai->plmn_id); + if (Tai->tac) + tai.tac = ogs_uint24_from_string(Tai->tac); + + ogs_sbi_discovery_option_add_tai(discovery_option, &tai); + + OpenAPI_tai_free(Tai); + } else { + ogs_error("OpenAPI_snssai_parseFromJSON() failed : tai[%s]", + tai); + } + } else { + ogs_error("Invalid cJSON Type in snssias[%s]", tai); + } + } + cJSON_Delete(item); + + ogs_free(v); +} diff --git a/lib/sbi/message.h b/lib/sbi/message.h index 2363bb479..daa933546 100644 --- a/lib/sbi/message.h +++ b/lib/sbi/message.h @@ -279,6 +279,12 @@ extern "C" { OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_REQUESTER_NF_INSTANCE_ID #define OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES \ OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_SERVICE_NAMES +#define OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS \ + OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_SNSSAIS +#define OGS_SBI_CUSTOM_DISCOVERY_DNN \ + OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_DNN +#define OGS_SBI_CUSTOM_DISCOVERY_TAI \ + OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_TAI #define OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES \ OGS_SBI_CUSTOM_DISCOVERY_COMMON OGS_SBI_PARAM_REQUESTER_FEATURES #define OGS_SBI_CUSTOM_PRODUCER_ID \ @@ -314,6 +320,8 @@ extern "C" { #define OGS_SBI_PARAM_PLMN_ID "plmn-id" #define OGS_SBI_PARAM_SINGLE_NSSAI "single-nssai" #define OGS_SBI_PARAM_SNSSAI "snssai" +#define OGS_SBI_PARAM_SNSSAIS "snssais" +#define OGS_SBI_PARAM_TAI "tai" #define OGS_SBI_PARAM_SLICE_INFO_REQUEST_FOR_PDU_SESSION \ "slice-info-request-for-pdu-session" #define OGS_SBI_PARAM_IPV4ADDR "ipv4Addr" @@ -407,6 +415,12 @@ typedef struct ogs_sbi_discovery_option_s { int num_of_service_names; char *service_names[OGS_SBI_MAX_NUM_OF_SERVICE_TYPE]; + int num_of_snssais; + ogs_s_nssai_t snssais[OGS_MAX_NUM_OF_SLICE]; + char *dnn; + int num_of_tai; + ogs_5gs_tai_t tai[OGS_MAX_NUM_OF_TAI]; + uint64_t requester_features; } ogs_sbi_discovery_option_t; @@ -591,6 +605,8 @@ void ogs_sbi_discovery_option_set_target_nf_instance_id( void ogs_sbi_discovery_option_set_requester_nf_instance_id( ogs_sbi_discovery_option_t *discovery_option, char *requester_nf_instance_id); +void ogs_sbi_discovery_option_set_dnn( + ogs_sbi_discovery_option_t *discovery_option, char *dnn); void ogs_sbi_discovery_option_add_service_names( ogs_sbi_discovery_option_t *discovery_option, @@ -601,6 +617,20 @@ void ogs_sbi_discovery_option_parse_service_names( ogs_sbi_discovery_option_t *discovery_option, char *service_names); +void ogs_sbi_discovery_option_add_snssais( + ogs_sbi_discovery_option_t *discovery_option, ogs_s_nssai_t *s_nssai); +char *ogs_sbi_discovery_option_build_snssais( + ogs_sbi_discovery_option_t *discovery_option); +void ogs_sbi_discovery_option_parse_snssais( + ogs_sbi_discovery_option_t *discovery_option, char *snssais); + +void ogs_sbi_discovery_option_add_tai( + ogs_sbi_discovery_option_t *discovery_option, ogs_5gs_tai_t *tai); +char *ogs_sbi_discovery_option_build_tai( + ogs_sbi_discovery_option_t *discovery_option); +void ogs_sbi_discovery_option_parse_tai( + ogs_sbi_discovery_option_t *discovery_option, char *tai); + #ifdef __cplusplus } #endif diff --git a/lib/sbi/nnrf-build.c b/lib/sbi/nnrf-build.c index 9d2fc5af8..fc1caa899 100644 --- a/lib/sbi/nnrf-build.c +++ b/lib/sbi/nnrf-build.c @@ -777,7 +777,7 @@ static OpenAPI_smf_info_t *build_smf_info(ogs_sbi_nf_info_t *nf_info) ogs_error("No TaiItem->tac"); if (TaiItem) { if (TaiItem->plmn_id) - ogs_free(TaiItem->plmn_id); + ogs_sbi_free_plmn_id(TaiItem->plmn_id); ogs_free(TaiItem); } free_smf_info(SmfInfo); @@ -972,7 +972,7 @@ static OpenAPI_amf_info_t *build_amf_info(ogs_sbi_nf_info_t *nf_info) ogs_error("No TaiItem->tac"); if (TaiItem) { if (TaiItem->plmn_id) - ogs_free(TaiItem->plmn_id); + ogs_sbi_free_plmn_id(TaiItem->plmn_id); ogs_free(TaiItem); } free_amf_info(AmfInfo); diff --git a/lib/sbi/path.c b/lib/sbi/path.c index 93a51455c..76588c44a 100644 --- a/lib/sbi/path.c +++ b/lib/sbi/path.c @@ -274,6 +274,72 @@ int ogs_sbi_discover_and_send(ogs_sbi_xact_t *xact) nf_instance->id); } + if (discovery_option && discovery_option->num_of_snssais) { + bool rc = false; + char *v = ogs_sbi_discovery_option_build_snssais( + discovery_option); + ogs_expect(v); + + if (v) { + char *encoded = ogs_sbi_url_encode(v); + ogs_expect(encoded); + + if (encoded) { + /* + * In http.params, the CURL library automatically encodes the URL. + * http.headers implements open5gs to directly encode URLs. + * + * Since it is http.headers, + * we need to encode `v` using ogs_sbi_url_encode(); + */ + ogs_sbi_header_set(request->http.headers, + OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS, encoded); + ogs_free(encoded); + + rc = true; + } + ogs_free(v); + } + + if (rc == false) + ogs_error("build failed: snssais(%d)[SST:%d SD:0x%x]", + discovery_option->num_of_snssais, + discovery_option->snssais[0].sst, + discovery_option->snssais[0].sd.v); + } + + if (discovery_option && discovery_option->dnn) { + ogs_sbi_header_set(request->http.headers, + OGS_SBI_CUSTOM_DISCOVERY_DNN, discovery_option->dnn); + } + + if (discovery_option && discovery_option->num_of_tai) { + bool rc = false; + char *v = ogs_sbi_discovery_option_build_tai(discovery_option); + ogs_expect(v); + + if (v) { + char *encoded = ogs_sbi_url_encode(v); + ogs_expect(encoded); + + if (encoded) { + ogs_sbi_header_set(request->http.headers, + OGS_SBI_CUSTOM_DISCOVERY_TAI, encoded); + ogs_free(encoded); + + rc = true; + } + ogs_free(v); + } + + if (rc == false) + ogs_error("build failed: tai(%d)[PLMN_ID:%06x,TAC:%d]", + discovery_option->num_of_tai, + ogs_plmn_id_hexdump( + &discovery_option->tai[0].plmn_id), + discovery_option->tai[0].tac.v); + } + if (discovery_option && discovery_option->requester_features) { char *v = ogs_uint64_to_string( @@ -615,19 +681,39 @@ static void build_default_discovery_parameter( if (ogs_sbi_self()->discovery_config. no_service_names == false && discovery_option->num_of_service_names) { + bool rc = false; /* send array items separated by a comma */ char *v = ogs_sbi_discovery_option_build_service_names( discovery_option); + ogs_expect(v); + if (v) { - ogs_sbi_header_set(request->http.headers, - OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES, v); + char *encoded = ogs_sbi_url_encode(v); + ogs_expect(encoded); + + if (encoded) { + /* + * In http.params, the CURL library automatically encodes the URL. + * http.headers implements open5gs to directly encode URLs. + * + * Since it is http.headers, + * we need to encode `v` using ogs_sbi_url_encode(); + */ + ogs_sbi_header_set(request->http.headers, + OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES, encoded); + ogs_free(encoded); + + rc = true; + } ogs_free(v); - } else { + + } + + if (rc == false) ogs_warn("invalid service names failed[%d:%s]", discovery_option->num_of_service_names, discovery_option->service_names[0]); - } } } diff --git a/src/amf/context.c b/src/amf/context.c index 52ed981b7..6d415f067 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -2160,8 +2160,6 @@ amf_sess_t *amf_sess_cycle(amf_sess_t *sess) return ogs_pool_cycle(&amf_sess_pool, sess); } -static bool check_smf_info(ogs_sbi_nf_info_t *nf_info, void *context); - void amf_sbi_select_nf( ogs_sbi_object_t *sbi_object, ogs_sbi_service_type_e service_type, @@ -2170,7 +2168,6 @@ void amf_sbi_select_nf( { OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL; ogs_sbi_nf_instance_t *nf_instance = NULL; - ogs_sbi_nf_info_t *nf_info = NULL; amf_sess_t *sess = NULL; ogs_assert(sbi_object); @@ -2198,22 +2195,6 @@ void amf_sbi_select_nf( false) continue; - if ((nf_instance->nf_type == OpenAPI_nf_type_SMF) && - (ogs_list_count(&nf_instance->nf_info_list) > 0)) { - - ogs_list_for_each(&nf_instance->nf_info_list, nf_info) { - if (nf_info->nf_type != nf_instance->nf_type) - continue; - if (check_smf_info(nf_info, sess) == false) - continue; - - break; - } - - if (!nf_info) - continue; - } - OGS_SBI_SETUP_NF_INSTANCE( sbi_object->service_type_array[service_type], nf_instance); break; @@ -2551,90 +2532,6 @@ static void stats_remove_amf_session(void) ogs_info("[Removed] Number of AMF-Sessions is now %d", num_of_amf_sess); } -static bool check_smf_info_s_nssai( - ogs_sbi_smf_info_t *smf_info, amf_sess_t *sess); -static bool check_smf_info_nr_tai( - ogs_sbi_smf_info_t *smf_info, amf_sess_t *sess); - -static bool check_smf_info(ogs_sbi_nf_info_t *nf_info, void *context) -{ - amf_sess_t *sess = NULL; - - ogs_assert(nf_info); - ogs_assert(nf_info->nf_type == OpenAPI_nf_type_SMF); - sess = context; - ogs_assert(sess); - - if (check_smf_info_s_nssai(&nf_info->smf, sess) == false) - return false; - if (check_smf_info_nr_tai(&nf_info->smf, sess) == false) - return false; - - return true; -} - -static bool check_smf_info_s_nssai( - ogs_sbi_smf_info_t *smf_info, amf_sess_t *sess) -{ - int i, j; - - ogs_assert(sess); - ogs_assert(sess->dnn); - ogs_assert(smf_info); - - for (i = 0; i < smf_info->num_of_slice; i++) { - if (sess->s_nssai.sst == smf_info->slice[i].s_nssai.sst && - sess->s_nssai.sd.v == smf_info->slice[i].s_nssai.sd.v) { - - for (j = 0; j < smf_info->slice[i].num_of_dnn; j++) { - if (ogs_strcasecmp(sess->dnn, smf_info->slice[i].dnn[j]) == 0) - return true; - } - } - } - - return false; -} - -static bool check_smf_info_nr_tai( - ogs_sbi_smf_info_t *smf_info, amf_sess_t *sess) -{ - amf_ue_t *amf_ue = NULL; - int i, j; - - ogs_assert(sess); - amf_ue = sess->amf_ue; - ogs_assert(amf_ue); - ogs_assert(smf_info); - - if (smf_info->num_of_nr_tai == 0 && smf_info->num_of_nr_tai_range == 0) - return true; - - for (i = 0; i < smf_info->num_of_nr_tai; i++) { - if (memcmp(&amf_ue->nr_tai.plmn_id, - &smf_info->nr_tai[i].plmn_id, OGS_PLMN_ID_LEN) == 0) { - if (amf_ue->nr_tai.tac.v == smf_info->nr_tai[i].tac.v) - return true; - } - } - - for (i = 0; i < smf_info->num_of_nr_tai_range; i++) { - if (memcmp(&amf_ue->nr_tai.plmn_id, - &smf_info->nr_tai_range[i].plmn_id, OGS_PLMN_ID_LEN) == 0) { - for (j = 0; j < smf_info->nr_tai_range[i].num_of_tac_range; j++) { - if (amf_ue->nr_tai.tac.v >= - smf_info->nr_tai_range[i].start[j].v && - amf_ue->nr_tai.tac.v <= - smf_info->nr_tai_range[i].end[j].v) { - return true; - } - } - } - } - - return false; -} - /* * Issues #2482 * diff --git a/src/amf/gmm-handler.c b/src/amf/gmm-handler.c index 3e7347b70..64790d3d8 100644 --- a/src/amf/gmm-handler.c +++ b/src/amf/gmm-handler.c @@ -1244,14 +1244,27 @@ int gmm_handle_ul_nas_transport(amf_ue_t *amf_ue, sess->s_nssai.sst = selected_slice->s_nssai.sst; sess->s_nssai.sd.v = selected_slice->s_nssai.sd.v; - ogs_info("UE SUPI[%s] DNN[%s] S_NSSAI[SST:%d SD:0x%x]", - amf_ue->supi, sess->dnn, sess->s_nssai.sst, sess->s_nssai.sd.v); + ogs_info("UE SUPI[%s] DNN[%s] S_NSSAI[SST:%d SD:0x%x] " + "smContextRef [%s]", + amf_ue->supi, sess->dnn, sess->s_nssai.sst, sess->s_nssai.sd.v, + sess->sm_context_ref ? sess->sm_context_ref : "NULL"); if (!SESSION_CONTEXT_IN_SMF(sess)) { ogs_sbi_nf_instance_t *nf_instance = NULL; ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION; + ogs_sbi_discovery_option_t *discovery_option = NULL; + + discovery_option = ogs_sbi_discovery_option_new(); + ogs_assert(discovery_option); + + ogs_sbi_discovery_option_add_snssais( + discovery_option, &sess->s_nssai); + ogs_sbi_discovery_option_set_dnn(discovery_option, sess->dnn); + ogs_sbi_discovery_option_add_tai( + discovery_option, &amf_ue->nr_tai); + nf_instance = sess->sbi. service_type_array[service_type].nf_instance; if (!nf_instance) { @@ -1263,21 +1276,29 @@ int gmm_handle_ul_nas_transport(amf_ue_t *amf_ue, &sess->sbi, OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, requester_nf_type, - NULL); + discovery_option); nf_instance = sess->sbi. service_type_array[service_type].nf_instance; - } + + if (!nf_instance) + ogs_info("No SMF Instance"); + else + ogs_info("SMF Instance [%s]", nf_instance->id); + } else + ogs_info("SMF Instance [%s]", nf_instance->id); if (nf_instance) { r = amf_sess_sbi_discover_and_send( - OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, + discovery_option, amf_nsmf_pdusession_build_create_sm_context, sess, AMF_CREATE_SM_CONTEXT_NO_STATE, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } else { r = amf_sess_sbi_discover_and_send( - OGS_SBI_SERVICE_TYPE_NNSSF_NSSELECTION, NULL, + OGS_SBI_SERVICE_TYPE_NNSSF_NSSELECTION, + discovery_option, amf_nnssf_nsselection_build_get, sess, 0, NULL); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); diff --git a/src/amf/nnssf-handler.c b/src/amf/nnssf-handler.c index 962d8a52f..a1d33811f 100644 --- a/src/amf/nnssf-handler.c +++ b/src/amf/nnssf-handler.c @@ -29,6 +29,7 @@ int amf_nnssf_nsselection_handle_get( OpenAPI_uri_scheme_e scheme = OpenAPI_uri_scheme_NULL; ogs_sbi_client_t *client = NULL, *scp_client = NULL; ogs_sockaddr_t *addr = NULL; + ogs_sbi_discovery_option_t *discovery_option = NULL; OpenAPI_authorized_network_slice_info_t *AuthorizedNetworkSliceInfo = NULL; OpenAPI_nsi_information_t *NsiInformation = NULL; @@ -87,6 +88,13 @@ int amf_nnssf_nsselection_handle_get( return OGS_ERROR; } + discovery_option = ogs_sbi_discovery_option_new(); + ogs_assert(discovery_option); + + ogs_sbi_discovery_option_add_snssais(discovery_option, &sess->s_nssai); + ogs_sbi_discovery_option_set_dnn(discovery_option, sess->dnn); + ogs_sbi_discovery_option_add_tai(discovery_option, &amf_ue->nr_tai); + if (sess->nssf.nrf.id) ogs_free(sess->nssf.nrf.id); sess->nssf.nrf.id = ogs_strdup(NsiInformation->nrf_id); @@ -101,7 +109,7 @@ int amf_nnssf_nsselection_handle_get( param.nrf_uri.nrf.id = sess->nssf.nrf.id; r = amf_sess_sbi_discover_and_send( - OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL, + OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, discovery_option, amf_nsmf_pdusession_build_create_sm_context, sess, AMF_CREATE_SM_CONTEXT_NO_STATE, ¶m); ogs_expect(r == OGS_OK); @@ -115,6 +123,9 @@ int amf_nnssf_nsselection_handle_get( amf_ue, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); + + ogs_sbi_discovery_option_free(discovery_option); + return OGS_ERROR;; } @@ -128,7 +139,7 @@ int amf_nnssf_nsselection_handle_get( ogs_freeaddrinfo(addr); r = amf_sess_sbi_discover_by_nsi( - sess, OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, NULL); + sess, OGS_SBI_SERVICE_TYPE_NSMF_PDUSESSION, discovery_option); ogs_expect(r == OGS_OK); ogs_assert(r != OGS_ERROR); } diff --git a/src/nrf/nnrf-handler.c b/src/nrf/nnrf-handler.c index e601ce586..987e16de4 100644 --- a/src/nrf/nnrf-handler.c +++ b/src/nrf/nnrf-handler.c @@ -756,6 +756,22 @@ bool nrf_nnrf_handle_nf_discover( ogs_debug("[%d] service-names[%s]", i, discovery_option->service_names[i]); } + if (discovery_option->num_of_snssais) { + for (i = 0; i < discovery_option->num_of_snssais; i++) + ogs_debug("[%d] snssais[SST:%d SD:0x%x]", i, + discovery_option->snssais[i].sst, + discovery_option->snssais[i].sd.v); + } + if (discovery_option->dnn) { + ogs_debug("dnn[%s]", discovery_option->dnn); + } + if (discovery_option->num_of_tai) { + for (i = 0; i < discovery_option->num_of_tai; i++) + ogs_debug("[%d] tai[PLMN_ID:%06x,TAC:%d]", i, + ogs_plmn_id_hexdump( + &discovery_option->tai[0].plmn_id), + discovery_option->tai[0].tac.v); + } if (discovery_option->requester_features) { ogs_debug("requester-features[0x%llx]", (long long)discovery_option->requester_features); diff --git a/src/scp/context.c b/src/scp/context.c index 6ac039ccf..e1082ad69 100644 --- a/src/scp/context.c +++ b/src/scp/context.c @@ -283,6 +283,8 @@ void scp_assoc_remove(scp_assoc_t *assoc) ogs_sbi_client_remove(assoc->client); if (assoc->nrf_client) ogs_sbi_client_remove(assoc->nrf_client); + if (assoc->discovery_option) + ogs_sbi_discovery_option_free(assoc->discovery_option); ogs_pool_free(&scp_assoc_pool, assoc); } diff --git a/src/scp/context.h b/src/scp/context.h index e5419059a..5e6b61ac5 100644 --- a/src/scp/context.h +++ b/src/scp/context.h @@ -51,6 +51,7 @@ typedef struct scp_assoc_s { ogs_sbi_request_t *request; ogs_sbi_service_type_e service_type; OpenAPI_nf_type_e requester_nf_type; + ogs_sbi_discovery_option_t *discovery_option; ogs_sbi_nf_instance_t *nf_service_producer; } scp_assoc_t; diff --git a/src/scp/sbi-path.c b/src/scp/sbi-path.c index 4f4b6f1f0..55ad68e07 100644 --- a/src/scp/sbi-path.c +++ b/src/scp/sbi-path.c @@ -201,6 +201,14 @@ static int request_handler(ogs_sbi_request_t *request, void *data) service_type = ogs_sbi_service_type_from_name( discovery_option->service_names[0]); } + } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_SNSSAIS)) { + if (val) + ogs_sbi_discovery_option_parse_snssais(discovery_option, val); + } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_DNN)) { + ogs_sbi_discovery_option_set_dnn(discovery_option, val); + } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TAI)) { + if (val) + ogs_sbi_discovery_option_parse_tai(discovery_option, val); } else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES)) { if (val) @@ -466,18 +474,46 @@ static int request_handler(ogs_sbi_request_t *request, void *data) ogs_assert(assoc->service_type); assoc->requester_nf_type = requester_nf_type; ogs_assert(assoc->requester_nf_type); + assoc->discovery_option = discovery_option; + ogs_assert(assoc->discovery_option); ogs_assert(target_nf_type); - ogs_assert(discovery_option); + + if (!discovery_option->num_of_service_names) { + ogs_error("No service names"); + scp_assoc_remove(assoc); + return OGS_ERROR; + } else if (discovery_option->num_of_service_names > 1) { + /* + * TS29.500 + * 6.10.3 NF Discovery and Selection for indirect communication + * with Delegated Discovery + * 6.10.3.2 Conveyance of NF Discovery Factors + * + * If the NF service consumer includes more than one service name in the + * 3gpp-Sbi-Discovery-service-names header, the service name corresponding + * to the service request shall be listed as the first service name + * in the header. + * + * NOTE 3: The SCP can assume that the service request corresponds + * to the first service name in the header. + */ + int i; + + for (i = 1; i < discovery_option->num_of_service_names; i++) + ogs_free(discovery_option->service_names[i]); + discovery_option->num_of_service_names = 1; + + ogs_error("NOTE 3: The SCP can assume that the service request " + "corresponds to the first service name in the header " + "in TS29.500"); + } nrf_request = ogs_nnrf_disc_build_discover( target_nf_type, requester_nf_type, discovery_option); if (!nrf_request) { ogs_error("ogs_nnrf_disc_build_discover() failed"); - - ogs_sbi_discovery_option_free(discovery_option); scp_assoc_remove(assoc); - return OGS_ERROR; } @@ -486,15 +522,12 @@ static int request_handler(ogs_sbi_request_t *request, void *data) ogs_error("ogs_sbi_client_send_request() failed"); scp_assoc_remove(assoc); - ogs_sbi_request_free(nrf_request); - ogs_sbi_discovery_option_free(discovery_option); return OGS_ERROR; } ogs_sbi_request_free(nrf_request); - ogs_sbi_discovery_option_free(discovery_option); return OGS_OK; } @@ -577,7 +610,9 @@ static int discover_handler( ogs_sbi_stream_t *stream = NULL; ogs_sbi_request_t *request = NULL; ogs_sbi_service_type_e service_type = OGS_SBI_SERVICE_TYPE_NULL; + OpenAPI_nf_type_e target_nf_type = OpenAPI_nf_type_NULL; OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL; + ogs_sbi_discovery_option_t *discovery_option = NULL; ogs_sbi_request_t scp_request; char *apiroot = NULL; @@ -592,8 +627,12 @@ static int discover_handler( ogs_assert(request); service_type = assoc->service_type; ogs_assert(service_type); + target_nf_type = ogs_sbi_service_type_to_nf_type(service_type); + ogs_assert(target_nf_type); requester_nf_type = assoc->requester_nf_type; ogs_assert(requester_nf_type); + discovery_option = assoc->discovery_option; + ogs_assert(discovery_option); if (status != OGS_OK) { @@ -631,8 +670,8 @@ static int discover_handler( ogs_nnrf_disc_handle_nf_discover_search_result(message.SearchResult); - nf_instance = ogs_sbi_nf_instance_find_by_service_type( - service_type, requester_nf_type); + nf_instance = ogs_sbi_nf_instance_find_by_discovery_param( + target_nf_type, requester_nf_type, discovery_option); if (!nf_instance) { strerror = ogs_msprintf("(NF discover) No NF-Instance [%s:%s]", ogs_sbi_service_type_to_name(service_type), diff --git a/tests/unit/sbi-message-test.c b/tests/unit/sbi-message-test.c index 47f3b8c6e..8d0c91457 100644 --- a/tests/unit/sbi-message-test.c +++ b/tests/unit/sbi-message-test.c @@ -820,6 +820,20 @@ static void sbi_message_test8(abts_case *tc, void *data) ogs_sbi_service_type_from_name(OGS_SBI_SERVICE_NAME_NNSSAAF_NSSAA)); } +static void sbi_message_test9(abts_case *tc, void *data) +{ + const char *original = "{\"sst\": 1, \"sd\": \"A08923\"}"; + char *encoded = ogs_sbi_url_encode(original); + char *decoded = ogs_sbi_url_decode(encoded); + + ABTS_STR_EQUAL(tc, + "%7B%22sst%22%3A 1%2C %22sd%22%3A %22A08923%22%7D", encoded); + ABTS_STR_EQUAL(tc, original, decoded); + + ogs_free(encoded); + ogs_free(decoded); +} + abts_suite *test_sbi_message(abts_suite *suite) { suite = ADD_SUITE(suite) @@ -832,6 +846,7 @@ abts_suite *test_sbi_message(abts_suite *suite) abts_run_test(suite, sbi_message_test6, NULL); abts_run_test(suite, sbi_message_test7, NULL); abts_run_test(suite, sbi_message_test8, NULL); + abts_run_test(suite, sbi_message_test9, NULL); return suite; }