/* * Copyright (C) 2019-2023 by Sukchan Lee * * 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 . */ #include "ogs-sbi.h" #include "yuarel.h" static int parse_scheme_output( char *_protection_scheme_id, char *_scheme_output, ogs_datum_t *ecckey, ogs_datum_t *cipher_text, uint8_t *mactag) { uint8_t protection_scheme_id; uint8_t scheme_output_size; uint8_t *scheme_output = NULL; uint8_t *p = NULL; ogs_assert(_protection_scheme_id); ogs_assert(_scheme_output); ogs_assert(ecckey); ogs_assert(mactag); ogs_assert(cipher_text); scheme_output_size = strlen(_scheme_output)/2; if (scheme_output_size <= ((OGS_ECCKEY_LEN+1) + OGS_MACTAG_LEN)) { ogs_error("Not enought length [%d]", (int)strlen(_scheme_output)); return OGS_ERROR; } scheme_output = ogs_calloc(1, scheme_output_size); ogs_assert(scheme_output); ogs_ascii_to_hex(_scheme_output, strlen(_scheme_output), scheme_output, scheme_output_size); protection_scheme_id = atoi(_protection_scheme_id); if (protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_A) { ecckey->size = OGS_ECCKEY_LEN; } else if (protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_B) { ecckey->size = OGS_ECCKEY_LEN+1; } else { ogs_free(scheme_output); ogs_fatal("Invalid protection scheme id [%s]", _protection_scheme_id); ogs_assert_if_reached(); return OGS_ERROR; } cipher_text->size = OGS_MSIN_LEN; p = scheme_output; ecckey->data = ogs_memdup(p, ecckey->size); ogs_assert(ecckey->data); p += ecckey->size; cipher_text->data = ogs_memdup(p, cipher_text->size); ogs_assert(cipher_text->data); p += cipher_text->size; memcpy(mactag, p, OGS_MACTAG_LEN); ogs_free(scheme_output); return OGS_OK; } char *ogs_supi_from_suci(char *suci) { #define MAX_SUCI_TOKEN 16 char *array[MAX_SUCI_TOKEN]; char *p, *tmp; int i; char *supi = NULL; ogs_assert(suci); tmp = ogs_strdup(suci); if (!tmp) { ogs_error("ogs_strdup() failed"); return NULL; } p = tmp; i = 0; while((array[i++] = strsep(&p, "-"))) { /* Empty Body */ } SWITCH(array[0]) CASE("suci") SWITCH(array[1]) CASE("0") /* SUPI format : IMSI */ if (array[2] && array[3] && array[5] && array[6] && array[7]) { uint8_t protection_scheme_id = atoi(array[5]); uint8_t home_network_pki_value = atoi(array[6]); if (protection_scheme_id == OGS_PROTECTION_SCHEME_NULL) { supi = ogs_msprintf("imsi-%s%s%s", array[2], array[3], array[7]); } else if (protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_A || protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_B) { ogs_datum_t pubkey; ogs_datum_t cipher_text; ogs_datum_t plain_text; char *plain_bcd; uint8_t mactag1[OGS_MACTAG_LEN], mactag2[OGS_MACTAG_LEN]; uint8_t z[OGS_ECCKEY_LEN]; uint8_t ek[OGS_KEY_LEN]; uint8_t icb[OGS_IVEC_LEN]; uint8_t mk[OGS_SHA256_DIGEST_SIZE]; if (home_network_pki_value < OGS_HOME_NETWORK_PKI_VALUE_MIN || home_network_pki_value > OGS_HOME_NETWORK_PKI_VALUE_MAX) { ogs_error("Invalid HNET PKI Value [%s]", array[6]); break; } if (!ogs_sbi_self()->hnet[home_network_pki_value].avail) { ogs_error("HNET PKI Value Not Avaiable [%s]", array[6]); break; } if (ogs_sbi_self()->hnet[home_network_pki_value].scheme != protection_scheme_id) { ogs_error("Scheme Not Matched [%d != %s]", ogs_sbi_self()->hnet[protection_scheme_id].scheme, array[5]); break; } if (parse_scheme_output( array[5], array[7], &pubkey, &cipher_text, mactag1) != OGS_OK) { ogs_error("parse_scheme_output[%s] failed", array[7]); break; } if (protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_A) { curve25519_donna(z, ogs_sbi_self()->hnet[home_network_pki_value].key, pubkey.data); } else if (protection_scheme_id == OGS_PROTECTION_SCHEME_PROFILE_B) { if (ecdh_shared_secret( pubkey.data, ogs_sbi_self()-> hnet[home_network_pki_value].key, z) != 1) { ogs_error("ecdh_shared_secret() failed"); ogs_log_hexdump(OGS_LOG_ERROR, pubkey.data, OGS_ECCKEY_LEN); ogs_log_hexdump(OGS_LOG_ERROR, ogs_sbi_self()-> hnet[home_network_pki_value].key, OGS_ECCKEY_LEN); goto cleanup; } } else ogs_assert_if_reached(); ogs_kdf_ansi_x963( z, OGS_ECCKEY_LEN, pubkey.data, pubkey.size, ek, icb, mk); ogs_hmac_sha256( mk, OGS_SHA256_DIGEST_SIZE, cipher_text.data, cipher_text.size, mactag2, OGS_MACTAG_LEN); if (memcmp(mactag1, mactag2, OGS_MACTAG_LEN) != 0) { ogs_error("MAC-tag not matched"); ogs_log_hexdump(OGS_LOG_ERROR, mactag1, OGS_MACTAG_LEN); ogs_log_hexdump(OGS_LOG_ERROR, mactag2, OGS_MACTAG_LEN); goto cleanup; } plain_text.size = cipher_text.size; plain_text.data = ogs_calloc(1, plain_text.size); ogs_assert(plain_text.data); ogs_aes_ctr128_encrypt( ek, icb, cipher_text.data, cipher_text.size, plain_text.data); plain_bcd = ogs_calloc(1, plain_text.size*2+1); ogs_assert(plain_bcd); ogs_buffer_to_bcd( plain_text.data, plain_text.size, plain_bcd); supi = ogs_msprintf("imsi-%s%s%s", array[2], array[3], plain_bcd); ogs_assert(supi); if (plain_text.data) ogs_free(plain_text.data); ogs_free(plain_bcd); cleanup: if (pubkey.data) ogs_free(pubkey.data); if (cipher_text.data) ogs_free(cipher_text.data); } else { ogs_error("Invalid Protection Scheme [%s]", array[5]); } } break; DEFAULT ogs_error("Not implemented [%s]", array[1]); break; END break; DEFAULT ogs_error("Not implemented [%s]", array[0]); break; END ogs_free(tmp); return supi; } char *ogs_supi_from_supi_or_suci(char *supi_or_suci) { char *type = NULL; char *supi = NULL; ogs_assert(supi_or_suci); type = ogs_id_get_type(supi_or_suci); if (!type) { ogs_error("ogs_id_get_type[%s] failed", supi_or_suci); goto cleanup; } SWITCH(type) CASE("imsi") supi = ogs_strdup(supi_or_suci); ogs_expect(supi); break; CASE("suci") supi = ogs_supi_from_suci(supi_or_suci); ogs_expect(supi); break; DEFAULT ogs_error("Not implemented [%s]", type); break; END cleanup: if (type) ogs_free(type); return supi; } char *ogs_uridup( OpenAPI_uri_scheme_e scheme, char *fqdn, ogs_sockaddr_t *addr, ogs_sockaddr_t *addr6, uint16_t port, ogs_sbi_header_t *h) { char buf[OGS_ADDRSTRLEN]; char uri[OGS_HUGE_LEN]; char *p, *last; int i; ogs_assert(scheme); ogs_assert(fqdn || addr || addr6); p = uri; last = uri + OGS_HUGE_LEN; /* HTTP scheme is selected based on TLS information */ if (scheme == OpenAPI_uri_scheme_https) p = ogs_slprintf(p, last, "https://"); else if (scheme == OpenAPI_uri_scheme_http) p = ogs_slprintf(p, last, "http://"); else { ogs_fatal("Invalid scheme [%d]", scheme); ogs_assert_if_reached(); } /* Hostname/IP address */ if (fqdn) p = ogs_slprintf(p, last, "%s", fqdn); else if (addr6) { p = ogs_slprintf(p, last, "[%s]", OGS_ADDR(addr6, buf)); } else if (addr) { p = ogs_slprintf(p, last, "%s", OGS_ADDR(addr, buf)); } else ogs_assert_if_reached(); /* Port number */ if (port) p = ogs_slprintf(p, last, ":%d", port); /* API */ if (h) { ogs_assert(h->service.name); p = ogs_slprintf(p, last, "/%s", h->service.name); ogs_assert(h->api.version); p = ogs_slprintf(p, last, "/%s", h->api.version); /* Resource */ ogs_assert(h->resource.component[0]); for (i = 0; i < OGS_SBI_MAX_NUM_OF_RESOURCE_COMPONENT && h->resource.component[i]; i++) p = ogs_slprintf(p, last, "/%s", h->resource.component[i]); } return ogs_strdup(uri); } char *ogs_sbi_server_uri(ogs_sbi_server_t *server, ogs_sbi_header_t *h) { ogs_sockaddr_t *advertise = NULL; ogs_assert(server); advertise = server->advertise; if (!advertise) advertise = server->node.addr; ogs_assert(advertise); return ogs_sbi_sockaddr_uri(server->scheme, advertise, h); } uint16_t ogs_sbi_uri_port_from_scheme_and_addr( OpenAPI_uri_scheme_e scheme, ogs_sockaddr_t *addr) { uint16_t port = 0; ogs_assert(scheme); ogs_assert(addr); if (scheme == OpenAPI_uri_scheme_https && OGS_PORT(addr) == OGS_SBI_HTTPS_PORT) { /* No Port in URI */ } else if (scheme == OpenAPI_uri_scheme_http && OGS_PORT(addr) == OGS_SBI_HTTP_PORT) { /* No Port in URI */ } else { port = OGS_PORT(addr); } return port; } char *ogs_sbi_sockaddr_uri( OpenAPI_uri_scheme_e scheme, ogs_sockaddr_t *sa_list, ogs_sbi_header_t *h) { int rv; char *hostname = NULL; ogs_sockaddr_t *addr = NULL, *addr6 = NULL; uint16_t port = 0; char *uri = NULL; ogs_assert(scheme); ogs_assert(sa_list); hostname = ogs_gethostname(sa_list); rv = ogs_copyaddrinfo(&addr, sa_list); ogs_assert(rv == OGS_OK); rv = ogs_copyaddrinfo(&addr6, addr); ogs_assert(rv == OGS_OK); rv = ogs_filteraddrinfo(&addr, AF_INET); ogs_assert(rv == OGS_OK); rv = ogs_filteraddrinfo(&addr6, AF_INET6); ogs_assert(rv == OGS_OK); if (addr6) port = ogs_sbi_uri_port_from_scheme_and_addr(scheme, addr6); else if (addr) port = ogs_sbi_uri_port_from_scheme_and_addr(scheme, addr); uri = ogs_uridup(scheme, hostname, addr, addr6, port, h); ogs_freeaddrinfo(addr); ogs_freeaddrinfo(addr6); return uri; } char *ogs_sbi_client_uri(ogs_sbi_client_t *client, ogs_sbi_header_t *h) { uint16_t port = 0; ogs_assert(client); if (client->fqdn) { port = client->fqdn_port; } else { if (client->addr6) { port = ogs_sbi_uri_port_from_scheme_and_addr( client->scheme, client->addr6); } else if (client->addr) { port = ogs_sbi_uri_port_from_scheme_and_addr( client->scheme, client->addr); } } return ogs_uridup( client->scheme, client->fqdn, client->addr, client->addr6, port, h); } char *ogs_sbi_client_apiroot(ogs_sbi_client_t *client) { return ogs_sbi_client_uri(client, NULL); } /** * Returns a url-decoded version of str * IMPORTANT: be sure to free() the returned string after use * Thanks Geek Hideout! * http://www.geekhideout.com/urlcode.shtml */ char *ogs_sbi_url_encode(const char *str) { if (str != NULL) { 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); while (*pstr) { if (*pstr == '%') { if (pstr[1] && pstr[2]) { *pbuf++ = ogs_from_hex(pstr[1]) << 4 | ogs_from_hex(pstr[2]); pstr += 2; } } else if (*pstr == '+') { *pbuf++ = ' '; } else { *pbuf++ = * pstr; } pstr++; } *pbuf = '\0'; return buf; } else { return NULL; } } char *ogs_sbi_parse_uri(char *uri, const char *delim, char **saveptr) { char *item = NULL; item = ogs_sbi_url_decode(ogs_strtok_r(uri, delim, saveptr)); if (!item) { return NULL; } return item; } bool ogs_sbi_getaddr_from_uri( OpenAPI_uri_scheme_e *scheme, char **fqdn, uint16_t *fqdn_port, ogs_sockaddr_t **addr, ogs_sockaddr_t **addr6, char *uri) { int rv; ogs_sockaddr_t tmp; struct yuarel yuarel; char *p = NULL; int port = 0; ogs_assert(fqdn); ogs_assert(fqdn_port); ogs_assert(addr); ogs_assert(addr6); ogs_assert(uri); p = ogs_strdup(uri); rv = yuarel_parse(&yuarel, p); if (rv != OGS_OK) { ogs_free(p); ogs_error("yuarel_parse() failed [%s]", uri); return false; } if (!yuarel.scheme) { ogs_error("No http.scheme found [%s]", uri); ogs_free(p); return false; } if (strcmp(yuarel.scheme, "https") == 0) { *scheme = OpenAPI_uri_scheme_https; } else if (strcmp(yuarel.scheme, "http") == 0) { *scheme = OpenAPI_uri_scheme_http; } else { ogs_error("Invalid http.scheme [%s:%s]", yuarel.scheme, uri); ogs_free(p); return false; } if (!yuarel.host) { ogs_error("No http.host found [%s]", uri); ogs_free(p); return false; } if (yuarel.port) port = yuarel.port; if (ogs_inet_pton(AF_INET, yuarel.host, &tmp) == OGS_OK || ogs_inet_pton(AF_INET6, yuarel.host, &tmp) == OGS_OK) { rv = ogs_getaddrinfo(addr, AF_UNSPEC, yuarel.host, port, 0); if (rv != OGS_OK) { ogs_error("ogs_getaddrinfo() failed [%s]", uri); ogs_free(p); return false; } rv = ogs_copyaddrinfo(addr6, *addr); ogs_assert(rv == OGS_OK); rv = ogs_filteraddrinfo(addr, AF_INET); ogs_assert(rv == OGS_OK); rv = ogs_filteraddrinfo(addr6, AF_INET6); ogs_assert(rv == OGS_OK); } else { *fqdn = ogs_strdup(yuarel.host); ogs_assert(*fqdn); *fqdn_port = port; } ogs_free(p); return true; } bool ogs_sbi_getpath_from_uri(char **path, char *uri) { int rv; struct yuarel yuarel; char *p = NULL; ogs_assert(uri); p = ogs_strdup(uri); rv = yuarel_parse(&yuarel, p); if (rv != OGS_OK) { ogs_error("yuarel_parse() failed [%s]", uri); ogs_free(p); return false; } if (!yuarel.scheme) { ogs_error("No http.scheme found [%s]", uri); ogs_free(p); return false; } if (strcmp(yuarel.scheme, "https") == 0) { } else if (strcmp(yuarel.scheme, "http") == 0) { } else { ogs_error("Invalid http.scheme [%s:%s]", yuarel.scheme, uri); ogs_free(p); return false; } if (!yuarel.host) { ogs_error("No http.host found [%s]", uri); ogs_free(p); return false; } if (!yuarel.path) { ogs_error("No http.path found [%s]", uri); ogs_free(p); return false; } *path = ogs_strdup(yuarel.path); ogs_assert(*path); ogs_free(p); return true; } char *ogs_sbi_client_resolve( OpenAPI_uri_scheme_e scheme, char *fqdn, uint16_t fqdn_port, const char **resolve, int num_of_resolve) { int i; uint16_t port; char *result = NULL; ogs_assert(scheme); ogs_assert(fqdn); ogs_assert(resolve); ogs_assert(resolve[0]); ogs_assert(num_of_resolve); port = fqdn_port; if (!port) { if (scheme == OpenAPI_uri_scheme_https) port = OGS_SBI_HTTPS_PORT; else if (scheme == OpenAPI_uri_scheme_http) port = OGS_SBI_HTTP_PORT; else ogs_assert_if_reached(); } result = ogs_msprintf("%s:%d:%s", fqdn, port, resolve[0]); if (!result) { ogs_error("ogs_msprintf() failed"); return NULL; } for (i = 1; i < num_of_resolve; i++) { ogs_assert(resolve[i]); result = ogs_mstrcatf(result, ",%s", resolve[i]); if (!result) { ogs_error("ogs_mstrcatf() failed"); ogs_free(result); return NULL; } } return result; } char *ogs_sbi_bitrate_to_string(uint64_t bitrate, int unit) { if (unit == OGS_SBI_BITRATE_KBPS) { return ogs_msprintf("%lld Kbps", (long long)bitrate / 1000); } else if (unit == OGS_SBI_BITRATE_MBPS) { return ogs_msprintf("%lld Mbps", (long long)bitrate / 1000 / 1000); } else if (unit == OGS_SBI_BITRATE_GBPS) { return ogs_msprintf("%lld Gbps", (long long)bitrate / 1000 / 1000 / 1000); } else if (unit == OGS_SBI_BITRATE_TBPS) { return ogs_msprintf("%lld Tbps", (long long)bitrate / 1000 / 1000 / 1000 / 1000); } return ogs_msprintf("%lld bps", (long long)bitrate); } uint64_t ogs_sbi_bitrate_from_string(char *str) { char *unit = NULL; uint64_t bitrate = 0; ogs_assert(str); uint64_t mul = 1; unit = strrchr(str, ' '); bitrate = atoll(str); if (!unit) { ogs_error("No Unit [%s]", str); return bitrate; } SWITCH(unit+1) CASE("Kbps") mul = 1000ul; break; CASE("Mbps") mul = 1000ul * 1000ul; break; CASE("Gbps") mul = 1000ul * 1000ul * 1000ul; break; CASE("Tbps") mul = 1000ul * 1000ul * 1000ul * 1000ul; break; DEFAULT END if (bitrate >= (INT64_MAX / mul)) bitrate = INT64_MAX; else bitrate *= mul; return bitrate; } #define MAX_TIMESTR_LEN 128 int ogs_strftimezone(char *str, size_t size, int tm_gmtoff) { uint8_t off_sign; int off; int len; ogs_assert(str); ogs_assert(size); off_sign = '+'; off = tm_gmtoff; if (tm_gmtoff < 0) { off_sign = '-'; off = -off; } len = ogs_snprintf(str, size, "%c%02d:%02d", off_sign, off / 3600, (off % 3600) / 60); if (len != 6) { ogs_fatal("Unknown tm_gmtoff[%d:%d], len[%d], str[%s]", tm_gmtoff, off, len, str); ogs_assert_if_reached(); } return len; } #define USE_MILLISECONDS_IN_RFC3339 0 char *ogs_sbi_localtime_string(ogs_time_t timestamp) { struct tm tm; char datetime[MAX_TIMESTR_LEN]; char timezone[MAX_TIMESTR_LEN]; int len; ogs_localtime(ogs_time_sec(timestamp), &tm); ogs_strftime(datetime, sizeof datetime, "%Y-%m-%dT%H:%M:%S", &tm); len = ogs_strftimezone(timezone, MAX_TIMESTR_LEN, tm.tm_gmtoff); ogs_assert(len == 6); #if USE_MILLISECONDS_IN_RFC3339 return ogs_msprintf("%s.%03lld%s", datetime, (long long)ogs_time_msec(timestamp), timezone); #else return ogs_msprintf("%s.%06lld%s", datetime, (long long)ogs_time_usec(timestamp), timezone); #endif } char *ogs_sbi_gmtime_string(ogs_time_t timestamp) { struct tm tm; char datetime[MAX_TIMESTR_LEN]; ogs_gmtime(ogs_time_sec(timestamp), &tm); ogs_strftime(datetime, sizeof datetime, "%Y-%m-%dT%H:%M:%S", &tm); #if USE_MILLISECONDS_IN_RFC3339 return ogs_msprintf("%s.%03lldZ", datetime, (long long)ogs_time_msec(timestamp)); #else return ogs_msprintf("%s.%06lldZ", datetime, (long long)ogs_time_usec(timestamp)); #endif } char *ogs_sbi_timezone_string(int tm_gmtoff) { char timezone[MAX_TIMESTR_LEN]; int len; len = ogs_strftimezone(timezone, MAX_TIMESTR_LEN, tm_gmtoff); ogs_assert(len == 6); return ogs_msprintf("%s", timezone); } bool ogs_sbi_time_from_string(ogs_time_t *timestamp, char *str) { int rv, i, j, k; struct tm tm; bool is_subsecs, is_time, timezone_found; char seconds[MAX_TIMESTR_LEN]; char subsecs[MAX_TIMESTR_LEN]; ogs_time_t usecs; ogs_assert(str); ogs_assert(timestamp); memset(seconds, 0, sizeof seconds); memset(subsecs, 0, sizeof subsecs); is_subsecs = false; is_time = false; timezone_found = false; i = 0; j = 0, k = 0; while(str[i]) { if (is_subsecs == false && str[i] == '.') is_subsecs = true; else if (is_subsecs == false && str[i] == 'T') is_time = true; else if (is_subsecs == true && (str[i] < '0' || str[i] > '9')) is_subsecs = false; if (is_time == true && (str[i] == '+' || str[i] == '-')) timezone_found = true; if (is_subsecs == false) { if (str[i] == ':' && i >= 3 && (str[i-3] == '+' || str[i-3] == '-')) { /* remove ':' character in timezone string range */ } else { seconds[j++] = str[i]; } } else { subsecs[k++] = str[i]; } i++; } memset(&tm, 0, sizeof(tm)); if (timezone_found == true) ogs_strptime(seconds, "%Y-%m-%dT%H:%M:%S%z", &tm); else ogs_strptime(seconds, "%Y-%m-%dT%H:%M:%S", &tm); #if USE_MATH usecs = (ogs_time_t)floor(atof(subsecs) * 1000000.0 + 0.5); #else usecs = (ogs_time_t)((atof(subsecs) * 10000000 + 5) / 10); #endif rv = ogs_time_from_gmt(timestamp, &tm, usecs); if (rv != OGS_OK) { ogs_error("Cannot convert time [%s]", str); return false; } return true; } int ogs_sbi_rfc7231_string(char *date_str, ogs_time_t time) { const char ogs_month_snames[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; const char ogs_day_snames[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; struct tm gmt; const char *s; int real_year; ogs_time_t sec = ogs_time_sec(time); ogs_time_t msec = ogs_time_msec(time); ogs_assert(date_str); ogs_gmtime(sec, &gmt); /* example: "Sun, 04 Aug 2019 08:49:37.845 GMT" */ /* 123456789012345678901234567890123 */ s = &ogs_day_snames[gmt.tm_wday][0]; *date_str++ = *s++; *date_str++ = *s++; *date_str++ = *s++; *date_str++ = ','; *date_str++ = ' '; *date_str++ = gmt.tm_mday / 10 + '0'; *date_str++ = gmt.tm_mday % 10 + '0'; *date_str++ = ' '; s = &ogs_month_snames[gmt.tm_mon][0]; *date_str++ = *s++; *date_str++ = *s++; *date_str++ = *s++; *date_str++ = ' '; real_year = 1900 + gmt.tm_year; /* This routine isn't y10k ready. */ *date_str++ = real_year / 1000 + '0'; *date_str++ = real_year % 1000 / 100 + '0'; *date_str++ = real_year % 100 / 10 + '0'; *date_str++ = real_year % 10 + '0'; *date_str++ = ' '; *date_str++ = gmt.tm_hour / 10 + '0'; *date_str++ = gmt.tm_hour % 10 + '0'; *date_str++ = ':'; *date_str++ = gmt.tm_min / 10 + '0'; *date_str++ = gmt.tm_min % 10 + '0'; *date_str++ = ':'; *date_str++ = gmt.tm_sec / 10 + '0'; *date_str++ = gmt.tm_sec % 10 + '0'; *date_str++ = '.'; *date_str++ = msec / 100 + '0'; *date_str++ = msec % 100 / 10 + '0'; *date_str++ = msec % 10 + '0'; *date_str++ = ' '; *date_str++ = 'G'; *date_str++ = 'M'; *date_str++ = 'T'; *date_str++ = 0; return OGS_OK; } char *ogs_sbi_s_nssai_to_json(ogs_s_nssai_t *s_nssai) { cJSON *item = NULL; OpenAPI_snssai_t sNSSAI; char *v = NULL; ogs_assert(s_nssai); sNSSAI.sst = s_nssai->sst; sNSSAI.sd = ogs_s_nssai_sd_to_string(s_nssai->sd); item = OpenAPI_snssai_convertToJSON(&sNSSAI); if (!item) { ogs_error("OpenAPI_snssai_convertToJSON() failed"); return NULL; } if (sNSSAI.sd) ogs_free(sNSSAI.sd); v = cJSON_PrintUnformatted(item); ogs_expect(v); cJSON_Delete(item); return v; } bool ogs_sbi_s_nssai_from_json(ogs_s_nssai_t *s_nssai, char *str) { bool rc = false; cJSON *item = NULL; OpenAPI_snssai_t *sNSSAI = NULL; ogs_assert(s_nssai); ogs_assert(str); item = cJSON_Parse(str); if (item) { sNSSAI = OpenAPI_snssai_parseFromJSON(item); if (sNSSAI) { s_nssai->sst = sNSSAI->sst; s_nssai->sd = ogs_s_nssai_sd_from_string(sNSSAI->sd); OpenAPI_snssai_free(sNSSAI); rc = true; } cJSON_Delete(item); } return rc; } char *ogs_sbi_s_nssai_to_string(ogs_s_nssai_t *s_nssai) { ogs_assert(s_nssai); if (s_nssai->sd.v != OGS_S_NSSAI_NO_SD_VALUE) { return ogs_msprintf("%d-%06x", s_nssai->sst, s_nssai->sd.v); } else { return ogs_msprintf("%d", s_nssai->sst); } } bool ogs_sbi_s_nssai_from_string(ogs_s_nssai_t *s_nssai, char *str) { bool rc = false; char *token, *p, *tofree; char *sst = NULL; char *sd = NULL; ogs_assert(s_nssai); ogs_assert(str); tofree = p = ogs_strdup(str); if (!p) { ogs_error("ogs_strdup[%s] failed", str); goto cleanup; } token = strsep(&p, "-"); if (!token) { ogs_error("strsep[%s] failed", str); goto cleanup; } sst = ogs_strdup(token); if (!sst) { ogs_error("ogs_strdup[%s:%s] failed", str, token); goto cleanup; } s_nssai->sst = atoi(sst); s_nssai->sd.v = OGS_S_NSSAI_NO_SD_VALUE; if (p) { sd = ogs_strdup(p); if (!sd) { ogs_error("ogs_strdup[%s:%s] failed", str, token); goto cleanup; } s_nssai->sd = ogs_uint24_from_string(sd); } rc = true; cleanup: if (tofree) ogs_free(tofree); if (sst) ogs_free(sst); if (sd) ogs_free(sd); return rc; } OpenAPI_plmn_id_t *ogs_sbi_build_plmn_id(ogs_plmn_id_t *plmn_id) { OpenAPI_plmn_id_t *PlmnId = NULL; ogs_assert(plmn_id); PlmnId = ogs_calloc(1, sizeof(*PlmnId)); if (!PlmnId) { ogs_error("ogs_calloc() failed"); return NULL; } PlmnId->mcc = ogs_plmn_id_mcc_string(plmn_id); if (!PlmnId->mcc) { ogs_error("ogs_plmn_id_mcc_string() failed"); ogs_sbi_free_plmn_id(PlmnId); return NULL; } PlmnId->mnc = ogs_plmn_id_mnc_string(plmn_id); if (!PlmnId->mnc) { ogs_error("ogs_plmn_id_mnc_string() failed"); ogs_sbi_free_plmn_id(PlmnId); return NULL; } return PlmnId; } bool ogs_sbi_parse_plmn_id( ogs_plmn_id_t *plmn_id, OpenAPI_plmn_id_t *PlmnId) { ogs_assert(plmn_id); ogs_assert(PlmnId); ogs_assert(PlmnId->mcc); ogs_assert(PlmnId->mnc); ogs_plmn_id_build(plmn_id, atoi(PlmnId->mcc), atoi(PlmnId->mnc), strlen(PlmnId->mnc)); return true; } void ogs_sbi_free_plmn_id(OpenAPI_plmn_id_t *PlmnId) { ogs_assert(PlmnId); if (PlmnId->mcc) ogs_free(PlmnId->mcc); if (PlmnId->mnc) ogs_free(PlmnId->mnc); ogs_free(PlmnId); } OpenAPI_list_t *ogs_sbi_build_plmn_list( ogs_plmn_id_t *plmn_list, int num_of_plmn_list) { OpenAPI_list_t *PlmnList = NULL; OpenAPI_plmn_id_t *PlmnId = NULL; int i; ogs_assert(plmn_list); ogs_assert(num_of_plmn_list); PlmnList = OpenAPI_list_create(); ogs_assert(PlmnList); for (i = 0; i < num_of_plmn_list; i++) { PlmnId = ogs_sbi_build_plmn_id(plmn_list + i); ogs_assert(PlmnId); OpenAPI_list_add(PlmnList, PlmnId); } return PlmnList; } int ogs_sbi_parse_plmn_list( ogs_plmn_id_t *plmn_list, OpenAPI_list_t *PlmnList) { OpenAPI_plmn_id_t *PlmnId = NULL; OpenAPI_lnode_t *node = NULL; int num_of_plmn_list = 0; ogs_assert(plmn_list); ogs_assert(PlmnList); num_of_plmn_list = 0; OpenAPI_list_for_each(PlmnList, node) { PlmnId = node->data; if (PlmnId) { ogs_assert(PlmnId->mcc); ogs_assert(PlmnId->mnc); ogs_plmn_id_build(plmn_list + num_of_plmn_list, atoi(PlmnId->mcc), atoi(PlmnId->mnc), strlen(PlmnId->mnc)); num_of_plmn_list++; } } return num_of_plmn_list; } void ogs_sbi_free_plmn_list(OpenAPI_list_t *PlmnList) { OpenAPI_plmn_id_t *PlmnId = NULL; OpenAPI_lnode_t *node = NULL; ogs_assert(PlmnList); OpenAPI_list_for_each(PlmnList, node) { PlmnId = node->data; if (PlmnId) ogs_sbi_free_plmn_id(PlmnId); } OpenAPI_list_free(PlmnList); } OpenAPI_plmn_id_nid_t *ogs_sbi_build_plmn_id_nid(ogs_plmn_id_t *plmn_id) { OpenAPI_plmn_id_nid_t *PlmnIdNid = NULL; ogs_assert(plmn_id); PlmnIdNid = ogs_calloc(1, sizeof(*PlmnIdNid)); if (!PlmnIdNid) { ogs_error("ogs_calloc() failed"); return NULL; } PlmnIdNid->mcc = ogs_plmn_id_mcc_string(plmn_id); if (!PlmnIdNid->mcc) { ogs_error("ogs_plmn_id_mcc_string() failed"); ogs_sbi_free_plmn_id_nid(PlmnIdNid); return NULL; } PlmnIdNid->mnc = ogs_plmn_id_mnc_string(plmn_id); if (!PlmnIdNid->mnc) { ogs_error("ogs_plmn_id_mnc_string() failed"); ogs_sbi_free_plmn_id_nid(PlmnIdNid); return NULL; } return PlmnIdNid; } bool ogs_sbi_parse_plmn_id_nid( ogs_plmn_id_t *plmn_id, OpenAPI_plmn_id_nid_t *PlmnIdNid) { ogs_assert(plmn_id); ogs_assert(PlmnIdNid); ogs_assert(PlmnIdNid->mcc); ogs_assert(PlmnIdNid->mnc); ogs_plmn_id_build(plmn_id, atoi(PlmnIdNid->mcc), atoi(PlmnIdNid->mnc), strlen(PlmnIdNid->mnc)); return true; } void ogs_sbi_free_plmn_id_nid(OpenAPI_plmn_id_nid_t *PlmnIdNid) { ogs_assert(PlmnIdNid); if (PlmnIdNid->mcc) ogs_free(PlmnIdNid->mcc); if (PlmnIdNid->mnc) ogs_free(PlmnIdNid->mnc); if (PlmnIdNid->nid) ogs_free(PlmnIdNid->nid); ogs_free(PlmnIdNid); } OpenAPI_guami_t *ogs_sbi_build_guami(ogs_guami_t *guami) { OpenAPI_guami_t *Guami = NULL; ogs_assert(guami); Guami = ogs_calloc(1, sizeof(*Guami)); if (!Guami) { ogs_error("ogs_calloc() failed"); return NULL; } Guami->plmn_id = ogs_sbi_build_plmn_id_nid(&guami->plmn_id); if (!Guami->plmn_id) { ogs_error("ogs_sbi_build_plmn_id_nid() failed"); ogs_sbi_free_guami(Guami); return NULL; } Guami->amf_id = ogs_amf_id_to_string(&guami->amf_id); if (!Guami->amf_id) { ogs_error("ogs_amf_id_to_string() failed"); ogs_sbi_free_guami(Guami); return NULL; } return Guami; } bool ogs_sbi_parse_guami(ogs_guami_t *guami, OpenAPI_guami_t *Guami) { ogs_assert(guami); ogs_assert(Guami); ogs_assert(Guami->amf_id); ogs_assert(Guami->plmn_id); ogs_amf_id_from_string(&guami->amf_id, Guami->amf_id); ogs_sbi_parse_plmn_id_nid(&guami->plmn_id, Guami->plmn_id); return true; } void ogs_sbi_free_guami(OpenAPI_guami_t *Guami) { ogs_assert(Guami); if (Guami->plmn_id) ogs_sbi_free_plmn_id_nid(Guami->plmn_id); if (Guami->amf_id) ogs_free(Guami->amf_id); ogs_free(Guami); } OpenAPI_nr_location_t *ogs_sbi_build_nr_location( ogs_5gs_tai_t *tai, ogs_nr_cgi_t *nr_cgi) { OpenAPI_nr_location_t *NrLocation = NULL; OpenAPI_tai_t *Tai = NULL; OpenAPI_ncgi_t *Ncgi = NULL; ogs_assert(tai); ogs_assert(nr_cgi); NrLocation = ogs_calloc(1, sizeof(*NrLocation)); if (!NrLocation) { ogs_error("ogs_calloc() failed"); return NULL; } NrLocation->tai = Tai = ogs_calloc(1, sizeof(*Tai)); if (!Tai) { ogs_error("ogs_calloc() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } Tai->plmn_id = ogs_sbi_build_plmn_id(&tai->plmn_id); if (!Tai->plmn_id) { ogs_error("ogs_sbi_build_plmn_id() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } Tai->tac = ogs_uint24_to_0string(tai->tac); if (!Tai->tac) { ogs_error("ogs_uint24_to_0string() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } NrLocation->ncgi = Ncgi = ogs_calloc(1, sizeof(*Ncgi)); if (!Ncgi) { ogs_error("ogs_calloc() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } Ncgi->plmn_id = ogs_sbi_build_plmn_id(&nr_cgi->plmn_id); if (!Ncgi->plmn_id) { ogs_error("ogs_sbi_build_plmn_id() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } Ncgi->nr_cell_id = ogs_uint36_to_0string(nr_cgi->cell_id); if (!Ncgi->nr_cell_id) { ogs_error("ogs_uint36_to_0string() failed"); ogs_sbi_free_nr_location(NrLocation); return NULL; } return NrLocation; } bool ogs_sbi_parse_nr_location(ogs_5gs_tai_t *tai, ogs_nr_cgi_t *nr_cgi, OpenAPI_nr_location_t *NrLocation) { OpenAPI_tai_t *Tai = NULL; OpenAPI_ncgi_t *Ncgi = NULL; ogs_assert(tai); ogs_assert(nr_cgi); ogs_assert(NrLocation); Tai = NrLocation->tai; if (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); } Ncgi = NrLocation->ncgi; if (Ncgi) { if (Ncgi->plmn_id) ogs_sbi_parse_plmn_id(&nr_cgi->plmn_id, Ncgi->plmn_id); if (Ncgi->nr_cell_id) nr_cgi->cell_id = ogs_uint64_from_string(Ncgi->nr_cell_id); } return true; } void ogs_sbi_free_nr_location(OpenAPI_nr_location_t *NrLocation) { OpenAPI_tai_t *Tai = NULL; OpenAPI_ncgi_t *Ncgi = NULL; ogs_assert(NrLocation); Tai = NrLocation->tai; if (Tai) { if (Tai->plmn_id) ogs_sbi_free_plmn_id(Tai->plmn_id); if (Tai->tac) ogs_free(Tai->tac); ogs_free(Tai); } Ncgi = NrLocation->ncgi; if (Ncgi) { if (Ncgi->plmn_id) ogs_sbi_free_plmn_id(Ncgi->plmn_id); if (Ncgi->nr_cell_id) ogs_free(Ncgi->nr_cell_id); ogs_free(Ncgi); } ogs_free(NrLocation); } OpenAPI_pcc_rule_t *ogs_sbi_build_pcc_rule( ogs_pcc_rule_t *pcc_rule, int flow_presence) { OpenAPI_pcc_rule_t *PccRule = NULL; OpenAPI_list_t *FlowInformationList = NULL; OpenAPI_flow_information_t *FlowInformation = NULL; int i; ogs_assert(pcc_rule); PccRule = ogs_calloc(1, sizeof(*PccRule)); ogs_assert(PccRule); /* * At this point, only 1 QosData is used for PccRule. * Therefore, QoS ID uses the same value as PCC Rule ID. */ PccRule->pcc_rule_id = pcc_rule->id; PccRule->ref_qos_data = OpenAPI_list_create(); ogs_assert(PccRule->ref_qos_data); OpenAPI_list_add(PccRule->ref_qos_data, PccRule->pcc_rule_id); PccRule->is_precedence = true; PccRule->precedence = pcc_rule->precedence; if (flow_presence == 1) { FlowInformationList = OpenAPI_list_create(); ogs_assert(FlowInformationList); for (i = 0; i < pcc_rule->num_of_flow; i++) { ogs_flow_t *flow = &pcc_rule->flow[i]; ogs_assert(flow); FlowInformation = ogs_calloc(1, sizeof(*FlowInformation)); ogs_assert(FlowInformation); if (flow->direction == OGS_FLOW_UPLINK_ONLY) FlowInformation->flow_direction = OpenAPI_flow_direction_UPLINK; else if (flow->direction == OGS_FLOW_DOWNLINK_ONLY) FlowInformation->flow_direction = OpenAPI_flow_direction_DOWNLINK; else if (flow->direction == OGS_FLOW_BIDIRECTIONAL) FlowInformation->flow_direction = OpenAPI_flow_direction_BIDIRECTIONAL; else { ogs_fatal("Unsupported direction [%d]", flow->direction); ogs_assert_if_reached(); } ogs_assert(flow->description); FlowInformation->flow_description = flow->description; OpenAPI_list_add(FlowInformationList, FlowInformation); } if (FlowInformationList->count) PccRule->flow_infos = FlowInformationList; else OpenAPI_list_free(FlowInformationList); } return PccRule; } void ogs_sbi_free_pcc_rule(OpenAPI_pcc_rule_t *PccRule) { OpenAPI_flow_information_t *FlowInformation = NULL; OpenAPI_lnode_t *node = NULL; ogs_assert(PccRule); if (PccRule->ref_qos_data) OpenAPI_list_free(PccRule->ref_qos_data); if (PccRule->flow_infos) { OpenAPI_list_for_each(PccRule->flow_infos, node) { FlowInformation = node->data; if (FlowInformation) ogs_free(FlowInformation); } OpenAPI_list_free(PccRule->flow_infos); } ogs_free(PccRule); } OpenAPI_qos_data_t *ogs_sbi_build_qos_data(ogs_pcc_rule_t *pcc_rule) { OpenAPI_qos_data_t *QosData = NULL; ogs_assert(pcc_rule); QosData = ogs_calloc(1, sizeof(*QosData)); ogs_assert(QosData); /* * At this point, only 1 QosData is used for PccRule. * Therefore, QoS ID uses the same value as PCC Rule ID. */ QosData->qos_id = pcc_rule->id; QosData->is__5qi = true; QosData->_5qi = pcc_rule->qos.index; QosData->is_priority_level = true; QosData->priority_level = pcc_rule->qos.arp.priority_level; QosData->arp = ogs_calloc(1, sizeof(OpenAPI_arp_t)); ogs_assert(QosData->arp); if (pcc_rule->qos.arp.pre_emption_capability == OGS_5GC_PRE_EMPTION_ENABLED) QosData->arp->preempt_cap = OpenAPI_preemption_capability_MAY_PREEMPT; else if (pcc_rule->qos.arp.pre_emption_capability == OGS_5GC_PRE_EMPTION_DISABLED) QosData->arp->preempt_cap = OpenAPI_preemption_capability_NOT_PREEMPT; ogs_assert(pcc_rule->qos.arp.pre_emption_capability); if (pcc_rule->qos.arp.pre_emption_vulnerability == OGS_5GC_PRE_EMPTION_ENABLED) QosData->arp->preempt_vuln = OpenAPI_preemption_vulnerability_PREEMPTABLE; else if (pcc_rule->qos.arp.pre_emption_vulnerability == OGS_5GC_PRE_EMPTION_DISABLED) QosData->arp->preempt_vuln = OpenAPI_preemption_vulnerability_NOT_PREEMPTABLE; ogs_assert(pcc_rule->qos.arp.pre_emption_vulnerability); QosData->arp->priority_level = pcc_rule->qos.arp.priority_level; if (pcc_rule->qos.mbr.uplink) QosData->maxbr_ul = ogs_sbi_bitrate_to_string( pcc_rule->qos.mbr.uplink, OGS_SBI_BITRATE_BPS); if (pcc_rule->qos.mbr.downlink) QosData->maxbr_dl = ogs_sbi_bitrate_to_string( pcc_rule->qos.mbr.downlink, OGS_SBI_BITRATE_BPS); if (pcc_rule->qos.gbr.uplink) QosData->gbr_ul = ogs_sbi_bitrate_to_string( pcc_rule->qos.gbr.uplink, OGS_SBI_BITRATE_BPS); if (pcc_rule->qos.gbr.downlink) QosData->gbr_dl = ogs_sbi_bitrate_to_string( pcc_rule->qos.gbr.downlink, OGS_SBI_BITRATE_BPS); return QosData; } void ogs_sbi_free_qos_data(OpenAPI_qos_data_t *QosData) { ogs_assert(QosData); if (QosData->arp) ogs_free(QosData->arp); if (QosData->maxbr_ul) ogs_free(QosData->maxbr_ul); if (QosData->maxbr_dl) ogs_free(QosData->maxbr_dl); if (QosData->gbr_ul) ogs_free(QosData->gbr_ul); if (QosData->gbr_dl) ogs_free(QosData->gbr_dl); ogs_free(QosData); }