mirror of
https://github.com/open5gs/open5gs.git
synced 2026-07-09 16:08:45 +00:00
merge main branch
This commit is contained in:
parent
82956da7b8
commit
21ef6bd350
139 changed files with 7722 additions and 2602 deletions
42
.github/SECURITY.md
vendored
Normal file
42
.github/SECURITY.md
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
The Open5GS project takes security vulnerabilities seriously.
|
||||
|
||||
If you believe you have discovered a security issue in Open5GS, please report it privately and avoid public disclosure until the issue has been reviewed and addressed.
|
||||
|
||||
Please report vulnerabilities privately using GitHub's "Report a vulnerability" button under the Security tab (Security → Advisories → Report a vulnerability). This opens a private advisory visible only to the maintainers.
|
||||
|
||||
When reporting a vulnerability, please include:
|
||||
|
||||
* Description of the issue
|
||||
* Affected version(s)
|
||||
* Steps to reproduce
|
||||
* Proof of concept, if available
|
||||
* Potential impact
|
||||
|
||||
## Scope
|
||||
|
||||
This policy covers security vulnerabilities in the source code of the
|
||||
open5gs/open5gs repository, including the network function implementations and
|
||||
the bundled WebUI.
|
||||
|
||||
The following are generally considered out of scope:
|
||||
|
||||
* Vulnerabilities in third-party software or dependencies
|
||||
* Issues caused by deployment or configuration choices outside Open5GS
|
||||
* Reports generated only by automated scanners without demonstrated impact
|
||||
* Theoretical issues without a realistic threat model or attack scenario
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Users are encouraged to use the latest stable release of Open5GS.
|
||||
|
||||
Security fixes are generally applied to actively maintained versions.
|
||||
|
||||
## Responsible Disclosure
|
||||
|
||||
Please allow maintainers reasonable time to investigate and address reported vulnerabilities before public disclosure.
|
||||
|
||||
The Open5GS project appreciates responsible and coordinated vulnerability disclosure.
|
||||
|
|
@ -340,6 +340,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.1.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- plmn_id:
|
||||
mcc: 001
|
||||
|
|
|
|||
|
|
@ -342,6 +342,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.2.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- plmn_id:
|
||||
mcc: 999
|
||||
|
|
|
|||
|
|
@ -342,6 +342,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.3.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- plmn_id:
|
||||
mcc: 999
|
||||
|
|
|
|||
|
|
@ -350,6 +350,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.1.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 001010000000001-001019999999999
|
||||
|
|
|
|||
|
|
@ -351,6 +351,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.2.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 999700000000001-999709999999999
|
||||
|
|
|
|||
|
|
@ -351,6 +351,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.3.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 999700000000001-999709999999999
|
||||
|
|
|
|||
|
|
@ -355,6 +355,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.1.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 001010000000001-001019999999999
|
||||
|
|
|
|||
|
|
@ -356,6 +356,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.2.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 999700000000001-999709999999999
|
||||
|
|
|
|||
|
|
@ -356,6 +356,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.3.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
policy:
|
||||
- supi_range:
|
||||
- 999700000000001-999709999999999
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ mme:
|
|||
time:
|
||||
# t3402:
|
||||
# value: 720 # 12 minutes * 60 = 720 seconds
|
||||
# t3396:
|
||||
# value: 720 # 12 minutes * 60 = 720 seconds
|
||||
# t3412:
|
||||
# value: 3240 # 54 minutes * 60 = 3240 seconds
|
||||
# t3423:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,26 @@ pcf:
|
|||
- address: 127.0.0.13
|
||||
port: 9090
|
||||
|
||||
################################################################################
|
||||
# QoS Profile Mapping (Npcf_PolicyAuthorization / TS 29.514)
|
||||
################################################################################
|
||||
# Maps AF-supplied qosReference strings to configured 5QI/QoS index values.
|
||||
# Required only when AFs send qosReference in MediaComponent.
|
||||
# If absent, the PCF falls back to media-type mapping (AUDIO->1, VIDEO->2,
|
||||
# CONTROL->5). Unknown qosReference values result in 403 Forbidden.
|
||||
#
|
||||
# qos_profiles:
|
||||
# - reference: "conversational-voice"
|
||||
# qos_index: 1
|
||||
# - reference: "conversational-video"
|
||||
# qos_index: 2
|
||||
# - reference: "ims-signalling"
|
||||
# qos_index: 5
|
||||
# - reference: "standard-data"
|
||||
# qos_index: 9
|
||||
# - reference: "low-latency"
|
||||
# qos_index: 3
|
||||
|
||||
################################################################################
|
||||
# PCF Policy Configuration: UE Home PLMN and SUPI Range Based Policies
|
||||
################################################################################
|
||||
|
|
|
|||
|
|
@ -286,6 +286,11 @@ pcf:
|
|||
server:
|
||||
- address: 127.0.0.13
|
||||
port: 9090
|
||||
qos_profiles:
|
||||
- reference: "test-audio"
|
||||
qos_index: 1
|
||||
- reference: "test-video"
|
||||
qos_index: 2
|
||||
|
||||
nssf:
|
||||
sbi:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ If you have tested radio hardware from a vendor not listed with Open5GS, please
|
|||
* NOKIA AEQE (SW: 5G20A)
|
||||
* NOKIA AEQD (SW: 5G20A)
|
||||
* NOKIA AEQP (SW: 5G21A)
|
||||
* NOKIA AIRSCALE ASIK+ABIL BBU + AWHQE (n78) (SW: SBTS 25R2)
|
||||
* MOSO Networks Canopy 5GID1 Indoor 2T2R (5G n48 n78)
|
||||
* ZTE ITBBU ITRAN-PNF V5.65.20.20F10 (n78, n1, n3)
|
||||
|
||||
|
|
@ -62,6 +63,7 @@ If you have tested radio hardware from a vendor not listed with Open5GS, please
|
|||
* Nokia FWGR Flexi Zone Mini-Macro Outdoor BTS, 2x20w Band 1
|
||||
* Nokia FWHG Flexi Zone Indoor Pico BTS, 2x250 mW Band 7
|
||||
* Nokia FW2HHD Flexi Zone Multiband Indoor Pico BTS, Band 38/41(S/W TLS18SP_ENB)
|
||||
* Nokia Airscale - ASIA + ABIA + AHIB Band 66 (SW: SBTS325R2)
|
||||
* Mikrotik Intercell B1+B3 IC322GC-b1D+b3D
|
||||
* Ruckus Q710 and Q910
|
||||
* Sercomm SCE4255W "Englewood" (band 48/CBRS, SW version DG3934v3@2308041842)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ permalink: /docs/
|
|||
head_inline: "<style> ul { padding-bottom: 1em; } </style>"
|
||||
---
|
||||
|
||||
- Featured Community Projects
|
||||
- [Open5GS Network Management System (NMS)](https://github.com/paulmataruso/open5gs-nms) - Web UI for Open5GS: config, subscribers, monitoring, and topology in one place
|
||||
- [Docker Open5GS](https://github.com/herlesupreeth/docker_open5gs) - One-command Docker setup for a full 4G/5G Open5GS stack (Core, IMS, RAN, UE).
|
||||
|
||||
- User's Guide
|
||||
- [Quickstart](guide/01-quickstart)
|
||||
- [Building Open5GS from Sources](guide/02-building-open5gs-from-sources)
|
||||
|
|
|
|||
|
|
@ -256,6 +256,9 @@ int ogs_app_parse_global_conf(ogs_yaml_iter_t *parent)
|
|||
} else if (!strcmp(parameter_key, "fake_csfb")) {
|
||||
global_conf.parameter.fake_csfb =
|
||||
ogs_yaml_iter_bool(¶meter_iter);
|
||||
} else if (!strcmp(parameter_key, "no_ims")) {
|
||||
global_conf.parameter.no_ims =
|
||||
ogs_yaml_iter_bool(¶meter_iter);
|
||||
} else if (!strcmp(parameter_key,
|
||||
"no_ipv4v6_local_addr_in_packet_filter")) {
|
||||
global_conf.parameter.
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ typedef struct ogs_global_conf_s {
|
|||
|
||||
int use_openair;
|
||||
int fake_csfb;
|
||||
int no_ims;
|
||||
int use_upg_vpp;
|
||||
int no_ipv4v6_local_addr_in_packet_filter;
|
||||
|
||||
|
|
|
|||
|
|
@ -374,6 +374,32 @@ void *ogs_hash_get_or_set_debug(ogs_hash_t *ht,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
bool ogs_hash_unset_if_owner_debug(ogs_hash_t *ht,
|
||||
const void *key, int klen, const void *owner, const char *file_line)
|
||||
{
|
||||
void *indexed = NULL;
|
||||
|
||||
ogs_assert(ht);
|
||||
ogs_assert(key);
|
||||
ogs_assert(klen);
|
||||
ogs_assert(owner);
|
||||
|
||||
indexed = ogs_hash_get_debug(ht, key, klen, file_line);
|
||||
if (indexed == owner) {
|
||||
ogs_hash_set_debug(ht, key, klen, NULL, file_line);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A newer context has re-claimed this key (the same identity re-attached
|
||||
* while this context was still being torn down). Leave the entry alone so
|
||||
* the live context stays findable; clearing it would orphan that entry.
|
||||
*/
|
||||
ogs_error("%s: hash unset skipped: entry not owned by expected context",
|
||||
file_line);
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int ogs_hash_count(ogs_hash_t *ht)
|
||||
{
|
||||
ogs_assert(ht);
|
||||
|
|
@ -382,12 +408,37 @@ unsigned int ogs_hash_count(ogs_hash_t *ht)
|
|||
|
||||
void ogs_hash_clear(ogs_hash_t *ht)
|
||||
{
|
||||
ogs_hash_index_t *hi;
|
||||
unsigned int i;
|
||||
|
||||
ogs_assert(ht);
|
||||
ogs_assert(ht->array);
|
||||
|
||||
for (hi = ogs_hash_first(ht); hi; hi = ogs_hash_next(hi))
|
||||
ogs_hash_set(ht, hi->this->key, hi->this->klen, NULL);
|
||||
/*
|
||||
* Recycle entries directly through the bucket array instead of
|
||||
* re-locating each one with ogs_hash_set(ht, key, klen, NULL).
|
||||
*
|
||||
* Clearing the table only needs the entries we already hold, not their
|
||||
* hash values, so re-hashing the key here is unnecessary -- and unsafe.
|
||||
* During teardown a key may point at memory that an owning context has
|
||||
* already freed; hashfunc_default() reads the key byte-by-byte (and
|
||||
* find_entry() then memcmp()s it), which is a use-after-free read on a
|
||||
* dangling key. Walking the buckets never dereferences key contents, so
|
||||
* a stale entry is recycled harmlessly rather than crashing the exit
|
||||
* path. (The hash never owns the key memory, so nothing is freed here.)
|
||||
*/
|
||||
for (i = 0; i <= ht->max; i++) {
|
||||
ogs_hash_entry_t *he = ht->array[i];
|
||||
|
||||
ht->array[i] = NULL;
|
||||
while (he) {
|
||||
ogs_hash_entry_t *next_he = he->next;
|
||||
|
||||
he->next = ht->free;
|
||||
ht->free = he;
|
||||
he = next_he;
|
||||
}
|
||||
}
|
||||
ht->count = 0;
|
||||
}
|
||||
|
||||
/* This is basically the following...
|
||||
|
|
|
|||
|
|
@ -67,6 +67,23 @@ void *ogs_hash_get_debug(ogs_hash_t *ht,
|
|||
void *ogs_hash_get_or_set_debug(ogs_hash_t *ht,
|
||||
const void *key, int klen, const void *val, const char *file_line);
|
||||
|
||||
/*
|
||||
* Clear an entry ONLY if it still points at the expected owner.
|
||||
*
|
||||
* Identity-keyed tables (SUPI/SUCI/GUTI/IMSI/...) can be re-claimed by a
|
||||
* freshly created context while an older context that holds the same key is
|
||||
* still being torn down (re-attach races, deferred removal). A blind
|
||||
* ogs_hash_set(ht, key, klen, NULL) in the old context's teardown would then
|
||||
* orphan the newer entry, leaving the live context unfindable.
|
||||
*
|
||||
* Returns true if the entry was unset (owner matched), false if it was left
|
||||
* in place because a newer context owns it now.
|
||||
*/
|
||||
#define ogs_hash_unset_if_owner(ht, key, klen, owner) \
|
||||
ogs_hash_unset_if_owner_debug(ht, key, klen, owner, OGS_FILE_LINE)
|
||||
bool ogs_hash_unset_if_owner_debug(ogs_hash_t *ht,
|
||||
const void *key, int klen, const void *owner, const char *file_line);
|
||||
|
||||
ogs_hash_index_t *ogs_hash_first(ogs_hash_t *ht);
|
||||
ogs_hash_index_t *ogs_hash_next(ogs_hash_index_t *hi);
|
||||
void ogs_hash_this(ogs_hash_index_t *hi,
|
||||
|
|
|
|||
|
|
@ -512,8 +512,8 @@ static int tlv_parse_leaf(void *msg, ogs_tlv_desc_t *desc, ogs_tlv_t *tlv)
|
|||
{
|
||||
ogs_tlv_uint16_t *v = (ogs_tlv_uint16_t *)msg;
|
||||
|
||||
if (tlv->length < 1 || tlv->length > 2) {
|
||||
ogs_error("Invalid TLV length %d.", tlv->length);
|
||||
if (tlv->length != 2) {
|
||||
ogs_error("Invalid TLV length %d. It should be 2", tlv->length);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
v->u16 = ((((uint8_t*)tlv->value)[0]<< 8)&0xff00) |
|
||||
|
|
@ -527,8 +527,8 @@ static int tlv_parse_leaf(void *msg, ogs_tlv_desc_t *desc, ogs_tlv_t *tlv)
|
|||
{
|
||||
ogs_tlv_uint24_t *v = (ogs_tlv_uint24_t *)msg;
|
||||
|
||||
if (tlv->length < 1 || tlv->length > 3) {
|
||||
ogs_error("Invalid TLV length %d.", tlv->length);
|
||||
if (tlv->length != 3) {
|
||||
ogs_error("Invalid TLV length %d. It should be 3", tlv->length);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
v->u24 = ((((uint8_t*)tlv->value)[0]<<16)&0x00ff0000) |
|
||||
|
|
@ -543,8 +543,8 @@ static int tlv_parse_leaf(void *msg, ogs_tlv_desc_t *desc, ogs_tlv_t *tlv)
|
|||
{
|
||||
ogs_tlv_uint32_t *v = (ogs_tlv_uint32_t *)msg;
|
||||
|
||||
if (tlv->length < 1 || tlv->length > 4) {
|
||||
ogs_error("Invalid TLV length %d.", tlv->length);
|
||||
if (tlv->length != 4) {
|
||||
ogs_error("Invalid TLV length %d. It should be 4", tlv->length);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
v->u32 = ((((uint8_t*)tlv->value)[0]<<24)&0xff000000) |
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019-2022 by Sukchan Lee <acetcom@gmail.com>
|
||||
* Copyright (C) 2019-2026 by Sukchan Lee <acetcom@gmail.com>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
|
|
@ -78,125 +78,191 @@ static const unsigned char pr2six[256] =
|
|||
64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
|
||||
};
|
||||
|
||||
int ogs_base64_decode_len(const char *bufcoded)
|
||||
static bool ogs_base64_scan(
|
||||
const char *in, size_t *out_nprbytes, size_t *out_decoded_size)
|
||||
{
|
||||
int nbytesdecoded;
|
||||
register const unsigned char *bufin;
|
||||
register size_t nprbytes;
|
||||
const unsigned char *p = NULL;
|
||||
size_t nprbytes = 0, padding = 0;
|
||||
size_t decoded_size = 0;
|
||||
|
||||
bufin = (const unsigned char *) bufcoded;
|
||||
while (pr2six[*(bufin++)] <= 63);
|
||||
if (!in)
|
||||
return false;
|
||||
|
||||
nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
|
||||
nbytesdecoded = (((int)nprbytes + 3) / 4) * 3;
|
||||
p = (const unsigned char *)in;
|
||||
while (*p) {
|
||||
if (pr2six[*p] <= 63) {
|
||||
if (padding)
|
||||
return false;
|
||||
nprbytes++;
|
||||
} else if (*p == '=') {
|
||||
padding++;
|
||||
if (padding > 2)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
return nbytesdecoded + 1;
|
||||
if (!nprbytes)
|
||||
return false;
|
||||
|
||||
/* A single remaining base64 character cannot produce valid output. */
|
||||
if (nprbytes % 4 == 1)
|
||||
return false;
|
||||
|
||||
/* If padding exists, the encoded length including padding must be aligned. */
|
||||
if (padding && ((nprbytes + padding) % 4))
|
||||
return false;
|
||||
|
||||
decoded_size = ((nprbytes + 3) / 4) * 3;
|
||||
decoded_size -= (4 - nprbytes) & 3;
|
||||
|
||||
if (!decoded_size)
|
||||
return false;
|
||||
|
||||
if (out_nprbytes)
|
||||
*out_nprbytes = nprbytes;
|
||||
if (out_decoded_size)
|
||||
*out_decoded_size = decoded_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ogs_base64_decode(char *bufplain, const char *bufcoded)
|
||||
int ogs_base64_decoded_size(const char *in)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = ogs_base64_decode_binary((unsigned char *) bufplain, bufcoded);
|
||||
bufplain[len] = '\0';
|
||||
return len;
|
||||
size_t decoded_size = 0;
|
||||
|
||||
if (!ogs_base64_scan(in, NULL, &decoded_size))
|
||||
return OGS_ERROR;
|
||||
|
||||
return (int)decoded_size;
|
||||
}
|
||||
|
||||
/* This is the same as ogs_base64_decode() except:
|
||||
* - no \0 is appended
|
||||
*/
|
||||
int ogs_base64_decode_binary(unsigned char *bufplain, const char *bufcoded)
|
||||
int ogs_base64_decode_to_buffer(
|
||||
uint8_t *out, size_t out_size, const char *in)
|
||||
{
|
||||
int nbytesdecoded;
|
||||
register const unsigned char *bufin;
|
||||
register unsigned char *bufout;
|
||||
register size_t nprbytes;
|
||||
const unsigned char *bufin = NULL;
|
||||
unsigned char *bufout = NULL;
|
||||
size_t nprbytes = 0, decoded_size = 0;
|
||||
|
||||
bufin = (const unsigned char *) bufcoded;
|
||||
while (pr2six[*(bufin++)] <= 63);
|
||||
nprbytes = (bufin - (const unsigned char *) bufcoded) - 1;
|
||||
nbytesdecoded = (((int)nprbytes + 3) / 4) * 3;
|
||||
if (!out || !out_size)
|
||||
return OGS_ERROR;
|
||||
|
||||
bufout = (unsigned char *) bufplain;
|
||||
bufin = (const unsigned char *) bufcoded;
|
||||
if (!ogs_base64_scan(in, &nprbytes, &decoded_size))
|
||||
return OGS_ERROR;
|
||||
|
||||
if (out_size < decoded_size)
|
||||
return OGS_ERROR;
|
||||
|
||||
bufout = out;
|
||||
bufin = (const unsigned char *)in;
|
||||
|
||||
while (nprbytes > 4) {
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
|
||||
bufin += 4;
|
||||
nprbytes -= 4;
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
|
||||
bufin += 4;
|
||||
nprbytes -= 4;
|
||||
}
|
||||
|
||||
/* Note: (nprbytes == 1) would be an error, so just ignore that case */
|
||||
if (nprbytes > 1) {
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
|
||||
}
|
||||
if (nprbytes > 2) {
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
|
||||
}
|
||||
if (nprbytes > 3) {
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
|
||||
*(bufout++) =
|
||||
(unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
|
||||
}
|
||||
|
||||
nbytesdecoded -= (4 - (int)nprbytes) & 3;
|
||||
return nbytesdecoded;
|
||||
return (int)decoded_size;
|
||||
}
|
||||
|
||||
int ogs_base64_decode_to_string(char *out, size_t out_size, const char *in)
|
||||
{
|
||||
int len;
|
||||
|
||||
if (!out || out_size <= 1)
|
||||
return OGS_ERROR;
|
||||
|
||||
len = ogs_base64_decode_to_buffer((uint8_t *)out, out_size - 1, in);
|
||||
if (len <= 0)
|
||||
return OGS_ERROR;
|
||||
|
||||
out[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
static const char basis_64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
int ogs_base64_encode_len(int len)
|
||||
int ogs_base64_encoded_size(size_t in_size)
|
||||
{
|
||||
return ((len + 2) / 3 * 4) + 1;
|
||||
size_t encoded_size;
|
||||
|
||||
if (in_size > (SIZE_MAX - 1) / 4 * 3)
|
||||
return OGS_ERROR;
|
||||
|
||||
encoded_size = ((in_size + 2) / 3 * 4) + 1;
|
||||
return (int)encoded_size;
|
||||
}
|
||||
|
||||
int ogs_base64_encode(char *encoded, const char *string, int len)
|
||||
int ogs_base64_encode_from_buffer(
|
||||
char *out, size_t out_size, const uint8_t *in, size_t in_size)
|
||||
{
|
||||
return ogs_base64_encode_binary(
|
||||
encoded, (const unsigned char *) string, len);
|
||||
}
|
||||
size_t i;
|
||||
int needed;
|
||||
char *p = NULL;
|
||||
|
||||
/* This is the same as ogs_base64_encode() except on EBCDIC machines, where
|
||||
* the conversion of the input to ascii is left out.
|
||||
*/
|
||||
int ogs_base64_encode_binary(
|
||||
char *encoded, const unsigned char *string, int len)
|
||||
{
|
||||
int i;
|
||||
char *p;
|
||||
if (!out || !out_size || (!in && in_size))
|
||||
return OGS_ERROR;
|
||||
|
||||
p = encoded;
|
||||
for (i = 0; i < len - 2; i += 3) {
|
||||
*p++ = basis_64[(string[i] >> 2) & 0x3F];
|
||||
*p++ = basis_64[((string[i] & 0x3) << 4) |
|
||||
((int) (string[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = basis_64[((string[i + 1] & 0xF) << 2) |
|
||||
((int) (string[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = basis_64[string[i + 2] & 0x3F];
|
||||
needed = ogs_base64_encoded_size(in_size);
|
||||
if (needed <= 0 || out_size < (size_t)needed)
|
||||
return OGS_ERROR;
|
||||
|
||||
p = out;
|
||||
for (i = 0; i + 2 < in_size; i += 3) {
|
||||
*p++ = basis_64[(in[i] >> 2) & 0x3F];
|
||||
*p++ = basis_64[((in[i] & 0x3) << 4) |
|
||||
((int) (in[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = basis_64[((in[i + 1] & 0xF) << 2) |
|
||||
((int) (in[i + 2] & 0xC0) >> 6)];
|
||||
*p++ = basis_64[in[i + 2] & 0x3F];
|
||||
}
|
||||
if (i < len) {
|
||||
*p++ = basis_64[(string[i] >> 2) & 0x3F];
|
||||
if (i == (len - 1)) {
|
||||
*p++ = basis_64[((string[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
}
|
||||
else {
|
||||
*p++ = basis_64[((string[i] & 0x3) << 4) |
|
||||
((int) (string[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = basis_64[((string[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
|
||||
if (i < in_size) {
|
||||
*p++ = basis_64[(in[i] >> 2) & 0x3F];
|
||||
if (i == (in_size - 1)) {
|
||||
*p++ = basis_64[((in[i] & 0x3) << 4)];
|
||||
*p++ = '=';
|
||||
} else {
|
||||
*p++ = basis_64[((in[i] & 0x3) << 4) |
|
||||
((int) (in[i + 1] & 0xF0) >> 4)];
|
||||
*p++ = basis_64[((in[i + 1] & 0xF) << 2)];
|
||||
}
|
||||
*p++ = '=';
|
||||
}
|
||||
|
||||
*p++ = '\0';
|
||||
return (int)(p - encoded);
|
||||
return (int)(p - out);
|
||||
}
|
||||
|
||||
int ogs_base64_encode_from_string(
|
||||
char *out, size_t out_size, const char *in)
|
||||
{
|
||||
if (!in)
|
||||
return OGS_ERROR;
|
||||
|
||||
return ogs_base64_encode_from_buffer(
|
||||
out, out_size, (const uint8_t *)in, strlen(in));
|
||||
}
|
||||
|
||||
/* copies data to result but removes newlines and <CR>
|
||||
|
|
@ -246,7 +312,7 @@ int ogs_fbase64_decode(const char *header,
|
|||
static const char top[] = "-----BEGIN ";
|
||||
static const char bottom[] = "-----END ";
|
||||
uint8_t *rdata, *kdata;
|
||||
int rdata_size;
|
||||
int rdata_size, decoded_size;
|
||||
#define MAX_PEM_HEADER_LEN 128
|
||||
char pem_header[MAX_PEM_HEADER_LEN];
|
||||
|
||||
|
|
@ -323,18 +389,28 @@ int ogs_fbase64_decode(const char *header,
|
|||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
result->data = ogs_calloc(1, bufcoded_len);
|
||||
if (result->data == NULL) {
|
||||
ogs_error("ogs_calloc() failed [%d]", bufcoded_len);
|
||||
decoded_size = ogs_base64_decoded_size((const char *)bufcoded);
|
||||
if (decoded_size <= 0) {
|
||||
ogs_error("ogs_base64_decoded_size() failed");
|
||||
ogs_log_hexdump(OGS_LOG_ERROR, bufcoded, bufcoded_len);
|
||||
ogs_free(bufcoded);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
result->size = ogs_base64_decode_binary(
|
||||
result->data, (const char *)bufcoded);
|
||||
if (result->size == 0) {
|
||||
ogs_error("ogs_base64_decode_binary() failed");
|
||||
result->data = ogs_calloc(1, decoded_size);
|
||||
if (result->data == NULL) {
|
||||
ogs_error("ogs_calloc() failed [%d]", decoded_size);
|
||||
ogs_free(bufcoded);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
result->size = ogs_base64_decode_to_buffer(
|
||||
result->data, decoded_size, (const char *)bufcoded);
|
||||
if (result->size <= 0) {
|
||||
ogs_error("ogs_base64_decode_to_buffer() failed");
|
||||
ogs_log_hexdump(OGS_LOG_ERROR, bufcoded, bufcoded_len);
|
||||
ogs_free(result->data);
|
||||
result->data = NULL;
|
||||
ogs_free(bufcoded);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2019-2022 by Sukchan Lee <acetcom@gmail.com>
|
||||
* Copyright (C) 2019-2026 by Sukchan Lee <acetcom@gmail.com>
|
||||
*
|
||||
* This file is part of Open5GS.
|
||||
*
|
||||
|
|
@ -65,16 +65,17 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ogs_base64_decode_len(const char *bufcoded);
|
||||
int ogs_base64_decode(char *bufplain, const char *bufcoded);
|
||||
int ogs_base64_decode_binary(
|
||||
unsigned char *bufplain, const char *bufcoded);
|
||||
int ogs_base64_decoded_size(const char *in);
|
||||
int ogs_base64_decode_to_buffer(
|
||||
uint8_t *out, size_t out_size, const char *in);
|
||||
int ogs_base64_decode_to_string(
|
||||
char *out, size_t out_size, const char *in);
|
||||
|
||||
int ogs_base64_encode_len(int len);
|
||||
int ogs_base64_encode(
|
||||
char *encoded, const char *string, int len);
|
||||
int ogs_base64_encode_binary(
|
||||
char *encoded, const unsigned char *string, int len);
|
||||
int ogs_base64_encoded_size(size_t in_size);
|
||||
int ogs_base64_encode_from_buffer(
|
||||
char *out, size_t out_size, const uint8_t *in, size_t in_size);
|
||||
int ogs_base64_encode_from_string(
|
||||
char *out, size_t out_size, const char *in);
|
||||
|
||||
int ogs_fbase64_decode(const char *header,
|
||||
const uint8_t *data, size_t data_size, ogs_datum_t *result);
|
||||
|
|
|
|||
|
|
@ -133,10 +133,10 @@ int ogs_dbi_ims_data(char *supi, ogs_ims_data_t *ims_data)
|
|||
|
||||
memset(ims_data, 0, sizeof(*ims_data));
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
query = BCON_NEW(supi_type, BCON_UTF8(supi_id));
|
||||
#if MONGOC_CHECK_VERSION(1, 5, 0)
|
||||
|
|
|
|||
|
|
@ -45,10 +45,10 @@ int ogs_dbi_session_data(
|
|||
ogs_assert(dnn);
|
||||
ogs_assert(session_data);
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
query = BCON_NEW(supi_type, BCON_UTF8(supi_id));
|
||||
#if MONGOC_CHECK_VERSION(1, 5, 0)
|
||||
|
|
|
|||
|
|
@ -138,10 +138,10 @@ int ogs_dbi_update_sqn(char *supi, uint64_t sqn)
|
|||
|
||||
ogs_assert(supi);
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
query = BCON_NEW(supi_type, BCON_UTF8(supi_id));
|
||||
update = BCON_NEW("$set",
|
||||
|
|
@ -177,10 +177,10 @@ int ogs_dbi_update_imeisv(char *supi, char *imeisv)
|
|||
|
||||
ogs_assert(supi);
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
ogs_debug("SUPI type: %s, SUPI id: %s, imeisv: %s",
|
||||
supi_type, supi_id, imeisv);
|
||||
|
|
@ -219,10 +219,10 @@ int ogs_dbi_update_mme(char *supi, char *mme_host, char *mme_realm,
|
|||
|
||||
ogs_assert(supi);
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
ogs_debug("SUPI type: %s, SUPI id: %s, mme_host: %s, mme_realm: %s",
|
||||
supi_type, supi_id, mme_host, mme_realm);
|
||||
|
|
@ -264,10 +264,10 @@ int ogs_dbi_increment_sqn(char *supi)
|
|||
|
||||
ogs_assert(supi);
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
query = BCON_NEW(supi_type, BCON_UTF8(supi_id));
|
||||
update = BCON_NEW("$inc",
|
||||
|
|
@ -327,10 +327,10 @@ int ogs_dbi_subscription_data(char *supi,
|
|||
|
||||
memset(subscription_data, 0, sizeof(*subscription_data));
|
||||
|
||||
supi_type = ogs_id_get_type(supi);
|
||||
ogs_assert(supi_type);
|
||||
supi_id = ogs_id_get_value(supi);
|
||||
ogs_assert(supi_id);
|
||||
if (ogs_id_get_type_value(supi, &supi_type, &supi_id) == false) {
|
||||
ogs_error("Invalid supi=%s", supi);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
query = BCON_NEW(supi_type, BCON_UTF8(supi_id));
|
||||
#if MONGOC_CHECK_VERSION(1, 5, 0)
|
||||
|
|
|
|||
|
|
@ -187,7 +187,8 @@ void ogs_gtp2_fill_header(
|
|||
gtp_hlen = OGS_GTPV1U_HEADER_LEN+OGS_GTPV1U_EXTENSION_HEADER_LEN;
|
||||
|
||||
i = 0;
|
||||
while(ext_hdesc->array[i].len) {
|
||||
while (i < OGS_GTP2_NUM_OF_EXTENSION_HEADER &&
|
||||
ext_hdesc->array[i].len) {
|
||||
gtp_hlen += (ext_hdesc->array[i].len*4);
|
||||
i++;
|
||||
}
|
||||
|
|
@ -243,6 +244,7 @@ void ogs_gtp2_fill_header(
|
|||
|
||||
i = 0;
|
||||
while (i < OGS_GTP2_NUM_OF_EXTENSION_HEADER &&
|
||||
ext_hdesc->array[i].len &&
|
||||
(ext_h - pkbuf->data) < gtp_hlen) {
|
||||
int len = ext_hdesc->array[i].len*4;
|
||||
|
||||
|
|
@ -250,7 +252,8 @@ void ogs_gtp2_fill_header(
|
|||
memcpy(ext_h, &ext_hdesc->array[i].len, len-1);
|
||||
|
||||
/* Check if Next Header is Available */
|
||||
if (ext_hdesc->array[i+1].len)
|
||||
if ((i + 1) < OGS_GTP2_NUM_OF_EXTENSION_HEADER &&
|
||||
ext_hdesc->array[i+1].len)
|
||||
ext_h[len-1] = ext_hdesc->array[i+1].type;
|
||||
else
|
||||
ext_h[len-1] =
|
||||
|
|
|
|||
|
|
@ -3281,26 +3281,65 @@ add_mactype(ipfw_insn *cmd, char *av, int cblen)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ipfw_proto_by_name(const char *name)
|
||||
{
|
||||
struct protoent *pe;
|
||||
|
||||
ogs_assert(name);
|
||||
|
||||
pe = getprotobyname(name);
|
||||
if (pe)
|
||||
return pe->p_proto;
|
||||
|
||||
/*
|
||||
* /etc/protocols lookup failed. Warn so the operator knows the
|
||||
* environment is degraded, then try the built-in fallback.
|
||||
* Protocol names follow lowercase 3GPP IPFilterRule convention
|
||||
* (TS 29.212 / TS 29.514).
|
||||
*/
|
||||
ogs_error("getprotobyname('%s') failed; falling back to built-in table. "
|
||||
"Check /etc/protocols (install netbase or iana-etc)", name);
|
||||
|
||||
/*
|
||||
* SDF filters commonly use protocol names such as udp/tcp.
|
||||
* Do not make rule compilation depend entirely on /etc/protocols
|
||||
* or NSS, and never abort SMF on a lookup failure.
|
||||
*/
|
||||
if (strcmp(name, "tcp") == 0)
|
||||
return IPPROTO_TCP;
|
||||
if (strcmp(name, "udp") == 0)
|
||||
return IPPROTO_UDP;
|
||||
if (strcmp(name, "icmp") == 0)
|
||||
return IPPROTO_ICMP;
|
||||
#ifdef IPPROTO_ICMPV6
|
||||
if (strcmp(name, "icmp6") == 0 ||
|
||||
strcmp(name, "ipv6-icmp") == 0)
|
||||
return IPPROTO_ICMPV6;
|
||||
#endif
|
||||
#ifdef IPPROTO_SCTP
|
||||
if (strcmp(name, "sctp") == 0)
|
||||
return IPPROTO_SCTP;
|
||||
#endif
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static ipfw_insn *
|
||||
add_proto0(ipfw_insn *cmd, char *av, u_char *protop)
|
||||
{
|
||||
struct protoent *pe;
|
||||
char *ep;
|
||||
int proto;
|
||||
|
||||
proto = strtol(av, &ep, 10);
|
||||
if (*ep != '\0' || proto <= 0) {
|
||||
#if 0 /* modified by acetcom */
|
||||
if ((pe = getprotobyname(av)) == NULL)
|
||||
proto = ipfw_proto_by_name(av);
|
||||
if (proto < 0) {
|
||||
ogs_error("Unknown protocol '%s' in flow description "
|
||||
"(getprotobyname() failed; "
|
||||
"check /etc/protocols)", av);
|
||||
return NULL;
|
||||
#else
|
||||
if ((pe = getprotobyname(av)) == NULL) {
|
||||
ogs_fatal("getprotobyname('%s') failed", av);
|
||||
ogs_assert_if_reached();
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
proto = pe->p_proto;
|
||||
}
|
||||
}
|
||||
|
||||
fill_cmd(cmd, O_PROTO, 0, proto);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,10 @@ char *ogs_nas_5gs_suci_from_mobile_identity(
|
|||
char *scheme_output_string_or_bcd = NULL;
|
||||
|
||||
ogs_assert(mobile_identity);
|
||||
ogs_assert(mobile_identity->buffer);
|
||||
|
||||
ogs_assert(mobile_identity->length >=
|
||||
(OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1));
|
||||
|
||||
mobile_identity_suci =
|
||||
(ogs_nas_5gs_mobile_identity_suci_t *)mobile_identity->buffer;
|
||||
|
|
@ -154,14 +158,9 @@ char *ogs_nas_5gs_suci_from_mobile_identity(
|
|||
scheme_output =
|
||||
(uint8_t *)mobile_identity->buffer +
|
||||
OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE;
|
||||
if (mobile_identity->length < OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE) {
|
||||
ogs_error("The length of Mobile Identity(%d) is less then the min(%d)",
|
||||
mobile_identity->length, OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE);
|
||||
ogs_free(suci);
|
||||
return NULL;
|
||||
}
|
||||
scheme_output_size =
|
||||
mobile_identity->length - OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE;
|
||||
ogs_assert(scheme_output_size);
|
||||
scheme_output_string_or_bcd = ogs_calloc(1, scheme_output_size*2+1);
|
||||
ogs_assert(scheme_output_string_or_bcd);
|
||||
|
||||
|
|
|
|||
|
|
@ -504,6 +504,12 @@ int ogs_nas_parse_qos_flow_descriptions(
|
|||
|
||||
size = 0;
|
||||
while (size < length) {
|
||||
if ((description - first) >=
|
||||
OGS_NAS_MAX_NUM_OF_QOS_FLOW_DESCRIPTION) {
|
||||
ogs_error("Too many QoS flow descriptions");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memset(description, 0, sizeof(*description));
|
||||
|
||||
if (size+3 > length) {
|
||||
|
|
@ -513,8 +519,14 @@ int ogs_nas_parse_qos_flow_descriptions(
|
|||
memcpy(description, buffer+size, 3);
|
||||
size += 3;
|
||||
|
||||
for (i = 0; i < description->num_of_parameter &&
|
||||
i < OGS_NAS_MAX_NUM_OF_QOS_FLOW_PARAMETER; i++) {
|
||||
if (description->num_of_parameter >
|
||||
OGS_NAS_MAX_NUM_OF_QOS_FLOW_PARAMETER) {
|
||||
ogs_error("Too many QoS flow parameters [%d]",
|
||||
description->num_of_parameter);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < description->num_of_parameter; i++) {
|
||||
if (size+sizeof(description->param[i].identifier) > length) {
|
||||
ogs_error("Overflow : size[%d] length[%d]", size, length);
|
||||
goto cleanup;
|
||||
|
|
@ -564,6 +576,12 @@ int ogs_nas_parse_qos_flow_descriptions(
|
|||
buffer+size, description->param[i].len);
|
||||
description->param[i].br.value =
|
||||
be16toh(description->param[i].br.value);
|
||||
if (!ogs_nas_bitrate_unit_is_valid(
|
||||
&description->param[i].br)) {
|
||||
ogs_error("Invalid QoS flow bitrate unit [%d]",
|
||||
description->param[i].br.unit);
|
||||
goto cleanup;
|
||||
}
|
||||
size += description->param[i].len;
|
||||
break;
|
||||
default:
|
||||
|
|
@ -576,9 +594,10 @@ int ogs_nas_parse_qos_flow_descriptions(
|
|||
description++;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
return (int)(description-first);
|
||||
|
||||
cleanup:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ogs_nas_build_qos_rules(ogs_nas_qos_rules_t *rules,
|
||||
|
|
@ -806,11 +825,44 @@ int ogs_nas_build_qos_rules(ogs_nas_qos_rules_t *rules,
|
|||
}
|
||||
|
||||
/* Parse "Packet filter list", 3GPP TS 24.501 Figure 9.11.4.13.3 and Figure 9.11.4.13. */
|
||||
static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const uint8_t *buffer, uint16_t length) {
|
||||
static int parse_qos_rules_packet_filter_list(
|
||||
ogs_nas_qos_rule_t *rule, const uint8_t *buffer, uint16_t length)
|
||||
{
|
||||
uint16_t size = 0;
|
||||
int i, j, len = 0;
|
||||
|
||||
for (i = 0; i < rule->num_of_packet_filter && i < OGS_MAX_NUM_OF_FLOW_IN_GTP; i++) {
|
||||
/*
|
||||
* Check that 'needed' more bytes can be read into the current packet filter
|
||||
* content without exceeding either the declared content length of this PF
|
||||
* (rule->pf[i].content.length) or the overall buffer length.
|
||||
*
|
||||
* Both bounds are enforced. Previously only the buffer length was checked,
|
||||
* which allowed a malformed PF declaring content.length=N to read past N
|
||||
* into the next PF's bytes as long as the overall buffer was large enough.
|
||||
*/
|
||||
#define CHECK_PF_CONTENT_ROOM(needed) \
|
||||
do { \
|
||||
if (len + (int)(needed) > rule->pf[i].content.length || \
|
||||
size + len + (needed) > length) { \
|
||||
ogs_error("RuleId[%u] PF[%d] Overflow: " \
|
||||
"size[%d] len[%d] need[%d] " \
|
||||
"content[%d] length[%d]", \
|
||||
rule->identifier, i, size, len, (int)(needed), \
|
||||
rule->pf[i].content.length, length); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
ogs_assert(rule);
|
||||
ogs_assert(buffer);
|
||||
|
||||
if (rule->num_of_packet_filter > OGS_MAX_NUM_OF_FLOW_IN_NAS) {
|
||||
ogs_error("RuleId[%u] Too many packet filters [%d]",
|
||||
rule->identifier, rule->num_of_packet_filter);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (i = 0; i < rule->num_of_packet_filter; i++) {
|
||||
if (size+sizeof(rule->pf[i].flags) > length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] length[%d]", i, size, length);
|
||||
goto cleanup;
|
||||
|
|
@ -830,14 +882,23 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
sizeof(rule->pf[i].content.length));
|
||||
size += sizeof(rule->pf[i].content.length);
|
||||
|
||||
j = 0; len = 0;
|
||||
if (rule->pf[i].content.length == 0) {
|
||||
ogs_error("RuleId[%u] PF[%d] Empty packet filter content",
|
||||
rule->identifier, i);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
len = 0;
|
||||
while(len < rule->pf[i].content.length) {
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].type) > length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
if (j >= OGS_NAS_MAX_NUM_OF_PACKET_FILTER_COMPONENT) {
|
||||
ogs_error("RuleId[%u] PF[%d] Too many packet filter "
|
||||
"components", rule->identifier, i);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].type));
|
||||
memcpy(&rule->pf[i].content.component[j].type,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].type));
|
||||
|
|
@ -846,13 +907,8 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
case OGS_PACKET_FILTER_MATCH_ALL:
|
||||
break;
|
||||
case OGS_PACKET_FILTER_PROTOCOL_IDENTIFIER_NEXT_HEADER_TYPE:
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].proto) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].proto));
|
||||
memcpy(&rule->pf[i].content.component[j].proto,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].proto));
|
||||
|
|
@ -860,25 +916,15 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
break;
|
||||
case OGS_PACKET_FILTER_IPV4_REMOTE_ADDRESS_TYPE:
|
||||
case OGS_PACKET_FILTER_IPV4_LOCAL_ADDRESS_TYPE:
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.addr) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.addr));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv4.addr,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.addr));
|
||||
len += sizeof(rule->pf[i].content.component[j].ipv4.addr);
|
||||
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.mask) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.mask));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv4.mask,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].ipv4.mask));
|
||||
|
|
@ -886,74 +932,40 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
break;
|
||||
case OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_PREFIX_LENGTH_TYPE:
|
||||
case OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_PREFIX_LENGTH_TYPE:
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].ipv6.addr) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv6.addr));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv6.addr,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].ipv6.addr));
|
||||
len += sizeof(rule->pf[i].content.component[j].ipv6.addr);
|
||||
|
||||
if (size+len+
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6.prefixlen) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv6.prefixlen));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv6.prefixlen,
|
||||
buffer+size+len,
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6.prefixlen));
|
||||
len += sizeof(
|
||||
rule->pf[i].content.component[j].ipv6.prefixlen);
|
||||
sizeof(rule->pf[i].content.component[j].ipv6.prefixlen));
|
||||
len += sizeof(rule->pf[i].content.component[j].ipv6.prefixlen);
|
||||
break;
|
||||
case OGS_PACKET_FILTER_IPV6_LOCAL_ADDRESS_TYPE:
|
||||
case OGS_PACKET_FILTER_IPV6_REMOTE_ADDRESS_TYPE:
|
||||
if (size+len+
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.addr) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv6_mask.addr));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv6_mask.addr,
|
||||
buffer+size+len,
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.addr));
|
||||
len += sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.addr);
|
||||
sizeof(rule->pf[i].content.component[j].ipv6_mask.addr));
|
||||
len += sizeof(rule->pf[i].content.component[j].ipv6_mask.addr);
|
||||
|
||||
if (size+len+
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.mask) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].ipv6_mask.mask));
|
||||
memcpy(&rule->pf[i].content.component[j].ipv6_mask.mask,
|
||||
buffer+size+len,
|
||||
sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.mask));
|
||||
len += sizeof(
|
||||
rule->pf[i].content.component[j].ipv6_mask.mask);
|
||||
sizeof(rule->pf[i].content.component[j].ipv6_mask.mask));
|
||||
len += sizeof(rule->pf[i].content.component[j].ipv6_mask.mask);
|
||||
break;
|
||||
case OGS_PACKET_FILTER_SINGLE_LOCAL_PORT_TYPE:
|
||||
case OGS_PACKET_FILTER_SINGLE_REMOTE_PORT_TYPE:
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].port.low) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].port.low));
|
||||
memcpy(&rule->pf[i].content.component[j].port.low,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].port.low));
|
||||
|
|
@ -963,13 +975,8 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
break;
|
||||
case OGS_PACKET_FILTER_LOCAL_PORT_RANGE_TYPE:
|
||||
case OGS_PACKET_FILTER_REMOTE_PORT_RANGE_TYPE:
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].port.low) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].port.low));
|
||||
memcpy(&rule->pf[i].content.component[j].port.low,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].port.low));
|
||||
|
|
@ -977,13 +984,8 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
be16toh(rule->pf[i].content.component[j].port.low);
|
||||
len += sizeof(rule->pf[i].content.component[j].port.low);
|
||||
|
||||
if (size+len+
|
||||
sizeof(rule->pf[i].content.component[j].port.high) >
|
||||
length) {
|
||||
ogs_error("PF[%d] Overflow: size[%d] len[%d] length[%d]",
|
||||
i, size, len, length);
|
||||
goto cleanup;
|
||||
}
|
||||
CHECK_PF_CONTENT_ROOM(
|
||||
sizeof(rule->pf[i].content.component[j].port.high));
|
||||
memcpy(&rule->pf[i].content.component[j].port.high,
|
||||
buffer+size+len,
|
||||
sizeof(rule->pf[i].content.component[j].port.high));
|
||||
|
|
@ -1001,8 +1003,13 @@ static int parse_qos_rules_packet_filter_list(ogs_nas_qos_rule_t *rule, const ui
|
|||
rule->pf[i].content.num_of_component = j;
|
||||
size += len;
|
||||
}
|
||||
|
||||
#undef CHECK_PF_CONTENT_ROOM
|
||||
|
||||
return size;
|
||||
|
||||
cleanup:
|
||||
#undef CHECK_PF_CONTENT_ROOM
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -1034,6 +1041,12 @@ int ogs_nas_parse_qos_rules(
|
|||
|
||||
while (size < length) {
|
||||
bool have_octet_m1, have_octet_m2;
|
||||
|
||||
if ((rule - first) >= OGS_NAS_MAX_NUM_OF_QOS_RULE) {
|
||||
ogs_error("Too many QoS rules");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
memset(rule, 0, sizeof(*rule));
|
||||
|
||||
if (size+sizeof(rule->identifier) > length) {
|
||||
|
|
@ -1073,7 +1086,6 @@ int ogs_nas_parse_qos_rules(
|
|||
if (rule->num_of_packet_filter != 0) {
|
||||
ogs_error("RuleId[%u] Invalid QoS rule code[%d] and number of packet filter[%d]",
|
||||
rule->identifier, rule->code, rule->num_of_packet_filter);
|
||||
rule->num_of_packet_filter = 0;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
|
@ -1149,7 +1161,8 @@ int ogs_nas_parse_qos_rules(
|
|||
rule++;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
||||
return (int)(rule-first);
|
||||
|
||||
cleanup:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -444,10 +444,23 @@ void ogs_nas_bitrate_from_uint64(ogs_nas_bitrate_t *nas, uint64_t bitrate)
|
|||
nas->value = bitrate;
|
||||
}
|
||||
|
||||
bool ogs_nas_bitrate_unit_is_valid(ogs_nas_bitrate_t *nas_bitrate)
|
||||
{
|
||||
ogs_assert(nas_bitrate);
|
||||
|
||||
return nas_bitrate->unit >= OGS_NAS_BR_UNIT_1K &&
|
||||
nas_bitrate->unit <= OGS_NAS_BR_UNIT_256P;
|
||||
}
|
||||
|
||||
uint64_t ogs_nas_bitrate_to_uint64(ogs_nas_bitrate_t *nas_bitrate)
|
||||
{
|
||||
ogs_assert(nas_bitrate);
|
||||
|
||||
if (!ogs_nas_bitrate_unit_is_valid(nas_bitrate)) {
|
||||
ogs_error("Invalid bitrate unit [%d]", nas_bitrate->unit);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nas_bitrate->unit == OGS_NAS_BR_UNIT_1K)
|
||||
return nas_bitrate->value * 1000;
|
||||
if (nas_bitrate->unit == OGS_NAS_BR_UNIT_4K)
|
||||
|
|
@ -499,8 +512,5 @@ uint64_t ogs_nas_bitrate_to_uint64(ogs_nas_bitrate_t *nas_bitrate)
|
|||
if (nas_bitrate->unit == OGS_NAS_BR_UNIT_256P)
|
||||
return nas_bitrate->value * 256 * 1000 * 1000 * 1000 * 1000 * 1000;
|
||||
|
||||
ogs_fatal("Unknown unit [%d]", nas_bitrate->unit);
|
||||
ogs_assert_if_reached();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -979,6 +979,7 @@ typedef struct ogs_nas_bitrate_s {
|
|||
|
||||
void ogs_nas_bitrate_from_uint64(
|
||||
ogs_nas_bitrate_t *nas_bitrate, uint64_t bitrate);
|
||||
bool ogs_nas_bitrate_unit_is_valid(ogs_nas_bitrate_t *nas_bitrate);
|
||||
uint64_t ogs_nas_bitrate_to_uint64(ogs_nas_bitrate_t *nas_bitrate);
|
||||
|
||||
typedef struct ogs_nas_session_ambr_s {
|
||||
|
|
|
|||
|
|
@ -544,7 +544,8 @@ ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess,
|
|||
|
||||
ogs_pfcp_rule_remove_all(pdr);
|
||||
|
||||
for (i = 0; i < OGS_MAX_NUM_OF_FLOW_IN_PDR; i++) {
|
||||
for (i = 0; i < ogs_min(OGS_ARRAY_SIZE(message->pdi.sdf_filter),
|
||||
OGS_MAX_NUM_OF_FLOW_IN_PDR); i++) {
|
||||
ogs_pfcp_sdf_filter_t sdf_filter;
|
||||
ogs_pfcp_rule_t *rule = NULL;
|
||||
ogs_pfcp_rule_t *oppsite_direction_rule = NULL;
|
||||
|
|
@ -933,7 +934,8 @@ ogs_pfcp_pdr_t *ogs_pfcp_handle_update_pdr(ogs_pfcp_sess_t *sess,
|
|||
|
||||
ogs_pfcp_rule_remove_all(pdr);
|
||||
|
||||
for (i = 0; i < OGS_MAX_NUM_OF_FLOW_IN_PDR; i++) {
|
||||
for (i = 0; i < ogs_min(OGS_ARRAY_SIZE(message->pdi.sdf_filter),
|
||||
OGS_MAX_NUM_OF_FLOW_IN_PDR); i++) {
|
||||
ogs_pfcp_sdf_filter_t sdf_filter;
|
||||
ogs_pfcp_rule_t *rule = NULL;
|
||||
ogs_pfcp_rule_t *oppsite_direction_rule = NULL;
|
||||
|
|
@ -1189,8 +1191,15 @@ ogs_pfcp_far_t *ogs_pfcp_handle_create_far(ogs_pfcp_sess_t *sess,
|
|||
ogs_pfcp_tlv_outer_header_creation_t *outer_header_creation =
|
||||
&message->forwarding_parameters.outer_header_creation;
|
||||
|
||||
ogs_assert(outer_header_creation->data);
|
||||
ogs_assert(outer_header_creation->len);
|
||||
if (!outer_header_creation->data ||
|
||||
outer_header_creation->len == 0) {
|
||||
ogs_error("Invalid Outer Header Creation [data:%p,len:%d]",
|
||||
outer_header_creation->data,
|
||||
outer_header_creation->len);
|
||||
*cause_value = OGS_PFCP_CAUSE_INVALID_LENGTH;
|
||||
*offending_ie_value = OGS_PFCP_OUTER_HEADER_CREATION_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&far->outer_header_creation, outer_header_creation->data,
|
||||
ogs_min(sizeof(far->outer_header_creation),
|
||||
|
|
@ -1307,8 +1316,15 @@ ogs_pfcp_far_t *ogs_pfcp_handle_update_far(ogs_pfcp_sess_t *sess,
|
|||
ogs_pfcp_tlv_outer_header_creation_t *outer_header_creation =
|
||||
&message->update_forwarding_parameters.outer_header_creation;
|
||||
|
||||
ogs_assert(outer_header_creation->data);
|
||||
ogs_assert(outer_header_creation->len);
|
||||
if (!outer_header_creation->data ||
|
||||
outer_header_creation->len == 0) {
|
||||
ogs_error("Invalid Outer Header Creation [data:%p,len:%d]",
|
||||
outer_header_creation->data,
|
||||
outer_header_creation->len);
|
||||
*cause_value = OGS_PFCP_CAUSE_INVALID_LENGTH;
|
||||
*offending_ie_value = OGS_PFCP_OUTER_HEADER_CREATION_TYPE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(&far->outer_header_creation, outer_header_creation->data,
|
||||
ogs_min(sizeof(far->outer_header_creation),
|
||||
|
|
|
|||
|
|
@ -35,49 +35,146 @@
|
|||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
|
||||
static int decode_ipv6_header(
|
||||
struct ip6_hdr *ip6_h, uint8_t *proto, uint16_t *hlen)
|
||||
static int decode_ipv6_header(struct ip6_hdr *ip6_h, size_t buflen,
|
||||
uint8_t *proto, uint16_t *hlen, size_t *plen)
|
||||
{
|
||||
int done = 0;
|
||||
uint8_t *p, *jp, *endp;
|
||||
uint8_t *start, *p, *endp, *bufend;
|
||||
uint8_t nxt; /* Next Header */
|
||||
|
||||
ogs_assert(ip6_h);
|
||||
ogs_assert(proto);
|
||||
ogs_assert(hlen);
|
||||
ogs_assert(plen);
|
||||
|
||||
if (buflen < sizeof(*ip6_h)) {
|
||||
ogs_error("Invalid IPv6 packet: too short [len:%zu]", buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
start = (uint8_t *)ip6_h;
|
||||
bufend = start + buflen;
|
||||
nxt = ip6_h->ip6_nxt;
|
||||
p = (uint8_t *)ip6_h + sizeof(*ip6_h);
|
||||
endp = p + be16toh(ip6_h->ip6_plen);
|
||||
p = start + sizeof(*ip6_h);
|
||||
|
||||
jp = p + sizeof(struct ip6_hbh);
|
||||
while (p == endp) { /* Jumbo Frame */
|
||||
uint32_t jp_len = 0;
|
||||
struct ip6_opt_jumbo *jumbo = NULL;
|
||||
if (be16toh(ip6_h->ip6_plen) == 0) {
|
||||
uint8_t *jp, *optend;
|
||||
size_t ext_len;
|
||||
int jumbo_found = 0;
|
||||
struct ip6_ext *ext = NULL;
|
||||
|
||||
if (nxt != 0) {
|
||||
ogs_error("Invalid IPv6 jumbo: plen=0 but NextHeader=%u", nxt);
|
||||
return OGS_ERROR; /* Drop packet safely */
|
||||
if (nxt != IPPROTO_HOPOPTS) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: missing hop-by-hop header "
|
||||
"[nxt:%u len:%zu]", nxt, buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
jumbo = (struct ip6_opt_jumbo *)jp;
|
||||
memcpy(&jp_len, jumbo->ip6oj_jumbo_len, sizeof(jp_len));
|
||||
jp_len = be32toh(jp_len);
|
||||
switch (jumbo->ip6oj_type) {
|
||||
case IP6OPT_JUMBO:
|
||||
endp = p + jp_len;
|
||||
break;
|
||||
case 0:
|
||||
jp++;
|
||||
break;
|
||||
default:
|
||||
jp += (sizeof(struct ip6_opt) + jp_len);
|
||||
break;
|
||||
if ((size_t)(bufend - p) < sizeof(struct ip6_ext)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: truncated hop-by-hop "
|
||||
"header [remaining:%zu len:%zu]",
|
||||
(size_t)(bufend - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
ext = (struct ip6_ext *)p;
|
||||
ext_len = ((size_t)ext->ip6e_len << 3) + 8;
|
||||
if (ext_len > (size_t)(bufend - p)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: hop-by-hop header exceeds "
|
||||
"packet [ext_len:%zu remaining:%zu len:%zu]",
|
||||
ext_len, (size_t)(bufend - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
jp = p + sizeof(struct ip6_hbh);
|
||||
optend = p + ext_len;
|
||||
|
||||
while (jp < optend) {
|
||||
uint8_t opt_type = *jp;
|
||||
struct ip6_opt *opt = NULL;
|
||||
size_t opt_len;
|
||||
|
||||
if (opt_type == 0) { /* Pad1 */
|
||||
jp++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((size_t)(optend - jp) < sizeof(struct ip6_opt)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: truncated option "
|
||||
"[remaining:%zu len:%zu]",
|
||||
(size_t)(optend - jp), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
opt = (struct ip6_opt *)jp;
|
||||
opt_len = sizeof(struct ip6_opt) + opt->ip6o_len;
|
||||
if (opt_len > (size_t)(optend - jp)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: option exceeds "
|
||||
"hop-by-hop header [type:%u opt_len:%zu "
|
||||
"remaining:%zu len:%zu]",
|
||||
opt->ip6o_type, opt_len, (size_t)(optend - jp),
|
||||
buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (opt->ip6o_type == IP6OPT_JUMBO) {
|
||||
uint32_t jp_len = 0;
|
||||
struct ip6_opt_jumbo *jumbo = NULL;
|
||||
|
||||
if (opt->ip6o_len != sizeof(jp_len)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: invalid jumbo "
|
||||
"option length [opt_len:%u len:%zu]",
|
||||
opt->ip6o_len, buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
jumbo = (struct ip6_opt_jumbo *)jp;
|
||||
memcpy(&jp_len, jumbo->ip6oj_jumbo_len, sizeof(jp_len));
|
||||
jp_len = be32toh(jp_len);
|
||||
|
||||
if (jp_len <= 0xffff) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: jumbo payload "
|
||||
"length is too small [jumbo_len:%u len:%zu]",
|
||||
jp_len, buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (jp_len > (size_t)(bufend - p)) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: jumbo payload "
|
||||
"exceeds packet [jumbo_len:%u remaining:%zu "
|
||||
"len:%zu]", jp_len, (size_t)(bufend - p),
|
||||
buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
endp = p + jp_len;
|
||||
jumbo_found = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
jp += opt_len;
|
||||
}
|
||||
|
||||
if (!jumbo_found) {
|
||||
ogs_error("Invalid IPv6 jumbo packet: jumbo option not found "
|
||||
"[len:%zu]", buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
} else {
|
||||
size_t payload_len = be16toh(ip6_h->ip6_plen);
|
||||
|
||||
if (payload_len > (size_t)(bufend - p)) {
|
||||
ogs_error("Invalid IPv6 packet: payload length exceeds packet "
|
||||
"[payload_len:%zu remaining:%zu len:%zu]",
|
||||
payload_len, (size_t)(bufend - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
endp = p + payload_len;
|
||||
}
|
||||
|
||||
while (p < endp) {
|
||||
struct ip6_ext *ext = (struct ip6_ext *)p;
|
||||
struct ip6_ext *ext = NULL;
|
||||
size_t ext_len;
|
||||
|
||||
switch (nxt) {
|
||||
case IPPROTO_HOPOPTS:
|
||||
case IPPROTO_ROUTING:
|
||||
|
|
@ -87,27 +184,68 @@ static int decode_ipv6_header(
|
|||
case 140: /* shim6 */
|
||||
case 253: /* testing, experimental */
|
||||
case 254: /* testing, experimental */
|
||||
p += ((ext->ip6e_len << 3) + 8);
|
||||
if ((size_t)(endp - p) < sizeof(struct ip6_ext)) {
|
||||
ogs_error("Invalid IPv6 packet: truncated extension header "
|
||||
"[nxt:%u remaining:%zu len:%zu]",
|
||||
nxt, (size_t)(endp - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
ext = (struct ip6_ext *)p;
|
||||
ext_len = ((size_t)ext->ip6e_len << 3) + 8;
|
||||
if (ext_len > (size_t)(endp - p)) {
|
||||
ogs_error("Invalid IPv6 packet: extension header exceeds "
|
||||
"payload [nxt:%u ext_len:%zu remaining:%zu "
|
||||
"len:%zu]", nxt, ext_len, (size_t)(endp - p),
|
||||
buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
nxt = ext->ip6e_nxt;
|
||||
p += ext_len;
|
||||
break;
|
||||
case IPPROTO_FRAGMENT:
|
||||
if ((size_t)(endp - p) < sizeof(struct ip6_frag)) {
|
||||
ogs_error("Invalid IPv6 packet: truncated fragment header "
|
||||
"[remaining:%zu len:%zu]",
|
||||
(size_t)(endp - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
ext = (struct ip6_ext *)p;
|
||||
nxt = ext->ip6e_nxt;
|
||||
p += sizeof(struct ip6_frag);
|
||||
break;
|
||||
case IPPROTO_AH:
|
||||
p += ((ext->ip6e_len + 2) << 2);
|
||||
if ((size_t)(endp - p) < sizeof(struct ip6_ext)) {
|
||||
ogs_error("Invalid IPv6 packet: truncated AH header "
|
||||
"[remaining:%zu len:%zu]",
|
||||
(size_t)(endp - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
ext = (struct ip6_ext *)p;
|
||||
ext_len = ((size_t)ext->ip6e_len + 2) << 2;
|
||||
if (ext_len > (size_t)(endp - p)) {
|
||||
ogs_error("Invalid IPv6 packet: AH header exceeds payload "
|
||||
"[ext_len:%zu remaining:%zu len:%zu]",
|
||||
ext_len, (size_t)(endp - p), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
nxt = ext->ip6e_nxt;
|
||||
p += ext_len;
|
||||
break;
|
||||
default: /* Upper Layer */
|
||||
done = 1;
|
||||
break;
|
||||
|
||||
goto done;
|
||||
}
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
|
||||
nxt = ext->ip6e_nxt;
|
||||
done:
|
||||
if ((size_t)(p - start) > 0xffff) {
|
||||
ogs_error("Invalid IPv6 packet: header length too large "
|
||||
"[hlen:%zu len:%zu]", (size_t)(p - start), buflen);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
*proto = nxt;
|
||||
*hlen = p - (uint8_t *)ip6_h;
|
||||
*hlen = p - start;
|
||||
*plen = endp - start;
|
||||
|
||||
return OGS_OK;
|
||||
}
|
||||
|
|
@ -117,11 +255,13 @@ ogs_pfcp_rule_t *ogs_pfcp_pdr_rule_find_by_packet(
|
|||
{
|
||||
struct ip *ip_h = NULL;
|
||||
struct ip6_hdr *ip6_h = NULL;
|
||||
uint32_t *src_addr = NULL;
|
||||
uint32_t *dst_addr = NULL;
|
||||
uint32_t src_addr[4] = {0, };
|
||||
uint32_t dst_addr[4] = {0, };
|
||||
int addr_len = 0;
|
||||
uint8_t proto = 0;
|
||||
uint16_t ip_hlen = 0;
|
||||
size_t pkt_len = 0;
|
||||
uint8_t ip_v;
|
||||
|
||||
ogs_pfcp_rule_t *rule = NULL;
|
||||
|
||||
|
|
@ -129,6 +269,77 @@ ogs_pfcp_rule_t *ogs_pfcp_pdr_rule_find_by_packet(
|
|||
ogs_assert(pkbuf->len);
|
||||
ogs_assert(pkbuf->data);
|
||||
|
||||
pkt_len = pkbuf->len;
|
||||
ip_v = (*(uint8_t *)pkbuf->data) >> 4;
|
||||
|
||||
if (ip_v == 4) {
|
||||
size_t ip_len;
|
||||
|
||||
if (pkt_len < sizeof(struct ip)) {
|
||||
ogs_error("Invalid IPv4 packet: too short [len:%zu]", pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ip_h = (struct ip *)pkbuf->data;
|
||||
|
||||
ip_hlen = ip_h->ip_hl * 4;
|
||||
ip_len = be16toh(ip_h->ip_len);
|
||||
|
||||
if (ip_hlen < sizeof(struct ip) || ip_hlen > ip_len) {
|
||||
ogs_error("Invalid IPv4 packet: invalid header length "
|
||||
"[hlen:%u ip_len:%zu len:%zu]",
|
||||
ip_hlen, ip_len, pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
if (ip_len > pkt_len) {
|
||||
ogs_error("Invalid IPv4 packet: total length exceeds packet "
|
||||
"[ip_len:%zu len:%zu]", ip_len, pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pkt_len = ip_len;
|
||||
proto = ip_h->ip_p;
|
||||
|
||||
src_addr[0] = ip_h->ip_src.s_addr;
|
||||
dst_addr[0] = ip_h->ip_dst.s_addr;
|
||||
addr_len = OGS_IPV4_LEN;
|
||||
} else if (ip_v == 6) {
|
||||
if (pkt_len < sizeof(struct ip6_hdr)) {
|
||||
ogs_error("Invalid IPv6 packet: too short [len:%zu]", pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ip6_h = (struct ip6_hdr *)pkbuf->data;
|
||||
|
||||
if (OGS_OK != decode_ipv6_header(ip6_h, pkt_len,
|
||||
&proto, &ip_hlen, &pkt_len))
|
||||
return NULL;
|
||||
|
||||
memcpy(src_addr, ip6_h->ip6_src.s6_addr, OGS_IPV6_LEN);
|
||||
memcpy(dst_addr, ip6_h->ip6_dst.s6_addr, OGS_IPV6_LEN);
|
||||
addr_len = OGS_IPV6_LEN;
|
||||
} else {
|
||||
ogs_error("Invalid IP packet: unsupported version "
|
||||
"[version:%u len:%zu]", ip_v, pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (proto == IPPROTO_TCP) {
|
||||
if ((size_t)ip_hlen > pkt_len ||
|
||||
pkt_len - ip_hlen < sizeof(struct tcphdr)) {
|
||||
ogs_error("Invalid TCP packet: truncated header "
|
||||
"[hlen:%u len:%zu]", ip_hlen, pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
} else if (proto == IPPROTO_UDP) {
|
||||
if ((size_t)ip_hlen > pkt_len ||
|
||||
pkt_len - ip_hlen < sizeof(struct udphdr)) {
|
||||
ogs_error("Invalid UDP packet: truncated header "
|
||||
"[hlen:%u len:%zu]", ip_hlen, pkt_len);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ogs_list_for_each(&pdr->rule_list, rule) {
|
||||
int k;
|
||||
uint32_t src_mask[4];
|
||||
|
|
@ -138,39 +349,6 @@ ogs_pfcp_rule_t *ogs_pfcp_pdr_rule_find_by_packet(
|
|||
ipfw = &rule->ipfw;
|
||||
ogs_assert(ipfw);
|
||||
|
||||
ip_h = (struct ip *)pkbuf->data;
|
||||
if (ip_h->ip_v == 4) {
|
||||
ip_h = (struct ip *)pkbuf->data;
|
||||
ip6_h = NULL;
|
||||
|
||||
proto = ip_h->ip_p;
|
||||
ip_hlen = (ip_h->ip_hl)*4;
|
||||
|
||||
src_addr = (void *)&ip_h->ip_src.s_addr;
|
||||
dst_addr = (void *)&ip_h->ip_dst.s_addr;
|
||||
addr_len = OGS_IPV4_LEN;
|
||||
} else if (ip_h->ip_v == 6) {
|
||||
ip_h = NULL;
|
||||
ip6_h = (struct ip6_hdr *)pkbuf->data;
|
||||
|
||||
if (OGS_OK != decode_ipv6_header(ip6_h, &proto, &ip_hlen)) {
|
||||
/* Drop malformed IPv6 packet gracefully */
|
||||
ogs_error("Malformed IPv6 packet while matching PDR [plen:%d]",
|
||||
pkbuf->len);
|
||||
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
|
||||
continue;
|
||||
}
|
||||
|
||||
src_addr = (void *)ip6_h->ip6_src.s6_addr;
|
||||
dst_addr = (void *)ip6_h->ip6_dst.s6_addr;
|
||||
addr_len = OGS_IPV6_LEN;
|
||||
} else {
|
||||
ogs_error("Invalid packet [IP version:%d, Packet Length:%d]",
|
||||
ip_h->ip_v, pkbuf->len);
|
||||
ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len);
|
||||
continue;
|
||||
}
|
||||
|
||||
ogs_trace("PROTO:%d SRC:%08x %08x %08x %08x",
|
||||
proto, be32toh(src_addr[0]), be32toh(src_addr[1]),
|
||||
be32toh(src_addr[2]), be32toh(src_addr[3]));
|
||||
|
|
|
|||
|
|
@ -365,6 +365,129 @@ cleanup:
|
|||
return ueid;
|
||||
}
|
||||
|
||||
bool ogs_id_get_type_value(const char *str, char **type, char **value)
|
||||
{
|
||||
ogs_assert(str);
|
||||
ogs_assert(type);
|
||||
ogs_assert(value);
|
||||
|
||||
*type = ogs_id_get_type(str);
|
||||
*value = ogs_id_get_value(str);
|
||||
|
||||
if (!*type || !*value || !strlen(*type) || !strlen(*value))
|
||||
goto cleanup;
|
||||
|
||||
/* Reject extra '-' components that ogs_id_get_value() ignores. */
|
||||
if (strlen(str) != strlen(*type) + 1 + strlen(*value))
|
||||
goto cleanup;
|
||||
|
||||
return true;
|
||||
|
||||
cleanup:
|
||||
if (*type) {
|
||||
ogs_free(*type);
|
||||
*type = NULL;
|
||||
}
|
||||
if (*value) {
|
||||
ogs_free(*value);
|
||||
*value = NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ogs_bcd_string_is_valid(const char *bcd, int max_len)
|
||||
{
|
||||
int i, len;
|
||||
|
||||
ogs_assert(bcd);
|
||||
ogs_assert(max_len > 0);
|
||||
|
||||
len = strlen(bcd);
|
||||
if (len == 0 || len > max_len) {
|
||||
ogs_error("Invalid BCD length [%d:%s]", len, bcd);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (bcd[i] < '0' || bcd[i] > '9') {
|
||||
ogs_error("Invalid BCD digit [%d:%c:%s]", i, bcd[i], bcd);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ogs_pdu_session_id_is_valid(int psi)
|
||||
{
|
||||
return psi > OGS_NAS_PDU_SESSION_IDENTITY_UNASSIGNED &&
|
||||
psi <= OGS_NAS_PDU_SESSION_IDENTITY_MAX;
|
||||
}
|
||||
|
||||
/*
|
||||
* ogs_bcd_to_buffer() only converts bytes. It does not validate that
|
||||
* the input is a bounded decimal IMSI string, so check it first.
|
||||
*/
|
||||
bool ogs_imsi_bcd_is_valid(const char *imsi_bcd)
|
||||
{
|
||||
return ogs_bcd_string_is_valid(imsi_bcd, OGS_MAX_IMSI_BCD_LEN);
|
||||
}
|
||||
|
||||
bool ogs_imeisv_bcd_is_valid(const char *imeisv_bcd)
|
||||
{
|
||||
return ogs_bcd_string_is_valid(imeisv_bcd, OGS_MAX_IMEISV_BCD_LEN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the decimal IMSI string only for IMSI-type SUPI. Other SUPI
|
||||
* types cannot be used as EPC IMSI keys and remain SUPI-only.
|
||||
*
|
||||
* Return OGS_ERROR only when the SUPI claims to be IMSI-type but the
|
||||
* IMSI payload is malformed. That must not fall back to SUPI-only.
|
||||
*/
|
||||
int ogs_supi_to_imsi_bcd(
|
||||
const char *supi, char *imsi_bcd, bool *imsi_supi)
|
||||
{
|
||||
int rv = OGS_ERROR;
|
||||
char *type = NULL;
|
||||
char *value = NULL;
|
||||
|
||||
ogs_assert(supi);
|
||||
ogs_assert(imsi_bcd);
|
||||
ogs_assert(imsi_supi);
|
||||
|
||||
*imsi_supi = false;
|
||||
|
||||
if (ogs_id_get_type_value(supi, &type, &value) == false) {
|
||||
ogs_error("Invalid SUPI [%s]", supi);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Non-IMSI SUPI is valid, but has no EPC IMSI alias. */
|
||||
if (strcmp(type, OGS_ID_SUPI_TYPE_IMSI) != 0) {
|
||||
rv = OGS_OK;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (ogs_imsi_bcd_is_valid(value) == false) {
|
||||
ogs_error("Invalid IMSI SUPI [%s]", supi);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ogs_cpystrn(imsi_bcd, value, OGS_MAX_IMSI_BCD_LEN+1);
|
||||
*imsi_supi = true;
|
||||
rv = OGS_OK;
|
||||
|
||||
cleanup:
|
||||
if (type)
|
||||
ogs_free(type);
|
||||
if (value)
|
||||
ogs_free(value);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
char *ogs_s_nssai_sd_to_string(const ogs_uint24_t sd)
|
||||
{
|
||||
char *string = NULL;
|
||||
|
|
@ -918,6 +1041,11 @@ static int flow_rx_to_gx(ogs_flow_t *rx_flow, ogs_flow_t *gx_flow)
|
|||
ogs_assert(rx_flow);
|
||||
ogs_assert(gx_flow);
|
||||
|
||||
if (!rx_flow->description) {
|
||||
ogs_error("No Flow Description");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (!strncmp(rx_flow->description,
|
||||
"permit out", strlen("permit out"))) {
|
||||
gx_flow->direction = OGS_FLOW_DOWNLINK_ONLY;
|
||||
|
|
@ -934,10 +1062,35 @@ static int flow_rx_to_gx(ogs_flow_t *rx_flow, ogs_flow_t *gx_flow)
|
|||
gx_flow->description = ogs_calloc(1, len);
|
||||
ogs_assert(gx_flow->description);
|
||||
strcpy(gx_flow->description, "permit out");
|
||||
from_str = strstr(&rx_flow->description[strlen("permit in")], "from");
|
||||
ogs_assert(from_str);
|
||||
to_str = strstr(&rx_flow->description[strlen("permit in")], "to");
|
||||
ogs_assert(to_str);
|
||||
|
||||
/*
|
||||
* Match "from" and "to" as space-padded tokens, and ensure
|
||||
* "to" follows "from" before using pointer-length arithmetic.
|
||||
*/
|
||||
from_str = strstr(&rx_flow->description[strlen("permit in")],
|
||||
" from ");
|
||||
if (!from_str) {
|
||||
ogs_error("Invalid Flow Description : [%s] (missing 'from')",
|
||||
rx_flow->description);
|
||||
ogs_free(gx_flow->description);
|
||||
gx_flow->description = NULL;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
to_str = strstr(&rx_flow->description[strlen("permit in")],
|
||||
" to ");
|
||||
if (!to_str || to_str <= from_str) {
|
||||
ogs_error("Invalid Flow Description : [%s] "
|
||||
"(missing 'to' or 'to' precedes 'from')",
|
||||
rx_flow->description);
|
||||
ogs_free(gx_flow->description);
|
||||
gx_flow->description = NULL;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
from_str++;
|
||||
to_str++;
|
||||
|
||||
strncat(gx_flow->description,
|
||||
&rx_flow->description[strlen("permit in")],
|
||||
strlen(rx_flow->description) -
|
||||
|
|
|
|||
|
|
@ -149,6 +149,9 @@ extern "C" {
|
|||
#define OGS_NAS_PROCEDURE_TRANSACTION_IDENTITY_UNASSIGNED 0
|
||||
/* 3GPP TS 24.007 Table 11.2.3.1c.1: */
|
||||
#define OGS_NAS_PDU_SESSION_IDENTITY_UNASSIGNED 0
|
||||
#define OGS_NAS_PDU_SESSION_IDENTITY_MAX 255
|
||||
|
||||
bool ogs_pdu_session_id_is_valid(int psi);
|
||||
|
||||
#define OGS_ACCESS_TYPE_3GPP 1
|
||||
#define OGS_ACCESS_TYPE_NON_3GPP 2
|
||||
|
|
@ -308,6 +311,12 @@ ogs_amf_id_t *ogs_amf_id_build(ogs_amf_id_t *amf_id,
|
|||
#define OGS_ID_5G_GUTI_TYPE "5g-guti"
|
||||
char *ogs_id_get_type(const char *str);
|
||||
char *ogs_id_get_value(const char *str);
|
||||
bool ogs_id_get_type_value(const char *str, char **type, char **value);
|
||||
bool ogs_bcd_string_is_valid(const char *bcd, int max_len);
|
||||
bool ogs_imsi_bcd_is_valid(const char *imsi_bcd);
|
||||
bool ogs_imeisv_bcd_is_valid(const char *imeisv_bcd);
|
||||
int ogs_supi_to_imsi_bcd(
|
||||
const char *supi, char *imsi_bcd, bool *imsi_supi);
|
||||
|
||||
/************************************
|
||||
* TAI Structure */
|
||||
|
|
|
|||
|
|
@ -834,6 +834,11 @@ bool ogs_sbi_client_send_via_scp_or_sepp(
|
|||
ogs_assert(apiroot);
|
||||
|
||||
rc = ogs_sbi_getpath_from_uri(&path, request->h.uri);
|
||||
if (rc == false) {
|
||||
ogs_error("Cannot extract path from URI [%s]", request->h.uri);
|
||||
ogs_free(apiroot);
|
||||
return false;
|
||||
}
|
||||
ogs_assert(path);
|
||||
|
||||
request->h.uri = ogs_msprintf("%s/%s", apiroot, path);
|
||||
|
|
|
|||
|
|
@ -1240,7 +1240,11 @@ ogs_sbi_nf_instance_t *ogs_sbi_nf_instance_add(void)
|
|||
ogs_sbi_nf_instance_t *nf_instance = NULL;
|
||||
|
||||
ogs_pool_alloc(&nf_instance_pool, &nf_instance);
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("OVERFLOW nf_instance_pool [pool:%llu]",
|
||||
(unsigned long long)ogs_app()->pool.nf);
|
||||
return NULL;
|
||||
}
|
||||
memset(nf_instance, 0, sizeof(ogs_sbi_nf_instance_t));
|
||||
|
||||
nf_instance->time.heartbeat_interval =
|
||||
|
|
@ -1346,6 +1350,9 @@ void ogs_sbi_nf_instance_clear(ogs_sbi_nf_instance_t *nf_instance)
|
|||
nf_instance->num_of_ipv6 = 0;
|
||||
|
||||
nf_instance->num_of_allowed_nf_type = 0;
|
||||
|
||||
nf_instance->num_of_s_nssai = 0;
|
||||
nf_instance->num_of_allowed_nssai = 0;
|
||||
}
|
||||
|
||||
void ogs_sbi_nf_instance_remove(ogs_sbi_nf_instance_t *nf_instance)
|
||||
|
|
@ -1462,10 +1469,6 @@ ogs_sbi_nf_instance_t *ogs_sbi_nf_instance_find_by_service(
|
|||
return nf_instance;
|
||||
}
|
||||
|
||||
bool ogs_sbi_nf_instance_maximum_number_is_reached(void)
|
||||
{
|
||||
return nf_instance_pool.avail <= 0;
|
||||
}
|
||||
|
||||
ogs_sbi_nf_service_t *ogs_sbi_nf_service_add(
|
||||
ogs_sbi_nf_instance_t *nf_instance,
|
||||
|
|
@ -1480,12 +1483,26 @@ ogs_sbi_nf_service_t *ogs_sbi_nf_service_add(
|
|||
ogs_assert(name);
|
||||
|
||||
ogs_pool_alloc(&nf_service_pool, &nf_service);
|
||||
ogs_assert(nf_service);
|
||||
if (!nf_service) {
|
||||
ogs_error("OVERFLOW nf_service_pool [pool:%llu]",
|
||||
(unsigned long long)ogs_app()->pool.nf_service);
|
||||
return NULL;
|
||||
}
|
||||
memset(nf_service, 0, sizeof(ogs_sbi_nf_service_t));
|
||||
|
||||
nf_service->id = ogs_strdup(id);
|
||||
ogs_assert(nf_service->id);
|
||||
if (!nf_service->id) {
|
||||
ogs_error("ogs_strdup() failed for nf_service->id");
|
||||
ogs_pool_free(&nf_service_pool, nf_service);
|
||||
return NULL;
|
||||
}
|
||||
nf_service->name = name;
|
||||
if (!nf_service->name) {
|
||||
ogs_error("ogs_strdup() failed for nf_service->name");
|
||||
ogs_free(nf_service->id);
|
||||
ogs_pool_free(&nf_service_pool, nf_service);
|
||||
return NULL;
|
||||
}
|
||||
nf_service->scheme = scheme;
|
||||
ogs_assert(nf_service->scheme);
|
||||
|
||||
|
|
@ -1673,7 +1690,9 @@ ogs_sbi_nf_info_t *ogs_sbi_nf_info_add(
|
|||
|
||||
ogs_pool_alloc(&nf_info_pool, &nf_info);
|
||||
if (!nf_info) {
|
||||
ogs_fatal("ogs_pool_alloc() failed");
|
||||
ogs_error("OVERFLOW nf_info_pool [pool:%llu]",
|
||||
(unsigned long long)(ogs_app()->pool.nf *
|
||||
OGS_MAX_NUM_OF_NF_INFO));
|
||||
return NULL;
|
||||
}
|
||||
memset(nf_info, 0, sizeof(*nf_info));
|
||||
|
|
@ -1934,7 +1953,15 @@ ogs_sbi_nf_service_t *ogs_sbi_nf_service_build_default(
|
|||
ogs_assert(scheme);
|
||||
|
||||
nf_service = ogs_sbi_nf_service_add(nf_instance, id, name, scheme);
|
||||
ogs_assert(nf_service);
|
||||
if (!nf_service) {
|
||||
ogs_error("Cannot build default NF service [%s]: "
|
||||
"nf_service_pool exhausted at startup. "
|
||||
"Increase 'max.peer' (current pool capacity = "
|
||||
"max.peer * 16 = %llu).",
|
||||
OpenAPI_service_name_ToString(name),
|
||||
(unsigned long long)ogs_app()->pool.nf_service);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hostname = NULL;
|
||||
for (server = ogs_sbi_server_first();
|
||||
|
|
@ -2166,6 +2193,34 @@ bool ogs_sbi_discovery_option_is_matched(
|
|||
nf_instance, discovery_option) == false)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* TS 33.518 4.2.2.2.1 - Target allowed_nssai filtering
|
||||
*
|
||||
* If the target NF registered with allowedNssais, it may only be
|
||||
* discovered for queries whose S-NSSAI falls within that set.
|
||||
*/
|
||||
if (nf_instance->num_of_allowed_nssai &&
|
||||
discovery_option->num_of_snssais) {
|
||||
bool nssai_allowed = false;
|
||||
|
||||
for (i = 0; i < discovery_option->num_of_snssais; i++) {
|
||||
int j;
|
||||
for (j = 0; j < nf_instance->num_of_allowed_nssai; j++) {
|
||||
if (discovery_option->snssais[i].sst ==
|
||||
nf_instance->allowed_nssai[j].sst &&
|
||||
discovery_option->snssais[i].sd.v ==
|
||||
nf_instance->allowed_nssai[j].sd.v) {
|
||||
nssai_allowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (nssai_allowed) break;
|
||||
}
|
||||
|
||||
if (!nssai_allowed)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Determine which SMF filters are requested */
|
||||
if (nf_instance->nf_type == OpenAPI_nf_type_SMF) {
|
||||
need_smf_slice = (discovery_option->num_of_snssais &&
|
||||
|
|
@ -2679,6 +2734,30 @@ void ogs_sbi_xact_remove(ogs_sbi_xact_t *xact)
|
|||
if (xact->target_apiroot)
|
||||
ogs_free(xact->target_apiroot);
|
||||
|
||||
/*
|
||||
* Detach from the originating stream's xact_list, if attached.
|
||||
*
|
||||
* Two paths reach here:
|
||||
*
|
||||
* (a) Normal completion: a response arrived (or the send
|
||||
* failed early) and the NF calls ogs_sbi_xact_remove().
|
||||
* xact_detach() unlinks via the cached to_stream_list
|
||||
* head in O(1), no stream lookup needed.
|
||||
*
|
||||
* (b) Stream close: stream_remove_xact_all() (or its MHD
|
||||
* counterpart) walks the stream's xact_list and calls
|
||||
* ogs_sbi_xact_remove() on each entry. This detach runs
|
||||
* first and unlinks the node before the for_each_safe
|
||||
* iterator advances; the iterator's cached "next" pointer
|
||||
* keeps the loop sound.
|
||||
*
|
||||
* Idempotent for transactions that never had an inbound stream
|
||||
* (NRF discovery initiated by the NF itself, status
|
||||
* notifications): to_stream_list stays NULL and the helper is
|
||||
* a no-op.
|
||||
*/
|
||||
ogs_sbi_server_detach_xact(xact);
|
||||
|
||||
/*
|
||||
* Release optional user context attached to the transaction.
|
||||
* The transaction owns this memory and is responsible for
|
||||
|
|
@ -2759,7 +2838,12 @@ ogs_sbi_subscription_data_t *ogs_sbi_subscription_data_add(void)
|
|||
ogs_sbi_subscription_data_t *subscription_data = NULL;
|
||||
|
||||
ogs_pool_alloc(&subscription_data_pool, &subscription_data);
|
||||
ogs_assert(subscription_data);
|
||||
if (!subscription_data) {
|
||||
ogs_error("OVERFLOW subscription_data_pool [pool:%llu]",
|
||||
(unsigned long long)ogs_app()->pool.subscription);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(subscription_data, 0, sizeof(ogs_sbi_subscription_data_t));
|
||||
|
||||
ogs_list_add(&ogs_sbi_self()->subscription_data_list, subscription_data);
|
||||
|
|
|
|||
|
|
@ -173,6 +173,22 @@ typedef struct ogs_sbi_nf_instance_s {
|
|||
#define OGS_SBI_MAX_NUM_OF_NF_TYPE 128
|
||||
OpenAPI_nf_type_e allowed_nf_type[OGS_SBI_MAX_NUM_OF_NF_TYPE];
|
||||
|
||||
/*
|
||||
* TS 33.518 4.2.2.2.1 - NF discovery authorization for specific slice
|
||||
*
|
||||
* s_nssai[]: S-NSSAIs this NF instance serves (from NFProfile.sNssais).
|
||||
* Used to authorize the requester during NF discovery.
|
||||
*
|
||||
* allowed_nssai[]: S-NSSAIs for which this NF may be discovered
|
||||
* (from NFProfile.allowedNssais).
|
||||
* Used to filter target NFs during NF discovery.
|
||||
*/
|
||||
int num_of_s_nssai;
|
||||
ogs_s_nssai_t s_nssai[OGS_MAX_NUM_OF_SLICE];
|
||||
|
||||
int num_of_allowed_nssai;
|
||||
ogs_s_nssai_t allowed_nssai[OGS_MAX_NUM_OF_SLICE];
|
||||
|
||||
#define OGS_SBI_DEFAULT_PRIORITY 0
|
||||
#define OGS_SBI_DEFAULT_CAPACITY 100
|
||||
#define OGS_SBI_DEFAULT_LOAD 0
|
||||
|
|
@ -270,6 +286,42 @@ typedef struct ogs_sbi_xact_s {
|
|||
|
||||
ogs_pool_id_t assoc_stream_id;
|
||||
|
||||
/*
|
||||
* Linkage into the server-side stream that triggered this
|
||||
* outbound transaction.
|
||||
*
|
||||
* When the inbound HTTP/2 stream is closed by the peer (e.g.
|
||||
* RST_STREAM or connection close) before the response from the
|
||||
* upstream NF arrives, every transaction registered on the
|
||||
* stream's xact_list is cancelled. This releases the response
|
||||
* timer back to the pool immediately instead of holding a slot
|
||||
* until the SBI client wait timeout expires, which is the
|
||||
* accumulation that leads to timer-pool exhaustion when a peer
|
||||
* resets streams rapidly while the upstream NF is slow or
|
||||
* unresponsive (issues #4472 and #4473).
|
||||
*
|
||||
* The linkage is managed entirely by lib/sbi:
|
||||
* - ogs_sbi_discover_and_send() attaches when assoc_stream_id
|
||||
* is set;
|
||||
* - ogs_sbi_xact_remove() detaches automatically;
|
||||
* - stream/session close drains the list and removes every
|
||||
* attached transaction.
|
||||
*
|
||||
* to_stream_list is both the attachment flag and the cached
|
||||
* list head:
|
||||
* to_stream_list != NULL
|
||||
* <=> to_stream_node is on *to_stream_list
|
||||
* <=> assoc_stream_id refers to the owning stream
|
||||
*
|
||||
* Caching the list head address lets detach unlink in O(1)
|
||||
* without re-resolving the stream from assoc_stream_id.
|
||||
* Transactions with no associated inbound stream (e.g. NRF
|
||||
* discovery initiated by the NF itself, status notifications)
|
||||
* leave the whole linkage group zero-initialised.
|
||||
*/
|
||||
ogs_lnode_t to_stream_node;
|
||||
ogs_list_t *to_stream_list;
|
||||
|
||||
/*
|
||||
* Optional user context attached to this SBI transaction.
|
||||
*
|
||||
|
|
@ -485,7 +537,6 @@ ogs_sbi_nf_instance_t *ogs_sbi_nf_instance_find_by_discovery_param(
|
|||
ogs_sbi_nf_instance_t *ogs_sbi_nf_instance_find_by_service(
|
||||
OpenAPI_service_name_e service_name,
|
||||
OpenAPI_nf_type_e requester_nf_type);
|
||||
bool ogs_sbi_nf_instance_maximum_number_is_reached(void);
|
||||
|
||||
ogs_sbi_nf_service_t *ogs_sbi_nf_service_add(
|
||||
ogs_sbi_nf_instance_t *nf_instance,
|
||||
|
|
|
|||
|
|
@ -150,9 +150,9 @@ char *ogs_supi_from_suci(char *suci)
|
|||
|
||||
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]);
|
||||
ogs_error("Scheme Not Matched [%d != %d]",
|
||||
ogs_sbi_self()->hnet[home_network_pki_value].scheme,
|
||||
protection_scheme_id);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -882,9 +882,17 @@ bool ogs_sbi_time_from_string(ogs_time_t *timestamp, char *str)
|
|||
(str[i-3] == '+' || str[i-3] == '-')) {
|
||||
/* remove ':' character in timezone string range */
|
||||
} else {
|
||||
if (j >= MAX_TIMESTR_LEN - 1) {
|
||||
ogs_error("Too long time string [%d]", (int)strlen(str));
|
||||
return false;
|
||||
}
|
||||
seconds[j++] = str[i];
|
||||
}
|
||||
} else {
|
||||
if (k >= MAX_TIMESTR_LEN - 1) {
|
||||
ogs_error("Too long time string [%d]", (int)strlen(str));
|
||||
return false;
|
||||
}
|
||||
subsecs[k++] = str[i];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1002,20 +1002,31 @@ int ogs_sbi_parse_request(
|
|||
*/
|
||||
char *v = ogs_hash_this_val(hi);
|
||||
if (v) {
|
||||
ogs_sbi_discovery_option_parse_service_names(
|
||||
discovery_option, v);
|
||||
if (ogs_sbi_discovery_option_parse_service_names(
|
||||
discovery_option, v) != OGS_OK) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
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);
|
||||
if (ogs_sbi_discovery_option_parse_snssais(
|
||||
discovery_option, v) != OGS_OK) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option_presence = true;
|
||||
}
|
||||
} else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_GUAMI)) {
|
||||
char *v = ogs_hash_this_val(hi);
|
||||
if (v) {
|
||||
ogs_sbi_discovery_option_parse_guami(discovery_option, v);
|
||||
if (ogs_sbi_discovery_option_parse_guami(
|
||||
discovery_option, v) != OGS_OK) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option_presence = true;
|
||||
}
|
||||
} else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_DNN)) {
|
||||
|
|
@ -1027,25 +1038,37 @@ int ogs_sbi_parse_request(
|
|||
} 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);
|
||||
if (ogs_sbi_discovery_option_parse_tai(
|
||||
discovery_option, v) != OGS_OK) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option_presence = true;
|
||||
}
|
||||
} else if (!strcmp(ogs_hash_this_key(hi),
|
||||
OGS_SBI_PARAM_TARGET_PLMN_LIST)) {
|
||||
char *v = ogs_hash_this_val(hi);
|
||||
if (v) {
|
||||
discovery_option->num_of_target_plmn_list =
|
||||
ogs_sbi_discovery_option_parse_plmn_list(
|
||||
int n = ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->target_plmn_list, v);
|
||||
if (n < 0) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option->num_of_target_plmn_list = n;
|
||||
discovery_option_presence = true;
|
||||
}
|
||||
} else if (!strcmp(ogs_hash_this_key(hi),
|
||||
OGS_SBI_PARAM_REQUESTER_PLMN_LIST)) {
|
||||
char *v = ogs_hash_this_val(hi);
|
||||
if (v) {
|
||||
discovery_option->num_of_requester_plmn_list =
|
||||
ogs_sbi_discovery_option_parse_plmn_list(
|
||||
int n = ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->requester_plmn_list, v);
|
||||
if (n < 0) {
|
||||
ogs_sbi_discovery_option_free(discovery_option);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option->num_of_requester_plmn_list = n;
|
||||
discovery_option_presence = true;
|
||||
}
|
||||
} else if (!strcmp(ogs_hash_this_key(hi), OGS_SBI_PARAM_HNRF_URI)) {
|
||||
|
|
@ -3172,12 +3195,23 @@ static int on_header_field(
|
|||
data = multipart_parser_get_data(parser);
|
||||
ogs_assert(data);
|
||||
|
||||
if (at && length) {
|
||||
if (data->header_field)
|
||||
ogs_free(data->header_field);
|
||||
data->header_field = ogs_strndup(at, length);
|
||||
ogs_assert(data->header_field);
|
||||
/*
|
||||
* The multipart state machine emits a zero-length field name when a
|
||||
* header line begins with ':' (e.g. ":application/json"). Such input is
|
||||
* malformed; rejecting it here prevents a NULL data->header_field from
|
||||
* propagating into on_header_value(). See Issues #4608
|
||||
*/
|
||||
if (!at || !length) {
|
||||
ogs_error("Invalid multipart header field");
|
||||
data->parse_error = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->header_field)
|
||||
ogs_free(data->header_field);
|
||||
data->header_field = ogs_strndup(at, length);
|
||||
ogs_assert(data->header_field);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -3190,6 +3224,18 @@ static int on_header_value(
|
|||
data = multipart_parser_get_data(parser);
|
||||
ogs_assert(data);
|
||||
|
||||
/*
|
||||
* Defense in depth: a header value must be preceded by a valid field
|
||||
* name. on_header_field() already rejects empty names, but guard here as
|
||||
* well so ogs_strcasecmp() (plain strcasecmp, not NULL-safe) is never
|
||||
* called with a NULL data->header_field.
|
||||
*/
|
||||
if (!data->header_field) {
|
||||
ogs_error("Multipart header value without field name");
|
||||
data->parse_error = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->num_of_part < OGS_SBI_MAX_NUM_OF_PART && at && length) {
|
||||
if (!ogs_strcasecmp(data->header_field, OGS_SBI_CONTENT_TYPE)) {
|
||||
ogs_assert(data->part[data->num_of_part].content_type == NULL);
|
||||
|
|
@ -3471,7 +3517,8 @@ static bool build_multipart(
|
|||
|
||||
ogs_random(digest, 16);
|
||||
strcpy(boundary, "=-");
|
||||
ogs_base64_encode_binary(boundary + 2, digest, 16);
|
||||
ogs_assert(ogs_base64_encode_from_buffer(
|
||||
boundary + 2, sizeof(boundary) - 2, digest, sizeof(digest)) > 0);
|
||||
|
||||
p = http->content = ogs_calloc(1, OGS_MAX_SDU_LEN);
|
||||
if (!p) {
|
||||
|
|
@ -3685,7 +3732,7 @@ char *ogs_sbi_discovery_option_build_service_names(
|
|||
return service_names;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_parse_service_names(
|
||||
int ogs_sbi_discovery_option_parse_service_names(
|
||||
ogs_sbi_discovery_option_t *discovery_option,
|
||||
char *service_names)
|
||||
{
|
||||
|
|
@ -3700,7 +3747,7 @@ void ogs_sbi_discovery_option_parse_service_names(
|
|||
if (!v) {
|
||||
ogs_error("ogs_sbi_url_decode() failed : service_names[%s]",
|
||||
service_names);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -3721,7 +3768,18 @@ void ogs_sbi_discovery_option_parse_service_names(
|
|||
*/
|
||||
token = ogs_strtok_r(v, ",", &saveptr);
|
||||
while (token != NULL) {
|
||||
OpenAPI_service_name_e name = OpenAPI_service_name_FromString(token);
|
||||
OpenAPI_service_name_e name = OpenAPI_service_name_NULL;
|
||||
|
||||
if (discovery_option->num_of_service_names >=
|
||||
OGS_SBI_MAX_NUM_OF_SERVICE_NAME) {
|
||||
ogs_error("Too many service-names [%d:%d]",
|
||||
discovery_option->num_of_service_names + 1,
|
||||
OGS_SBI_MAX_NUM_OF_SERVICE_NAME);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
name = OpenAPI_service_name_FromString(token);
|
||||
if (name)
|
||||
ogs_sbi_discovery_option_add_service_names(discovery_option, name);
|
||||
else
|
||||
|
|
@ -3731,6 +3789,7 @@ void ogs_sbi_discovery_option_parse_service_names(
|
|||
}
|
||||
|
||||
ogs_free(v);
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_add_snssais(
|
||||
|
|
@ -3783,7 +3842,7 @@ char *ogs_sbi_discovery_option_build_snssais(
|
|||
|
||||
return v;
|
||||
}
|
||||
void ogs_sbi_discovery_option_parse_snssais(
|
||||
int ogs_sbi_discovery_option_parse_snssais(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *snssais)
|
||||
{
|
||||
cJSON *item = NULL;
|
||||
|
|
@ -3796,41 +3855,62 @@ void ogs_sbi_discovery_option_parse_snssais(
|
|||
v = ogs_sbi_url_decode(snssais);
|
||||
if (!v) {
|
||||
ogs_error("ogs_sbi_url_decode() failed : snssais[%s]", snssais);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
item = cJSON_Parse(v);
|
||||
if (!item) {
|
||||
ogs_error("Cannot parse snssais[%s]", snssais);
|
||||
ogs_free(v);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (!cJSON_IsArray(item)) {
|
||||
ogs_error("snssais is not a JSON array [%s]", snssais);
|
||||
cJSON_Delete(item);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
cJSON_ArrayForEach(snssaiItem, item) {
|
||||
if (cJSON_IsObject(snssaiItem)) {
|
||||
OpenAPI_snssai_t *sNSSAI = OpenAPI_snssai_parseFromJSON(snssaiItem);
|
||||
OpenAPI_snssai_t *sNSSAI = NULL;
|
||||
ogs_s_nssai_t s_nssai;
|
||||
|
||||
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);
|
||||
if (discovery_option->num_of_snssais >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("Too many snssais [%d:%d]",
|
||||
discovery_option->num_of_snssais + 1,
|
||||
OGS_MAX_NUM_OF_SLICE);
|
||||
cJSON_Delete(item);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (!cJSON_IsObject(snssaiItem)) {
|
||||
ogs_error("Invalid cJSON Type in snssais[%s]", snssais);
|
||||
cJSON_Delete(item);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
sNSSAI = OpenAPI_snssai_parseFromJSON(snssaiItem);
|
||||
if (!sNSSAI) {
|
||||
ogs_error("OpenAPI_snssai_parseFromJSON() failed : snssais[%s]",
|
||||
snssais);
|
||||
cJSON_Delete(item);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
cJSON_Delete(item);
|
||||
|
||||
ogs_free(v);
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_set_guami(
|
||||
|
|
@ -3868,10 +3948,11 @@ char *ogs_sbi_discovery_option_build_guami(
|
|||
return v;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_parse_guami(
|
||||
int ogs_sbi_discovery_option_parse_guami(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *guami)
|
||||
{
|
||||
OpenAPI_guami_t *Guami = NULL;
|
||||
ogs_guami_t parsed_guami;
|
||||
cJSON *guamItem = NULL;
|
||||
char *v = NULL;
|
||||
|
||||
|
|
@ -3881,30 +3962,38 @@ void ogs_sbi_discovery_option_parse_guami(
|
|||
v = ogs_sbi_url_decode(guami);
|
||||
if (!v) {
|
||||
ogs_error("ogs_sbi_url_decode() failed : guami[%s]", guami);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
guamItem = cJSON_Parse(v);
|
||||
if (!guamItem) {
|
||||
ogs_error("Cannot parse guami[%s]", guami);
|
||||
ogs_free(v);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (!cJSON_IsObject(guamItem)) {
|
||||
ogs_error("guami is not a JSON object [%s]", guami);
|
||||
cJSON_Delete(guamItem);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
Guami = OpenAPI_guami_parseFromJSON(guamItem);
|
||||
|
||||
if (Guami) {
|
||||
ogs_guami_t guami;
|
||||
ogs_sbi_parse_guami(&guami, Guami);
|
||||
ogs_sbi_discovery_option_set_guami(discovery_option, &guami);
|
||||
OpenAPI_guami_free(Guami);
|
||||
} else {
|
||||
ogs_error("OpenAPI_guami_parseFromJSON() failed : guami[%s]",
|
||||
guami);
|
||||
if (!Guami) {
|
||||
ogs_error("OpenAPI_guami_parseFromJSON() failed : guami[%s]", guami);
|
||||
cJSON_Delete(guamItem);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
ogs_sbi_parse_guami(&parsed_guami, Guami);
|
||||
ogs_sbi_discovery_option_set_guami(discovery_option, &parsed_guami);
|
||||
OpenAPI_guami_free(Guami);
|
||||
cJSON_Delete(guamItem);
|
||||
|
||||
ogs_free(v);
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_set_tai(
|
||||
|
|
@ -3947,9 +4036,11 @@ char *ogs_sbi_discovery_option_build_tai(
|
|||
|
||||
return v;
|
||||
}
|
||||
void ogs_sbi_discovery_option_parse_tai(
|
||||
int ogs_sbi_discovery_option_parse_tai(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *tai)
|
||||
{
|
||||
OpenAPI_tai_t *Tai = NULL;
|
||||
ogs_5gs_tai_t parsed_tai;
|
||||
cJSON *taiItem = NULL;
|
||||
char *v = NULL;
|
||||
|
||||
|
|
@ -3959,42 +4050,45 @@ void ogs_sbi_discovery_option_parse_tai(
|
|||
v = ogs_sbi_url_decode(tai);
|
||||
if (!v) {
|
||||
ogs_error("ogs_sbi_url_decode() failed : tai[%s]", tai);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
taiItem = cJSON_Parse(v);
|
||||
if (!taiItem) {
|
||||
ogs_error("Cannot parse tai[%s]", tai);
|
||||
ogs_free(v);
|
||||
return;
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
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_hexadecimal(Tai->tac);
|
||||
|
||||
ogs_sbi_discovery_option_set_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);
|
||||
if (!cJSON_IsObject(taiItem)) {
|
||||
ogs_error("tai is not a JSON object [%s]", tai);
|
||||
cJSON_Delete(taiItem);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
Tai = OpenAPI_tai_parseFromJSON(taiItem);
|
||||
if (!Tai) {
|
||||
ogs_error("OpenAPI_tai_parseFromJSON() failed : tai[%s]", tai);
|
||||
cJSON_Delete(taiItem);
|
||||
ogs_free(v);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
memset(&parsed_tai, 0, sizeof(parsed_tai));
|
||||
|
||||
if (Tai->plmn_id)
|
||||
ogs_sbi_parse_plmn_id(&parsed_tai.plmn_id, Tai->plmn_id);
|
||||
if (Tai->tac)
|
||||
parsed_tai.tac = ogs_uint24_from_string_hexadecimal(Tai->tac);
|
||||
|
||||
ogs_sbi_discovery_option_set_tai(discovery_option, &parsed_tai);
|
||||
|
||||
OpenAPI_tai_free(Tai);
|
||||
cJSON_Delete(taiItem);
|
||||
|
||||
ogs_free(v);
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
void ogs_sbi_discovery_option_add_target_plmn_list(
|
||||
|
|
@ -4074,35 +4168,51 @@ int ogs_sbi_discovery_option_parse_plmn_list(
|
|||
cJSON *PlmnIdJSON = NULL;
|
||||
OpenAPI_list_t *PlmnList = NULL;
|
||||
OpenAPI_plmn_id_t *PlmnId = NULL;
|
||||
int num_of_plmn_list = 0;
|
||||
int num_of_plmn_list = -1;
|
||||
|
||||
ogs_assert(v);
|
||||
ogs_assert(plmn_list);
|
||||
|
||||
item = cJSON_Parse(v);
|
||||
if (item) {
|
||||
PlmnList = OpenAPI_list_create();
|
||||
ogs_assert(PlmnList);
|
||||
cJSON_ArrayForEach(PlmnIdJSON, item) {
|
||||
if (!cJSON_IsObject(PlmnIdJSON)) {
|
||||
ogs_error("Unknown JSON");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!item) {
|
||||
ogs_error("Cannot parse PLMN list [%s]", v);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PlmnId = OpenAPI_plmn_id_parseFromJSON(PlmnIdJSON);
|
||||
if (!PlmnId) {
|
||||
ogs_error("No PlmnId");
|
||||
goto cleanup;
|
||||
}
|
||||
if (!cJSON_IsArray(item)) {
|
||||
ogs_error("PLMN list is not a JSON array [%s]", v);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
OpenAPI_list_add(PlmnList, PlmnId);
|
||||
if (cJSON_GetArraySize(item) > OGS_MAX_NUM_OF_PLMN) {
|
||||
ogs_error("Too many PLMN IDs in list [%d:%d]",
|
||||
cJSON_GetArraySize(item), OGS_MAX_NUM_OF_PLMN);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
PlmnList = OpenAPI_list_create();
|
||||
ogs_assert(PlmnList);
|
||||
|
||||
cJSON_ArrayForEach(PlmnIdJSON, item) {
|
||||
if (!cJSON_IsObject(PlmnIdJSON)) {
|
||||
ogs_error("Invalid cJSON Type in PLMN list [%s]", v);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
PlmnId = OpenAPI_plmn_id_parseFromJSON(PlmnIdJSON);
|
||||
if (!PlmnId) {
|
||||
ogs_error("OpenAPI_plmn_id_parseFromJSON() failed [%s]", v);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
OpenAPI_list_add(PlmnList, PlmnId);
|
||||
}
|
||||
|
||||
num_of_plmn_list = ogs_sbi_parse_plmn_list(plmn_list, PlmnList);
|
||||
|
||||
cleanup:
|
||||
ogs_sbi_free_plmn_list(PlmnList);
|
||||
if (PlmnList)
|
||||
ogs_sbi_free_plmn_list(PlmnList);
|
||||
cJSON_Delete(item);
|
||||
|
||||
return num_of_plmn_list;
|
||||
|
|
|
|||
|
|
@ -700,7 +700,7 @@ void ogs_sbi_discovery_option_add_service_names(
|
|||
OpenAPI_service_name_e service_name);
|
||||
char *ogs_sbi_discovery_option_build_service_names(
|
||||
ogs_sbi_discovery_option_t *discovery_option);
|
||||
void ogs_sbi_discovery_option_parse_service_names(
|
||||
int ogs_sbi_discovery_option_parse_service_names(
|
||||
ogs_sbi_discovery_option_t *discovery_option,
|
||||
char *service_names);
|
||||
|
||||
|
|
@ -708,21 +708,21 @@ 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(
|
||||
int ogs_sbi_discovery_option_parse_snssais(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *snssais);
|
||||
|
||||
void ogs_sbi_discovery_option_set_guami(
|
||||
ogs_sbi_discovery_option_t *discovery_option, ogs_guami_t *guami);
|
||||
char *ogs_sbi_discovery_option_build_guami(
|
||||
ogs_sbi_discovery_option_t *discovery_option);
|
||||
void ogs_sbi_discovery_option_parse_guami(
|
||||
int ogs_sbi_discovery_option_parse_guami(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *guami);
|
||||
|
||||
void ogs_sbi_discovery_option_set_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(
|
||||
int ogs_sbi_discovery_option_parse_tai(
|
||||
ogs_sbi_discovery_option_t *discovery_option, char *tai);
|
||||
|
||||
void ogs_sbi_discovery_option_add_target_plmn_list(
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ static ogs_sbi_server_t *server_from_stream(ogs_sbi_stream_t *stream);
|
|||
static ogs_pool_id_t id_from_stream(ogs_sbi_stream_t *stream);
|
||||
static void *stream_find_by_id(ogs_pool_id_t id);
|
||||
|
||||
static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact);
|
||||
static void xact_detach(ogs_sbi_xact_t *xact);
|
||||
|
||||
const ogs_sbi_server_actions_t ogs_mhd_server_actions = {
|
||||
server_init,
|
||||
server_final,
|
||||
|
|
@ -59,6 +62,9 @@ const ogs_sbi_server_actions_t ogs_mhd_server_actions = {
|
|||
server_from_stream,
|
||||
id_from_stream,
|
||||
stream_find_by_id,
|
||||
|
||||
xact_attach,
|
||||
xact_detach,
|
||||
};
|
||||
|
||||
static void run(short when, ogs_socket_t fd, void *data);
|
||||
|
|
@ -110,6 +116,15 @@ typedef struct ogs_sbi_session_s {
|
|||
*/
|
||||
ogs_timer_t *timer;
|
||||
|
||||
/*
|
||||
* Outbound SBI transactions originated by this session. Drained
|
||||
* on session_remove() so response timers are returned to the
|
||||
* pool immediately when the inbound HTTP connection goes away.
|
||||
* See the matching xact_list on the HTTP/2 backend (stream_s in
|
||||
* lib/sbi/nghttp2-server.c).
|
||||
*/
|
||||
ogs_list_t xact_list;
|
||||
|
||||
void *data;
|
||||
} ogs_sbi_session_t;
|
||||
|
||||
|
|
@ -141,6 +156,8 @@ static ogs_sbi_session_t *session_add(ogs_sbi_server_t *server,
|
|||
sbi_sess->request = request;
|
||||
sbi_sess->connection = connection;
|
||||
|
||||
ogs_list_init(&sbi_sess->xact_list);
|
||||
|
||||
sbi_sess->timer = ogs_timer_add(
|
||||
ogs_app()->timer_mgr, session_timer_expired,
|
||||
OGS_UINT_TO_POINTER(sbi_sess->id));
|
||||
|
|
@ -160,6 +177,73 @@ static ogs_sbi_session_t *session_add(ogs_sbi_server_t *server,
|
|||
return sbi_sess;
|
||||
}
|
||||
|
||||
static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_sbi_session_t *sbi_sess = (ogs_sbi_session_t *)stream;
|
||||
|
||||
ogs_assert(sbi_sess);
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Invariant: the server-layer wrapper (ogs_sbi_server_attach_xact)
|
||||
* already filtered out the already-attached case. Reaching the
|
||||
* backend with to_stream_list set is a programming error.
|
||||
*/
|
||||
ogs_assert(!xact->to_stream_list);
|
||||
|
||||
/*
|
||||
* Cache the list head so xact_detach() can unlink in O(1).
|
||||
* to_stream_list serves as both attachment flag and cached head.
|
||||
*/
|
||||
ogs_list_add(&sbi_sess->xact_list, &xact->to_stream_node);
|
||||
xact->to_stream_list = &sbi_sess->xact_list;
|
||||
}
|
||||
|
||||
static void xact_detach(ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Invariant: the server-layer wrapper (ogs_sbi_server_detach_xact)
|
||||
* already filtered out the not-attached case. Reaching the
|
||||
* backend with to_stream_list cleared is a programming error.
|
||||
*/
|
||||
ogs_assert(xact->to_stream_list);
|
||||
|
||||
ogs_list_remove(xact->to_stream_list, &xact->to_stream_node);
|
||||
|
||||
xact->to_stream_list = NULL;
|
||||
xact->assoc_stream_id = OGS_INVALID_POOL_ID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel outbound SBI transactions originated by this session before
|
||||
* tearing it down. Same rationale as stream_remove_xact_all() on the
|
||||
* HTTP/2 backend: release response timers immediately instead of
|
||||
* holding pool slots until the SBI client wait timeout.
|
||||
*/
|
||||
static void session_remove_xact_all(ogs_sbi_session_t *sbi_sess)
|
||||
{
|
||||
ogs_sbi_xact_t *xact = NULL, *next_xact = NULL;
|
||||
|
||||
ogs_assert(sbi_sess);
|
||||
|
||||
ogs_list_for_each_entry_safe(
|
||||
&sbi_sess->xact_list, next_xact, xact, to_stream_node) {
|
||||
/*
|
||||
* Logged at error level so the cancellation shows up in
|
||||
* production traces alongside the upstream NF activity that
|
||||
* was abandoned. See the matching log in stream_remove() on
|
||||
* the HTTP/2 backend.
|
||||
*/
|
||||
ogs_error("Canceling pending outbound SBI transaction "
|
||||
"on MHD session close [xact:%d,session:%d,service:%s]",
|
||||
(int)xact->id, (int)sbi_sess->id,
|
||||
OpenAPI_service_name_ToString(xact->service_name));
|
||||
ogs_sbi_xact_remove(xact);
|
||||
}
|
||||
}
|
||||
|
||||
static void session_remove(ogs_sbi_session_t *sbi_sess)
|
||||
{
|
||||
struct MHD_Connection *connection;
|
||||
|
|
@ -171,6 +255,8 @@ static void session_remove(ogs_sbi_session_t *sbi_sess)
|
|||
|
||||
ogs_list_remove(&server->session_list, sbi_sess);
|
||||
|
||||
session_remove_xact_all(sbi_sess);
|
||||
|
||||
ogs_assert(sbi_sess->timer);
|
||||
ogs_timer_delete(sbi_sess->timer);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,21 @@ static void handle_nf_profile_retrieval(
|
|||
ogs_assert(nf_instance_id);
|
||||
ogs_assert(NFProfile);
|
||||
|
||||
if (!NFProfile->nf_instance_id) {
|
||||
ogs_error("No NFProfile.NFInstanceId");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NFProfile->nf_type) {
|
||||
ogs_error("No NFProfile.NFType");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NFProfile->nf_status) {
|
||||
ogs_error("No NFProfile.NFStatus");
|
||||
return;
|
||||
}
|
||||
|
||||
nf_instance = ogs_sbi_nf_instance_find(nf_instance_id);
|
||||
if (nf_instance) {
|
||||
/* already have this nf_instance; done */
|
||||
|
|
@ -42,11 +57,19 @@ static void handle_nf_profile_retrieval(
|
|||
}
|
||||
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add retrieved NF instance [%s] "
|
||||
"due to insufficient space", nf_instance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ogs_sbi_nf_instance_set_id(nf_instance, nf_instance_id);
|
||||
|
||||
ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile);
|
||||
if (ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile) == false) {
|
||||
ogs_error("[%s] (NRF-profile-get) Invalid NFProfile", nf_instance_id);
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
return;
|
||||
}
|
||||
|
||||
/* verify against our subscription list that we want to save this
|
||||
* nf instance to our context */
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@ static ogs_sbi_server_t *server_from_stream(ogs_sbi_stream_t *stream);
|
|||
static ogs_pool_id_t id_from_stream(ogs_sbi_stream_t *stream);
|
||||
static void *stream_find_by_id(ogs_pool_id_t id);
|
||||
|
||||
static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact);
|
||||
static void xact_detach(ogs_sbi_xact_t *xact);
|
||||
|
||||
const ogs_sbi_server_actions_t ogs_nghttp2_server_actions = {
|
||||
server_init,
|
||||
server_final,
|
||||
|
|
@ -58,6 +61,9 @@ const ogs_sbi_server_actions_t ogs_nghttp2_server_actions = {
|
|||
|
||||
id_from_stream,
|
||||
stream_find_by_id,
|
||||
|
||||
xact_attach,
|
||||
xact_detach,
|
||||
};
|
||||
|
||||
struct h2_settings {
|
||||
|
|
@ -96,6 +102,15 @@ typedef struct ogs_sbi_stream_s {
|
|||
bool memory_overflow;
|
||||
|
||||
ogs_sbi_session_t *session;
|
||||
|
||||
/*
|
||||
* Outbound SBI transactions originated by this inbound stream.
|
||||
* Populated automatically when ogs_sbi_discover_and_send() sees
|
||||
* xact->assoc_stream_id pointing at this stream, and drained at
|
||||
* stream close so response timers are freed promptly rather
|
||||
* than lingering until the SBI client wait timeout.
|
||||
*/
|
||||
ogs_list_t xact_list;
|
||||
} ogs_sbi_stream_t;
|
||||
|
||||
static void session_remove(ogs_sbi_session_t *sbi_sess);
|
||||
|
|
@ -771,11 +786,87 @@ static ogs_sbi_stream_t *stream_add(
|
|||
|
||||
stream->session = sbi_sess;
|
||||
|
||||
ogs_list_init(&stream->xact_list);
|
||||
|
||||
ogs_list_add(&sbi_sess->stream_list, stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
static void xact_attach(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_assert(stream);
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Invariant: the server-layer wrapper (ogs_sbi_server_attach_xact)
|
||||
* already filtered out the already-attached case. Reaching the
|
||||
* backend with to_stream_list set is a programming error.
|
||||
*/
|
||||
ogs_assert(!xact->to_stream_list);
|
||||
|
||||
/*
|
||||
* Cache the list head so xact_detach() can unlink in O(1)
|
||||
* without re-resolving the stream from assoc_stream_id.
|
||||
* to_stream_list serves as both attachment flag and cached head.
|
||||
*/
|
||||
ogs_list_add(&stream->xact_list, &xact->to_stream_node);
|
||||
xact->to_stream_list = &stream->xact_list;
|
||||
}
|
||||
|
||||
static void xact_detach(ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Invariant: the server-layer wrapper (ogs_sbi_server_detach_xact)
|
||||
* already filtered out the not-attached case. Reaching the
|
||||
* backend with to_stream_list cleared is a programming error.
|
||||
*/
|
||||
ogs_assert(xact->to_stream_list);
|
||||
|
||||
ogs_list_remove(xact->to_stream_list, &xact->to_stream_node);
|
||||
|
||||
xact->to_stream_list = NULL;
|
||||
xact->assoc_stream_id = OGS_INVALID_POOL_ID;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cancel every outbound SBI transaction that was triggered by this
|
||||
* stream. Without this, ogs_sbi_xact_remove() and its response timer
|
||||
* would only be reached when the upstream NF responds or the SBI
|
||||
* client wait timer expires; a peer that rapidly resets streams
|
||||
* while the upstream NF stalls would pile up those timers until the
|
||||
* pool is exhausted (the crash signature in issues #4472 / #4473).
|
||||
*
|
||||
* ogs_sbi_xact_remove() invokes xact_detach() internally, which is
|
||||
* what unlinks the node from this list. ogs_list_for_each_entry_safe
|
||||
* caches the next pointer before the body runs, so detach-during-
|
||||
* iteration is well-defined.
|
||||
*/
|
||||
static void stream_remove_xact_all(ogs_sbi_stream_t *stream)
|
||||
{
|
||||
ogs_sbi_xact_t *xact = NULL, *next_xact = NULL;
|
||||
|
||||
ogs_assert(stream);
|
||||
|
||||
ogs_list_for_each_entry_safe(
|
||||
&stream->xact_list, next_xact, xact, to_stream_node) {
|
||||
/*
|
||||
* Logged at error level so the cancellation shows up in
|
||||
* production traces alongside the upstream NF activity that
|
||||
* was abandoned. Useful for diagnosing #4472/#4473 style
|
||||
* patterns and any future regression where a peer resets
|
||||
* streams while upstream NFs are slow.
|
||||
*/
|
||||
ogs_error("Canceling pending outbound SBI transaction "
|
||||
"on HTTP/2 stream close [xact:%d,stream:%d,service:%s]",
|
||||
(int)xact->id, stream->stream_id,
|
||||
OpenAPI_service_name_ToString(xact->service_name));
|
||||
ogs_sbi_xact_remove(xact);
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_remove(ogs_sbi_stream_t *stream)
|
||||
{
|
||||
ogs_sbi_session_t *sbi_sess = NULL;
|
||||
|
|
@ -786,6 +877,8 @@ static void stream_remove(ogs_sbi_stream_t *stream)
|
|||
|
||||
ogs_list_remove(&sbi_sess->stream_list, stream);
|
||||
|
||||
stream_remove_xact_all(stream);
|
||||
|
||||
ogs_assert(stream->request);
|
||||
ogs_sbi_request_free(stream->request);
|
||||
|
||||
|
|
@ -1615,6 +1708,7 @@ static int on_begin_headers(nghttp2_session *session,
|
|||
{
|
||||
ogs_sbi_session_t *sbi_sess = user_data;
|
||||
ogs_sbi_stream_t *stream = NULL;
|
||||
int rv;
|
||||
|
||||
ogs_assert(sbi_sess);
|
||||
ogs_assert(session);
|
||||
|
|
@ -1626,7 +1720,20 @@ static int on_begin_headers(nghttp2_session *session,
|
|||
}
|
||||
|
||||
stream = stream_add(sbi_sess, frame->hd.stream_id);
|
||||
ogs_assert(stream);
|
||||
if (!stream) {
|
||||
ogs_error("stream_add() failed for stream [%d]",
|
||||
frame->hd.stream_id);
|
||||
|
||||
rv = nghttp2_submit_rst_stream(
|
||||
session, NGHTTP2_FLAG_NONE,
|
||||
frame->hd.stream_id, NGHTTP2_REFUSED_STREAM);
|
||||
if (rv != 0)
|
||||
ogs_error("nghttp2_submit_rst_stream() failed (%d:%s)",
|
||||
rv, nghttp2_strerror(rv));
|
||||
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
ogs_debug("STREAM added [%d]", frame->hd.stream_id);
|
||||
|
||||
nghttp2_session_set_stream_user_data(session, frame->hd.stream_id, stream);
|
||||
|
|
|
|||
|
|
@ -256,6 +256,45 @@ OpenAPI_nf_profile_t *ogs_nnrf_nfm_build_nf_profile(
|
|||
else
|
||||
OpenAPI_list_free(AllowedNfTypeList);
|
||||
|
||||
if (nf_instance->num_of_s_nssai) {
|
||||
OpenAPI_list_t *sNssaiList = OpenAPI_list_create();
|
||||
ogs_assert(sNssaiList);
|
||||
|
||||
for (i = 0; i < nf_instance->num_of_s_nssai; i++) {
|
||||
OpenAPI_ext_snssai_t *sNssai = OpenAPI_ext_snssai_create(
|
||||
nf_instance->s_nssai[i].sst,
|
||||
ogs_s_nssai_sd_to_string(nf_instance->s_nssai[i].sd),
|
||||
NULL, false, 0);
|
||||
ogs_assert(sNssai);
|
||||
OpenAPI_list_add(sNssaiList, sNssai);
|
||||
}
|
||||
|
||||
if (sNssaiList->count)
|
||||
NFProfile->s_nssais = sNssaiList;
|
||||
else
|
||||
OpenAPI_list_free(sNssaiList);
|
||||
}
|
||||
|
||||
if (nf_instance->num_of_allowed_nssai) {
|
||||
OpenAPI_list_t *allowedNssaiList = OpenAPI_list_create();
|
||||
ogs_assert(allowedNssaiList);
|
||||
|
||||
for (i = 0; i < nf_instance->num_of_allowed_nssai; i++) {
|
||||
OpenAPI_ext_snssai_t *sNssai = OpenAPI_ext_snssai_create(
|
||||
nf_instance->allowed_nssai[i].sst,
|
||||
ogs_s_nssai_sd_to_string(
|
||||
nf_instance->allowed_nssai[i].sd),
|
||||
NULL, false, 0);
|
||||
ogs_assert(sNssai);
|
||||
OpenAPI_list_add(allowedNssaiList, sNssai);
|
||||
}
|
||||
|
||||
if (allowedNssaiList->count)
|
||||
NFProfile->allowed_nssais = allowedNssaiList;
|
||||
else
|
||||
OpenAPI_list_free(allowedNssaiList);
|
||||
}
|
||||
|
||||
NFServiceList = OpenAPI_list_create();
|
||||
if (!NFServiceList) {
|
||||
ogs_error("No nf_service_list");
|
||||
|
|
@ -441,6 +480,14 @@ void ogs_nnrf_nfm_free_nf_profile(OpenAPI_nf_profile_t *NFProfile)
|
|||
|
||||
OpenAPI_list_free(NFProfile->allowed_nf_types);
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->s_nssais, node)
|
||||
OpenAPI_ext_snssai_free(node->data);
|
||||
OpenAPI_list_free(NFProfile->s_nssais);
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->allowed_nssais, node)
|
||||
OpenAPI_ext_snssai_free(node->data);
|
||||
OpenAPI_list_free(NFProfile->allowed_nssais);
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->nf_services, node) {
|
||||
NFService = node->data;
|
||||
ogs_assert(NFService);
|
||||
|
|
|
|||
|
|
@ -21,13 +21,13 @@
|
|||
|
||||
static void handle_nf_service(
|
||||
ogs_sbi_nf_service_t *nf_service, OpenAPI_nf_service_t *NFService);
|
||||
static void handle_smf_info(
|
||||
static bool handle_smf_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_smf_info_t *SmfInfo);
|
||||
static void handle_scp_info(
|
||||
static bool handle_scp_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_scp_info_t *ScpInfo);
|
||||
static void handle_sepp_info(
|
||||
static bool handle_sepp_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_sepp_info_t *SeppInfo);
|
||||
static void handle_amf_info(
|
||||
static bool handle_amf_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo);
|
||||
|
||||
void ogs_nnrf_nfm_handle_nf_register(
|
||||
|
|
@ -72,7 +72,7 @@ void ogs_nnrf_nfm_handle_nf_register(
|
|||
}
|
||||
}
|
||||
|
||||
void ogs_nnrf_nfm_handle_nf_profile(
|
||||
bool ogs_nnrf_nfm_handle_nf_profile(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_nf_profile_t *NFProfile)
|
||||
{
|
||||
int rv;
|
||||
|
|
@ -181,6 +181,45 @@ void ogs_nnrf_nfm_handle_nf_profile(
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TS 33.518 4.2.2.2.1 - Store S-NSSAIs for slice-based discovery
|
||||
* authorization. sNssais identifies the slices this NF serves;
|
||||
* allowedNssais restricts for which slices the NF may be discovered.
|
||||
*/
|
||||
nf_instance->num_of_s_nssai = 0;
|
||||
OpenAPI_list_for_each(NFProfile->s_nssais, node) {
|
||||
OpenAPI_ext_snssai_t *sNssai = node->data;
|
||||
if (sNssai) {
|
||||
if (nf_instance->num_of_s_nssai >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_warn("Overflow: NFProfile.sNssais exceeds %d",
|
||||
OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
nf_instance->s_nssai[nf_instance->num_of_s_nssai].sst =
|
||||
sNssai->sst;
|
||||
nf_instance->s_nssai[nf_instance->num_of_s_nssai].sd =
|
||||
ogs_s_nssai_sd_from_string(sNssai->sd);
|
||||
nf_instance->num_of_s_nssai++;
|
||||
}
|
||||
}
|
||||
|
||||
nf_instance->num_of_allowed_nssai = 0;
|
||||
OpenAPI_list_for_each(NFProfile->allowed_nssais, node) {
|
||||
OpenAPI_ext_snssai_t *sNssai = node->data;
|
||||
if (sNssai) {
|
||||
if (nf_instance->num_of_allowed_nssai >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_warn("Overflow: NFProfile.allowedNssais exceeds %d",
|
||||
OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
nf_instance->allowed_nssai[nf_instance->num_of_allowed_nssai].sst =
|
||||
sNssai->sst;
|
||||
nf_instance->allowed_nssai[nf_instance->num_of_allowed_nssai].sd =
|
||||
ogs_s_nssai_sd_from_string(sNssai->sd);
|
||||
nf_instance->num_of_allowed_nssai++;
|
||||
}
|
||||
}
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->nf_services, node) {
|
||||
ogs_sbi_nf_service_t *nf_service = NULL;
|
||||
OpenAPI_nf_service_t *NFService = node->data;
|
||||
|
|
@ -212,7 +251,11 @@ void ogs_nnrf_nfm_handle_nf_profile(
|
|||
nf_instance,
|
||||
NFService->service_instance_id,
|
||||
NFService->service_name, NFService->scheme);
|
||||
ogs_assert(nf_service);
|
||||
if (!nf_service) {
|
||||
ogs_error("Failed to add NFService [%s]",
|
||||
NFService->service_instance_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ogs_sbi_nf_service_clear(nf_service);
|
||||
|
|
@ -255,7 +298,11 @@ void ogs_nnrf_nfm_handle_nf_profile(
|
|||
nf_instance,
|
||||
NFService->service_instance_id,
|
||||
NFService->service_name, NFService->scheme);
|
||||
ogs_assert(nf_service);
|
||||
if (!nf_service) {
|
||||
ogs_error("Failed to add NFService [%s]",
|
||||
NFService->service_instance_id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ogs_sbi_nf_service_clear(nf_service);
|
||||
|
|
@ -266,26 +313,34 @@ void ogs_nnrf_nfm_handle_nf_profile(
|
|||
|
||||
ogs_sbi_nf_info_remove_all(&nf_instance->nf_info_list);
|
||||
|
||||
if (NFProfile->smf_info)
|
||||
handle_smf_info(nf_instance, NFProfile->smf_info);
|
||||
if (NFProfile->smf_info &&
|
||||
handle_smf_info(nf_instance, NFProfile->smf_info) == false)
|
||||
return false;
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->smf_info_list, node) {
|
||||
OpenAPI_map_t *SmfInfoMap = node->data;
|
||||
if (SmfInfoMap && SmfInfoMap->value)
|
||||
handle_smf_info(nf_instance, SmfInfoMap->value);
|
||||
if (SmfInfoMap && SmfInfoMap->value &&
|
||||
handle_smf_info(nf_instance, SmfInfoMap->value) == false)
|
||||
return false;
|
||||
}
|
||||
if (NFProfile->amf_info)
|
||||
handle_amf_info(nf_instance, NFProfile->amf_info);
|
||||
if (NFProfile->amf_info &&
|
||||
handle_amf_info(nf_instance, NFProfile->amf_info) == false)
|
||||
return false;
|
||||
|
||||
OpenAPI_list_for_each(NFProfile->amf_info_list, node) {
|
||||
OpenAPI_map_t *AmfInfoMap = node->data;
|
||||
if (AmfInfoMap && AmfInfoMap->value)
|
||||
handle_amf_info(nf_instance, AmfInfoMap->value);
|
||||
if (AmfInfoMap && AmfInfoMap->value &&
|
||||
handle_amf_info(nf_instance, AmfInfoMap->value) == false)
|
||||
return false;
|
||||
}
|
||||
if (NFProfile->scp_info)
|
||||
handle_scp_info(nf_instance, NFProfile->scp_info);
|
||||
if (NFProfile->sepp_info)
|
||||
handle_sepp_info(nf_instance, NFProfile->sepp_info);
|
||||
if (NFProfile->scp_info &&
|
||||
handle_scp_info(nf_instance, NFProfile->scp_info) == false)
|
||||
return false;
|
||||
if (NFProfile->sepp_info &&
|
||||
handle_sepp_info(nf_instance, NFProfile->sepp_info) == false)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_nf_service(
|
||||
|
|
@ -387,9 +442,10 @@ static void handle_nf_service(
|
|||
nf_service->load = NFService->load;
|
||||
}
|
||||
|
||||
static void handle_smf_info(
|
||||
static bool handle_smf_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_smf_info_t *SmfInfo)
|
||||
{
|
||||
bool rv = true;
|
||||
ogs_sbi_nf_info_t *nf_info = NULL;
|
||||
|
||||
OpenAPI_list_t *sNssaiSmfInfoList = NULL;
|
||||
|
|
@ -412,7 +468,10 @@ static void handle_smf_info(
|
|||
|
||||
nf_info = ogs_sbi_nf_info_add(
|
||||
&nf_instance->nf_info_list, OpenAPI_nf_type_SMF);
|
||||
ogs_assert(nf_info);
|
||||
if (!nf_info) {
|
||||
ogs_error("Failed to add SMF nfInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
sNssaiSmfInfoList = SmfInfo->s_nssai_smf_info_list;
|
||||
OpenAPI_list_for_each(sNssaiSmfInfoList, node) {
|
||||
|
|
@ -421,6 +480,7 @@ static void handle_smf_info(
|
|||
if (nf_info->smf.num_of_slice >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("OVERFLOW Slice [%d:%d]",
|
||||
nf_info->smf.num_of_slice, OGS_MAX_NUM_OF_SLICE);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -431,17 +491,28 @@ static void handle_smf_info(
|
|||
int dnn_index = nf_info->smf.slice
|
||||
[nf_info->smf.num_of_slice].num_of_dnn;
|
||||
|
||||
ogs_assert(dnn_index < OGS_MAX_NUM_OF_DNN);
|
||||
if (dnn_index >= OGS_MAX_NUM_OF_DNN) {
|
||||
ogs_error("OVERFLOW DNN [%d:%d]",
|
||||
dnn_index, OGS_MAX_NUM_OF_DNN);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
nf_info->smf.slice[nf_info->smf.num_of_slice].
|
||||
dnn[dnn_index] = ogs_strdup(DnnSmfInfoItem->dnn);
|
||||
ogs_assert(
|
||||
nf_info->smf.slice[nf_info->smf.num_of_slice].
|
||||
dnn[dnn_index]);
|
||||
if (!nf_info->smf.slice[nf_info->smf.num_of_slice].
|
||||
dnn[dnn_index]) {
|
||||
ogs_error("No memory for DNN");
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
nf_info->smf.slice[nf_info->smf.num_of_slice].
|
||||
num_of_dnn++;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == false)
|
||||
break;
|
||||
|
||||
if (!nf_info->smf.slice[nf_info->smf.num_of_slice].num_of_dnn) {
|
||||
ogs_error("No DNN");
|
||||
continue;
|
||||
|
|
@ -460,10 +531,12 @@ static void handle_smf_info(
|
|||
}
|
||||
}
|
||||
|
||||
if (rv == false) goto out;
|
||||
|
||||
if (nf_info->smf.num_of_slice == 0) {
|
||||
ogs_error("No S-NSSAI(DNN) in smfInfo");
|
||||
ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
TaiList = SmfInfo->tai_list;
|
||||
|
|
@ -474,6 +547,7 @@ static void handle_smf_info(
|
|||
if (nf_info->smf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TAI [%d:%d]",
|
||||
nf_info->smf.num_of_nr_tai, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -486,6 +560,8 @@ static void handle_smf_info(
|
|||
}
|
||||
}
|
||||
|
||||
if (rv == false) goto out;
|
||||
|
||||
TaiRangeList = SmfInfo->tai_range_list;
|
||||
OpenAPI_list_for_each(TaiRangeList, node) {
|
||||
TaiRangeItem = node->data;
|
||||
|
|
@ -495,6 +571,7 @@ static void handle_smf_info(
|
|||
if (nf_info->smf.num_of_nr_tai_range >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TaiRangeItem [%d:%d]",
|
||||
nf_info->smf.num_of_nr_tai_range, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -513,6 +590,7 @@ static void handle_smf_info(
|
|||
if (tac_index >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TAI [%d:%d]",
|
||||
tac_index, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -533,12 +611,36 @@ static void handle_smf_info(
|
|||
}
|
||||
}
|
||||
|
||||
if (rv == false)
|
||||
break;
|
||||
|
||||
nf_info->smf.num_of_nr_tai_range++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (rv == false) {
|
||||
ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_scp_info(
|
||||
static void scp_info_free(ogs_sbi_scp_info_t *scp_info)
|
||||
{
|
||||
int i;
|
||||
|
||||
ogs_assert(scp_info);
|
||||
|
||||
for (i = 0; i < scp_info->num_of_domain; i++) {
|
||||
ogs_free(scp_info->domain[i].name);
|
||||
ogs_free(scp_info->domain[i].fqdn);
|
||||
}
|
||||
memset(scp_info, 0, sizeof(*scp_info));
|
||||
}
|
||||
|
||||
static bool handle_scp_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_scp_info_t *ScpInfo)
|
||||
{
|
||||
ogs_sbi_nf_info_t *nf_info = NULL;
|
||||
|
|
@ -636,13 +738,19 @@ static void handle_scp_info(
|
|||
scp_info.num_of_domain) {
|
||||
nf_info = ogs_sbi_nf_info_add(
|
||||
&nf_instance->nf_info_list, OpenAPI_nf_type_SCP);
|
||||
ogs_assert(nf_info);
|
||||
if (!nf_info) {
|
||||
ogs_error("Failed to add SCP nfInfo");
|
||||
scp_info_free(&scp_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&nf_info->scp, &scp_info, sizeof(scp_info));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_sepp_info(
|
||||
static bool handle_sepp_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_sepp_info_t *SeppInfo)
|
||||
{
|
||||
ogs_sbi_nf_info_t *nf_info = NULL;
|
||||
|
|
@ -692,7 +800,10 @@ static void handle_sepp_info(
|
|||
if (http.presence || https.presence) {
|
||||
nf_info = ogs_sbi_nf_info_add(
|
||||
&nf_instance->nf_info_list, OpenAPI_nf_type_SEPP);
|
||||
ogs_assert(nf_info);
|
||||
if (!nf_info) {
|
||||
ogs_error("Failed to add SEPP nfInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
nf_info->sepp.http.presence = http.presence;
|
||||
nf_info->sepp.http.port = http.port;
|
||||
|
|
@ -700,11 +811,14 @@ static void handle_sepp_info(
|
|||
nf_info->sepp.https.presence = https.presence;
|
||||
nf_info->sepp.https.port = https.port;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_amf_info(
|
||||
static bool handle_amf_info(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_amf_info_t *AmfInfo)
|
||||
{
|
||||
bool rv = true;
|
||||
ogs_sbi_nf_info_t *nf_info = NULL;
|
||||
OpenAPI_list_t *GuamiList = NULL;
|
||||
OpenAPI_guami_t *GuamiAmfInfoItem = NULL;
|
||||
|
|
@ -721,7 +835,10 @@ static void handle_amf_info(
|
|||
|
||||
nf_info = ogs_sbi_nf_info_add(
|
||||
&nf_instance->nf_info_list, OpenAPI_nf_type_AMF);
|
||||
ogs_assert(nf_info);
|
||||
if (!nf_info) {
|
||||
ogs_error("Failed to add AMF nfInfo");
|
||||
return false;
|
||||
}
|
||||
|
||||
nf_info->amf.amf_set_id = ogs_uint64_from_string_hexadecimal(
|
||||
AmfInfo->amf_set_id);
|
||||
|
|
@ -735,6 +852,7 @@ static void handle_amf_info(
|
|||
if (nf_info->amf.num_of_guami >= OGS_MAX_NUM_OF_SERVED_GUAMI) {
|
||||
ogs_error("OVERFLOW Guami [%d:%d]",
|
||||
nf_info->amf.num_of_guami, OGS_MAX_NUM_OF_SERVED_GUAMI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -749,6 +867,7 @@ static void handle_amf_info(
|
|||
}
|
||||
}
|
||||
}
|
||||
if (rv == false) goto out;
|
||||
|
||||
TaiList = AmfInfo->tai_list;
|
||||
OpenAPI_list_for_each(TaiList, node) {
|
||||
|
|
@ -759,6 +878,7 @@ static void handle_amf_info(
|
|||
if (nf_info->amf.num_of_nr_tai >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TaiItem [%d:%d]",
|
||||
nf_info->amf.num_of_nr_tai, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -769,6 +889,7 @@ static void handle_amf_info(
|
|||
nf_info->amf.num_of_nr_tai++;
|
||||
}
|
||||
}
|
||||
if (rv == false) goto out;
|
||||
|
||||
TaiRangeList = AmfInfo->tai_range_list;
|
||||
OpenAPI_list_for_each(TaiRangeList, node) {
|
||||
|
|
@ -778,6 +899,7 @@ static void handle_amf_info(
|
|||
if (nf_info->amf.num_of_nr_tai_range >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TaiRangeItem [%d:%d]",
|
||||
nf_info->amf.num_of_nr_tai_range, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -793,7 +915,12 @@ static void handle_amf_info(
|
|||
TacRangeItem->start && TacRangeItem->end) {
|
||||
int tac_index = nf_info->amf.nr_tai_range
|
||||
[nf_info->amf.num_of_nr_tai_range].num_of_tac_range;
|
||||
ogs_assert(tac_index < OGS_MAX_NUM_OF_TAI);
|
||||
if (tac_index >= OGS_MAX_NUM_OF_TAI) {
|
||||
ogs_error("OVERFLOW TAI [%d:%d]",
|
||||
tac_index, OGS_MAX_NUM_OF_TAI);
|
||||
rv = false;
|
||||
break;
|
||||
}
|
||||
|
||||
nf_info->amf.nr_tai_range
|
||||
[nf_info->amf.num_of_nr_tai_range].start[tac_index] =
|
||||
|
|
@ -808,9 +935,21 @@ static void handle_amf_info(
|
|||
[nf_info->amf.num_of_nr_tai_range].num_of_tac_range++;
|
||||
}
|
||||
}
|
||||
|
||||
if (rv == false)
|
||||
break;
|
||||
|
||||
nf_info->amf.num_of_nr_tai_range++;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (rv == false) {
|
||||
ogs_sbi_nf_info_remove(&nf_instance->nf_info_list, nf_info);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void handle_validity_time(
|
||||
|
|
@ -1105,6 +1244,7 @@ bool ogs_nnrf_nfm_handle_nf_status_notify(
|
|||
OpenAPI_notification_event_type_NF_REGISTERED) {
|
||||
|
||||
OpenAPI_nf_profile_t *NFProfile = NULL;
|
||||
bool nf_instance_created = false;
|
||||
|
||||
NFProfile = NotificationData->nf_profile;
|
||||
if (!NFProfile) {
|
||||
|
|
@ -1150,11 +1290,22 @@ bool ogs_nnrf_nfm_handle_nf_status_notify(
|
|||
nf_instance = ogs_sbi_nf_instance_find(message.h.resource.component[1]);
|
||||
if (!nf_instance) {
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add notified NF instance [%s] "
|
||||
"due to insufficient space",
|
||||
message.h.resource.component[1]);
|
||||
ogs_assert(true == ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_PAYLOAD_TOO_LARGE,
|
||||
recvmsg, "Insufficient space",
|
||||
message.h.resource.component[1], NULL));
|
||||
ogs_sbi_header_free(&header);
|
||||
return false;
|
||||
}
|
||||
|
||||
ogs_sbi_nf_instance_set_id(
|
||||
nf_instance, message.h.resource.component[1]);
|
||||
ogs_sbi_nf_fsm_init(nf_instance);
|
||||
nf_instance_created = true;
|
||||
|
||||
ogs_info("[%s] (NRF-notify) NF registered", nf_instance->id);
|
||||
} else {
|
||||
|
|
@ -1168,7 +1319,31 @@ bool ogs_nnrf_nfm_handle_nf_status_notify(
|
|||
}
|
||||
}
|
||||
|
||||
ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile);
|
||||
if (ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile) == false) {
|
||||
ogs_error("[%s] (NRF-notify) Invalid NFProfile [type:%s]",
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
|
||||
/*
|
||||
* ogs_nnrf_nfm_handle_nf_profile() rebuilds the NF profile in
|
||||
* place. If parsing fails for a newly created cache entry, remove
|
||||
* it because it may contain a partial profile. If the entry
|
||||
* already existed, keep it in the registry to avoid deleting a
|
||||
* previously usable local cache entry because of one bad notify.
|
||||
*/
|
||||
if (nf_instance_created == true) {
|
||||
ogs_sbi_nf_fsm_fini(nf_instance);
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
}
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
recvmsg, "Invalid NFProfile",
|
||||
NFProfile->nf_instance_id, NULL));
|
||||
ogs_sbi_header_free(&header);
|
||||
return false;
|
||||
}
|
||||
|
||||
ogs_info("[%s] (NRF-notify) NF Profile updated [type:%s]",
|
||||
nf_instance->id,
|
||||
|
|
@ -1237,6 +1412,7 @@ void ogs_nnrf_disc_handle_nf_discover_search_result(
|
|||
|
||||
OpenAPI_list_for_each(SearchResult->nf_instances, node) {
|
||||
OpenAPI_nf_profile_t *NFProfile = NULL;
|
||||
bool nf_instance_created = false;
|
||||
|
||||
if (!node->data) continue;
|
||||
|
||||
|
|
@ -1265,27 +1441,52 @@ void ogs_nnrf_disc_handle_nf_discover_search_result(
|
|||
nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id);
|
||||
if (!nf_instance) {
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add discovered NF instance [%s:%s] "
|
||||
"due to insufficient space",
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
continue;
|
||||
}
|
||||
|
||||
ogs_sbi_nf_instance_set_id(nf_instance, NFProfile->nf_instance_id);
|
||||
ogs_sbi_nf_fsm_init(nf_instance);
|
||||
nf_instance_created = true;
|
||||
|
||||
ogs_info("[%s] (NRF-discover) NF registered [type:%s]",
|
||||
nf_instance->id,
|
||||
OpenAPI_nf_type_ToString(nf_instance->nf_type));
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
} else {
|
||||
ogs_warn("[%s] (NRF-discover) NF has already been added [type:%s]",
|
||||
nf_instance->id,
|
||||
OpenAPI_nf_type_ToString(nf_instance->nf_type));
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
if (!OGS_FSM_CHECK(&nf_instance->sm, ogs_sbi_nf_state_registered)) {
|
||||
ogs_error("[%s] (NRF-notify) NF invalid state [type:%s]",
|
||||
nf_instance->id,
|
||||
OpenAPI_nf_type_ToString(nf_instance->nf_type));
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
}
|
||||
}
|
||||
|
||||
if (NF_INSTANCE_ID_IS_OTHERS(nf_instance->id)) {
|
||||
ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile);
|
||||
if (ogs_nnrf_nfm_handle_nf_profile(
|
||||
nf_instance, NFProfile) == false) {
|
||||
ogs_error("[%s] (NRF-discover) Invalid NFProfile [type:%s]",
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
|
||||
/*
|
||||
* Do not leave a newly created partial NFProfile in the
|
||||
* registry. If the entry already existed, keep it to avoid
|
||||
* deleting a previously usable local cache entry because of
|
||||
* one bad discovery result.
|
||||
*/
|
||||
if (nf_instance_created == true) {
|
||||
ogs_sbi_nf_fsm_fini(nf_instance);
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ogs_sbi_client_associate(nf_instance);
|
||||
|
||||
|
|
@ -1306,8 +1507,8 @@ void ogs_nnrf_disc_handle_nf_discover_search_result(
|
|||
|
||||
ogs_info("[%s] (NF-discover) NF Profile updated "
|
||||
"[type:%s validity:%ds]",
|
||||
nf_instance->id,
|
||||
OpenAPI_nf_type_ToString(nf_instance->nf_type),
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type),
|
||||
nf_instance->time.validity_duration);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ extern "C" {
|
|||
|
||||
void ogs_nnrf_nfm_handle_nf_register(
|
||||
ogs_sbi_nf_instance_t *nf_instance, ogs_sbi_message_t *recvmsg);
|
||||
void ogs_nnrf_nfm_handle_nf_profile(
|
||||
bool ogs_nnrf_nfm_handle_nf_profile(
|
||||
ogs_sbi_nf_instance_t *nf_instance, OpenAPI_nf_profile_t *NFProfile);
|
||||
|
||||
void ogs_nnrf_nfm_handle_nf_status_subscribe(
|
||||
|
|
|
|||
|
|
@ -101,11 +101,16 @@ bool ogs_nnrf_nfm_send_nf_status_subscribe(
|
|||
ogs_assert(!subscr_cond_nf_type || !subscr_cond_service_name);
|
||||
|
||||
subscription_data = ogs_sbi_subscription_data_add();
|
||||
ogs_assert(subscription_data);
|
||||
if (!subscription_data) {
|
||||
ogs_error("ogs_sbi_subscription_data_add() failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
subscription_data->req_nf_type = req_nf_type;
|
||||
if (req_nf_instance_id)
|
||||
if (req_nf_instance_id) {
|
||||
subscription_data->req_nf_instance_id = ogs_strdup(req_nf_instance_id);
|
||||
ogs_assert(subscription_data->req_nf_instance_id);
|
||||
}
|
||||
|
||||
if (subscr_cond_nf_type)
|
||||
subscription_data->subscr_cond.nf_type = subscr_cond_nf_type;
|
||||
|
|
@ -119,6 +124,7 @@ bool ogs_nnrf_nfm_send_nf_status_subscribe(
|
|||
request = ogs_nnrf_nfm_build_status_subscribe(subscription_data);
|
||||
if (!request) {
|
||||
ogs_error("No Request");
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +135,54 @@ bool ogs_nnrf_nfm_send_nf_status_subscribe(
|
|||
|
||||
ogs_sbi_request_free(request);
|
||||
|
||||
if (rc != true)
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
ogs_sbi_subscription_data_t *subscription_data)
|
||||
{
|
||||
char *req_nf_instance_id = NULL;
|
||||
OpenAPI_service_name_e subscr_cond_service_name = OpenAPI_service_name_NULL;
|
||||
OpenAPI_nf_type_e req_nf_type;
|
||||
OpenAPI_nf_type_e subscr_cond_nf_type;
|
||||
bool rc;
|
||||
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
/*
|
||||
* The old subscription still occupies subscription_data_pool here.
|
||||
* Keep local copies of the fields needed to rebuild the request,
|
||||
* remove the old subscription first so the pool has a free slot,
|
||||
* then resubscribe. Without this, a full pool would make renewal
|
||||
* fail forever even though the renewal itself does not change the
|
||||
* net occupancy.
|
||||
*/
|
||||
req_nf_type = subscription_data->req_nf_type;
|
||||
subscr_cond_nf_type = subscription_data->subscr_cond.nf_type;
|
||||
|
||||
if (subscription_data->req_nf_instance_id) {
|
||||
req_nf_instance_id =
|
||||
ogs_strdup(subscription_data->req_nf_instance_id);
|
||||
ogs_assert(req_nf_instance_id);
|
||||
}
|
||||
|
||||
if (subscription_data->subscr_cond.service_name)
|
||||
subscr_cond_service_name = subscription_data->subscr_cond.service_name;
|
||||
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
/* subscription_data is invalid past this point */
|
||||
|
||||
rc = ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
req_nf_type, req_nf_instance_id,
|
||||
subscr_cond_nf_type, subscr_cond_service_name);
|
||||
if (rc != true)
|
||||
ogs_error("NF status subscription renewal failed");
|
||||
|
||||
ogs_free(req_nf_instance_id);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ bool ogs_nnrf_nfm_send_nf_status_subscribe(
|
|||
OpenAPI_nf_type_e req_nf_type, char *req_nf_instance_id,
|
||||
OpenAPI_nf_type_e subscr_cond_nf_type,
|
||||
OpenAPI_service_name_e subscr_cond_service_name);
|
||||
bool ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
ogs_sbi_subscription_data_t *subscription_data);
|
||||
bool ogs_nnrf_nfm_send_nf_status_update(
|
||||
ogs_sbi_subscription_data_t *subscription_data);
|
||||
bool ogs_nnrf_nfm_send_nf_status_unsubscribe(
|
||||
|
|
|
|||
|
|
@ -270,6 +270,48 @@ int ogs_sbi_discover_and_send(ogs_sbi_xact_t *xact)
|
|||
request = xact->request;
|
||||
ogs_assert(request);
|
||||
|
||||
/*
|
||||
* If the NF associated this transaction with an inbound server
|
||||
* stream (by assigning xact->assoc_stream_id before calling us),
|
||||
* register the transaction on that stream's outbound xact list.
|
||||
*
|
||||
* When the inbound stream is later closed by the peer (HTTP/2
|
||||
* RST_STREAM, connection drop) before the upstream NF response
|
||||
* arrives, stream_remove() walks the list and cancels every
|
||||
* outstanding transaction. The response timer is returned to
|
||||
* the pool immediately rather than holding a slot until the
|
||||
* SBI client wait timeout, so a burst of short-lived inbound
|
||||
* requests cannot pile up enough pending timers to exhaust the
|
||||
* pool (issues #4472 and #4473).
|
||||
*
|
||||
* Transactions with no inbound stream (e.g. NRF discovery
|
||||
* initiated by the NF itself, status notifications) leave
|
||||
* assoc_stream_id at OGS_INVALID_POOL_ID and the helper is a
|
||||
* no-op (returns OGS_OK).
|
||||
*
|
||||
* If attach reports failure (originating stream already
|
||||
* closed), we DO NOT abort the upstream send and we DO NOT
|
||||
* propagate the failure to the NF caller: most NF wrappers
|
||||
* treat any non-OK return as a hard send failure and remove
|
||||
* the transaction, which would orphan an upstream request
|
||||
* already on the wire. The diagnostic context (xact id,
|
||||
* stream id, service type, file:line) is already emitted by
|
||||
* ogs_sbi_server_attach_xact() itself and by the ogs_error()
|
||||
* below, so the situation is observable without changing the
|
||||
* caller-visible return code. If a response later arrives,
|
||||
* the NF response handler will see no stream and drop it
|
||||
* through the existing "STREAM has already been removed"
|
||||
* path.
|
||||
*/
|
||||
if (ogs_sbi_server_attach_xact(xact) != OGS_OK) {
|
||||
ogs_error("ogs_sbi_discover_and_send: attach failed, "
|
||||
"proceeding with upstream send "
|
||||
"[xact:%d,assoc_stream_id:%d,service:%s]",
|
||||
(int)xact->id, (int)xact->assoc_stream_id,
|
||||
OpenAPI_service_name_ToString(service_name));
|
||||
/* fall through — upstream send must still happen */
|
||||
}
|
||||
|
||||
discovery_option = xact->discovery_option;
|
||||
|
||||
/* SCP Availability */
|
||||
|
|
|
|||
|
|
@ -262,6 +262,97 @@ void *ogs_sbi_stream_find_by_id(ogs_pool_id_t id)
|
|||
return ogs_sbi_server_actions.stream_find_by_id(id);
|
||||
}
|
||||
|
||||
int ogs_sbi_server_attach_xact(ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_sbi_stream_t *stream = NULL;
|
||||
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Idempotent. NFs may set xact->assoc_stream_id and reach the
|
||||
* SBI path multiple times for retries; attaching twice would
|
||||
* corrupt the list, so we skip when already attached. This is
|
||||
* not an error in itself — return OK so the caller continues.
|
||||
*/
|
||||
if (xact->to_stream_list) {
|
||||
ogs_error("ogs_sbi_server_attach_xact: already attached "
|
||||
"[xact:%d,assoc_stream_id:%d,service:%s]",
|
||||
(int)xact->id, (int)xact->assoc_stream_id,
|
||||
OpenAPI_service_name_ToString(xact->service_name));
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* No associated stream is normal for self-initiated transactions
|
||||
* (NRF discovery from the NF itself, status notifications, etc.).
|
||||
* Silent skip, OK.
|
||||
*/
|
||||
if (xact->assoc_stream_id < OGS_MIN_POOL_ID ||
|
||||
xact->assoc_stream_id > OGS_MAX_POOL_ID)
|
||||
return OGS_OK;
|
||||
|
||||
/*
|
||||
* xact->assoc_stream_id is a "best-effort" linkage: it means
|
||||
* "if a response arrives and this stream is still alive,
|
||||
* deliver the response there." The stream is not required to
|
||||
* be alive at the moment the outbound transaction is sent.
|
||||
*
|
||||
* NFs are allowed to pass the same stream to discover_and_send()
|
||||
* even after that stream has already been used to answer the
|
||||
* original peer (and is therefore being closed). The common
|
||||
* case is a "respond then start follow-up" pattern, e.g. SMF
|
||||
* answering the AMF with HTTP 201 Created and then kicking off
|
||||
* Npcf_SMPolicyControl_Create.
|
||||
*
|
||||
* Two outcomes:
|
||||
*
|
||||
* - Stream still alive: attach to its xact_list so the
|
||||
* transaction is cancelled (and its response timer freed)
|
||||
* if the peer later resets the stream. This is the
|
||||
* #4472/#4473 protection path. Returns OGS_OK.
|
||||
*
|
||||
* - Stream already gone: nothing to attach. Returns
|
||||
* OGS_ERROR so the caller can log with its own context
|
||||
* (NF name, UE/session id, etc.) and decide what to do.
|
||||
* The caller may legitimately ignore the error and let
|
||||
* the transaction proceed; the response (if any) will be
|
||||
* dropped through the existing "STREAM has already been
|
||||
* removed" path in the response handler.
|
||||
*/
|
||||
stream = ogs_sbi_stream_find_by_id(xact->assoc_stream_id);
|
||||
if (!stream) {
|
||||
ogs_error("ogs_sbi_server_attach_xact: originating SBI stream "
|
||||
"already closed "
|
||||
"[xact:%d,assoc_stream_id:%d,service:%s]",
|
||||
(int)xact->id, (int)xact->assoc_stream_id,
|
||||
OpenAPI_service_name_ToString(xact->service_name));
|
||||
return OGS_NOTFOUND;
|
||||
}
|
||||
|
||||
ogs_assert(ogs_sbi_server_actions.xact_attach);
|
||||
ogs_sbi_server_actions.xact_attach(stream, xact);
|
||||
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
void ogs_sbi_server_detach_xact(ogs_sbi_xact_t *xact)
|
||||
{
|
||||
ogs_assert(xact);
|
||||
|
||||
/*
|
||||
* Idempotent. No-op when not attached, including transactions
|
||||
* that never had an inbound stream (NRF discovery, status
|
||||
* notifications) where to_stream_list stays NULL. This is
|
||||
* invoked unconditionally from ogs_sbi_xact_remove(), so
|
||||
* streamless transactions reach here on every cleanup.
|
||||
*/
|
||||
if (!xact->to_stream_list)
|
||||
return;
|
||||
|
||||
ogs_assert(ogs_sbi_server_actions.xact_detach);
|
||||
ogs_sbi_server_actions.xact_detach(xact);
|
||||
}
|
||||
|
||||
static ogs_sbi_server_t *ogs_sbi_server_find_by_interface(
|
||||
ogs_sbi_server_t *current, const char *interface)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ extern "C" {
|
|||
#include <openssl/err.h>
|
||||
|
||||
typedef struct ogs_sbi_stream_s ogs_sbi_stream_t;
|
||||
typedef struct ogs_sbi_xact_s ogs_sbi_xact_t;
|
||||
|
||||
typedef struct ogs_sbi_server_s {
|
||||
ogs_socknode_t node;
|
||||
|
|
@ -71,6 +72,15 @@ typedef struct ogs_sbi_server_actions_s {
|
|||
|
||||
ogs_pool_id_t (*id_from_stream)(ogs_sbi_stream_t *stream);
|
||||
void *(*stream_find_by_id)(ogs_pool_id_t id);
|
||||
|
||||
/*
|
||||
* Per-backend hooks linking an outbound SBI transaction into the
|
||||
* inbound server stream that triggered it. The list head lives
|
||||
* inside the backend-private stream/session struct, so each
|
||||
* backend (HTTP/2 vs MHD) provides its own attach/detach.
|
||||
*/
|
||||
void (*xact_attach)(ogs_sbi_stream_t *stream, ogs_sbi_xact_t *xact);
|
||||
void (*xact_detach)(ogs_sbi_xact_t *xact);
|
||||
} ogs_sbi_server_actions_t;
|
||||
|
||||
void ogs_sbi_server_init(int num_of_session_pool, int num_of_stream_pool);
|
||||
|
|
@ -107,6 +117,36 @@ ogs_sbi_server_t *ogs_sbi_server_from_stream(ogs_sbi_stream_t *stream);
|
|||
ogs_pool_id_t ogs_sbi_id_from_stream(ogs_sbi_stream_t *stream);
|
||||
void *ogs_sbi_stream_find_by_id(ogs_pool_id_t id);
|
||||
|
||||
/*
|
||||
* Helpers used by lib/sbi to link an outbound SBI transaction into
|
||||
* the inbound stream that triggered it. NFs do not call these
|
||||
* directly: ogs_sbi_discover_and_send() attaches when
|
||||
* xact->assoc_stream_id is set, and ogs_sbi_xact_remove() /
|
||||
* stream close detach automatically.
|
||||
*
|
||||
* Both helpers are idempotent and consult xact->to_stream_list as
|
||||
* the ground-truth attachment flag.
|
||||
*
|
||||
* ogs_sbi_server_attach_xact() returns OGS_OK when the transaction
|
||||
* is either freshly attached, already attached, or has no stream
|
||||
* to attach to (streamless self-initiated xact). It returns
|
||||
* OGS_NOTFOUND when the originating stream id is valid but the
|
||||
* stream itself is already gone.
|
||||
*
|
||||
* ogs_sbi_discover_and_send() does not propagate this OGS_NOTFOUND
|
||||
* to the NF caller. Some NF procedures send the original HTTP response
|
||||
* first and then continue with follow-up SBI transactions while still
|
||||
* passing the original stream pointer for convenience. Treating this
|
||||
* as a hard send failure would make those wrappers call their normal
|
||||
* error path, which may try to send another response on a stream that
|
||||
* has already been answered.
|
||||
*
|
||||
* Therefore, this condition is logged here, stream attachment is skipped,
|
||||
* and the upstream send continues.
|
||||
*/
|
||||
int ogs_sbi_server_attach_xact(ogs_sbi_xact_t *xact);
|
||||
void ogs_sbi_server_detach_xact(ogs_sbi_xact_t *xact);
|
||||
|
||||
ogs_sbi_server_t *ogs_sbi_server_first(void);
|
||||
ogs_sbi_server_t *ogs_sbi_server_next(ogs_sbi_server_t *current);
|
||||
ogs_sbi_server_t *ogs_sbi_server_first_by_interface(const char *interface);
|
||||
|
|
|
|||
168
src/amf/amf-sm.c
168
src/amf/amf-sm.c
|
|
@ -64,7 +64,6 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
|
|||
amf_ue_t *amf_ue = NULL;
|
||||
amf_sess_t *sess = NULL;
|
||||
|
||||
ogs_sbi_object_t *sbi_object = NULL;
|
||||
ogs_pool_id_t sbi_object_id = OGS_INVALID_POOL_ID;
|
||||
ogs_sbi_xact_t *sbi_xact = NULL;
|
||||
ogs_pool_id_t sbi_xact_id = OGS_INVALID_POOL_ID;
|
||||
|
|
@ -73,15 +72,12 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
|
|||
ogs_sbi_stream_t *stream = NULL;
|
||||
ogs_pool_id_t stream_id = OGS_INVALID_POOL_ID;
|
||||
ogs_sbi_request_t *sbi_request = NULL;
|
||||
OpenAPI_service_name_e service_name = OpenAPI_service_name_NULL;
|
||||
|
||||
ogs_sbi_nf_instance_t *nf_instance = NULL;
|
||||
ogs_sbi_subscription_data_t *subscription_data = NULL;
|
||||
ogs_sbi_response_t *sbi_response = NULL;
|
||||
ogs_sbi_message_t sbi_message;
|
||||
|
||||
OpenAPI_nf_type_e requester_nf_type = OpenAPI_nf_type_NULL;
|
||||
ogs_sbi_discovery_option_t *discovery_option = NULL;
|
||||
int service_name_id = OpenAPI_service_name_NULL;
|
||||
|
||||
amf_sm_debug(e);
|
||||
|
|
@ -740,24 +736,26 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
@ -802,145 +800,7 @@ void amf_state_operational(ogs_fsm_t *s, amf_event_t *e)
|
|||
break;
|
||||
}
|
||||
|
||||
sbi_object = sbi_xact->sbi_object;
|
||||
ogs_assert(sbi_object);
|
||||
|
||||
sbi_object_id = sbi_xact->sbi_object_id;
|
||||
ogs_assert(sbi_object_id >= OGS_MIN_POOL_ID &&
|
||||
sbi_object_id <= OGS_MAX_POOL_ID);
|
||||
|
||||
if (sbi_xact->user_data) {
|
||||
amf_sbi_xact_ctx_t *ctx = sbi_xact->user_data;
|
||||
|
||||
if (ctx->ran_ue_id != OGS_INVALID_POOL_ID)
|
||||
ran_ue_id = ctx->ran_ue_id;
|
||||
}
|
||||
|
||||
service_name = sbi_xact->service_name;
|
||||
requester_nf_type = sbi_xact->requester_nf_type;
|
||||
discovery_option = sbi_xact->discovery_option;
|
||||
|
||||
ogs_sbi_xact_remove(sbi_xact);
|
||||
|
||||
ogs_assert(sbi_object->type > OGS_SBI_OBJ_BASE &&
|
||||
sbi_object->type < OGS_SBI_OBJ_TOP);
|
||||
|
||||
switch(sbi_object->type) {
|
||||
case OGS_SBI_OBJ_UE_TYPE:
|
||||
amf_ue = amf_ue_find_by_id(sbi_object_id);
|
||||
if (!amf_ue) {
|
||||
ogs_error("UE(amf_ue) Context has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
ogs_error("[%s:%s] Cannot receive SBI message "
|
||||
"[type:%d,value:%d]", amf_ue->supi, amf_ue->suci,
|
||||
amf_ue->nas.message_type,
|
||||
amf_ue->nas.registration.value);
|
||||
|
||||
/*
|
||||
* TS 23.502
|
||||
* 4.2.2.2.2 General Registration
|
||||
* If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request
|
||||
* procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI.
|
||||
*/
|
||||
|
||||
if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST &&
|
||||
amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL &&
|
||||
requester_nf_type == OpenAPI_nf_type_AMF &&
|
||||
discovery_option->guami_presence) {
|
||||
|
||||
amf_ue->amf_ue_context_transfer_state =
|
||||
UE_CONTEXT_INITIAL_STATE;
|
||||
|
||||
if (!(AMF_UE_HAVE_SUCI(amf_ue) ||
|
||||
AMF_UE_HAVE_SUPI(amf_ue))) {
|
||||
CLEAR_AMF_UE_TIMER(amf_ue->t3570);
|
||||
rv = nas_5gs_send_identity_request(amf_ue);
|
||||
ogs_expect(rv == OGS_OK);
|
||||
ogs_assert(rv != OGS_ERROR);
|
||||
|
||||
break;
|
||||
}
|
||||
} else if (amf_ue->nas.message_type ==
|
||||
OGS_NAS_5GS_DEREGISTRATION_REQUEST_FROM_UE) {
|
||||
ogs_error("T3522 expired");
|
||||
break;
|
||||
}
|
||||
|
||||
r = nas_5gs_send_gmm_reject_from_sbi(amf_ue,
|
||||
OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
break;
|
||||
|
||||
case OGS_SBI_OBJ_SESS_TYPE:
|
||||
sess = amf_sess_find_by_id(sbi_object_id);
|
||||
if (!sess) {
|
||||
ogs_error("Session has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
amf_ue = amf_ue_find_by_id(sess->amf_ue_id);
|
||||
if (!amf_ue) {
|
||||
ogs_error("UE(amf_ue) Context has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
ogs_error("[%s:%s:%d:%d] Cannot receive SBI message",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
|
||||
if (ran_ue_id < OGS_MIN_POOL_ID ||
|
||||
ran_ue_id > OGS_MAX_POOL_ID) {
|
||||
ogs_error("No assoc RAN-UE id [%d]", ran_ue_id);
|
||||
break;
|
||||
}
|
||||
|
||||
ran_ue = ran_ue_find_by_id(ran_ue_id);
|
||||
if (!ran_ue) {
|
||||
ogs_error("[%s:%s:%d:%d] "
|
||||
"NG Context has already been removed",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ran_ue->amf_ue_id == OGS_INVALID_POOL_ID) {
|
||||
ogs_error("[%s:%s:%d:%d] "
|
||||
"RAN-UE has already been deassociated",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
break;
|
||||
}
|
||||
|
||||
if (amf_ue->id != ran_ue->amf_ue_id) {
|
||||
ogs_error("[%s:%s:%d:%d] AMF-UE mismatched [%d!=%d]",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti,
|
||||
amf_ue->id, ran_ue->amf_ue_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sess->payload_container_type) {
|
||||
r = nas_5gs_send_back_gsm_message(
|
||||
ran_ue, sess,
|
||||
OGS_5GMM_CAUSE_PAYLOAD_WAS_NOT_FORWARDED,
|
||||
AMF_NAS_BACKOFF_TIME);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
} else {
|
||||
r = ngap_send_error_indication2(
|
||||
ran_ue,
|
||||
NGAP_Cause_PR_transport,
|
||||
NGAP_CauseTransport_transport_resource_unavailable);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ogs_fatal("Not implemented [%s:%d]",
|
||||
OpenAPI_service_name_ToString(service_name),
|
||||
sbi_object->type);
|
||||
ogs_assert_if_reached();
|
||||
}
|
||||
amf_nnrf_handle_failed_amf_discovery(sbi_xact);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1328,14 +1328,20 @@ amf_gnb_t *amf_gnb_find_by_gnb_id(uint32_t gnb_id)
|
|||
return (amf_gnb_t *)ogs_hash_get(self.gnb_id_hash, &gnb_id, sizeof(gnb_id));
|
||||
}
|
||||
|
||||
int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id)
|
||||
int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id, uint8_t gnb_id_length)
|
||||
{
|
||||
ogs_assert(gnb);
|
||||
|
||||
if (gnb_id_length < 22 || gnb_id_length > 32) {
|
||||
ogs_error("Invalid gNB-ID length[%d]", gnb_id_length);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
if (gnb->gnb_id_presence == true)
|
||||
ogs_hash_set(self.gnb_id_hash, &gnb->gnb_id, sizeof(gnb->gnb_id), NULL);
|
||||
|
||||
gnb->gnb_id = gnb_id;
|
||||
gnb->gnb_id_length = gnb_id_length;
|
||||
ogs_hash_set(self.gnb_id_hash, &gnb->gnb_id, sizeof(gnb->gnb_id), gnb);
|
||||
|
||||
gnb->gnb_id_presence = true;
|
||||
|
|
@ -1676,6 +1682,11 @@ amf_ue_t *amf_ue_add(ran_ue_t *ran_ue)
|
|||
ogs_list_init(&amf_ue->sess_list);
|
||||
|
||||
/* Initialization */
|
||||
amf_ue->gnb_ostream_id = ran_ue->gnb_ostream_id;
|
||||
memcpy(&amf_ue->nr_tai, &ran_ue->saved.nr_tai, sizeof(ogs_5gs_tai_t));
|
||||
memcpy(&amf_ue->nr_cgi, &ran_ue->saved.nr_cgi, sizeof(ogs_nr_cgi_t));
|
||||
amf_ue->nr_cgi_gnb_id_length = ran_ue->saved.nr_cgi_gnb_id_length;
|
||||
|
||||
amf_ue->guami = &amf_self()->served_guami[0];
|
||||
amf_ue->nas.access_type = OGS_ACCESS_TYPE_3GPP;
|
||||
amf_ue->nas.amf.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE;
|
||||
|
|
@ -1717,19 +1728,21 @@ void amf_ue_remove(amf_ue_t *amf_ue)
|
|||
amf_sess_remove_all(amf_ue);
|
||||
|
||||
if (amf_ue->current.m_tmsi) {
|
||||
ogs_hash_set(self.guti_ue_hash,
|
||||
&amf_ue->current.guti, sizeof(ogs_nas_5gs_guti_t), NULL);
|
||||
ogs_hash_unset_if_owner(self.guti_ue_hash,
|
||||
&amf_ue->current.guti, sizeof(ogs_nas_5gs_guti_t), amf_ue);
|
||||
ogs_assert(amf_m_tmsi_free(amf_ue->current.m_tmsi) == OGS_OK);
|
||||
}
|
||||
if (amf_ue->next.m_tmsi) {
|
||||
ogs_assert(amf_m_tmsi_free(amf_ue->next.m_tmsi) == OGS_OK);
|
||||
}
|
||||
if (amf_ue->suci) {
|
||||
ogs_hash_set(self.suci_hash, amf_ue->suci, strlen(amf_ue->suci), NULL);
|
||||
ogs_hash_unset_if_owner(self.suci_hash,
|
||||
amf_ue->suci, strlen(amf_ue->suci), amf_ue);
|
||||
ogs_free(amf_ue->suci);
|
||||
}
|
||||
if (amf_ue->supi) {
|
||||
ogs_hash_set(self.supi_hash, amf_ue->supi, strlen(amf_ue->supi), NULL);
|
||||
ogs_hash_unset_if_owner(self.supi_hash,
|
||||
amf_ue->supi, strlen(amf_ue->supi), amf_ue);
|
||||
ogs_free(amf_ue->supi);
|
||||
}
|
||||
|
||||
|
|
@ -1877,8 +1890,17 @@ amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message)
|
|||
|
||||
switch (mobile_identity_header->type) {
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_SUCI:
|
||||
if (mobile_identity->length <
|
||||
(OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1)) {
|
||||
ogs_error("Too short SUCI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mobile_identity_suci =
|
||||
(ogs_nas_5gs_mobile_identity_suci_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_suci);
|
||||
|
||||
if (mobile_identity_suci->h.supi_format !=
|
||||
OGS_NAS_5GS_SUPI_FORMAT_IMSI) {
|
||||
|
|
@ -1914,6 +1936,14 @@ amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message)
|
|||
ogs_free(suci);
|
||||
break;
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_GUTI:
|
||||
if (mobile_identity->length <
|
||||
sizeof(ogs_nas_5gs_mobile_identity_guti_t)) {
|
||||
ogs_error("Too short 5G-GUTI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
(int)sizeof(ogs_nas_5gs_mobile_identity_guti_t));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mobile_identity_guti =
|
||||
(ogs_nas_5gs_mobile_identity_guti_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_guti);
|
||||
|
|
@ -1952,6 +1982,14 @@ amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message)
|
|||
|
||||
switch (mobile_identity_header->type) {
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_S_TMSI:
|
||||
if (mobile_identity->length <
|
||||
sizeof(ogs_nas_5gs_mobile_identity_s_tmsi_t)) {
|
||||
ogs_error("Too short 5G-S-TMSI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
(int)sizeof(ogs_nas_5gs_mobile_identity_s_tmsi_t));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mobile_identity_s_tmsi =
|
||||
(ogs_nas_5gs_mobile_identity_s_tmsi_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_s_tmsi);
|
||||
|
|
@ -1999,6 +2037,14 @@ amf_ue_t *amf_ue_find_by_message(ogs_nas_5gs_message_t *message)
|
|||
|
||||
switch (mobile_identity_header->type) {
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_GUTI:
|
||||
if (mobile_identity->length <
|
||||
sizeof(ogs_nas_5gs_mobile_identity_guti_t)) {
|
||||
ogs_error("Too short 5G-GUTI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
(int)sizeof(ogs_nas_5gs_mobile_identity_guti_t));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mobile_identity_guti =
|
||||
(ogs_nas_5gs_mobile_identity_guti_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_guti);
|
||||
|
|
@ -2122,15 +2168,145 @@ amf_ue_t *amf_ue_find_by_ue_context_id(char *ue_context_id)
|
|||
return amf_ue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release an OLD amf_ue that collides with a freshly registering amf_ue on
|
||||
* one of the UE indexes, moving any session context to the NEW amf_ue.
|
||||
*
|
||||
* Reached from two complementary detection points:
|
||||
*
|
||||
* - amf_ue_set_suci(): the UE re-attached re-using the SAME SUCI, so the
|
||||
* old context is found via suci_hash (delete-path collision).
|
||||
*
|
||||
* - amf_ue_set_supi(): the UE re-attached with a FRESH SUCI (USIM-toggle,
|
||||
* post-deregistration timer, flight-mode, push-driven re-registration,
|
||||
* PoC radio attach loop), so amf_ue_set_suci() could NOT detect it
|
||||
* (suci_hash miss). The old context is only discovered later, once
|
||||
* AKA-Auth reveals the SUPI and supi_hash still points at the stale
|
||||
* slot. Without this the slot leaks until the mobile-reachable timer
|
||||
* expires (create-path collision).
|
||||
*
|
||||
* Unifying the two paths here makes them behave identically, mirroring the
|
||||
* SMF-side unification of the IMSI/SUPI indexes (commit eeeef3d1b).
|
||||
*/
|
||||
static void amf_ue_release_old_context(
|
||||
amf_ue_t *amf_ue, amf_ue_t *old_amf_ue, const char *display)
|
||||
{
|
||||
amf_sess_t *old_sess = NULL;
|
||||
|
||||
ogs_assert(amf_ue);
|
||||
ogs_assert(old_amf_ue);
|
||||
ogs_assert(amf_ue != old_amf_ue);
|
||||
ogs_assert(display);
|
||||
|
||||
ogs_warn("[%s] OLD UE Context Release", display);
|
||||
if (CM_CONNECTED(old_amf_ue)) {
|
||||
ran_ue_t *ran_ue = ran_ue_find_by_id(old_amf_ue->ran_ue_id);
|
||||
ran_ue_t *ran_ue_holding = NULL;
|
||||
|
||||
/*
|
||||
* Keep the old NG context until the new registration is
|
||||
* authenticated. CLEAR_NG_CONTEXT(amf_ue) will then send
|
||||
* UEContextReleaseCommand to the old NG-RAN context.
|
||||
*
|
||||
* Do not use HOLDING_NG_CONTEXT(old_amf_ue) here: that macro
|
||||
* stores the holding id in old_amf_ue, but this function
|
||||
* removes old_amf_ue below after moving the session context
|
||||
* to the new amf_ue.
|
||||
*/
|
||||
ogs_warn("[%s] Holding old NG context", display);
|
||||
if (ran_ue) {
|
||||
int r;
|
||||
|
||||
ran_ue_holding =
|
||||
ran_ue_find_by_id(amf_ue->ran_ue_holding_id);
|
||||
if (ran_ue_holding) {
|
||||
ogs_error("[%s] Holding NG context already exists",
|
||||
display);
|
||||
ogs_error("[%s] RAN_UE_NGAP_ID[%lld] "
|
||||
"AMF_UE_NGAP_ID[%lld]",
|
||||
display,
|
||||
(long long)ran_ue_holding->ran_ue_ngap_id,
|
||||
(long long)ran_ue_holding->amf_ue_ngap_id);
|
||||
r = ngap_send_ran_ue_context_release_command(
|
||||
ran_ue_holding,
|
||||
NGAP_Cause_PR_nas,
|
||||
NGAP_CauseNas_normal_release,
|
||||
NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
} else if (amf_ue->ran_ue_holding_id !=
|
||||
OGS_INVALID_POOL_ID) {
|
||||
ogs_error("[%s] Holding NG context has already "
|
||||
"been removed", display);
|
||||
}
|
||||
amf_ue->ran_ue_holding_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
ran_ue->amf_ue_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
ogs_warn("[%s] RAN_UE_NGAP_ID[%lld] "
|
||||
"AMF_UE_NGAP_ID[%lld]",
|
||||
old_amf_ue->suci,
|
||||
(long long)ran_ue->ran_ue_ngap_id,
|
||||
(long long)ran_ue->amf_ue_ngap_id);
|
||||
|
||||
ran_ue->ue_ctx_rel_action =
|
||||
NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE;
|
||||
ogs_timer_start(ran_ue->t_ng_holding,
|
||||
amf_timer_cfg(AMF_TIMER_NG_HOLDING)->duration);
|
||||
|
||||
amf_ue->ran_ue_holding_id = old_amf_ue->ran_ue_id;
|
||||
old_amf_ue->ran_ue_id = OGS_INVALID_POOL_ID;
|
||||
} else {
|
||||
ogs_error("[%s] RAN-NG Context has already been removed",
|
||||
old_amf_ue->suci);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We should delete the AMF-Session Context in the AMF-UE Context.
|
||||
* Otherwise, all unnecessary SESSIONs remain in SMF/UPF.
|
||||
*
|
||||
* In order to do this, AMF-Session Context should be moved from OLD
|
||||
* AMF-UE Context to NEW AMF-UE Context. The stale sessions are then
|
||||
* released towards the SMF through the NEW context's registration flow
|
||||
* (AMF_RELEASE_SM_CONTEXT_REGISTRATION_ACCEPT), each release-completion
|
||||
* draining one session via AMF_SESS_CLEAR().
|
||||
*
|
||||
* Note that we should NOT send Session-Release to the SMF at this
|
||||
* point, and we must NOT amf_ue_remove() old_amf_ue synchronously
|
||||
* after a release dispatch: another SBI Transaction can cause fatal
|
||||
* errors, and a synchronous removal would free the sessions (and their
|
||||
* in-flight sess->sbi.xact_list entries) out from under the SBI layer.
|
||||
* Moving the session list keeps every session object alive and owned
|
||||
* by exactly one amf_ue at all times.
|
||||
*/
|
||||
|
||||
ogs_assert(ogs_list_empty(&amf_ue->sess_list));
|
||||
|
||||
/* Phase-1 : Change AMF-UE Context in Session Context */
|
||||
ogs_list_for_each(&old_amf_ue->sess_list, old_sess)
|
||||
old_sess->amf_ue_id = amf_ue->id;
|
||||
|
||||
/* Phase-2 : Move Session Context from OLD to NEW AMF-UE Context */
|
||||
memcpy(&amf_ue->sess_list,
|
||||
&old_amf_ue->sess_list, sizeof(amf_ue->sess_list));
|
||||
|
||||
/* Phase-3 : Clear Session Context in OLD AMF-UE Context */
|
||||
memset(&old_amf_ue->sess_list, 0, sizeof(old_amf_ue->sess_list));
|
||||
|
||||
amf_ue_remove(old_amf_ue);
|
||||
}
|
||||
|
||||
void amf_ue_set_suci(amf_ue_t *amf_ue,
|
||||
ogs_nas_5gs_mobile_identity_t *mobile_identity)
|
||||
{
|
||||
amf_ue_t *old_amf_ue = NULL;
|
||||
amf_sess_t *old_sess = NULL;
|
||||
char *suci = NULL;
|
||||
|
||||
ogs_assert(amf_ue);
|
||||
ogs_assert(mobile_identity);
|
||||
ogs_assert(mobile_identity->buffer);
|
||||
ogs_assert(mobile_identity->length >=
|
||||
(OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1));
|
||||
|
||||
suci = ogs_nas_5gs_suci_from_mobile_identity(mobile_identity);
|
||||
ogs_assert(suci);
|
||||
|
|
@ -2141,54 +2317,14 @@ void amf_ue_set_suci(amf_ue_t *amf_ue,
|
|||
/* Check if OLD amf_ue_t is different with NEW amf_ue_t */
|
||||
if (ogs_pool_index(&amf_ue_pool, amf_ue) !=
|
||||
ogs_pool_index(&amf_ue_pool, old_amf_ue)) {
|
||||
ogs_warn("[%s] OLD UE Context Release", suci);
|
||||
if (CM_CONNECTED(old_amf_ue)) {
|
||||
ran_ue_t *ran_ue = ran_ue_find_by_id(old_amf_ue->ran_ue_id);
|
||||
/* Implcit NG release */
|
||||
ogs_warn("[%s] Implicit NG release", suci);
|
||||
if (ran_ue) {
|
||||
ogs_warn("[%s] RAN_UE_NGAP_ID[%lld] "
|
||||
"AMF_UE_NGAP_ID[%lld]",
|
||||
old_amf_ue->suci,
|
||||
(long long)ran_ue->ran_ue_ngap_id,
|
||||
(long long)ran_ue->amf_ue_ngap_id);
|
||||
ran_ue_remove(ran_ue);
|
||||
} else {
|
||||
ogs_error("[%s] RAN-NG Context has already been removed",
|
||||
old_amf_ue->suci);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We should delete the AMF-Session Context in the AMF-UE Context.
|
||||
* Otherwise, all unnecessary SESSIONs remain in SMF/UPF.
|
||||
*
|
||||
* In order to do this, AMF-Session Context should be moved
|
||||
* from OLD AMF-UE Context to NEW AMF-UE Context.
|
||||
*
|
||||
* If needed, The Session deletion process in NEW-AMF UE context will work.
|
||||
*
|
||||
* Note that we should not send Session-Release to the SMF at this point.
|
||||
* Another SBI Transaction can cause fatal errors.
|
||||
*/
|
||||
|
||||
/* Phase-1 : Change AMF-UE Context in Session Context */
|
||||
ogs_list_for_each(&old_amf_ue->sess_list, old_sess)
|
||||
old_sess->amf_ue_id = amf_ue->id;
|
||||
|
||||
/* Phase-2 : Move Session Context from OLD to NEW AMF-UE Context */
|
||||
memcpy(&amf_ue->sess_list,
|
||||
&old_amf_ue->sess_list, sizeof(amf_ue->sess_list));
|
||||
|
||||
/* Phase-3 : Clear Session Context in OLD AMF-UE Context */
|
||||
memset(&old_amf_ue->sess_list, 0, sizeof(old_amf_ue->sess_list));
|
||||
|
||||
amf_ue_remove(old_amf_ue);
|
||||
/* Same-SUCI re-attach: tear the old context down. */
|
||||
amf_ue_release_old_context(amf_ue, old_amf_ue, suci);
|
||||
}
|
||||
}
|
||||
|
||||
if (amf_ue->suci) {
|
||||
ogs_hash_set(self.suci_hash, amf_ue->suci, strlen(amf_ue->suci), NULL);
|
||||
ogs_hash_unset_if_owner(self.suci_hash,
|
||||
amf_ue->suci, strlen(amf_ue->suci), amf_ue);
|
||||
ogs_free(amf_ue->suci);
|
||||
}
|
||||
amf_ue->suci = suci;
|
||||
|
|
@ -2200,8 +2336,27 @@ void amf_ue_set_supi(amf_ue_t *amf_ue, char *supi)
|
|||
ogs_assert(supi);
|
||||
|
||||
if (amf_ue->supi) {
|
||||
ogs_hash_set(self.supi_hash, amf_ue->supi, strlen(amf_ue->supi), NULL);
|
||||
/* Re-assignment: only clear our OWN supi_hash entry if it still
|
||||
* points at us (see ogs_hash_unset_if_owner). */
|
||||
ogs_hash_unset_if_owner(self.supi_hash,
|
||||
amf_ue->supi, strlen(amf_ue->supi), amf_ue);
|
||||
ogs_free(amf_ue->supi);
|
||||
} else {
|
||||
/*
|
||||
* First SUPI assignment for this amf_ue.
|
||||
*
|
||||
* Fresh-SUCI re-attach orphan cleanup (create path): if another
|
||||
* amf_ue already holds this SUPI, it is an orphan from a previous
|
||||
* attach cycle that amf_ue_set_suci() could NOT detect, because the
|
||||
* UE re-attached with a fresh SUCI (suci_hash miss). The delete-path
|
||||
* guard keeps the live entry findable but does not reclaim the stale
|
||||
* slot - without this the pool grows by one slot on every fresh-SUCI
|
||||
* re-registration, until the mobile-reachable timer expires. Tear it
|
||||
* down through the same unified path the same-SUCI re-attach uses.
|
||||
*/
|
||||
amf_ue_t *old_amf_ue = amf_ue_find_by_supi(supi);
|
||||
if (old_amf_ue && old_amf_ue != amf_ue)
|
||||
amf_ue_release_old_context(amf_ue, old_amf_ue, supi);
|
||||
}
|
||||
amf_ue->supi = ogs_strdup(supi);
|
||||
ogs_assert(amf_ue->supi);
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ typedef struct amf_gnb_s {
|
|||
|
||||
bool gnb_id_presence;
|
||||
uint32_t gnb_id; /* gNB_ID received from gNB */
|
||||
uint8_t gnb_id_length; /* gNB-ID BIT STRING length(22..32) */
|
||||
ogs_plmn_id_t plmn_id; /* gNB PLMN-ID received from gNB */
|
||||
ogs_sctp_sock_t sctp; /* SCTP socket */
|
||||
|
||||
|
|
@ -205,6 +206,7 @@ struct ran_ue_s {
|
|||
struct {
|
||||
ogs_5gs_tai_t nr_tai;
|
||||
ogs_nr_cgi_t nr_cgi;
|
||||
uint8_t nr_cgi_gnb_id_length;
|
||||
} saved;
|
||||
|
||||
/* NG Holding timer for removing this context */
|
||||
|
|
@ -363,6 +365,7 @@ struct amf_ue_s {
|
|||
uint16_t gnb_ostream_id;
|
||||
ogs_5gs_tai_t nr_tai;
|
||||
ogs_nr_cgi_t nr_cgi;
|
||||
uint8_t nr_cgi_gnb_id_length;
|
||||
ogs_time_t ue_location_timestamp;
|
||||
ogs_plmn_id_t last_visited_plmn_id;
|
||||
ogs_nas_ue_usage_setting_t ue_usage_setting;
|
||||
|
|
@ -441,6 +444,9 @@ struct amf_ue_s {
|
|||
/* Security Context */
|
||||
ogs_nas_ue_security_capability_t ue_security_capability;
|
||||
ogs_nas_ue_network_capability_t ue_network_capability;
|
||||
|
||||
/* Transient Path Switch state */
|
||||
bool send_ue_security_capability_in_path_switch_ack;
|
||||
#define CHECK_5G_AKA_CONFIRMATION(__aMF) \
|
||||
((__aMF) && ((__aMF)->confirmation_for_5g_aka.resource_uri))
|
||||
#define STORE_5G_AKA_CONFIRMATION(__aMF, __rESOURCE_URI) \
|
||||
|
|
@ -542,6 +548,24 @@ struct amf_ue_s {
|
|||
do { \
|
||||
ran_ue_t *ran_ue_holding = NULL; \
|
||||
\
|
||||
ran_ue_holding = ran_ue_find_by_id((__aMF)->ran_ue_holding_id); \
|
||||
if (ran_ue_holding) { \
|
||||
int r; \
|
||||
ogs_warn("[%s] Holding NG context already exists", \
|
||||
(__aMF)->suci); \
|
||||
ogs_warn("[%s] RAN_UE_NGAP_ID[%lld] AMF_UE_NGAP_ID[%lld]", \
|
||||
(__aMF)->suci, \
|
||||
(long long)ran_ue_holding->ran_ue_ngap_id, \
|
||||
(long long)ran_ue_holding->amf_ue_ngap_id); \
|
||||
r = ngap_send_ran_ue_context_release_command( \
|
||||
ran_ue_holding, \
|
||||
NGAP_Cause_PR_nas, NGAP_CauseNas_normal_release, \
|
||||
NGAP_UE_CTX_REL_NG_CONTEXT_REMOVE, 0); \
|
||||
ogs_expect(r == OGS_OK); \
|
||||
} else if ((__aMF)->ran_ue_holding_id != OGS_INVALID_POOL_ID) { \
|
||||
ogs_warn("[%s] Holding NG context has already been removed", \
|
||||
(__aMF)->suci); \
|
||||
} \
|
||||
(__aMF)->ran_ue_holding_id = OGS_INVALID_POOL_ID; \
|
||||
\
|
||||
ran_ue_holding = ran_ue_find_by_id((__aMF)->ran_ue_id); \
|
||||
|
|
@ -964,7 +988,7 @@ void amf_gnb_remove(amf_gnb_t *gnb);
|
|||
void amf_gnb_remove_all(void);
|
||||
amf_gnb_t *amf_gnb_find_by_addr(ogs_sockaddr_t *addr);
|
||||
amf_gnb_t *amf_gnb_find_by_gnb_id(uint32_t gnb_id);
|
||||
int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id);
|
||||
int amf_gnb_set_gnb_id(amf_gnb_t *gnb, uint32_t gnb_id, uint8_t gnb_id_length);
|
||||
int amf_gnb_sock_type(ogs_sock_t *sock);
|
||||
amf_gnb_t *amf_gnb_find_by_id(ogs_pool_id_t id);
|
||||
|
||||
|
|
|
|||
|
|
@ -131,12 +131,6 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_request(amf_ue_t *amf_ue,
|
|||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
if (mobile_identity->length < OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE) {
|
||||
ogs_error("The length of Mobile Identity(%d) is less then the min(%d)",
|
||||
mobile_identity->length, OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE);
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
mobile_identity_header =
|
||||
(ogs_nas_5gs_mobile_identity_header_t *)mobile_identity->buffer;
|
||||
|
||||
|
|
@ -144,8 +138,17 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_request(amf_ue_t *amf_ue,
|
|||
|
||||
switch (mobile_identity_header->type) {
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_SUCI:
|
||||
if (mobile_identity->length <
|
||||
(OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1)) {
|
||||
ogs_error("Too short SUCI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1);
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
mobile_identity_suci =
|
||||
(ogs_nas_5gs_mobile_identity_suci_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_suci);
|
||||
if (mobile_identity_suci->h.supi_format !=
|
||||
OGS_NAS_5GS_SUPI_FORMAT_IMSI) {
|
||||
ogs_error("Not implemented SUPI format [%d]",
|
||||
|
|
@ -183,13 +186,18 @@ ogs_nas_5gmm_cause_t gmm_handle_registration_request(amf_ue_t *amf_ue,
|
|||
ogs_info("[%s] SUCI", amf_ue->suci);
|
||||
break;
|
||||
case OGS_NAS_5GS_MOBILE_IDENTITY_GUTI:
|
||||
mobile_identity_guti =
|
||||
(ogs_nas_5gs_mobile_identity_guti_t *)mobile_identity->buffer;
|
||||
if (!mobile_identity_guti) {
|
||||
ogs_error("No mobile identity");
|
||||
if (mobile_identity->length <
|
||||
sizeof(ogs_nas_5gs_mobile_identity_guti_t)) {
|
||||
ogs_error("Too short 5G-GUTI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
(int)sizeof(ogs_nas_5gs_mobile_identity_guti_t));
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
mobile_identity_guti =
|
||||
(ogs_nas_5gs_mobile_identity_guti_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_guti);
|
||||
|
||||
ogs_nas_5gs_mobile_identity_guti_to_nas_guti(
|
||||
mobile_identity_guti, &amf_ue->old_guti);
|
||||
|
||||
|
|
@ -1024,18 +1032,21 @@ ogs_nas_5gmm_cause_t gmm_handle_identity_response(amf_ue_t *amf_ue,
|
|||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
if (mobile_identity->length < OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE) {
|
||||
ogs_error("The length of Mobile Identity(%d) is less then the min(%d)",
|
||||
mobile_identity->length, OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE);
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
mobile_identity_header =
|
||||
(ogs_nas_5gs_mobile_identity_header_t *)mobile_identity->buffer;
|
||||
|
||||
if (mobile_identity_header->type == OGS_NAS_5GS_MOBILE_IDENTITY_SUCI) {
|
||||
if (mobile_identity->length <
|
||||
(OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1)) {
|
||||
ogs_error("Too short SUCI Mobile Identity [%d:%d]",
|
||||
mobile_identity->length,
|
||||
OGS_NAS_5GS_MOBILE_IDENTITY_SUCI_MIN_SIZE + 1);
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
mobile_identity_suci =
|
||||
(ogs_nas_5gs_mobile_identity_suci_t *)mobile_identity->buffer;
|
||||
ogs_assert(mobile_identity_suci);
|
||||
if (mobile_identity_suci->h.supi_format !=
|
||||
OGS_NAS_5GS_SUPI_FORMAT_IMSI) {
|
||||
ogs_error("Not implemented SUPI format [%d]",
|
||||
|
|
@ -1755,6 +1766,7 @@ static ogs_nas_5gmm_cause_t gmm_handle_nas_message_container(
|
|||
ogs_nas_message_container_t *nas_message_container)
|
||||
{
|
||||
int gmm_cause;
|
||||
uint8_t expected_message_type = 0;
|
||||
|
||||
ogs_pkbuf_t *nasbuf = NULL;
|
||||
ogs_nas_5gs_message_t nas_message;
|
||||
|
|
@ -1811,6 +1823,55 @@ static ogs_nas_5gmm_cause_t gmm_handle_nas_message_container(
|
|||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* 3GPP TS 24.501 4.4.6 / TS 33.501 6.4.6
|
||||
*
|
||||
* Only an initial NAS message (REGISTRATION REQUEST or SERVICE REQUEST)
|
||||
* may be carried in the NAS message container IE. Reject any other
|
||||
* inner message type with SEMANTICALLY_INCORRECT_MESSAGE rather than
|
||||
* dispatching it.
|
||||
*/
|
||||
switch (nas_message.gmm.h.message_type) {
|
||||
case OGS_NAS_5GS_REGISTRATION_REQUEST:
|
||||
case OGS_NAS_5GS_SERVICE_REQUEST:
|
||||
break;
|
||||
default:
|
||||
ogs_error("[%s] Unexpected NAS message type [%d] in "
|
||||
"NAS message container [outer:%d]",
|
||||
amf_ue->supi,
|
||||
nas_message.gmm.h.message_type, message_type);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
return OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
}
|
||||
|
||||
/*
|
||||
* The protected NAS message must match the procedure being resumed.
|
||||
* In SecurityModeComplete, the procedure is the previously stored
|
||||
* initial NAS message. Otherwise, it is the current outer message.
|
||||
*
|
||||
* Without this check, a SECURITY MODE COMPLETE container can carry an
|
||||
* initial NAS message of a different type than the one that triggered
|
||||
* the security mode control procedure. The AMF would then resume on
|
||||
* the other path, bypassing per-procedure preconditions (e.g.
|
||||
* allowed_nssai is populated only on the registration path) and reach
|
||||
* ngap_ue_build_initial_context_setup_request() with an empty
|
||||
* allowed_nssai, hitting ogs_assert(amf_ue->allowed_nssai.num_of_s_nssai)
|
||||
* and aborting open5gs-amfd (issue #4422).
|
||||
*/
|
||||
expected_message_type =
|
||||
(message_type == OGS_NAS_5GS_SECURITY_MODE_COMPLETE) ?
|
||||
amf_ue->nas.message_type : message_type;
|
||||
if (nas_message.gmm.h.message_type != expected_message_type) {
|
||||
ogs_error("[%s] NAS message type mismatch in NAS message container "
|
||||
"[expected:%d, received:%d, outer:%d]",
|
||||
amf_ue->supi,
|
||||
expected_message_type,
|
||||
nas_message.gmm.h.message_type,
|
||||
message_type);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
return OGS_5GMM_CAUSE_MESSAGE_NOT_COMPATIBLE_WITH_THE_PROTOCOL_STATE;
|
||||
}
|
||||
|
||||
gmm_cause = OGS_5GMM_CAUSE_SEMANTICALLY_INCORRECT_MESSAGE;
|
||||
|
||||
switch (nas_message.gmm.h.message_type) {
|
||||
|
|
|
|||
|
|
@ -496,8 +496,9 @@ int amf_namf_comm_handle_n1_n2_message_transfer(
|
|||
break;
|
||||
|
||||
default:
|
||||
ogs_error("Not implemented ngapIeType[%d]", ngapIeType);
|
||||
ogs_assert_if_reached();
|
||||
ogs_error("Unsupported ngapIeType[%d]", ngapIeType);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
break;
|
||||
}
|
||||
|
||||
response = ogs_sbi_build_response(&sendmsg, status);
|
||||
|
|
@ -903,22 +904,32 @@ static int update_ambr_check_obj(cJSON *obj, ogs_bitrate_t *ambr,
|
|||
static int update_ambr(OpenAPI_change_item_t *item_change,
|
||||
ogs_bitrate_t *ambr, bool *ambr_changed)
|
||||
{
|
||||
cJSON* json = item_change->new_value->json;
|
||||
cJSON *json = NULL;
|
||||
|
||||
if (!item_change->path) {
|
||||
ogs_error("No 'path' field present");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
switch (item_change->op) {
|
||||
case OpenAPI_change_type_REPLACE:
|
||||
case OpenAPI_change_type_ADD:
|
||||
if (!item_change->new_value || !item_change->new_value->json) {
|
||||
ogs_error("No 'new_value' field present");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
json = item_change->new_value->json;
|
||||
|
||||
if (!strcmp(item_change->path, "")) {
|
||||
if (!cJSON_IsObject(json)) {
|
||||
ogs_error("Invalid type of am-data");
|
||||
return OGS_ERROR;
|
||||
}
|
||||
return update_ambr_check_obj(
|
||||
cJSON_GetObjectItemCaseSensitive(json, "subscribedUeAmbr"),
|
||||
ambr, ambr_changed);
|
||||
cJSON_GetObjectItemCaseSensitive(
|
||||
json, "subscribedUeAmbr"),
|
||||
ambr, ambr_changed);
|
||||
} else if (!strcmp(item_change->path, "/subscribedUeAmbr")) {
|
||||
return update_ambr_check_obj(json, ambr, ambr_changed);
|
||||
} else if (!strcmp(item_change->path, "/subscribedUeAmbr/uplink")) {
|
||||
|
|
@ -928,12 +939,12 @@ static int update_ambr(OpenAPI_change_item_t *item_change,
|
|||
}
|
||||
return OGS_OK;
|
||||
|
||||
|
||||
case OpenAPI_change_type__REMOVE:
|
||||
if (!strcmp(item_change->path, "/subscribedUeAmbr")) {
|
||||
update_ambr_check_obj(NULL, ambr, ambr_changed);
|
||||
return update_ambr_check_obj(NULL, ambr, ambr_changed);
|
||||
}
|
||||
return OGS_OK;
|
||||
|
||||
default:
|
||||
return OGS_OK;
|
||||
}
|
||||
|
|
@ -1123,6 +1134,7 @@ int amf_namf_comm_handle_ue_context_transfer_request(
|
|||
OpenAPI_key_amf_t Key_amf;
|
||||
OpenAPI_sc_type_e Tsc_type;
|
||||
|
||||
OpenAPI_ue_context_transfer_req_data_t *UeContextTransferReqData = NULL;
|
||||
OpenAPI_ue_context_transfer_rsp_data_t UeContextTransferRspData;
|
||||
|
||||
ogs_sbi_nf_instance_t *pcf_nf_instance = NULL;
|
||||
|
|
@ -1156,6 +1168,14 @@ int amf_namf_comm_handle_ue_context_transfer_request(
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
UeContextTransferReqData = recvmsg->UeContextTransferReqData;
|
||||
if (!UeContextTransferReqData) {
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
strerror = ogs_msprintf("[%s] No UeContextTransferReqData",
|
||||
amf_ue->supi ? amf_ue->supi : recvmsg->h.resource.component[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (amf_ue->amf_ue_context_transfer_state != UE_CONTEXT_INITIAL_STATE) {
|
||||
ogs_warn("Incorrect UE context transfer state");
|
||||
}
|
||||
|
|
@ -1228,7 +1248,7 @@ int amf_namf_comm_handle_ue_context_transfer_request(
|
|||
MmContextList = amf_namf_comm_encode_ue_mm_context_list(amf_ue);
|
||||
UeContext.mm_context_list = MmContextList;
|
||||
|
||||
if (recvmsg->UeContextTransferReqData->reason ==
|
||||
if (UeContextTransferReqData->reason ==
|
||||
OpenAPI_transfer_reason_MOBI_REG) {
|
||||
SessionContextList =
|
||||
amf_namf_comm_encode_ue_session_context_list(amf_ue);
|
||||
|
|
@ -1415,7 +1435,7 @@ static char *amf_namf_comm_base64_encode_ue_security_capability(
|
|||
/* Security guarantee */
|
||||
num_of_octets = ogs_min(
|
||||
num_of_octets, sizeof(ue_security_capability) + 1);
|
||||
enc_len = ogs_base64_encode_len(num_of_octets);
|
||||
enc_len = ogs_base64_encoded_size(num_of_octets);
|
||||
|
||||
enc = ogs_calloc(1, enc_len);
|
||||
ogs_assert(enc);
|
||||
|
|
@ -1423,7 +1443,8 @@ static char *amf_namf_comm_base64_encode_ue_security_capability(
|
|||
security_octets_string[0] = (uint8_t)
|
||||
OGS_NAS_5GS_REGISTRATION_REQUEST_UE_SECURITY_CAPABILITY_TYPE;
|
||||
memcpy(security_octets_string + 1, &ue_security_capability, num_of_octets);
|
||||
ogs_base64_encode(enc , security_octets_string, num_of_octets);
|
||||
ogs_assert(ogs_base64_encode_from_buffer(enc, enc_len,
|
||||
(const uint8_t *)security_octets_string, num_of_octets) > 0);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
|
@ -1457,7 +1478,7 @@ static char *amf_namf_comm_base64_encode_5gmm_capability(amf_ue_t *amf_ue)
|
|||
num_of_octets = ogs_min(
|
||||
num_of_octets, sizeof(ogs_nas_5gmm_capability_t) + 1);
|
||||
|
||||
enc_len = ogs_base64_encode_len(num_of_octets);
|
||||
enc_len = ogs_base64_encoded_size(num_of_octets);
|
||||
enc = ogs_calloc(1, enc_len);
|
||||
ogs_assert(enc);
|
||||
|
||||
|
|
@ -1466,7 +1487,8 @@ static char *amf_namf_comm_base64_encode_5gmm_capability(amf_ue_t *amf_ue)
|
|||
(uint8_t)OGS_NAS_5GS_REGISTRATION_REQUEST_5GMM_CAPABILITY_TYPE;
|
||||
memcpy(gmm_capability_octets_string + 1,
|
||||
&nas_gmm_capability, num_of_octets);
|
||||
ogs_base64_encode(enc, gmm_capability_octets_string, num_of_octets);
|
||||
ogs_assert(ogs_base64_encode_from_buffer(enc, enc_len,
|
||||
(const uint8_t *)gmm_capability_octets_string, num_of_octets) > 0);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
|
@ -1607,9 +1629,11 @@ static ogs_nas_5gmm_capability_t
|
|||
(char*) ogs_calloc(sizeof(gmm_capability) + 1, sizeof(char));
|
||||
ogs_assert(gmm_capability_octets_string);
|
||||
|
||||
len = ogs_base64_decode(gmm_capability_octets_string, encoded);
|
||||
len = ogs_base64_decode_to_buffer(
|
||||
(uint8_t *)gmm_capability_octets_string,
|
||||
sizeof(gmm_capability) + 1, encoded);
|
||||
|
||||
if (len == 0)
|
||||
if (len <= 0)
|
||||
ogs_error("Gmm capability not decoded");
|
||||
|
||||
ogs_assert(sizeof(gmm_capability_octets_string) <=
|
||||
|
|
@ -1643,7 +1667,9 @@ static ogs_nas_ue_security_capability_t
|
|||
(char*) ogs_calloc(sizeof(ue_security_capability) + 1, sizeof(char));
|
||||
ogs_assert(ue_security_capability_octets_string);
|
||||
|
||||
ogs_base64_decode(ue_security_capability_octets_string, encoded);
|
||||
ogs_base64_decode_to_buffer(
|
||||
(uint8_t *)ue_security_capability_octets_string,
|
||||
sizeof(ue_security_capability) + 1, encoded);
|
||||
|
||||
ogs_assert(sizeof(ue_security_capability_octets_string) <=
|
||||
sizeof(ogs_nas_ue_security_capability_t) + 1);
|
||||
|
|
@ -1680,6 +1706,10 @@ static void amf_namf_comm_decode_ue_mm_context_list(
|
|||
int num_of_nssai_mapping = 0;
|
||||
|
||||
MmContext = node->data;
|
||||
if (!MmContext) {
|
||||
ogs_error("No MmContext");
|
||||
continue;
|
||||
}
|
||||
|
||||
AllowedNssaiList = MmContext->allowed_nssai;
|
||||
NssaiMappingList = MmContext->nssai_mapping_list;
|
||||
|
|
@ -1687,7 +1717,16 @@ static void amf_namf_comm_decode_ue_mm_context_list(
|
|||
OpenAPI_list_for_each(AllowedNssaiList, node1) {
|
||||
OpenAPI_snssai_t *AllowedNssai = node1->data;
|
||||
|
||||
ogs_assert(num_of_s_nssai < OGS_MAX_NUM_OF_SLICE);
|
||||
if (!AllowedNssai) {
|
||||
ogs_error("No allowedNssai");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_of_s_nssai >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("Too many allowedNssai [%d:%d]",
|
||||
num_of_s_nssai + 1, OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
|
||||
amf_ue->allowed_nssai.s_nssai[num_of_s_nssai].sst =
|
||||
(uint8_t)AllowedNssai->sst;
|
||||
|
|
@ -1700,9 +1739,24 @@ static void amf_namf_comm_decode_ue_mm_context_list(
|
|||
|
||||
OpenAPI_list_for_each(NssaiMappingList, node1) {
|
||||
OpenAPI_nssai_mapping_t *NssaiMapping = node1->data;
|
||||
OpenAPI_snssai_t *HSnssai = NssaiMapping->h_snssai;
|
||||
OpenAPI_snssai_t *HSnssai = NULL;
|
||||
|
||||
ogs_assert(num_of_nssai_mapping < OGS_MAX_NUM_OF_SLICE);
|
||||
if (!NssaiMapping) {
|
||||
ogs_error("No nssaiMapping");
|
||||
continue;
|
||||
}
|
||||
|
||||
HSnssai = NssaiMapping->h_snssai;
|
||||
if (!HSnssai) {
|
||||
ogs_error("No hSnssai in nssaiMappingList");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (num_of_nssai_mapping >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("Too many nssaiMappingList [%d:%d]",
|
||||
num_of_nssai_mapping + 1, OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
|
||||
amf_ue->allowed_nssai.s_nssai[num_of_nssai_mapping].
|
||||
mapped_hplmn_sst = HSnssai->sst;
|
||||
|
|
@ -1718,6 +1772,7 @@ static void amf_namf_comm_decode_ue_mm_context_list(
|
|||
MmContext->ue_security_capability);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void amf_namf_comm_decode_ue_session_context_list(
|
||||
|
|
@ -1846,8 +1901,7 @@ int amf_namf_comm_handle_registration_status_update_request(
|
|||
ran_ue_t *ran_ue = NULL;
|
||||
amf_sess_t *sess = NULL;
|
||||
|
||||
OpenAPI_ue_reg_status_update_req_data_t *UeRegStatusUpdateReqData =
|
||||
recvmsg->UeRegStatusUpdateReqData;
|
||||
OpenAPI_ue_reg_status_update_req_data_t *UeRegStatusUpdateReqData = NULL;
|
||||
OpenAPI_ue_reg_status_update_rsp_data_t UeRegStatusUpdateRspData;
|
||||
|
||||
int status = 0;
|
||||
|
|
@ -1869,6 +1923,14 @@ int amf_namf_comm_handle_registration_status_update_request(
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
UeRegStatusUpdateReqData = recvmsg->UeRegStatusUpdateReqData;
|
||||
if (!UeRegStatusUpdateReqData) {
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
strerror = ogs_msprintf("[%s] No UeRegStatusUpdateReqData",
|
||||
amf_ue->supi ? amf_ue->supi : recvmsg->h.resource.component[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (amf_ue->amf_ue_context_transfer_state != UE_CONTEXT_TRANSFER_OLD_AMF_STATE) {
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
strerror = ogs_msprintf("Incorrect UE context transfer state");
|
||||
|
|
@ -1963,7 +2025,8 @@ cleanup:
|
|||
ogs_assert(true == ogs_sbi_server_send_error(stream, status, NULL, strerror, NULL, NULL));
|
||||
ogs_free(strerror);
|
||||
|
||||
amf_ue->amf_ue_context_transfer_state = UE_CONTEXT_INITIAL_STATE;
|
||||
if (amf_ue)
|
||||
amf_ue->amf_ue_context_transfer_state = UE_CONTEXT_INITIAL_STATE;
|
||||
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2295,8 +2295,10 @@ ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue)
|
|||
NGAP_AMF_UE_NGAP_ID_t *AMF_UE_NGAP_ID = NULL;
|
||||
NGAP_RAN_UE_NGAP_ID_t *RAN_UE_NGAP_ID = NULL;
|
||||
NGAP_SecurityContext_t *SecurityContext = NULL;
|
||||
NGAP_UESecurityCapabilities_t *UESecurityCapabilities = NULL;
|
||||
NGAP_PDUSessionResourceSwitchedList_t *PDUSessionResourceSwitchedList;
|
||||
NGAP_AllowedNSSAI_t *AllowedNSSAI = NULL;
|
||||
bool send_ue_security_capability = false;
|
||||
|
||||
ogs_assert(amf_ue);
|
||||
ran_ue = ran_ue_find_by_id(amf_ue->ran_ue_id);
|
||||
|
|
@ -2304,6 +2306,10 @@ ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue)
|
|||
|
||||
ogs_debug("PathSwitchAcknowledge");
|
||||
|
||||
send_ue_security_capability =
|
||||
amf_ue->send_ue_security_capability_in_path_switch_ack;
|
||||
amf_ue->send_ue_security_capability_in_path_switch_ack = false;
|
||||
|
||||
memset(&pdu, 0, sizeof (NGAP_NGAP_PDU_t));
|
||||
pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
|
||||
pdu.choice.successfulOutcome = CALLOC(1, sizeof(NGAP_SuccessfulOutcome_t));
|
||||
|
|
@ -2348,6 +2354,52 @@ ogs_pkbuf_t *ngap_build_path_switch_ack(amf_ue_t *amf_ue)
|
|||
ogs_assert(RAN_UE_NGAP_ID);
|
||||
ie->value.choice.RAN_UE_NGAP_ID = RAN_UE_NGAP_ID;
|
||||
|
||||
if (send_ue_security_capability) {
|
||||
ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t));
|
||||
ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie);
|
||||
|
||||
ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities;
|
||||
ie->criticality = NGAP_Criticality_reject;
|
||||
ie->value.present =
|
||||
NGAP_PathSwitchRequestAcknowledgeIEs__value_PR_UESecurityCapabilities;
|
||||
|
||||
UESecurityCapabilities = CALLOC(1, sizeof(*UESecurityCapabilities));
|
||||
ogs_assert(UESecurityCapabilities);
|
||||
ie->value.choice.UESecurityCapabilities = UESecurityCapabilities;
|
||||
|
||||
UESecurityCapabilities->nRencryptionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->nRencryptionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->
|
||||
nRencryptionAlgorithms.size, sizeof(uint8_t));
|
||||
UESecurityCapabilities->nRencryptionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->nRencryptionAlgorithms.buf[0] =
|
||||
(amf_ue->ue_security_capability.nr_ea << 1);
|
||||
|
||||
UESecurityCapabilities->nRintegrityProtectionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->nRintegrityProtectionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->
|
||||
nRintegrityProtectionAlgorithms.size, sizeof(uint8_t));
|
||||
UESecurityCapabilities->nRintegrityProtectionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->nRintegrityProtectionAlgorithms.buf[0] =
|
||||
(amf_ue->ue_security_capability.nr_ia << 1);
|
||||
|
||||
UESecurityCapabilities->eUTRAencryptionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->eUTRAencryptionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->
|
||||
eUTRAencryptionAlgorithms.size, sizeof(uint8_t));
|
||||
UESecurityCapabilities->eUTRAencryptionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->eUTRAencryptionAlgorithms.buf[0] =
|
||||
(amf_ue->ue_security_capability.eutra_ea << 1);
|
||||
|
||||
UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->
|
||||
eUTRAintegrityProtectionAlgorithms.size, sizeof(uint8_t));
|
||||
UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->eUTRAintegrityProtectionAlgorithms.buf[0] =
|
||||
(amf_ue->ue_security_capability.eutra_ia << 1);
|
||||
}
|
||||
|
||||
ie = CALLOC(1, sizeof(NGAP_PathSwitchRequestAcknowledgeIEs_t));
|
||||
ogs_assert(ie);
|
||||
ASN_SEQUENCE_ADD(PathSwitchRequestAcknowledge->protocolIEs, ie);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -168,8 +168,8 @@ int ngap_send_to_nas(ran_ue_t *ran_ue,
|
|||
ogs_assert(ran_ue);
|
||||
ogs_assert(nasPdu);
|
||||
|
||||
if (nasPdu->size == 0) {
|
||||
ogs_error("Empty NAS PDU");
|
||||
if (nasPdu->size < sizeof(ogs_nas_5gmm_header_t)) {
|
||||
ogs_error("NAS PDU too short [%d]", (int)nasPdu->size);
|
||||
ran_ue_remove(ran_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
@ -192,27 +192,53 @@ int ngap_send_to_nas(ran_ue_t *ran_ue,
|
|||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED:
|
||||
security_header_type.integrity_protected = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 7);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.ciphered = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 7);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_NEW_SECURITY_CONTEXT:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.new_security_context = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 7);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHTERD_WITH_NEW_INTEGRITY_CONTEXT:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.ciphered = 1;
|
||||
security_header_type.new_security_context = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 7);
|
||||
break;
|
||||
default:
|
||||
ogs_error("Not implemented(security header type:0x%x)",
|
||||
sh->security_header_type);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip the 7-octet NAS security header for integrity-protected messages.
|
||||
* ogs_pkbuf_pull() returns NULL when the buffer is shorter than the
|
||||
* requested length, so a protected NAS-PDU that is too short to even
|
||||
* contain its own security header is rejected here instead of being
|
||||
* parsed from a buffer that was never advanced.
|
||||
*/
|
||||
if (security_header_type.integrity_protected) {
|
||||
if (!ogs_pkbuf_pull(nasbuf, sizeof(ogs_nas_5gs_security_header_t))) {
|
||||
ogs_error("NAS PDU too short for security header [%d]",
|
||||
(int)nasPdu->size);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the (post-security-header) NAS message is large enough to
|
||||
* hold the 5GMM header that is dereferenced through 'h' below. This
|
||||
* also covers ciphered messages: the length is unchanged by decoding.
|
||||
*/
|
||||
if (nasbuf->len < sizeof(ogs_nas_5gmm_header_t)) {
|
||||
ogs_error("NAS PDU too short for 5GMM header [%d]", (int)nasbuf->len);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
@ -220,7 +246,8 @@ int ngap_send_to_nas(ran_ue_t *ran_ue,
|
|||
if (amf_ue) {
|
||||
if (nas_5gs_security_decode(amf_ue,
|
||||
security_header_type, nasbuf) != OGS_OK) {
|
||||
ogs_error("nas_eps_security_decode failed()");
|
||||
ogs_error("nas_5gs_security_decode failed()");
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
@ -235,6 +262,16 @@ int ngap_send_to_nas(ran_ue_t *ran_ue,
|
|||
ogs_error("Invalid extended_protocol_discriminator [%d]",
|
||||
h->extended_protocol_discriminator);
|
||||
|
||||
/*
|
||||
* 3GPP TS 38.413 clause 10.4:
|
||||
* Logical error in the NAS PDU carried in INITIAL UE MESSAGE.
|
||||
* Reply with ERROR INDICATION before cleaning up the
|
||||
* (newly-created) ran_ue context.
|
||||
*/
|
||||
ogs_expect(OGS_OK == ngap_send_error_indication2(ran_ue,
|
||||
NGAP_Cause_PR_protocol,
|
||||
NGAP_CauseProtocol_semantic_error));
|
||||
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
|
||||
|
|
@ -245,7 +282,21 @@ int ngap_send_to_nas(ran_ue_t *ran_ue,
|
|||
h->message_type != OGS_NAS_5GS_SERVICE_REQUEST &&
|
||||
h->message_type != OGS_NAS_5GS_DEREGISTRATION_REQUEST_FROM_UE) {
|
||||
|
||||
ogs_error("Invalid 5GMM message type [%d]", h->message_type);
|
||||
ogs_error("Invalid 5GMM message type [%d] in InitialUEMessage",
|
||||
h->message_type);
|
||||
|
||||
/*
|
||||
* 3GPP TS 38.413 clause 10.4 / TS 24.501:
|
||||
* INITIAL UE MESSAGE may carry only REGISTRATION REQUEST,
|
||||
* SERVICE REQUEST, or DEREGISTRATION REQUEST (from UE).
|
||||
* Any other 5GMM message (e.g. IDENTITY RESPONSE) carried
|
||||
* in INITIAL UE MESSAGE is a logical error - the NG-RAN
|
||||
* node must use UPLINK NAS TRANSPORT instead. Reply with
|
||||
* ERROR INDICATION.
|
||||
*/
|
||||
ogs_expect(OGS_OK == ngap_send_error_indication2(ran_ue,
|
||||
NGAP_Cause_PR_protocol,
|
||||
NGAP_CauseProtocol_message_not_compatible_with_receiver_state));
|
||||
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
ran_ue_remove(ran_ue);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,100 @@
|
|||
#include "ngap-path.h"
|
||||
#include "nnrf-handler.h"
|
||||
|
||||
|
||||
static bool amf_nnrf_try_old_amf_discovery_fallback(
|
||||
amf_ue_t *amf_ue, OpenAPI_nf_type_e requester_nf_type,
|
||||
ogs_sbi_discovery_option_t *discovery_option)
|
||||
{
|
||||
int r;
|
||||
|
||||
ogs_assert(amf_ue);
|
||||
|
||||
/*
|
||||
* TS 23.502
|
||||
* 4.2.2.2.2 General Registration
|
||||
* If the SUCI is not provided by the UE nor retrieved from the old AMF,
|
||||
* the Identity Request procedure is initiated by AMF sending an Identity
|
||||
* Request message to the UE requesting the SUCI.
|
||||
*/
|
||||
if (amf_ue->nas.message_type != OGS_NAS_5GS_REGISTRATION_REQUEST ||
|
||||
amf_ue->nas.registration.value !=
|
||||
OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL ||
|
||||
requester_nf_type != OpenAPI_nf_type_AMF ||
|
||||
!discovery_option || !discovery_option->guami_presence)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Returns true only when Identity Request is sent.
|
||||
* The context transfer state is reset even if SUCI/SUPI already exists,
|
||||
* matching the original old-AMF discovery fallback behavior.
|
||||
*/
|
||||
amf_ue->amf_ue_context_transfer_state = UE_CONTEXT_INITIAL_STATE;
|
||||
|
||||
if (AMF_UE_HAVE_SUCI(amf_ue) || AMF_UE_HAVE_SUPI(amf_ue))
|
||||
return false;
|
||||
|
||||
CLEAR_AMF_UE_TIMER(amf_ue->t3570);
|
||||
r = nas_5gs_send_identity_request(amf_ue);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void amf_nnrf_send_session_failure_to_ran(
|
||||
amf_ue_t *amf_ue, amf_sess_t *sess, ogs_pool_id_t ran_ue_id)
|
||||
{
|
||||
int r;
|
||||
ran_ue_t *ran_ue = NULL;
|
||||
|
||||
ogs_assert(amf_ue);
|
||||
ogs_assert(sess);
|
||||
|
||||
if (ran_ue_id < OGS_MIN_POOL_ID || ran_ue_id > OGS_MAX_POOL_ID) {
|
||||
ogs_error("[%s:%s:%d:%d] No assoc RAN-UE id [%d]",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti,
|
||||
(int)ran_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
ran_ue = ran_ue_find_by_id(ran_ue_id);
|
||||
if (!ran_ue) {
|
||||
ogs_error("[%s:%s:%d:%d] NG Context has already been removed",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ran_ue->amf_ue_id == OGS_INVALID_POOL_ID) {
|
||||
ogs_error("[%s:%s:%d:%d] RAN-UE has already been deassociated",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
return;
|
||||
}
|
||||
|
||||
if (amf_ue->id != ran_ue->amf_ue_id) {
|
||||
ogs_error("[%s:%s:%d:%d] AMF-UE mismatched [%d!=%d]",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti,
|
||||
amf_ue->id, ran_ue->amf_ue_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sess->payload_container_type) {
|
||||
r = nas_5gs_send_back_gsm_message(
|
||||
ran_ue, sess,
|
||||
OGS_5GMM_CAUSE_PAYLOAD_WAS_NOT_FORWARDED,
|
||||
AMF_NAS_BACKOFF_TIME);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
} else {
|
||||
r = ngap_send_error_indication2(
|
||||
ran_ue,
|
||||
NGAP_Cause_PR_transport,
|
||||
NGAP_CauseTransport_transport_resource_unavailable);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
void amf_nnrf_handle_nf_discover(
|
||||
ogs_sbi_xact_t *xact, ogs_sbi_message_t *recvmsg)
|
||||
{
|
||||
|
|
@ -36,7 +130,6 @@ void amf_nnrf_handle_nf_discover(
|
|||
ogs_sbi_discovery_option_t *discovery_option = NULL;
|
||||
|
||||
amf_ue_t *amf_ue = NULL;
|
||||
ran_ue_t *ran_ue = NULL;
|
||||
amf_sess_t *sess = NULL;
|
||||
|
||||
ogs_pool_id_t ran_ue_id = OGS_INVALID_POOL_ID;
|
||||
|
|
@ -93,10 +186,11 @@ void amf_nnrf_handle_nf_discover(
|
|||
ran_ue_id = ctx->ran_ue_id;
|
||||
}
|
||||
|
||||
ogs_assert(ran_ue_id >= OGS_MIN_POOL_ID &&
|
||||
ran_ue_id <= OGS_MAX_POOL_ID);
|
||||
ran_ue = ran_ue_find_by_id(ran_ue_id);
|
||||
ogs_assert(ran_ue);
|
||||
/*
|
||||
* ran_ue_id can remain OGS_INVALID_POOL_ID if this
|
||||
* transaction was created without an associated NG context.
|
||||
* Resolve ran_ue only where it is actually needed below.
|
||||
*/
|
||||
} else {
|
||||
ogs_fatal("(NF discover) Not implemented [%s:%d]",
|
||||
OpenAPI_service_name_ToString(service_name), sbi_object->type);
|
||||
|
|
@ -108,38 +202,22 @@ void amf_nnrf_handle_nf_discover(
|
|||
nf_instance = ogs_sbi_nf_instance_find_by_discovery_param(
|
||||
target_nf_type, requester_nf_type, discovery_option);
|
||||
if (!nf_instance) {
|
||||
ogs_sbi_xact_remove(xact);
|
||||
|
||||
switch(sbi_object->type) {
|
||||
case OGS_SBI_OBJ_UE_TYPE:
|
||||
amf_ue = (amf_ue_t *)sbi_object;
|
||||
ogs_assert(amf_ue);
|
||||
ogs_warn("[%s] (NF discover) No [%s]", amf_ue->suci,
|
||||
OpenAPI_service_name_ToString(service_name));
|
||||
|
||||
/*
|
||||
* TS 23.502
|
||||
* 4.2.2.2.2 General Registration
|
||||
* If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request
|
||||
* procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI.
|
||||
*/
|
||||
if (amf_nnrf_try_old_amf_discovery_fallback(
|
||||
amf_ue, requester_nf_type, discovery_option))
|
||||
break;
|
||||
|
||||
if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST &&
|
||||
amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL &&
|
||||
requester_nf_type == OpenAPI_nf_type_AMF &&
|
||||
discovery_option->guami_presence) {
|
||||
|
||||
amf_ue->amf_ue_context_transfer_state =
|
||||
UE_CONTEXT_INITIAL_STATE;
|
||||
|
||||
ogs_sbi_xact_remove(xact);
|
||||
|
||||
if (!(AMF_UE_HAVE_SUCI(amf_ue) ||
|
||||
AMF_UE_HAVE_SUPI(amf_ue))) {
|
||||
CLEAR_AMF_UE_TIMER(amf_ue->t3570);
|
||||
r = nas_5gs_send_identity_request(amf_ue);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
break;
|
||||
}
|
||||
if (amf_ue->nas.message_type ==
|
||||
OGS_NAS_5GS_DEREGISTRATION_REQUEST_FROM_UE) {
|
||||
ogs_error("[%s:%s] Deregistration request failed",
|
||||
amf_ue->supi, amf_ue->suci);
|
||||
break;
|
||||
}
|
||||
|
||||
r = nas_5gs_send_gmm_reject_from_sbi(amf_ue,
|
||||
|
|
@ -148,23 +226,10 @@ void amf_nnrf_handle_nf_discover(
|
|||
ogs_assert(r != OGS_ERROR);
|
||||
break;
|
||||
case OGS_SBI_OBJ_SESS_TYPE:
|
||||
ogs_error("[%d:%d] (NF discover) No [%s]", sess->psi, sess->pti,
|
||||
OpenAPI_service_name_ToString(service_name));
|
||||
if (sess->payload_container_type) {
|
||||
r = nas_5gs_send_back_gsm_message(
|
||||
ran_ue, sess,
|
||||
OGS_5GMM_CAUSE_PAYLOAD_WAS_NOT_FORWARDED,
|
||||
AMF_NAS_BACKOFF_TIME);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
} else {
|
||||
r = ngap_send_error_indication2(
|
||||
ran_ue,
|
||||
NGAP_Cause_PR_transport,
|
||||
NGAP_CauseTransport_transport_resource_unavailable);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
}
|
||||
ogs_warn("[%d:%d] (NF discover) No [%s]", sess->psi, sess->pti,
|
||||
OpenAPI_service_name_ToString(service_name));
|
||||
|
||||
amf_nnrf_send_session_failure_to_ran(amf_ue, sess, ran_ue_id);
|
||||
break;
|
||||
default:
|
||||
ogs_fatal("(NF discover) Not implemented [%s:%d]",
|
||||
|
|
@ -191,7 +256,11 @@ void amf_nnrf_handle_failed_amf_discovery(
|
|||
ogs_sbi_discovery_option_t *discovery_option = NULL;
|
||||
OpenAPI_service_name_e service_name = OpenAPI_service_name_NULL;
|
||||
ogs_sbi_object_t *sbi_object = NULL;
|
||||
ogs_pool_id_t sbi_object_id = OGS_INVALID_POOL_ID;
|
||||
ogs_pool_id_t ran_ue_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
amf_ue_t *amf_ue = NULL;
|
||||
amf_sess_t *sess = NULL;
|
||||
|
||||
ogs_assert(sbi_xact);
|
||||
sbi_object = sbi_xact->sbi_object;
|
||||
|
|
@ -201,42 +270,76 @@ void amf_nnrf_handle_failed_amf_discovery(
|
|||
requester_nf_type = sbi_xact->requester_nf_type;
|
||||
ogs_assert(requester_nf_type);
|
||||
|
||||
sbi_object_id = sbi_xact->sbi_object_id;
|
||||
ogs_assert(sbi_object_id >= OGS_MIN_POOL_ID &&
|
||||
sbi_object_id <= OGS_MAX_POOL_ID);
|
||||
|
||||
discovery_option = sbi_xact->discovery_option;
|
||||
|
||||
if (sbi_xact->user_data) {
|
||||
amf_sbi_xact_ctx_t *ctx = sbi_xact->user_data;
|
||||
|
||||
if (ctx->ran_ue_id != OGS_INVALID_POOL_ID)
|
||||
ran_ue_id = ctx->ran_ue_id;
|
||||
}
|
||||
|
||||
ogs_assert(sbi_object->type > OGS_SBI_OBJ_BASE &&
|
||||
sbi_object->type < OGS_SBI_OBJ_TOP);
|
||||
|
||||
if (sbi_object->type == OGS_SBI_OBJ_UE_TYPE) {
|
||||
ogs_sbi_xact_remove(sbi_xact);
|
||||
|
||||
amf_ue = (amf_ue_t *)sbi_object;
|
||||
ogs_assert(amf_ue);
|
||||
|
||||
/*
|
||||
* TS 23.502
|
||||
* 4.2.2.2.2 General Registration
|
||||
* If the SUCI is not provided by the UE nor retrieved from the old AMF the Identity Request
|
||||
* procedure is initiated by AMF sending an Identity Request message to the UE requesting the SUCI.
|
||||
*/
|
||||
|
||||
if (amf_ue->nas.message_type == OGS_NAS_5GS_REGISTRATION_REQUEST &&
|
||||
amf_ue->nas.registration.value == OGS_NAS_5GS_REGISTRATION_TYPE_INITIAL &&
|
||||
requester_nf_type == OpenAPI_nf_type_AMF &&
|
||||
discovery_option->guami_presence) {
|
||||
|
||||
amf_ue->amf_ue_context_transfer_state =
|
||||
UE_CONTEXT_INITIAL_STATE;
|
||||
|
||||
ogs_sbi_xact_remove(sbi_xact);
|
||||
|
||||
if (!(AMF_UE_HAVE_SUCI(amf_ue) ||
|
||||
AMF_UE_HAVE_SUPI(amf_ue))) {
|
||||
CLEAR_AMF_UE_TIMER(amf_ue->t3570);
|
||||
r = nas_5gs_send_identity_request(amf_ue);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
}
|
||||
switch(sbi_object->type) {
|
||||
case OGS_SBI_OBJ_UE_TYPE:
|
||||
amf_ue = amf_ue_find_by_id(sbi_object_id);
|
||||
if (!amf_ue) {
|
||||
ogs_error("UE(amf_ue) Context has already been removed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
ogs_error("[%s:%s] Cannot receive SBI message "
|
||||
"[type:%d,value:%d]", amf_ue->supi, amf_ue->suci,
|
||||
amf_ue->nas.message_type,
|
||||
amf_ue->nas.registration.value);
|
||||
|
||||
if (amf_nnrf_try_old_amf_discovery_fallback(
|
||||
amf_ue, requester_nf_type, discovery_option))
|
||||
break;
|
||||
|
||||
if (amf_ue->nas.message_type ==
|
||||
OGS_NAS_5GS_DEREGISTRATION_REQUEST_FROM_UE) {
|
||||
ogs_error("[%s:%s] Deregistration request failed",
|
||||
amf_ue->supi, amf_ue->suci);
|
||||
break;
|
||||
}
|
||||
|
||||
r = nas_5gs_send_gmm_reject_from_sbi(amf_ue,
|
||||
OGS_SBI_HTTP_STATUS_GATEWAY_TIMEOUT);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
break;
|
||||
|
||||
case OGS_SBI_OBJ_SESS_TYPE:
|
||||
sess = amf_sess_find_by_id(sbi_object_id);
|
||||
if (!sess) {
|
||||
ogs_error("Session has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
amf_ue = amf_ue_find_by_id(sess->amf_ue_id);
|
||||
if (!amf_ue) {
|
||||
ogs_error("UE(amf_ue) Context has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
ogs_error("[%s:%s:%d:%d] Cannot receive SBI message",
|
||||
amf_ue->supi, amf_ue->suci, sess->psi, sess->pti);
|
||||
|
||||
amf_nnrf_send_session_failure_to_ran(amf_ue, sess, ran_ue_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
ogs_fatal("Not implemented [%s:%d]",
|
||||
OpenAPI_service_name_ToString(service_name), sbi_object->type);
|
||||
ogs_assert_if_reached();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -361,8 +361,21 @@ int amf_nsmf_pdusession_handle_update_sm_context(
|
|||
AMF_UE_CLEAR_N2_TRANSFER(amf_ue, handover_request);
|
||||
}
|
||||
} else {
|
||||
ogs_error("Invalid STATE[%d]", state);
|
||||
ogs_assert_if_reached();
|
||||
/*
|
||||
* [Issue #4408]
|
||||
* avoid abort on SMF /modify success response with
|
||||
* n2SmInfoType=PDU_RES_SETUP_REQ in an unexpected AMF state.
|
||||
*
|
||||
* Replace ogs_assert_if_reached() with ogs_error to log the invalid state
|
||||
* and keep AMF running; logs error for debugging and improves availability.
|
||||
*/
|
||||
ogs_error("[%s:%d] Unexpected N2 SM info type [%s] "
|
||||
"for state [%d]",
|
||||
amf_ue->supi, sess->psi,
|
||||
OpenAPI_n2_sm_info_type_ToString(
|
||||
SmContextUpdatedData->n2_sm_info_type),
|
||||
state);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -636,14 +649,30 @@ int amf_nsmf_pdusession_handle_update_sm_context(
|
|||
}
|
||||
|
||||
} else if (state == AMF_UPDATE_SM_CONTEXT_REGISTRATION_REQUEST) {
|
||||
|
||||
/* Not reached here */
|
||||
ogs_assert_if_reached();
|
||||
/*
|
||||
* [Issue #4409]
|
||||
* avoid abort on SMF /modify success response missing n2SmInfo
|
||||
* during Registration Request activation.
|
||||
*
|
||||
* Replace ogs_assert_if_reached() with ogs_error to log the invalid state
|
||||
* and keep AMF running; logs error for debugging and improves availability.
|
||||
*/
|
||||
ogs_error("[%s:%d] No N2 SM information in registration "
|
||||
"request update", amf_ue->supi, sess->psi);
|
||||
return OGS_ERROR;
|
||||
|
||||
} else if (state == AMF_UPDATE_SM_CONTEXT_SERVICE_REQUEST) {
|
||||
|
||||
/* Not reached here */
|
||||
ogs_assert_if_reached();
|
||||
/*
|
||||
* [Issue #4409]
|
||||
* avoid abort on SMF /modify success response missing n2SmInfo
|
||||
* during Service Request activation.
|
||||
*
|
||||
* Replace ogs_assert_if_reached() with ogs_error to log the invalid state
|
||||
* and keep AMF running; logs error for debugging and improves availability.
|
||||
*/
|
||||
ogs_error("[%s:%d] No N2 SM information in service "
|
||||
"request update", amf_ue->supi, sess->psi);
|
||||
return OGS_ERROR;
|
||||
|
||||
} else if (state == AMF_UPDATE_SM_CONTEXT_N2_RELEASED) {
|
||||
|
||||
|
|
|
|||
|
|
@ -56,20 +56,40 @@ int amf_nudm_sdm_handle_provisioned(
|
|||
OpenAPI_list_for_each(gpsiList, node) {
|
||||
if (node->data) {
|
||||
char *gpsi = NULL;
|
||||
bool is_msisdn = false;
|
||||
|
||||
gpsi = ogs_id_get_type(node->data);
|
||||
if (gpsi) {
|
||||
if (strncmp(gpsi, OGS_ID_GPSI_TYPE_MSISDN,
|
||||
strlen(OGS_ID_GPSI_TYPE_MSISDN)) == 0) {
|
||||
amf_ue->msisdn[amf_ue->num_of_msisdn] =
|
||||
ogs_id_get_value(node->data);
|
||||
ogs_assert(amf_ue->
|
||||
msisdn[amf_ue->num_of_msisdn]);
|
||||
|
||||
amf_ue->num_of_msisdn++;
|
||||
}
|
||||
ogs_free(gpsi);
|
||||
if (!gpsi) {
|
||||
ogs_error("[%s] No type [%s]",
|
||||
amf_ue->supi, (char *)node->data);
|
||||
continue;
|
||||
}
|
||||
|
||||
is_msisdn =
|
||||
(strcmp(gpsi, OGS_ID_GPSI_TYPE_MSISDN) == 0);
|
||||
ogs_free(gpsi);
|
||||
|
||||
if (!is_msisdn) {
|
||||
ogs_error("[%s] Unsupported GPSI type [%s]",
|
||||
amf_ue->supi, (char *)node->data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (amf_ue->num_of_msisdn >= OGS_MAX_NUM_OF_MSISDN) {
|
||||
ogs_error("[%s] Ignore MSISDN; max %d reached",
|
||||
amf_ue->supi, OGS_MAX_NUM_OF_MSISDN);
|
||||
break;
|
||||
}
|
||||
|
||||
amf_ue->msisdn[amf_ue->num_of_msisdn] =
|
||||
ogs_id_get_value(node->data);
|
||||
if (!amf_ue->msisdn[amf_ue->num_of_msisdn]) {
|
||||
ogs_error("[%s] Invalid GPSI [%s]",
|
||||
amf_ue->supi, (char *)node->data);
|
||||
continue;
|
||||
}
|
||||
|
||||
amf_ue->num_of_msisdn++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,15 +116,25 @@ int amf_nudm_sdm_handle_provisioned(
|
|||
if (DefaultSingleNssaiList) {
|
||||
OpenAPI_list_for_each(DefaultSingleNssaiList, node) {
|
||||
OpenAPI_snssai_t *Snssai = node->data;
|
||||
ogs_slice_data_t *slice = NULL;
|
||||
|
||||
ogs_slice_data_t *slice =
|
||||
&amf_ue->slice[amf_ue->num_of_slice];
|
||||
if (Snssai) {
|
||||
slice->s_nssai.sst = Snssai->sst;
|
||||
slice->s_nssai.sd =
|
||||
ogs_s_nssai_sd_from_string(Snssai->sd);
|
||||
if (!Snssai) {
|
||||
ogs_error("[%s] No S-NSSAI", amf_ue->supi);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (amf_ue->num_of_slice >= OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("[%s] Ignore Default S-NSSAI; "
|
||||
"max %d reached",
|
||||
amf_ue->supi, OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
|
||||
slice = &amf_ue->slice[amf_ue->num_of_slice];
|
||||
slice->s_nssai.sst = Snssai->sst;
|
||||
slice->s_nssai.sd =
|
||||
ogs_s_nssai_sd_from_string(Snssai->sd);
|
||||
|
||||
/* DEFAULT S-NSSAI */
|
||||
slice->default_indicator = true;
|
||||
|
||||
|
|
@ -115,15 +145,26 @@ int amf_nudm_sdm_handle_provisioned(
|
|||
if (SingleNssaiList) {
|
||||
OpenAPI_list_for_each(SingleNssaiList, node) {
|
||||
OpenAPI_snssai_t *Snssai = node->data;
|
||||
ogs_slice_data_t *slice = NULL;
|
||||
|
||||
ogs_slice_data_t *slice =
|
||||
&amf_ue->slice[amf_ue->num_of_slice];
|
||||
if (Snssai) {
|
||||
slice->s_nssai.sst = Snssai->sst;
|
||||
slice->s_nssai.sd =
|
||||
ogs_s_nssai_sd_from_string(Snssai->sd);
|
||||
if (!Snssai) {
|
||||
ogs_error("[%s] No S-NSSAI", amf_ue->supi);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (amf_ue->num_of_slice >=
|
||||
OGS_MAX_NUM_OF_SLICE) {
|
||||
ogs_error("[%s] Ignore S-NSSAI; "
|
||||
"max %d reached",
|
||||
amf_ue->supi, OGS_MAX_NUM_OF_SLICE);
|
||||
break;
|
||||
}
|
||||
|
||||
slice = &amf_ue->slice[amf_ue->num_of_slice];
|
||||
slice->s_nssai.sst = Snssai->sst;
|
||||
slice->s_nssai.sd =
|
||||
ogs_s_nssai_sd_from_string(Snssai->sd);
|
||||
|
||||
/* Non default S-NSSAI */
|
||||
slice->default_indicator = false;
|
||||
|
||||
|
|
@ -214,27 +255,47 @@ int amf_nudm_sdm_handle_provisioned(
|
|||
DnnInfoList = SubscribedSnssaiInfo->dnn_infos;
|
||||
if (DnnInfoList) {
|
||||
OpenAPI_list_for_each(DnnInfoList, node2) {
|
||||
ogs_session_t *session = NULL;
|
||||
DnnInfo = node2->data;
|
||||
if (DnnInfo) {
|
||||
ogs_session_t *session =
|
||||
&slice->session
|
||||
[slice->num_of_session];
|
||||
session->name =
|
||||
ogs_strdup(DnnInfo->dnn);
|
||||
ogs_assert(session->name);
|
||||
if (DnnInfo->is_default_dnn_indicator ==
|
||||
true) {
|
||||
session->default_dnn_indicator =
|
||||
DnnInfo->default_dnn_indicator;
|
||||
}
|
||||
slice->num_of_session++;
|
||||
|
||||
if (DnnInfo->is_lbo_roaming_allowed ==
|
||||
true) {
|
||||
session->lbo_roaming_allowed =
|
||||
DnnInfo->lbo_roaming_allowed;
|
||||
}
|
||||
if (!DnnInfo) {
|
||||
ogs_error("No DnnInfo");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!DnnInfo->dnn) {
|
||||
ogs_error("No DnnInfo->dnn");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (slice->num_of_session >=
|
||||
OGS_MAX_NUM_OF_SESS) {
|
||||
ogs_error("[%s] Too many DNNs in "
|
||||
"S-NSSAI[SST:%d SD:0x%x]",
|
||||
amf_ue->supi, s_nssai.sst,
|
||||
s_nssai.sd.v);
|
||||
break;
|
||||
}
|
||||
|
||||
session =
|
||||
&slice->session[slice->num_of_session];
|
||||
|
||||
session->name = ogs_strdup(DnnInfo->dnn);
|
||||
ogs_assert(session->name);
|
||||
|
||||
if (DnnInfo->is_default_dnn_indicator ==
|
||||
true) {
|
||||
session->default_dnn_indicator =
|
||||
DnnInfo->default_dnn_indicator;
|
||||
}
|
||||
|
||||
if (DnnInfo->is_lbo_roaming_allowed ==
|
||||
true) {
|
||||
session->lbo_roaming_allowed =
|
||||
DnnInfo->lbo_roaming_allowed;
|
||||
}
|
||||
|
||||
slice->num_of_session++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -646,6 +646,31 @@ void amf_sbi_send_deactivate_all_sessions(
|
|||
}
|
||||
}
|
||||
|
||||
static void amf_sbi_release_ran_ue_on_gnb_remove(
|
||||
amf_ue_t *amf_ue, ran_ue_t *ran_ue)
|
||||
{
|
||||
ogs_assert(amf_ue);
|
||||
ogs_assert(ran_ue);
|
||||
|
||||
amf_ue_deassociate_ran_ue(amf_ue, ran_ue);
|
||||
ran_ue_remove(ran_ue);
|
||||
|
||||
/*
|
||||
* If the UE has already been registered, keep the AMF UE context
|
||||
* and let the mobile reachable timer handle implicit deregistration.
|
||||
*
|
||||
* If the UE is still in registration procedure, there is no valid
|
||||
* registered NAS context to keep. Since no SMF transaction is created
|
||||
* in this path, remove the AMF UE context immediately.
|
||||
*/
|
||||
if (OGS_FSM_CHECK(&amf_ue->sm, gmm_state_registered)) {
|
||||
ogs_timer_start(amf_ue->mobile_reachable.timer,
|
||||
ogs_time_from_sec(amf_self()->time.t3512.value + 240));
|
||||
} else {
|
||||
amf_ue_remove(amf_ue);
|
||||
}
|
||||
}
|
||||
|
||||
void amf_sbi_send_deactivate_all_ue_in_gnb(amf_gnb_t *gnb, int state)
|
||||
{
|
||||
amf_ue_t *amf_ue = NULL;
|
||||
|
|
@ -665,10 +690,8 @@ void amf_sbi_send_deactivate_all_ue_in_gnb(amf_gnb_t *gnb, int state)
|
|||
|
||||
new_xact_count = amf_sess_xact_count(amf_ue);
|
||||
|
||||
if (old_xact_count == new_xact_count) {
|
||||
amf_ue_deassociate_ran_ue(amf_ue, ran_ue);
|
||||
ran_ue_remove(ran_ue);
|
||||
}
|
||||
if (old_xact_count == new_xact_count)
|
||||
amf_sbi_release_ran_ue_on_gnb_remove(amf_ue, ran_ue);
|
||||
} else {
|
||||
ogs_warn("amf_sbi_send_deactivate_all_ue_in_gnb()");
|
||||
ogs_warn(" RAN_UE_NGAP_ID[%lld] AMF_UE_NGAP_ID[%lld] State[%d]",
|
||||
|
|
|
|||
|
|
@ -131,6 +131,26 @@ static inline uint32_t u24_to_u32(ogs_uint24_t v)
|
|||
return (x & 0xFFFFFFu);
|
||||
}
|
||||
|
||||
static void split_nr_cgi(
|
||||
uint64_t nci, uint8_t gnb_id_length,
|
||||
uint32_t *gnb_id, uint32_t *cell_id)
|
||||
{
|
||||
int cell_id_bits;
|
||||
|
||||
ogs_assert(gnb_id);
|
||||
ogs_assert(cell_id);
|
||||
|
||||
nci &= 0xFFFFFFFFFULL; /* NR Cell Identity is 36 bits */
|
||||
|
||||
if (gnb_id_length < 22 || gnb_id_length > 32)
|
||||
gnb_id_length = 22;
|
||||
|
||||
cell_id_bits = 36 - gnb_id_length;
|
||||
|
||||
*gnb_id = (uint32_t)(nci >> cell_id_bits);
|
||||
*cell_id = (uint32_t)(nci & ((1ULL << cell_id_bits) - 1));
|
||||
}
|
||||
|
||||
/* AM policy feature labels */
|
||||
static const char *am_policy_feature_names[64] = {
|
||||
/*0*/ "AM Policy Association",
|
||||
|
|
@ -222,9 +242,11 @@ static int add_gnb(cJSON *parent, const amf_ue_t *ue)
|
|||
|
||||
ran_ue_t *ran = ran_ue_find_by_id(ue->ran_ue_id);
|
||||
if (ran) {
|
||||
uint64_t nci = ue->nr_cgi.cell_id & 0xFFFFFFFFFULL; /* 36-bit */
|
||||
uint32_t gnb_id = (uint32_t)((nci >> 14) & 0x3FFFFF);
|
||||
uint32_t cell_id = (uint32_t)(nci & 0x3FFF);
|
||||
uint64_t nci = ue->nr_cgi.cell_id;
|
||||
uint32_t gnb_id = 0;
|
||||
uint32_t cell_id = 0;
|
||||
|
||||
split_nr_cgi(nci, ue->nr_cgi_gnb_id_length, &gnb_id, &cell_id);
|
||||
|
||||
cJSON *a = cJSON_CreateNumber((double)ran->amf_ue_ngap_id);
|
||||
cJSON *r = cJSON_CreateNumber((double)ran->ran_ue_ngap_id);
|
||||
|
|
@ -294,9 +316,11 @@ static int add_location(cJSON *parent, const amf_ue_t *ue)
|
|||
if (!p) { cJSON_Delete(cgi); cJSON_Delete(loc); return -1; }
|
||||
cJSON_AddItemToObjectCS(cgi, "plmn", p);
|
||||
|
||||
uint64_t nci = ue->nr_cgi.cell_id & 0xFFFFFFFFFULL; /* 36-bit */
|
||||
uint32_t gnb_id = (uint32_t)((nci >> 14) & 0x3FFFFF);
|
||||
uint32_t cell_id = (uint32_t)(nci & 0x3FFF);
|
||||
uint64_t nci = ue->nr_cgi.cell_id;
|
||||
uint32_t gnb_id = 0;
|
||||
uint32_t cell_id = 0;
|
||||
|
||||
split_nr_cgi(nci, ue->nr_cgi_gnb_id_length, &gnb_id, &cell_id);
|
||||
|
||||
cJSON *n = cJSON_CreateNumber((double)nci);
|
||||
cJSON *g = cJSON_CreateNumber((double)gnb_id);
|
||||
|
|
|
|||
|
|
@ -421,24 +421,26 @@ void ausf_state_operational(ogs_fsm_t *s, ausf_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
|
|||
|
|
@ -175,12 +175,13 @@ void ausf_ue_remove(ausf_ue_t *ausf_ue)
|
|||
ogs_free(ausf_ue->ctx_id);
|
||||
|
||||
ogs_assert(ausf_ue->suci);
|
||||
ogs_hash_set(self.suci_hash, ausf_ue->suci, strlen(ausf_ue->suci), NULL);
|
||||
ogs_hash_unset_if_owner(self.suci_hash,
|
||||
ausf_ue->suci, strlen(ausf_ue->suci), ausf_ue);
|
||||
ogs_free(ausf_ue->suci);
|
||||
|
||||
if (ausf_ue->supi) {
|
||||
ogs_hash_set(self.supi_hash,
|
||||
ausf_ue->supi, strlen(ausf_ue->supi), NULL);
|
||||
ogs_hash_unset_if_owner(self.supi_hash,
|
||||
ausf_ue->supi, strlen(ausf_ue->supi), ausf_ue);
|
||||
ogs_free(ausf_ue->supi);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,8 +170,8 @@ bool ausf_nudm_ueau_handle_get(ausf_ue_t *ausf_ue,
|
|||
|
||||
/* SUPI */
|
||||
if (ausf_ue->supi) {
|
||||
ogs_hash_set(ausf_self()->supi_hash,
|
||||
ausf_ue->supi, strlen(ausf_ue->supi), NULL);
|
||||
ogs_hash_unset_if_owner(ausf_self()->supi_hash,
|
||||
ausf_ue->supi, strlen(ausf_ue->supi), ausf_ue);
|
||||
ogs_free(ausf_ue->supi);
|
||||
}
|
||||
ausf_ue->supi = ogs_strdup(AuthenticationInfoResult->supi);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ void bsf_state_operational(ogs_fsm_t *s, bsf_event_t *e)
|
|||
int service_name_id = OpenAPI_service_name_NULL;
|
||||
|
||||
bsf_sess_t *sess = NULL;
|
||||
bool invalid = false;
|
||||
|
||||
ogs_sbi_stream_t *stream = NULL;
|
||||
ogs_pool_id_t stream_id = OGS_INVALID_POOL_ID;
|
||||
|
|
@ -146,28 +147,84 @@ void bsf_state_operational(ogs_fsm_t *s, bsf_event_t *e)
|
|||
(message.PcfBinding->ipv4_addr ||
|
||||
message.PcfBinding->ipv6_prefix)) {
|
||||
|
||||
if (message.PcfBinding->ipv4_addr)
|
||||
if (message.PcfBinding->ipv4_addr) {
|
||||
sess = bsf_sess_find_by_ipv4addr(
|
||||
message.PcfBinding->ipv4_addr,
|
||||
&invalid);
|
||||
if (invalid) {
|
||||
ogs_error("Invalid IPv4 address [%s]",
|
||||
message.PcfBinding->ipv4_addr);
|
||||
if (!sess && message.PcfBinding->ipv6_prefix)
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "Invalid IPv4 address",
|
||||
message.PcfBinding->ipv4_addr,
|
||||
NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (!sess && message.PcfBinding->ipv6_prefix) {
|
||||
sess = bsf_sess_find_by_ipv6prefix(
|
||||
message.PcfBinding->ipv6_prefix,
|
||||
&invalid);
|
||||
if (invalid) {
|
||||
ogs_error("Invalid IPv6 prefix [%s]",
|
||||
message.PcfBinding->ipv6_prefix);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "Invalid IPv6 prefix",
|
||||
message.PcfBinding->ipv6_prefix,
|
||||
NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sess) {
|
||||
sess = bsf_sess_add_by_ip_address(
|
||||
message.PcfBinding->ipv4_addr,
|
||||
message.PcfBinding->ipv6_prefix);
|
||||
ogs_assert(sess);
|
||||
if (!sess) {
|
||||
ogs_error("Cannot add BSF session");
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "Cannot add BSF session",
|
||||
NULL, NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
CASE(OGS_SBI_HTTP_METHOD_GET)
|
||||
if (message.param.ipv4addr)
|
||||
if (message.param.ipv4addr) {
|
||||
sess = bsf_sess_find_by_ipv4addr(
|
||||
message.param.ipv4addr, &invalid);
|
||||
if (invalid) {
|
||||
ogs_error("Invalid IPv4 address [%s]",
|
||||
message.param.ipv4addr);
|
||||
if (!sess && message.param.ipv6prefix)
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "Invalid IPv4 address",
|
||||
message.param.ipv4addr, NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (!sess && message.param.ipv6prefix) {
|
||||
sess = bsf_sess_find_by_ipv6prefix(
|
||||
message.param.ipv6prefix, &invalid);
|
||||
if (invalid) {
|
||||
ogs_error("Invalid IPv6 prefix [%s]",
|
||||
message.param.ipv6prefix);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "Invalid IPv6 prefix",
|
||||
message.param.ipv6prefix, NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
break;
|
||||
DEFAULT
|
||||
ogs_error("Invalid HTTP method [%s]", message.h.method);
|
||||
|
|
@ -207,6 +264,7 @@ void bsf_state_operational(ogs_fsm_t *s, bsf_event_t *e)
|
|||
}
|
||||
|
||||
/* In lib/sbi/server.c, notify_completed() releases 'request' buffer. */
|
||||
cleanup:
|
||||
ogs_sbi_message_free(&message);
|
||||
break;
|
||||
|
||||
|
|
@ -396,24 +454,26 @@ void bsf_state_operational(ogs_fsm_t *s, bsf_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
|
|||
|
|
@ -148,7 +148,12 @@ bsf_sess_t *bsf_sess_add_by_ip_address(
|
|||
}
|
||||
if (ipv6prefix_string &&
|
||||
bsf_sess_set_ipv6prefix(sess, ipv6prefix_string) == false) {
|
||||
ogs_error("bsf_sess_set_ipv6prefix[%s] failed", ipv4addr_string);
|
||||
ogs_error("bsf_sess_set_ipv6prefix[%s] failed", ipv6prefix_string);
|
||||
if (sess->ipv4addr_string) {
|
||||
ogs_hash_set(self.ipv4addr_hash,
|
||||
&sess->ipv4addr, sizeof(sess->ipv4addr), NULL);
|
||||
ogs_free(sess->ipv4addr_string);
|
||||
}
|
||||
ogs_pool_free(&bsf_sess_pool, sess);
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -273,7 +278,10 @@ bool bsf_sess_set_ipv6prefix(bsf_sess_t *sess, char *ipv6prefix_string)
|
|||
return false;
|
||||
}
|
||||
|
||||
ogs_assert(sess->ipv6prefix.len == OGS_IPV6_128_PREFIX_LEN);
|
||||
if (sess->ipv6prefix.len != OGS_IPV6_128_PREFIX_LEN) {
|
||||
ogs_error("Unsupported IPv6 prefix length [%d]", sess->ipv6prefix.len);
|
||||
return false;
|
||||
}
|
||||
|
||||
sess->ipv6prefix_string = ogs_strdup(ipv6prefix_string);
|
||||
if (!sess->ipv6prefix_string) {
|
||||
|
|
@ -313,23 +321,30 @@ bsf_sess_t *bsf_sess_find_by_binding_id(char *binding_id)
|
|||
return bsf_sess_find(atoll(binding_id));
|
||||
}
|
||||
|
||||
bsf_sess_t *bsf_sess_find_by_ipv4addr(char *ipv4addr_string)
|
||||
bsf_sess_t *bsf_sess_find_by_ipv4addr(
|
||||
char *ipv4addr_string, bool *invalid)
|
||||
{
|
||||
uint32_t ipv4addr;
|
||||
int rv;
|
||||
|
||||
ogs_assert(ipv4addr_string);
|
||||
|
||||
if (invalid)
|
||||
*invalid = false;
|
||||
|
||||
rv = ogs_ipv4_from_string(&ipv4addr, ipv4addr_string);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_error("ogs_ipv4_from_string() failed");
|
||||
if (invalid)
|
||||
*invalid = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ogs_hash_get(self.ipv4addr_hash, &ipv4addr, sizeof(ipv4addr));
|
||||
}
|
||||
|
||||
bsf_sess_t *bsf_sess_find_by_ipv6prefix(char *ipv6prefix_string)
|
||||
bsf_sess_t *bsf_sess_find_by_ipv6prefix(
|
||||
char *ipv6prefix_string, bool *invalid)
|
||||
{
|
||||
int rv;
|
||||
struct {
|
||||
|
|
@ -339,11 +354,24 @@ bsf_sess_t *bsf_sess_find_by_ipv6prefix(char *ipv6prefix_string)
|
|||
|
||||
ogs_assert(ipv6prefix_string);
|
||||
|
||||
if (invalid)
|
||||
*invalid = false;
|
||||
|
||||
rv = ogs_ipv6prefix_from_string(
|
||||
ipv6prefix.addr6, &ipv6prefix.len, ipv6prefix_string);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_error("ogs_ipv6prefix_from_string() failed");
|
||||
if (invalid)
|
||||
*invalid = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ogs_assert(ipv6prefix.len == OGS_IPV6_128_PREFIX_LEN);
|
||||
if (ipv6prefix.len != OGS_IPV6_128_PREFIX_LEN) {
|
||||
ogs_error("Unsupported IPv6 prefix length [%d]", ipv6prefix.len);
|
||||
if (invalid)
|
||||
*invalid = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ogs_hash_get(self.ipv6prefix_hash,
|
||||
&ipv6prefix, (ipv6prefix.len >> 3) + 1);
|
||||
|
|
|
|||
|
|
@ -97,8 +97,10 @@ bool bsf_sess_set_ipv6prefix(bsf_sess_t *sess, char *ipv6prefix);
|
|||
bsf_sess_t *bsf_sess_find(uint32_t index);
|
||||
bsf_sess_t *bsf_sess_find_by_snssai_and_dnn(ogs_s_nssai_t *s_nssai, char *dnn);
|
||||
bsf_sess_t *bsf_sess_find_by_binding_id(char *binding_id);
|
||||
bsf_sess_t *bsf_sess_find_by_ipv4addr(char *ipv4addr_string);
|
||||
bsf_sess_t *bsf_sess_find_by_ipv6prefix(char *ipv6prefix_string);
|
||||
bsf_sess_t *bsf_sess_find_by_ipv4addr(
|
||||
char *ipv4addr_string, bool *invalid);
|
||||
bsf_sess_t *bsf_sess_find_by_ipv6prefix(
|
||||
char *ipv6prefix_string, bool *invalid);
|
||||
int get_sess_load(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
|||
|
|
@ -33,6 +33,30 @@ static struct disp_hdl *hdl_cx_sar = NULL;
|
|||
/* handler for Location-Info-Request cb */
|
||||
static struct disp_hdl *hdl_cx_lir = NULL;
|
||||
|
||||
static bool hss_cx_user_name_to_bcd(
|
||||
const char *user_name, char *bcd, size_t bcd_len)
|
||||
{
|
||||
size_t i, j = 0;
|
||||
|
||||
ogs_assert(bcd);
|
||||
|
||||
if (!user_name || bcd_len == 0)
|
||||
return false;
|
||||
|
||||
bcd[0] = '\0';
|
||||
|
||||
for (i = 0; user_name[i]; i++) {
|
||||
if (user_name[i] >= '0' && user_name[i] <= '9') {
|
||||
if (j + 1 >= bcd_len)
|
||||
return false;
|
||||
bcd[j++] = user_name[i];
|
||||
}
|
||||
}
|
||||
bcd[j] = '\0';
|
||||
|
||||
return ogs_bcd_string_is_valid(bcd, OGS_MAX_IMSI_BCD_LEN);
|
||||
}
|
||||
|
||||
/* Default callback for the application. */
|
||||
static int hss_ogs_diam_cx_fb_cb(struct msg **msg, struct avp *avp,
|
||||
struct session *session, void *opaque, enum disp_action *act)
|
||||
|
|
@ -119,7 +143,13 @@ static int hss_ogs_diam_cx_uar_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ogs_extract_digit_from_string(imsi_or_msisdn_bcd, user_name);
|
||||
if (hss_cx_user_name_to_bcd(user_name,
|
||||
imsi_or_msisdn_bcd, sizeof(imsi_or_msisdn_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name BCD");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get Public-Identity AVP (Mandatory) */
|
||||
ret = fd_msg_search_avp(qry, ogs_diam_cx_public_identity, &avp);
|
||||
|
|
@ -516,6 +546,13 @@ static int hss_ogs_diam_cx_mar_cb(struct msg **msg, struct avp *avp,
|
|||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
if (ogs_imsi_bcd_is_valid(imsi_bcd) == false) {
|
||||
ogs_error("Invalid IMSI for User-Name[%s] Public-Identity[%s]",
|
||||
user_name, public_identity);
|
||||
result_code = OGS_DIAM_CX_ERROR_IDENTITY_NOT_REGISTERED;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the SIP-Auth-Data-Item AVP (Mandatory) */
|
||||
ret = fd_msg_search_avp(
|
||||
|
|
@ -1160,6 +1197,13 @@ static int hss_ogs_diam_cx_sar_cb(struct msg **msg, struct avp *avp,
|
|||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
if (ogs_imsi_bcd_is_valid(imsi_bcd) == false) {
|
||||
ogs_error("Invalid IMSI for User-Name[%s] Public-Identity[%s]",
|
||||
user_name, public_identity);
|
||||
result_code = OGS_DIAM_CX_ERROR_IDENTITY_NOT_REGISTERED;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check if Visited-Network-Identifier */
|
||||
visited_network_identifier =
|
||||
|
|
|
|||
|
|
@ -57,6 +57,25 @@ static void state_cleanup(struct sess_state *sess_data, os0_t sid, void *opaque)
|
|||
ogs_free(sess_data);
|
||||
}
|
||||
|
||||
static bool hss_s6a_user_name_to_imsi_bcd(
|
||||
struct avp_hdr *hdr, char *imsi_bcd, size_t imsi_bcd_len)
|
||||
{
|
||||
ogs_assert(imsi_bcd);
|
||||
|
||||
if (!hdr || !hdr->avp_value || !hdr->avp_value->os.data)
|
||||
return false;
|
||||
|
||||
if (hdr->avp_value->os.len == 0 ||
|
||||
hdr->avp_value->os.len > OGS_MAX_IMSI_BCD_LEN ||
|
||||
hdr->avp_value->os.len >= imsi_bcd_len)
|
||||
return false;
|
||||
|
||||
ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data,
|
||||
hdr->avp_value->os.len + 1);
|
||||
|
||||
return ogs_imsi_bcd_is_valid(imsi_bcd);
|
||||
}
|
||||
|
||||
/* Default callback for the application. */
|
||||
static int hss_ogs_diam_s6a_fb_cb(struct msg **msg, struct avp *avp,
|
||||
struct session *session, void *opaque, enum disp_action *act)
|
||||
|
|
@ -148,8 +167,13 @@ static int hss_ogs_diam_s6a_air_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data,
|
||||
ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1);
|
||||
if (hss_s6a_user_name_to_imsi_bcd(
|
||||
hdr, imsi_bcd, sizeof(imsi_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name IMSI");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get authentication info from database */
|
||||
rv = hss_db_auth_info(imsi_bcd, &auth_info);
|
||||
|
|
@ -938,7 +962,7 @@ static int hss_ogs_diam_s6a_ulr_cb(struct msg **msg, struct avp *avp,
|
|||
struct avp *avpch1 = NULL;
|
||||
union avp_value val;
|
||||
|
||||
char *imsi_bcd = NULL;
|
||||
char imsi_bcd[OGS_MAX_IMSI_BCD_LEN+1];
|
||||
char imeisv_bcd[OGS_MAX_IMEISV_BCD_LEN+1];
|
||||
char *mme_host = NULL;
|
||||
char *mme_realm = NULL;
|
||||
|
|
@ -959,6 +983,7 @@ static int hss_ogs_diam_s6a_ulr_cb(struct msg **msg, struct avp *avp,
|
|||
|
||||
/* Initialize variables */
|
||||
memset(&subscription_data, 0, sizeof(ogs_subscription_data_t));
|
||||
memset(imsi_bcd, 0, sizeof(imsi_bcd));
|
||||
memset(imeisv_bcd, 0, sizeof(imeisv_bcd));
|
||||
memset(&visited_plmn_id, 0, sizeof(visited_plmn_id));
|
||||
|
||||
|
|
@ -990,11 +1015,9 @@ static int hss_ogs_diam_s6a_ulr_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
imsi_bcd = ogs_strndup(
|
||||
(char*)hdr->avp_value->os.data,
|
||||
ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN) + 1);
|
||||
if (!imsi_bcd) {
|
||||
ogs_error("Failed to duplicate IMSI");
|
||||
if (hss_s6a_user_name_to_imsi_bcd(
|
||||
hdr, imsi_bcd, sizeof(imsi_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name IMSI");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
|
|
@ -1144,9 +1167,13 @@ static int hss_ogs_diam_s6a_ulr_cb(struct msg **msg, struct avp *avp,
|
|||
}
|
||||
|
||||
if (strlen(imeisv_bcd) > 0) {
|
||||
rv = hss_db_update_imeisv(imsi_bcd, imeisv_bcd);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_warn("Failed to update IMEISV for IMSI: %s", imsi_bcd);
|
||||
if (ogs_imeisv_bcd_is_valid(imeisv_bcd) == false) {
|
||||
ogs_warn("Ignore invalid IMEISV for IMSI: %s", imsi_bcd);
|
||||
} else {
|
||||
rv = hss_db_update_imeisv(imsi_bcd, imeisv_bcd);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_warn("Failed to update IMEISV for IMSI: %s", imsi_bcd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1446,7 +1473,6 @@ static int hss_ogs_diam_s6a_ulr_cb(struct msg **msg, struct avp *avp,
|
|||
|
||||
/* Cleanup resources */
|
||||
ogs_subscription_data_free(&subscription_data);
|
||||
if (imsi_bcd) ogs_free(imsi_bcd);
|
||||
if (mme_host) ogs_free(mme_host);
|
||||
if (mme_realm) ogs_free(mme_realm);
|
||||
|
||||
|
|
@ -1491,7 +1517,6 @@ out:
|
|||
|
||||
/* Cleanup resources */
|
||||
ogs_subscription_data_free(&subscription_data);
|
||||
if (imsi_bcd) ogs_free(imsi_bcd);
|
||||
if (mme_host) ogs_free(mme_host);
|
||||
if (mme_realm) ogs_free(mme_realm);
|
||||
|
||||
|
|
@ -1565,8 +1590,13 @@ static int hss_ogs_diam_s6a_pur_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ogs_cpystrn(imsi_bcd, (char*)hdr->avp_value->os.data,
|
||||
ogs_min(hdr->avp_value->os.len, OGS_MAX_IMSI_BCD_LEN)+1);
|
||||
if (hss_s6a_user_name_to_imsi_bcd(
|
||||
hdr, imsi_bcd, sizeof(imsi_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name IMSI");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get subscription data from database */
|
||||
rv = hss_db_subscription_data(imsi_bcd, &subscription_data);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,30 @@ static struct disp_hdl *hdl_swx_mar = NULL;
|
|||
/* handler for Server-Assignment-Request cb */
|
||||
static struct disp_hdl *hdl_swx_sar = NULL;
|
||||
|
||||
static bool hss_swx_user_name_to_imsi_bcd(
|
||||
const char *user_name, char *imsi_bcd, size_t imsi_bcd_len)
|
||||
{
|
||||
size_t i, j = 0;
|
||||
|
||||
ogs_assert(imsi_bcd);
|
||||
|
||||
if (!user_name || imsi_bcd_len == 0)
|
||||
return false;
|
||||
|
||||
imsi_bcd[0] = '\0';
|
||||
|
||||
for (i = 0; user_name[i]; i++) {
|
||||
if (user_name[i] >= '0' && user_name[i] <= '9') {
|
||||
if (j + 1 >= imsi_bcd_len)
|
||||
return false;
|
||||
imsi_bcd[j++] = user_name[i];
|
||||
}
|
||||
}
|
||||
imsi_bcd[j] = '\0';
|
||||
|
||||
return ogs_imsi_bcd_is_valid(imsi_bcd);
|
||||
}
|
||||
|
||||
/* Default callback for the application. */
|
||||
static int hss_ogs_diam_swx_fb_cb(struct msg **msg, struct avp *avp,
|
||||
struct session *session, void *opaque, enum disp_action *act)
|
||||
|
|
@ -130,7 +154,13 @@ static int hss_ogs_diam_swx_mar_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ogs_extract_digit_from_string(imsi_bcd, user_name);
|
||||
if (hss_swx_user_name_to_imsi_bcd(
|
||||
user_name, imsi_bcd, sizeof(imsi_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name IMSI");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Get the SIP-Auth-Data-Item AVP (Mandatory) */
|
||||
ret = fd_msg_search_avp(
|
||||
|
|
@ -668,7 +698,13 @@ static int hss_ogs_diam_swx_sar_cb(struct msg **msg, struct avp *avp,
|
|||
goto out;
|
||||
}
|
||||
|
||||
ogs_extract_digit_from_string(imsi_bcd, user_name);
|
||||
if (hss_swx_user_name_to_imsi_bcd(
|
||||
user_name, imsi_bcd, sizeof(imsi_bcd)) == false) {
|
||||
ogs_error("Invalid User-Name IMSI");
|
||||
result_code = OGS_DIAM_INVALID_AVP_VALUE;
|
||||
error_occurred = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* DB : HSS Subscription Data */
|
||||
rv = hss_db_subscription_data(imsi_bcd, &subscription_data);
|
||||
|
|
|
|||
|
|
@ -235,7 +235,11 @@ ogs_pkbuf_t *emm_build_attach_accept(
|
|||
} else {
|
||||
eps_network_feature_support->length = 1;
|
||||
}
|
||||
if (ogs_global_conf()->parameter.no_ims == true) {
|
||||
eps_network_feature_support->ims_voice_over_ps_session_in_s1_mode = 0;
|
||||
} else {
|
||||
eps_network_feature_support->ims_voice_over_ps_session_in_s1_mode = 1;
|
||||
}
|
||||
eps_network_feature_support->extended_protocol_configuration_options = 1;
|
||||
if (mme_self()->emergency.dnn)
|
||||
eps_network_feature_support->emergency_bearer_services_in_s1_mode = 1;
|
||||
|
|
@ -680,8 +684,13 @@ ogs_pkbuf_t *emm_build_tau_accept(mme_ue_t *mme_ue)
|
|||
} else {
|
||||
tau_accept->eps_network_feature_support.length = 1;
|
||||
}
|
||||
tau_accept->eps_network_feature_support.
|
||||
ims_voice_over_ps_session_in_s1_mode = 1;
|
||||
if (ogs_global_conf()->parameter.no_ims == true) {
|
||||
tau_accept->eps_network_feature_support.
|
||||
ims_voice_over_ps_session_in_s1_mode = 0;
|
||||
} else {
|
||||
tau_accept->eps_network_feature_support.
|
||||
ims_voice_over_ps_session_in_s1_mode = 1;
|
||||
}
|
||||
tau_accept->eps_network_feature_support.
|
||||
extended_protocol_configuration_options = 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -743,7 +743,7 @@ static void common_register_state(ogs_fsm_t *s, mme_event_t *e,
|
|||
|
||||
/* check BCS regardless of active_flag */
|
||||
if (mme_ue->tracking_area_update_request_presencemask &
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_TYPE) {
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_PRESENT) {
|
||||
ogs_info("[%s] TAU accept(active_flag=%d, BCS check)",
|
||||
mme_ue->imsi_bcd,
|
||||
mme_ue->nas_eps.update.active_flag);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,19 @@
|
|||
#undef OGS_LOG_DOMAIN
|
||||
#define OGS_LOG_DOMAIN __esm_log_domain
|
||||
|
||||
static bool esm_pdn_connectivity_reject_t3396_allowed(
|
||||
ogs_nas_esm_cause_t esm_cause)
|
||||
{
|
||||
switch (esm_cause) {
|
||||
case OGS_NAS_ESM_CAUSE_MISSING_OR_UNKNOWN_APN:
|
||||
case OGS_NAS_ESM_CAUSE_SERVICE_OPTION_NOT_SUPPORTED:
|
||||
case OGS_NAS_ESM_CAUSE_REQUESTED_SERVICE_OPTION_NOT_SUBSCRIBED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ogs_pkbuf_t *esm_build_pdn_connectivity_reject(
|
||||
mme_sess_t *sess, ogs_nas_esm_cause_t esm_cause, int create_action)
|
||||
{
|
||||
|
|
@ -55,6 +68,21 @@ ogs_pkbuf_t *esm_build_pdn_connectivity_reject(
|
|||
|
||||
pdn_connectivity_reject->esm_cause = esm_cause;
|
||||
|
||||
if (mme_self()->time.t3396.value &&
|
||||
esm_pdn_connectivity_reject_t3396_allowed(esm_cause)) {
|
||||
int rv;
|
||||
ogs_nas_gprs_timer_t gprs_timer;
|
||||
|
||||
rv = ogs_nas_gprs_timer_3_from_sec(
|
||||
&gprs_timer, mme_self()->time.t3396.value);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
|
||||
pdn_connectivity_reject->presencemask |=
|
||||
OGS_NAS_EPS_PDN_CONNECTIVITY_REJECT_BACK_OFF_TIMER_VALUE_PRESENT;
|
||||
pdn_connectivity_reject->back_off_timer_value.length = 1;
|
||||
pdn_connectivity_reject->back_off_timer_value.t = gprs_timer;
|
||||
}
|
||||
|
||||
if (create_action == OGS_GTP_CREATE_IN_ATTACH_REQUEST)
|
||||
return ogs_nas_eps_plain_encode(&message);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -215,6 +215,8 @@ static int mme_context_prepare(void)
|
|||
self.diam_config->cnf_port = DIAMETER_PORT;
|
||||
self.diam_config->cnf_port_tls = DIAMETER_SECURE_PORT;
|
||||
|
||||
/* Set the default T3396 to 12 minutes */
|
||||
self.time.t3396.value = 720;
|
||||
/* Set the default T3412 to 9 minutes for backward compatibility. */
|
||||
self.time.t3412.value = 540;
|
||||
|
||||
|
|
@ -297,12 +299,20 @@ static int mme_context_validation(void)
|
|||
ogs_app()->file);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (ogs_nas_gprs_timer_from_sec(&gprs_timer, self.time.t3402.value) !=
|
||||
if (self.time.t3402.value && /* Optional */
|
||||
ogs_nas_gprs_timer_from_sec(&gprs_timer, self.time.t3402.value) !=
|
||||
OGS_OK) {
|
||||
ogs_error("Not support GPRS Timer [%d]", (int)self.time.t3402.value);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (!self.time.t3412.value) {
|
||||
if (self.time.t3396.value && /* Optional */
|
||||
ogs_nas_gprs_timer_3_from_sec(&gprs_timer, self.time.t3396.value) !=
|
||||
OGS_OK) {
|
||||
ogs_error("Not support GPRS Timer 3 [%d]",
|
||||
(int)self.time.t3396.value);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (!self.time.t3412.value) { /* Mandatory */
|
||||
ogs_error("No mme.time.t3412.value in '%s'",
|
||||
ogs_app()->file);
|
||||
return OGS_ERROR;
|
||||
|
|
@ -312,7 +322,8 @@ static int mme_context_validation(void)
|
|||
ogs_error("Not support GPRS Timer [%d]", (int)self.time.t3412.value);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
if (ogs_nas_gprs_timer_from_sec(&gprs_timer, self.time.t3423.value) !=
|
||||
if (self.time.t3423.value && /* Optional */
|
||||
ogs_nas_gprs_timer_from_sec(&gprs_timer, self.time.t3423.value) !=
|
||||
OGS_OK) {
|
||||
ogs_error("Not support GPRS Timer [%d]", (int)self.time.t3423.value);
|
||||
return OGS_ERROR;
|
||||
|
|
@ -2488,6 +2499,22 @@ int mme_context_parse_config(void)
|
|||
} else
|
||||
ogs_warn("unknown key `%s`", t3402_key);
|
||||
}
|
||||
} else if (!strcmp(time_key, "t3396")) {
|
||||
ogs_yaml_iter_t t3396_iter;
|
||||
ogs_yaml_iter_recurse(&time_iter, &t3396_iter);
|
||||
|
||||
while (ogs_yaml_iter_next(&t3396_iter)) {
|
||||
const char *t3396_key =
|
||||
ogs_yaml_iter_key(&t3396_iter);
|
||||
ogs_assert(t3396_key);
|
||||
|
||||
if (!strcmp(t3396_key, "value")) {
|
||||
const char *v = ogs_yaml_iter_value(&t3396_iter);
|
||||
if (v)
|
||||
self.time.t3396.value = atoll(v);
|
||||
} else
|
||||
ogs_warn("unknown key `%s`", t3396_key);
|
||||
}
|
||||
} else if (!strcmp(time_key, "t3412")) {
|
||||
ogs_yaml_iter_t t3412_iter;
|
||||
ogs_yaml_iter_recurse(&time_iter, &t3412_iter);
|
||||
|
|
@ -3484,8 +3511,8 @@ void mme_ue_confirm_guti(mme_ue_t *mme_ue)
|
|||
if (MME_CURRENT_GUTI_IS_AVAILABLE(mme_ue)) {
|
||||
/* MME has a VALID GUTI
|
||||
* As such, we need to remove previous GUTI in hash table */
|
||||
ogs_hash_set(self.guti_ue_hash,
|
||||
&mme_ue->current.guti, sizeof(ogs_nas_eps_guti_t), NULL);
|
||||
ogs_hash_unset_if_owner(self.guti_ue_hash,
|
||||
&mme_ue->current.guti, sizeof(ogs_nas_eps_guti_t), mme_ue);
|
||||
ogs_assert(mme_m_tmsi_free(mme_ue->current.m_tmsi) == OGS_OK);
|
||||
}
|
||||
|
||||
|
|
@ -3755,12 +3782,12 @@ void mme_ue_remove(mme_ue_t *mme_ue)
|
|||
if (sgw_ue) sgw_ue_remove(sgw_ue);
|
||||
|
||||
if (mme_ue->imsi_len != 0)
|
||||
ogs_hash_set(mme_self()->imsi_ue_hash,
|
||||
mme_ue->imsi, mme_ue->imsi_len, NULL);
|
||||
ogs_hash_unset_if_owner(mme_self()->imsi_ue_hash,
|
||||
mme_ue->imsi, mme_ue->imsi_len, mme_ue);
|
||||
|
||||
if (MME_CURRENT_GUTI_IS_AVAILABLE(mme_ue)) {
|
||||
ogs_hash_set(self.guti_ue_hash,
|
||||
&mme_ue->current.guti, sizeof(ogs_nas_eps_guti_t), NULL);
|
||||
ogs_hash_unset_if_owner(self.guti_ue_hash,
|
||||
&mme_ue->current.guti, sizeof(ogs_nas_eps_guti_t), mme_ue);
|
||||
ogs_assert(mme_m_tmsi_free(mme_ue->current.m_tmsi) == OGS_OK);
|
||||
}
|
||||
|
||||
|
|
@ -4079,8 +4106,8 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd)
|
|||
* in mme_state_operational().
|
||||
*/
|
||||
if (mme_ue->imsi_len != 0)
|
||||
ogs_hash_set(mme_self()->imsi_ue_hash,
|
||||
mme_ue->imsi, mme_ue->imsi_len, NULL);
|
||||
ogs_hash_unset_if_owner(mme_self()->imsi_ue_hash,
|
||||
mme_ue->imsi, mme_ue->imsi_len, mme_ue);
|
||||
|
||||
ogs_cpystrn(mme_ue->imsi_bcd, imsi_bcd, OGS_MAX_IMSI_BCD_LEN+1);
|
||||
ogs_bcd_to_buffer(mme_ue->imsi_bcd, mme_ue->imsi, &mme_ue->imsi_len);
|
||||
|
|
@ -4094,14 +4121,59 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd)
|
|||
ogs_warn("[%s] OLD UE Context Release", mme_ue->imsi_bcd);
|
||||
if (ECM_CONNECTED(old_mme_ue)) {
|
||||
enb_ue_t *enb_ue = enb_ue_find_by_id(old_mme_ue->enb_ue_id);
|
||||
/* Implcit S1 release */
|
||||
ogs_warn("[%s] Implicit S1 release", mme_ue->imsi_bcd);
|
||||
enb_ue_t *enb_ue_holding = NULL;
|
||||
|
||||
/*
|
||||
* Keep the old S1 context until the new attach/TAU procedure
|
||||
* is authenticated. CLEAR_S1_CONTEXT(mme_ue) will then send
|
||||
* UEContextReleaseCommand to the old E-UTRAN context.
|
||||
*
|
||||
* Do not use HOLDING_S1_CONTEXT(old_mme_ue) here: that macro
|
||||
* stores the holding id in old_mme_ue, but this function
|
||||
* removes old_mme_ue below after moving the session context
|
||||
* to the new mme_ue.
|
||||
*/
|
||||
ogs_warn("[%s] Holding old S1 context", mme_ue->imsi_bcd);
|
||||
if (enb_ue) {
|
||||
int r;
|
||||
|
||||
enb_ue_holding =
|
||||
enb_ue_find_by_id(mme_ue->enb_ue_holding_id);
|
||||
if (enb_ue_holding) {
|
||||
ogs_error("[%s] Holding S1 context already exists",
|
||||
mme_ue->imsi_bcd);
|
||||
ogs_error("[%s] ENB_UE_S1AP_ID[%d] "
|
||||
"MME_UE_S1AP_ID[%d]",
|
||||
mme_ue->imsi_bcd,
|
||||
enb_ue_holding->enb_ue_s1ap_id,
|
||||
enb_ue_holding->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue_holding,
|
||||
S1AP_Cause_PR_nas,
|
||||
S1AP_CauseNas_normal_release,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
} else if (mme_ue->enb_ue_holding_id !=
|
||||
OGS_INVALID_POOL_ID) {
|
||||
ogs_error("[%s] Holding S1 context has already "
|
||||
"been removed", mme_ue->imsi_bcd);
|
||||
}
|
||||
mme_ue->enb_ue_holding_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
enb_ue->mme_ue_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
ogs_warn("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
|
||||
old_mme_ue->imsi_bcd,
|
||||
enb_ue->enb_ue_s1ap_id,
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
enb_ue_remove(enb_ue);
|
||||
|
||||
enb_ue->ue_ctx_rel_action =
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE;
|
||||
ogs_timer_start(enb_ue->t_s1_holding,
|
||||
mme_timer_cfg(MME_TIMER_S1_HOLDING)->duration);
|
||||
|
||||
mme_ue->enb_ue_holding_id = old_mme_ue->enb_ue_id;
|
||||
old_mme_ue->enb_ue_id = OGS_INVALID_POOL_ID;
|
||||
} else {
|
||||
ogs_error("[%s] S1 Context has already been removed",
|
||||
old_mme_ue->imsi_bcd);
|
||||
|
|
@ -4136,6 +4208,8 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd)
|
|||
}
|
||||
|
||||
/* Phase-2 : Move Session Context from OLD to NEW MME-UE Context */
|
||||
ogs_assert(ogs_list_empty(&mme_ue->sess_list));
|
||||
|
||||
memcpy(&mme_ue->sess_list,
|
||||
&old_mme_ue->sess_list, sizeof(mme_ue->sess_list));
|
||||
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ typedef struct mme_context_s {
|
|||
struct {
|
||||
struct {
|
||||
ogs_time_t value; /* Timer Value(Seconds) */
|
||||
} t3402, t3412, t3423;
|
||||
} t3402, t3396, t3412, t3423;
|
||||
} time;
|
||||
|
||||
struct {
|
||||
|
|
@ -564,6 +564,10 @@ struct mme_ue_s {
|
|||
|
||||
/* Security Context */
|
||||
ogs_nas_ue_network_capability_t ue_network_capability;
|
||||
|
||||
/* Transient Path Switch state */
|
||||
bool send_ue_security_capability_in_path_switch_ack;
|
||||
|
||||
ogs_nas_ms_network_capability_t ms_network_capability;
|
||||
ogs_nas_ue_additional_security_capability_t
|
||||
ue_additional_security_capability;
|
||||
|
|
@ -663,6 +667,24 @@ struct mme_ue_s {
|
|||
do { \
|
||||
enb_ue_t *enb_ue_holding = NULL; \
|
||||
\
|
||||
enb_ue_holding = enb_ue_find_by_id((__mME)->enb_ue_holding_id); \
|
||||
if (enb_ue_holding) { \
|
||||
int r; \
|
||||
ogs_warn("[%s] Holding S1 context already exists", \
|
||||
(__mME)->imsi_bcd); \
|
||||
ogs_warn("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", \
|
||||
(__mME)->imsi_bcd, \
|
||||
enb_ue_holding->enb_ue_s1ap_id, \
|
||||
enb_ue_holding->mme_ue_s1ap_id); \
|
||||
r = s1ap_send_ue_context_release_command( \
|
||||
enb_ue_holding, \
|
||||
S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, \
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0); \
|
||||
ogs_expect(r == OGS_OK); \
|
||||
} else if ((__mME)->enb_ue_holding_id != OGS_INVALID_POOL_ID) { \
|
||||
ogs_warn("[%s] Holding S1 context has already been removed", \
|
||||
(__mME)->imsi_bcd); \
|
||||
} \
|
||||
(__mME)->enb_ue_holding_id = OGS_INVALID_POOL_ID; \
|
||||
\
|
||||
enb_ue_holding = enb_ue_find_by_id((__mME)->enb_ue_id); \
|
||||
|
|
|
|||
|
|
@ -2186,6 +2186,8 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
|
|||
S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID = NULL;
|
||||
struct S1AP_E_RABToBeSwitchedULList *E_RABToBeSwitchedULList = NULL;
|
||||
S1AP_SecurityContext_t *SecurityContext = NULL;
|
||||
S1AP_UESecurityCapabilities_t *UESecurityCapabilities = NULL;
|
||||
bool send_ue_security_capability = false;
|
||||
|
||||
mme_sess_t *sess = NULL;
|
||||
mme_bearer_t *bearer = NULL;
|
||||
|
|
@ -2197,6 +2199,10 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
|
|||
|
||||
ogs_debug("PathSwitchAcknowledge");
|
||||
|
||||
send_ue_security_capability =
|
||||
mme_ue->send_ue_security_capability_in_path_switch_ack;
|
||||
mme_ue->send_ue_security_capability_in_path_switch_ack = false;
|
||||
|
||||
memset(&pdu, 0, sizeof (S1AP_S1AP_PDU_t));
|
||||
pdu.present = S1AP_S1AP_PDU_PR_successfulOutcome;
|
||||
pdu.choice.successfulOutcome =
|
||||
|
|
@ -2319,6 +2325,38 @@ ogs_pkbuf_t *s1ap_build_path_switch_ack(
|
|||
memcpy(SecurityContext->nextHopParameter.buf,
|
||||
mme_ue->nh, SecurityContext->nextHopParameter.size);
|
||||
|
||||
if (send_ue_security_capability) {
|
||||
ie = CALLOC(1, sizeof(S1AP_PathSwitchRequestAcknowledgeIEs_t));
|
||||
ASN_SEQUENCE_ADD(&PathSwitchRequestAcknowledge->protocolIEs, ie);
|
||||
|
||||
ie->id = S1AP_ProtocolIE_ID_id_UESecurityCapabilities;
|
||||
ie->criticality = S1AP_Criticality_ignore;
|
||||
ie->value.present =
|
||||
S1AP_PathSwitchRequestAcknowledgeIEs__value_PR_UESecurityCapabilities;
|
||||
|
||||
ie->value.choice.UESecurityCapabilities =
|
||||
CALLOC(1, sizeof(*UESecurityCapabilities));
|
||||
UESecurityCapabilities = ie->value.choice.UESecurityCapabilities;
|
||||
ogs_assert(UESecurityCapabilities);
|
||||
|
||||
UESecurityCapabilities->encryptionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->encryptionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->encryptionAlgorithms.size,
|
||||
sizeof(uint8_t));
|
||||
UESecurityCapabilities->encryptionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->encryptionAlgorithms.buf[0] =
|
||||
(mme_ue->ue_network_capability.eea << 1);
|
||||
|
||||
UESecurityCapabilities->integrityProtectionAlgorithms.size = 2;
|
||||
UESecurityCapabilities->integrityProtectionAlgorithms.buf =
|
||||
CALLOC(UESecurityCapabilities->
|
||||
integrityProtectionAlgorithms.size,
|
||||
sizeof(uint8_t));
|
||||
UESecurityCapabilities->integrityProtectionAlgorithms.bits_unused = 0;
|
||||
UESecurityCapabilities->integrityProtectionAlgorithms.buf[0] =
|
||||
(mme_ue->ue_network_capability.eia << 1);
|
||||
}
|
||||
|
||||
return ogs_s1ap_encode(&pdu);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,85 @@ static bool served_tai_is_found(mme_enb_t *enb)
|
|||
return false;
|
||||
}
|
||||
|
||||
static enb_ue_t *s1ap_find_enb_ue_by_message_ue_ids(
|
||||
mme_enb_t *enb, S1AP_MME_UE_S1AP_ID_t *MME_UE_S1AP_ID,
|
||||
S1AP_ENB_UE_S1AP_ID_t *ENB_UE_S1AP_ID)
|
||||
{
|
||||
int r;
|
||||
enb_ue_t *enb_ue = NULL;
|
||||
|
||||
ogs_assert(enb);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%ld]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (enb_ue->enb_id != enb->id) {
|
||||
ogs_error("MME_UE_S1AP_ID[%lld] does not belong to this eNB "
|
||||
"[UE:eNB-ID:%llu, Message:eNB-ID:%llu]",
|
||||
(long long)*MME_UE_S1AP_ID,
|
||||
(unsigned long long)enb_ue->enb_id,
|
||||
(unsigned long long)enb->id);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (enb_ue->enb_ue_s1ap_id != *ENB_UE_S1AP_ID) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID[%lld] for "
|
||||
"MME_UE_S1AP_ID[%lld] [expected:%u]",
|
||||
(long long)*ENB_UE_S1AP_ID,
|
||||
(long long)*MME_UE_S1AP_ID,
|
||||
enb_ue->enb_ue_s1ap_id);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return enb_ue;
|
||||
}
|
||||
|
||||
void s1ap_handle_s1_setup_request(mme_enb_t *enb, ogs_s1ap_message_t *message)
|
||||
{
|
||||
char buf[OGS_ADDRSTRLEN];
|
||||
|
|
@ -518,6 +597,7 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message)
|
|||
S1AP_CellIdentity_t *cell_ID = NULL;
|
||||
|
||||
enb_ue_t *enb_ue = NULL;
|
||||
mme_ue_t *mme_ue_from_stmsi = NULL;
|
||||
|
||||
ogs_assert(enb);
|
||||
ogs_assert(enb->sctp.sock);
|
||||
|
|
@ -590,8 +670,6 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message)
|
|||
if (S_TMSI) {
|
||||
served_gummei_t *served_gummei = &mme_self()->served_gummei[0];
|
||||
ogs_nas_eps_guti_t nas_guti;
|
||||
mme_ue_t *mme_ue = NULL;
|
||||
|
||||
memset(&nas_guti, 0, sizeof(ogs_nas_eps_guti_t));
|
||||
|
||||
/* Use the first configured plmn_id and mme group id */
|
||||
|
|
@ -627,44 +705,17 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message)
|
|||
memcpy(&nas_guti.m_tmsi, S_TMSI->m_TMSI.buf, S_TMSI->m_TMSI.size);
|
||||
nas_guti.m_tmsi = be32toh(nas_guti.m_tmsi);
|
||||
|
||||
mme_ue = mme_ue_find_by_guti(&nas_guti);
|
||||
if (!mme_ue) {
|
||||
mme_ue_from_stmsi = mme_ue_find_by_guti(&nas_guti);
|
||||
if (!mme_ue_from_stmsi) {
|
||||
ogs_info("Unknown UE by S_TMSI[G:%d,C:%d,M_TMSI:0x%x]",
|
||||
nas_guti.mme_gid, nas_guti.mme_code, nas_guti.m_tmsi);
|
||||
} else {
|
||||
ogs_info(" S_TMSI[G:%d,C:%d,M_TMSI:0x%x] IMSI:[%s]",
|
||||
mme_ue->current.guti.mme_gid,
|
||||
mme_ue->current.guti.mme_code,
|
||||
mme_ue->current.guti.m_tmsi,
|
||||
MME_UE_HAVE_IMSI(mme_ue)
|
||||
? mme_ue->imsi_bcd : "Unknown");
|
||||
|
||||
/* If NAS(mme_ue_t) has already been associated with
|
||||
* older S1(enb_ue_t) context */
|
||||
if (ECM_CONNECTED(mme_ue)) {
|
||||
/*
|
||||
* Issue #2786
|
||||
*
|
||||
* In cases where the UE sends an Integrity Un-Protected Attach
|
||||
* Request or Service Request, there is an issue of sending
|
||||
* a UEContextReleaseCommand for the OLD ENB Context.
|
||||
*
|
||||
* For example, if the UE switchs off and power-on after
|
||||
* the first connection, the EPC sends a UEContextReleaseCommand.
|
||||
*
|
||||
* However, since there is no ENB context for this on the eNB,
|
||||
* the eNB does not send a UEContextReleaseComplete,
|
||||
* so the deletion of the ENB Context does not function properly.
|
||||
*
|
||||
* To solve this problem, the EPC has been modified to implicitly
|
||||
* delete the ENB Context instead of sending a UEContextReleaseCommand.
|
||||
*/
|
||||
HOLDING_S1_CONTEXT(mme_ue);
|
||||
}
|
||||
enb_ue_associate_mme_ue(enb_ue, mme_ue);
|
||||
ogs_debug("Mobile Reachable timer stopped for IMSI[%s]",
|
||||
mme_ue->imsi_bcd);
|
||||
CLEAR_MME_UE_TIMER(mme_ue->t_mobile_reachable);
|
||||
mme_ue_from_stmsi->current.guti.mme_gid,
|
||||
mme_ue_from_stmsi->current.guti.mme_code,
|
||||
mme_ue_from_stmsi->current.guti.m_tmsi,
|
||||
MME_UE_HAVE_IMSI(mme_ue_from_stmsi)
|
||||
? mme_ue_from_stmsi->imsi_bcd : "Unknown");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -763,6 +814,36 @@ void s1ap_handle_initial_ue_message(mme_enb_t *enb, ogs_s1ap_message_t *message)
|
|||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mme_ue_from_stmsi) {
|
||||
/* If NAS(mme_ue_t) has already been associated with
|
||||
* older S1(enb_ue_t) context */
|
||||
if (ECM_CONNECTED(mme_ue_from_stmsi)) {
|
||||
/*
|
||||
* Issue #2786
|
||||
*
|
||||
* In cases where the UE sends an Integrity Un-Protected Attach
|
||||
* Request or Service Request, there is an issue of sending
|
||||
* a UEContextReleaseCommand for the OLD ENB Context.
|
||||
*
|
||||
* For example, if the UE switchs off and power-on after
|
||||
* the first connection, the EPC sends a UEContextReleaseCommand.
|
||||
*
|
||||
* However, since there is no ENB context for this on the eNB,
|
||||
* the eNB does not send a UEContextReleaseComplete,
|
||||
* so the deletion of the ENB Context does not function properly.
|
||||
*
|
||||
* To solve this problem, the EPC has been modified to implicitly
|
||||
* delete the ENB Context instead of sending a UEContextReleaseCommand.
|
||||
*/
|
||||
HOLDING_S1_CONTEXT(mme_ue_from_stmsi);
|
||||
}
|
||||
|
||||
enb_ue_associate_mme_ue(enb_ue, mme_ue_from_stmsi);
|
||||
ogs_debug("Mobile Reachable timer stopped for IMSI[%s]",
|
||||
mme_ue_from_stmsi->imsi_bcd);
|
||||
CLEAR_MME_UE_TIMER(mme_ue_from_stmsi->t_mobile_reachable);
|
||||
}
|
||||
memcpy(&enb_ue->saved.e_cgi.plmn_id, pLMNidentity->buf,
|
||||
sizeof(enb_ue->saved.e_cgi.plmn_id));
|
||||
memcpy(&enb_ue->saved.e_cgi.cell_id, cell_ID->buf,
|
||||
|
|
@ -840,48 +921,51 @@ void s1ap_handle_uplink_nas_transport(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
ogs_debug(" ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
|
||||
enb_ue->enb_ue_s1ap_id, enb_ue->mme_ue_s1ap_id);
|
||||
|
||||
/*
|
||||
* Stale S1 context. Dominant trigger: HOLDING_S1_CONTEXT clears
|
||||
* enb_ue->mme_ue_id when a newer S1-connection arrives for the same
|
||||
* NAS UE (mme_ue); the held enb_ue stays in the pool until
|
||||
* CLEAR_S1_CONTEXT sends UEContextReleaseCommand after authentication
|
||||
* completes on the new connection (per TS 23.401 §5.3.4.4 / mirrors
|
||||
* TS 23.502 §4.2.6 for 5G: "after successfully authenticating the
|
||||
* UE, the [MME/AMF] releases the old NAS signalling connection").
|
||||
*
|
||||
* If the eNB sends a stale UplinkNASTransport on the held IDs during
|
||||
* that window, send UEContextReleaseCommand here so the eNB can
|
||||
* promptly release its end of the stale RAN context. We do NOT
|
||||
* forward the held NAS message to the NAS layer - the UE has
|
||||
* abandoned this radio link, and replaying an old NAS message
|
||||
* through the EMM state machine could re-associate the held enb_ue
|
||||
* with the live mme_ue and cause responses to be sent on the wrong
|
||||
* S1 connection. The t_s1_holding timer guarantees local cleanup
|
||||
* even if the eNB does not respond.
|
||||
*
|
||||
* Mirrors the AMF NGAP handling in ngap_handle_uplink_nas_transport().
|
||||
*/
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("UplinkNASTransport on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NAS_PDU) {
|
||||
ogs_error("No NAS_PDU");
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
|
|
@ -1006,15 +1090,10 @@ void s1ap_handle_uplink_nas_transport(
|
|||
enb_ue->enb_ue_s1ap_id, enb_ue->mme_ue_s1ap_id,
|
||||
enb_ue->saved.tai.tac, enb_ue->saved.e_cgi.cell_id);
|
||||
|
||||
/* Copy Stream-No/TAI/ECGI from enb_ue */
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (mme_ue) {
|
||||
memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t));
|
||||
memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t));
|
||||
mme_ue->ue_location_timestamp = ogs_time_now();
|
||||
} else {
|
||||
ogs_error("No UE Context in UplinkNASTransport");
|
||||
}
|
||||
/* Copy Stream-No/TAI/ECGI from enb_ue (mme_ue checked at function entry) */
|
||||
memcpy(&mme_ue->tai, &enb_ue->saved.tai, sizeof(ogs_eps_tai_t));
|
||||
memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t));
|
||||
mme_ue->ue_location_timestamp = ogs_time_now();
|
||||
|
||||
ogs_expect(OGS_OK == s1ap_send_to_nas(
|
||||
enb_ue, S1AP_ProcedureCode_id_uplinkNASTransport, NAS_PDU));
|
||||
|
|
@ -1024,7 +1103,7 @@ void s1ap_handle_ue_capability_info_indication(
|
|||
mme_enb_t *enb, ogs_s1ap_message_t *message)
|
||||
{
|
||||
char buf[OGS_ADDRSTRLEN];
|
||||
int i, r;
|
||||
int i;
|
||||
|
||||
S1AP_InitiatingMessage_t *initiatingMessage = NULL;
|
||||
S1AP_UECapabilityInfoIndication_t *UECapabilityInfoIndication = NULL;
|
||||
|
|
@ -1069,42 +1148,10 @@ void s1ap_handle_ue_capability_info_indication(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1168,42 +1215,10 @@ void s1ap_handle_initial_context_setup_response(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1212,7 +1227,20 @@ void s1ap_handle_initial_context_setup_response(
|
|||
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("No UE(mme-ue) context");
|
||||
/*
|
||||
* Stale S1 context - see s1ap_handle_uplink_nas_transport()
|
||||
* for the rationale. Send UEContextReleaseCommand so the eNB
|
||||
* cleans up its end of the stale RAN context.
|
||||
*/
|
||||
ogs_error("InitialContextSetupResponse on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1375,42 +1403,10 @@ void s1ap_handle_initial_context_setup_failure(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1502,42 +1498,10 @@ void s1ap_handle_ue_context_modification_response(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1546,7 +1510,20 @@ void s1ap_handle_ue_context_modification_response(
|
|||
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("No UE(mme-ue) context");
|
||||
/*
|
||||
* Stale S1 context - see s1ap_handle_uplink_nas_transport()
|
||||
* for the rationale. Send UEContextReleaseCommand so the eNB
|
||||
* cleans up its end of the stale RAN context.
|
||||
*/
|
||||
ogs_error("UEContextModificationResponse on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1602,42 +1579,10 @@ void s1ap_handle_ue_context_modification_failure(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1658,7 +1603,20 @@ void s1ap_handle_ue_context_modification_failure(
|
|||
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("No UE(mme-ue) context");
|
||||
/*
|
||||
* Stale S1 context - see s1ap_handle_uplink_nas_transport()
|
||||
* for the rationale. Send UEContextReleaseCommand so the eNB
|
||||
* cleans up its end of the stale RAN context.
|
||||
*/
|
||||
ogs_error("UEContextModificationFailure on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
CLEAR_SERVICE_INDICATOR(mme_ue);
|
||||
|
|
@ -1729,42 +1687,10 @@ void s1ap_handle_e_rab_setup_response(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_error("No eNB UE Context : MME_UE_S1AP_ID[%lld]",
|
||||
(long long)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1773,7 +1699,20 @@ void s1ap_handle_e_rab_setup_response(
|
|||
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("No UE(mme-ue) context");
|
||||
/*
|
||||
* Stale S1 context - see s1ap_handle_uplink_nas_transport()
|
||||
* for the rationale. Send UEContextReleaseCommand so the eNB
|
||||
* cleans up its end of the stale RAN context.
|
||||
*/
|
||||
ogs_error("E-RABSetupResponse on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2437,42 +2376,10 @@ void s1ap_handle_e_rab_modification_indication(
|
|||
ogs_debug(" IP[%s] ENB_ID[%d]",
|
||||
OGS_ADDR(enb->sctp.addr, buf), enb->enb_id);
|
||||
|
||||
if (!ENB_UE_S1AP_ID) {
|
||||
ogs_error("No ENB_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (*ENB_UE_S1AP_ID > 0x00ffffff) {
|
||||
ogs_error("Invalid ENB_UE_S1AP_ID [%lx]", *ENB_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb, NULL, NULL,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MME_UE_S1AP_ID) {
|
||||
ogs_error("No MME_UE_S1AP_ID");
|
||||
r = s1ap_send_error_indication(enb, NULL, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
enb_ue = enb_ue_find_by_mme_ue_s1ap_id(*MME_UE_S1AP_ID);
|
||||
enb_ue = s1ap_find_enb_ue_by_message_ue_ids(
|
||||
enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID);
|
||||
if (!enb_ue) {
|
||||
ogs_warn("No ENB UE Context : MME_UE_S1AP_ID[%d]",
|
||||
(int)*MME_UE_S1AP_ID);
|
||||
r = s1ap_send_error_indication(enb,
|
||||
MME_UE_S1AP_ID, NULL,
|
||||
S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unknown_mme_ue_s1ap_id);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
ogs_error("%s: Failed to find eNB UE by S1AP UE IDs", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2490,7 +2397,20 @@ void s1ap_handle_e_rab_modification_indication(
|
|||
|
||||
mme_ue = mme_ue_find_by_id(enb_ue->mme_ue_id);
|
||||
if (!mme_ue) {
|
||||
ogs_error("No UE(mme-ue) context");
|
||||
/*
|
||||
* Stale S1 context - see s1ap_handle_uplink_nas_transport()
|
||||
* for the rationale. Send UEContextReleaseCommand so the eNB
|
||||
* cleans up its end of the stale RAN context.
|
||||
*/
|
||||
ogs_error("E-RABModificationIndication on stale S1 context "
|
||||
"[MME_UE_S1AP_ID:%d] - sending UEContextReleaseCommand",
|
||||
enb_ue->mme_ue_s1ap_id);
|
||||
r = s1ap_send_ue_context_release_command(
|
||||
enb_ue, S1AP_Cause_PR_radioNetwork,
|
||||
S1AP_CauseRadioNetwork_unspecified,
|
||||
S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2783,7 +2703,8 @@ void s1ap_handle_path_switch_request(
|
|||
S1AP_EncryptionAlgorithms_t *encryptionAlgorithms = NULL;
|
||||
S1AP_IntegrityProtectionAlgorithms_t *integrityProtectionAlgorithms = NULL;
|
||||
uint16_t eea = 0, eia = 0;
|
||||
uint8_t eea0 = 0, eia0 = 0;
|
||||
uint8_t received_eea = 0, received_eia = 0;
|
||||
bool ue_security_capability_mismatch = false;
|
||||
|
||||
enb_ue_t *enb_ue = NULL;
|
||||
mme_ue_t *mme_ue = NULL;
|
||||
|
|
@ -2956,6 +2877,8 @@ void s1ap_handle_path_switch_request(
|
|||
return;
|
||||
}
|
||||
|
||||
mme_ue->send_ue_security_capability_in_path_switch_ack = false;
|
||||
|
||||
if (!SECURITY_CONTEXT_IS_VALID(mme_ue)) {
|
||||
ogs_error("No Security Context");
|
||||
s1apbuf = s1ap_build_path_switch_failure(
|
||||
|
|
@ -2979,24 +2902,22 @@ void s1ap_handle_path_switch_request(
|
|||
ogs_plmn_id_hexdump(&mme_ue->e_cgi.plmn_id),
|
||||
mme_ue->e_cgi.cell_id);
|
||||
|
||||
/* Update ENB-UE-S1AP-ID */
|
||||
enb_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID;
|
||||
|
||||
/* Change enb_ue to the NEW eNB */
|
||||
enb_ue_switch_to_enb(enb_ue, enb);
|
||||
|
||||
ogs_info(" NEW ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
|
||||
enb_ue->enb_ue_s1ap_id, enb_ue->mme_ue_s1ap_id);
|
||||
if (!UESecurityCapabilities) {
|
||||
ogs_error("No UESecurityCapabilities");
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
pLMNidentity = &TAI->pLMNidentity;
|
||||
if (pLMNidentity->size != sizeof(enb_ue->saved.tai.plmn_id)) {
|
||||
ogs_error("Invalid pLMNidentity->size = %d (expected %d)",
|
||||
(int)pLMNidentity->size,
|
||||
(int)sizeof(enb_ue->saved.tai.plmn_id));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
|
|
@ -3005,28 +2926,20 @@ void s1ap_handle_path_switch_request(
|
|||
if (tAC->size != sizeof(enb_ue->saved.tai.tac)) {
|
||||
ogs_error("Invalid tAC->size = %d (expected %d)",
|
||||
(int)tAC->size, (int)sizeof(enb_ue->saved.tai.tac));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
memcpy(&enb_ue->saved.tai.plmn_id, pLMNidentity->buf,
|
||||
sizeof(enb_ue->saved.tai.plmn_id));
|
||||
memcpy(&enb_ue->saved.tai.tac, tAC->buf, sizeof(enb_ue->saved.tai.tac));
|
||||
enb_ue->saved.tai.tac = be16toh(enb_ue->saved.tai.tac);
|
||||
|
||||
pLMNidentity = &EUTRAN_CGI->pLMNidentity;
|
||||
if (pLMNidentity->size != sizeof(enb_ue->saved.e_cgi.plmn_id)) {
|
||||
ogs_error("Invalid pLMNidentity->size = %d (expected %d)",
|
||||
(int)pLMNidentity->size,
|
||||
(int)sizeof(enb_ue->saved.e_cgi.plmn_id));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
|
|
@ -3036,16 +2949,86 @@ void s1ap_handle_path_switch_request(
|
|||
ogs_error("Invalid cell_ID->size = %d (expected %d)",
|
||||
(int)cell_ID->size,
|
||||
(int)sizeof(enb_ue->saved.e_cgi.cell_id));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol, S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
encryptionAlgorithms =
|
||||
&UESecurityCapabilities->encryptionAlgorithms;
|
||||
integrityProtectionAlgorithms =
|
||||
&UESecurityCapabilities->integrityProtectionAlgorithms;
|
||||
|
||||
if (encryptionAlgorithms->size != sizeof(eea)) {
|
||||
ogs_error("Invalid encryptionAlgorithms->size = %d (expected %d)",
|
||||
(int)encryptionAlgorithms->size,
|
||||
(int)sizeof(eea));
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_message_not_compatible_with_receiver_state);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* The MME shall verify that the UE security capabilities received
|
||||
* from the target eNB are the same as the UE security capabilities
|
||||
* locally stored in the MME.
|
||||
*
|
||||
* If there is a mismatch, the MME shall send its locally stored
|
||||
* UE security capabilities to the target eNB in the
|
||||
* Path Switch Request Acknowledge message.
|
||||
*
|
||||
* Therefore, do not overwrite mme_ue->ue_network_capability
|
||||
* with the value received in PathSwitchRequest.
|
||||
*/
|
||||
memcpy(&eea, encryptionAlgorithms->buf, sizeof(eea));
|
||||
eea = be16toh(eea);
|
||||
received_eea = eea >> 9;
|
||||
|
||||
if (integrityProtectionAlgorithms->size != sizeof(eia)) {
|
||||
ogs_error("Invalid integrityProtectionAlgorithms->size = %d "
|
||||
"(expected %d)",
|
||||
(int)integrityProtectionAlgorithms->size,
|
||||
(int)sizeof(eia));
|
||||
r = s1ap_send_error_indication(enb, MME_UE_S1AP_ID, ENB_UE_S1AP_ID,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_message_not_compatible_with_receiver_state);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
memcpy(&eia, integrityProtectionAlgorithms->buf, sizeof(eia));
|
||||
eia = be16toh(eia);
|
||||
received_eia = eia >> 9;
|
||||
|
||||
if (received_eea != (mme_ue->ue_network_capability.eea & 0x7f) ||
|
||||
received_eia != (mme_ue->ue_network_capability.eia & 0x7f)) {
|
||||
ue_security_capability_mismatch = true;
|
||||
}
|
||||
|
||||
/* Update ENB-UE-S1AP-ID */
|
||||
enb_ue->enb_ue_s1ap_id = *ENB_UE_S1AP_ID;
|
||||
|
||||
/* Change enb_ue to the NEW eNB after mandatory IE validation */
|
||||
enb_ue_switch_to_enb(enb_ue, enb);
|
||||
|
||||
ogs_info(" NEW ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]",
|
||||
enb_ue->enb_ue_s1ap_id, enb_ue->mme_ue_s1ap_id);
|
||||
|
||||
pLMNidentity = &TAI->pLMNidentity;
|
||||
memcpy(&enb_ue->saved.tai.plmn_id, pLMNidentity->buf,
|
||||
sizeof(enb_ue->saved.tai.plmn_id));
|
||||
tAC = &TAI->tAC;
|
||||
memcpy(&enb_ue->saved.tai.tac, tAC->buf, sizeof(enb_ue->saved.tai.tac));
|
||||
enb_ue->saved.tai.tac = be16toh(enb_ue->saved.tai.tac);
|
||||
|
||||
pLMNidentity = &EUTRAN_CGI->pLMNidentity;
|
||||
memcpy(&enb_ue->saved.e_cgi.plmn_id, pLMNidentity->buf,
|
||||
sizeof(enb_ue->saved.e_cgi.plmn_id));
|
||||
cell_ID = &EUTRAN_CGI->cell_ID;
|
||||
memcpy(&enb_ue->saved.e_cgi.cell_id, cell_ID->buf,
|
||||
sizeof(enb_ue->saved.e_cgi.cell_id));
|
||||
enb_ue->saved.e_cgi.cell_id = (be32toh(enb_ue->saved.e_cgi.cell_id) >> 4);
|
||||
|
|
@ -3063,52 +3046,18 @@ void s1ap_handle_path_switch_request(
|
|||
memcpy(&mme_ue->e_cgi, &enb_ue->saved.e_cgi, sizeof(ogs_e_cgi_t));
|
||||
mme_ue->ue_location_timestamp = ogs_time_now();
|
||||
|
||||
ogs_assert(UESecurityCapabilities);
|
||||
encryptionAlgorithms =
|
||||
&UESecurityCapabilities->encryptionAlgorithms;
|
||||
integrityProtectionAlgorithms =
|
||||
&UESecurityCapabilities->integrityProtectionAlgorithms;
|
||||
if (ue_security_capability_mismatch) {
|
||||
mme_ue->send_ue_security_capability_in_path_switch_ack = true;
|
||||
|
||||
if (encryptionAlgorithms->size != sizeof(eea)) {
|
||||
ogs_error("Invalid encryptionAlgorithms->size = %d (expected %d)",
|
||||
(int)encryptionAlgorithms->size,
|
||||
(int)sizeof(eea));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
ogs_warn("[%s] UE Security Capability mismatch in "
|
||||
"PathSwitchRequest",
|
||||
MME_UE_HAVE_IMSI(mme_ue) ? mme_ue->imsi_bcd : "Unknown");
|
||||
ogs_warn(" Stored EEA[0x%x] EIA[0x%x]",
|
||||
mme_ue->ue_network_capability.eea,
|
||||
mme_ue->ue_network_capability.eia);
|
||||
ogs_warn(" Received EEA[0x%x] EIA[0x%x]",
|
||||
received_eea, received_eia);
|
||||
}
|
||||
memcpy(&eea, encryptionAlgorithms->buf, sizeof(eea));
|
||||
eea = be16toh(eea);
|
||||
eea0 = mme_ue->ue_network_capability.eea0;
|
||||
mme_ue->ue_network_capability.eea = eea >> 9;
|
||||
mme_ue->ue_network_capability.eea0 = eea0;
|
||||
|
||||
if (integrityProtectionAlgorithms->size != sizeof(eia)) {
|
||||
ogs_error("Invalid integrityProtectionAlgorithms->size = %d "
|
||||
"(expected %d)",
|
||||
(int)integrityProtectionAlgorithms->size,
|
||||
(int)sizeof(eia));
|
||||
r = s1ap_send_error_indication1(
|
||||
enb_ue,
|
||||
S1AP_Cause_PR_protocol,
|
||||
S1AP_CauseProtocol_semantic_error);
|
||||
ogs_expect(r == OGS_OK);
|
||||
ogs_assert(r != OGS_ERROR);
|
||||
return;
|
||||
}
|
||||
memcpy(&eia, integrityProtectionAlgorithms->buf, sizeof(eia));
|
||||
eia = be16toh(eia);
|
||||
eia0 = mme_ue->ue_network_capability.eia0;
|
||||
mme_ue->ue_network_capability.eia = eia >> 9;
|
||||
mme_ue->ue_network_capability.eia0 = eia0;
|
||||
|
||||
/* Update Security Context (NextHop) */
|
||||
mme_ue->nhcc++;
|
||||
ogs_kdf_nh_enb(mme_ue->kasme, mme_ue->nh, mme_ue->nh);
|
||||
|
||||
ogs_list_init(&mme_ue->bearer_to_modify_list);
|
||||
|
||||
|
|
@ -3198,6 +3147,18 @@ void s1ap_handle_path_switch_request(
|
|||
ogs_warn("Bearer [%d] Duplicated", (int)e_rab->e_RAB_ID);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update Security Context (NextHop)
|
||||
*
|
||||
* Defer NH/NCC derivation until every E-RAB in the PathSwitchRequest
|
||||
* has been validated. An unknown E-RAB ID (or malformed GTP-TEID /
|
||||
* transportLayerAddress) returns an ErrorIndication in the loop above;
|
||||
* advancing the NextHop chain before that point would desynchronize
|
||||
* the {NH, NCC} with the eNB with no rollback (TS 33.401 7.2.8).
|
||||
*/
|
||||
mme_ue->nhcc++;
|
||||
ogs_kdf_nh_enb(mme_ue->kasme, mme_ue->nh, mme_ue->nh);
|
||||
|
||||
relocation = sgw_ue_check_if_relocated(mme_ue);
|
||||
if (relocation == SGW_WITHOUT_RELOCATION) {
|
||||
if (ogs_list_count(&mme_ue->bearer_to_modify_list)) {
|
||||
|
|
|
|||
|
|
@ -173,8 +173,8 @@ int s1ap_send_to_nas(enb_ue_t *enb_ue,
|
|||
ogs_assert(enb_ue);
|
||||
ogs_assert(nasPdu);
|
||||
|
||||
if (nasPdu->size == 0) {
|
||||
ogs_error("Empty NAS PDU");
|
||||
if (nasPdu->size < sizeof(ogs_nas_emm_header_t)) {
|
||||
ogs_error("NAS PDU too short [%d]", (int)nasPdu->size);
|
||||
enb_ue_remove(enb_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
@ -200,27 +200,56 @@ int s1ap_send_to_nas(enb_ue_t *enb_ue,
|
|||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED:
|
||||
security_header_type.integrity_protected = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 6);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHERED:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.ciphered = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 6);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_NEW_SECURITY_CONTEXT:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.new_security_context = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 6);
|
||||
break;
|
||||
case OGS_NAS_SECURITY_HEADER_INTEGRITY_PROTECTED_AND_CIPHTERD_WITH_NEW_INTEGRITY_CONTEXT:
|
||||
security_header_type.integrity_protected = 1;
|
||||
security_header_type.ciphered = 1;
|
||||
security_header_type.new_security_context = 1;
|
||||
ogs_pkbuf_pull(nasbuf, 6);
|
||||
break;
|
||||
default:
|
||||
ogs_error("Not implemented(security header type:0x%x)",
|
||||
sh->security_header_type);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
enb_ue_remove(enb_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip the 6-octet NAS security header for integrity-protected messages.
|
||||
* ogs_pkbuf_pull() returns NULL when the buffer is shorter than the
|
||||
* requested length, so a protected NAS-PDU that is too short to even
|
||||
* contain its own security header is rejected here instead of being
|
||||
* parsed from a buffer that was never advanced.
|
||||
*
|
||||
* The PLAIN and SERVICE_REQUEST formats carry no security header and
|
||||
* must not be pulled; both leave integrity_protected == 0.
|
||||
*/
|
||||
if (security_header_type.integrity_protected) {
|
||||
if (!ogs_pkbuf_pull(nasbuf, sizeof(ogs_nas_eps_security_header_t))) {
|
||||
ogs_error("NAS PDU too short for security header [%d]",
|
||||
(int)nasPdu->size);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
enb_ue_remove(enb_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure the (post-security-header) NAS message is large enough to
|
||||
* hold the EMM header that is dereferenced through 'h' below. This also
|
||||
* covers ciphered messages: the length is unchanged by decoding.
|
||||
*/
|
||||
if (nasbuf->len < sizeof(ogs_nas_emm_header_t)) {
|
||||
ogs_error("NAS PDU too short for EMM header [%d]", (int)nasbuf->len);
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
enb_ue_remove(enb_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
@ -229,6 +258,7 @@ int s1ap_send_to_nas(enb_ue_t *enb_ue,
|
|||
if (nas_eps_security_decode(mme_ue,
|
||||
security_header_type, nasbuf) != OGS_OK) {
|
||||
ogs_error("nas_eps_security_decode failed()");
|
||||
ogs_pkbuf_free(nasbuf);
|
||||
enb_ue_remove(enb_ue);
|
||||
return OGS_ERROR;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ void sgsap_handle_location_update_accept(mme_vlr_t *vlr, ogs_pkbuf_t *pkbuf)
|
|||
|
||||
/* check BCS regardless of active_flag */
|
||||
if (mme_ue->tracking_area_update_request_presencemask &
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_TYPE) {
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_PRESENT) {
|
||||
ogs_info("[%s] LU accept + TAU accept(active_flag=%d, BCS)",
|
||||
mme_ue->imsi_bcd,
|
||||
mme_ue->nas_eps.update.active_flag);
|
||||
|
|
@ -317,7 +317,7 @@ void sgsap_handle_location_update_reject(mme_vlr_t *vlr, ogs_pkbuf_t *pkbuf)
|
|||
|
||||
/* check BCS regardless of active_flag */
|
||||
if (mme_ue->tracking_area_update_request_presencemask &
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_TYPE) {
|
||||
OGS_NAS_EPS_TRACKING_AREA_UPDATE_REQUEST_EPS_BEARER_CONTEXT_STATUS_PRESENT) {
|
||||
ogs_info("[%s] LU reject + TAU accept(active_flag=%d, BCS)",
|
||||
mme_ue->imsi_bcd,
|
||||
mme_ue->nas_eps.update.active_flag);
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@
|
|||
* "plmn": "99970",
|
||||
* "tac_hex": "0001",
|
||||
* "tac": 1
|
||||
* }
|
||||
* },
|
||||
* "timestamp": 1778223227627488
|
||||
* },
|
||||
* "ambr": {
|
||||
* "downlink": 1000000000,
|
||||
|
|
@ -176,6 +177,17 @@ static cJSON *build_location(const mme_ue_t *ue)
|
|||
if (!cJSON_AddNumberToObject(tai, "tac", (double)ue->tai.tac)) { cJSON_Delete(tai); goto end; }
|
||||
|
||||
cJSON_AddItemToObjectCS(loc, "tai", tai);
|
||||
|
||||
/* Last location update timestamp (epoch microseconds, ogs_time_t).
|
||||
* A value of 0 means the location has not yet been updated (i.e.
|
||||
* the UE has not completed Initial Attach / TAU / Handover /
|
||||
* Service Request response since the context was created).
|
||||
* Updated on Initial Attach, TAU, Handover and Service Request responses;
|
||||
* bounded by Periodic TAU (T3412). Keep the field name aligned with
|
||||
* AMF /ue-info, which exposes this value as location.timestamp. */
|
||||
if (!cJSON_AddNumberToObject(loc, "timestamp",
|
||||
(double)ue->ue_location_timestamp)) goto end;
|
||||
|
||||
return loc;
|
||||
|
||||
end:
|
||||
|
|
|
|||
|
|
@ -137,9 +137,20 @@ int nrf_context_parse_config(void)
|
|||
nrf_assoc_t *nrf_assoc_add(ogs_sbi_stream_t *stream)
|
||||
{
|
||||
nrf_assoc_t *assoc = NULL;
|
||||
ogs_pool_id_t stream_id = OGS_INVALID_POOL_ID;
|
||||
|
||||
ogs_assert(stream);
|
||||
|
||||
/*
|
||||
* The caller passes a live inbound stream taken straight out of
|
||||
* the nnrf-disc request callback, so the ID must be valid here.
|
||||
* Resolve it up front so we never have to dereference the raw
|
||||
* stream pointer later -- by the time the Home-NRF response
|
||||
* comes back the original client may already be gone.
|
||||
*/
|
||||
stream_id = ogs_sbi_id_from_stream(stream);
|
||||
ogs_assert(stream_id >= OGS_MIN_POOL_ID && stream_id <= OGS_MAX_POOL_ID);
|
||||
|
||||
ogs_pool_alloc(&nrf_assoc_pool, &assoc);
|
||||
if (!assoc) {
|
||||
ogs_error("Maximum number of association[%d] reached",
|
||||
|
|
@ -148,7 +159,7 @@ nrf_assoc_t *nrf_assoc_add(ogs_sbi_stream_t *stream)
|
|||
}
|
||||
memset(assoc, 0, sizeof *assoc);
|
||||
|
||||
assoc->stream = stream;
|
||||
assoc->stream_id = stream_id;
|
||||
|
||||
ogs_list_add(&self.assoc_list, assoc);
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,21 @@ typedef struct nrf_assoc_s nrf_assoc_t;
|
|||
typedef struct nrf_assoc_s {
|
||||
ogs_lnode_t lnode;
|
||||
|
||||
ogs_sbi_stream_t *stream;
|
||||
/*
|
||||
* Pool-ID of the inbound HTTP/2 stream that triggered an
|
||||
* inter-PLMN NF discovery forwarding request to the Home NRF.
|
||||
*
|
||||
* The raw ogs_sbi_stream_t pointer must NOT be cached here.
|
||||
* The original client may RST_STREAM or otherwise disconnect
|
||||
* before the (possibly delayed) Home-NRF response arrives,
|
||||
* in which case nghttp2 frees the stream and any cached
|
||||
* pointer becomes dangling. Storing the ID and resolving it
|
||||
* through ogs_sbi_stream_find_by_id() on the response path
|
||||
* lets us drop the response cleanly when the originating
|
||||
* stream is already gone, instead of asserting on a freed
|
||||
* socket fd inside the SBI server.
|
||||
*/
|
||||
ogs_pool_id_t stream_id;
|
||||
} nrf_assoc_t;
|
||||
|
||||
void nrf_context_init(void);
|
||||
|
|
|
|||
|
|
@ -266,8 +266,33 @@ void nrf_nf_state_registered(ogs_fsm_t *s, nrf_event_t *e)
|
|||
|
||||
handled = nrf_nnrf_handle_nf_update(
|
||||
nf_instance, stream, message);
|
||||
if (handled == false)
|
||||
OGS_FSM_TRAN(s, nrf_nf_state_exception);
|
||||
if (handled == false) {
|
||||
ogs_error("[%s] Invalid NF update [type:%s] - "
|
||||
"keeping existing registration",
|
||||
nf_instance->id,
|
||||
OpenAPI_nf_type_ToString(
|
||||
nf_instance->nf_type));
|
||||
/*
|
||||
* ogs_nnrf_nfm_handle_nf_profile() now guarantees
|
||||
* that the live nf_instance is unchanged on failure
|
||||
* (shadow-swap rebuild). nrf_nnrf_handle_nf_update()
|
||||
* has already sent the HTTP error response, so we
|
||||
* keep the existing registration intact - treating
|
||||
* a malformed PUT/PATCH update as an implicit
|
||||
* de-registration would evict an otherwise healthy
|
||||
* peer because of one bad message.
|
||||
*
|
||||
* The previous behaviour was to transition to
|
||||
* nrf_nf_state_exception, which causes the FSM
|
||||
* dispatcher in nrf_state_operational() to call
|
||||
* nrf_nf_fsm_fini() + ogs_sbi_nf_instance_remove().
|
||||
* That eviction is the correct response for
|
||||
* nrf_nf_state_will_register (initial registration
|
||||
* never completed), but is wrong for
|
||||
* nrf_nf_state_registered (the registration is
|
||||
* still valid - only this one update was bad).
|
||||
*/
|
||||
}
|
||||
break;
|
||||
|
||||
CASE(OGS_SBI_HTTP_METHOD_DELETE)
|
||||
|
|
|
|||
|
|
@ -121,8 +121,24 @@ bool nrf_nnrf_handle_nf_register(ogs_sbi_nf_instance_t *nf_instance,
|
|||
}
|
||||
}
|
||||
|
||||
ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile);
|
||||
if (ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile) == false) {
|
||||
ogs_error("[%s] Invalid NFProfile", NFProfile->nf_instance_id);
|
||||
|
||||
/*
|
||||
* Do not finalize or remove nf_instance here. This handler is called
|
||||
* from the NRF NF state machine. The caller performs OGS_FSM_TRAN()
|
||||
* on &nf_instance->sm immediately after this function returns, so
|
||||
* freeing nf_instance here would cause a use-after-free.
|
||||
*
|
||||
* Return failure and let the FSM dispatcher perform cleanup after the
|
||||
* transition to nrf_nf_state_exception.
|
||||
*/
|
||||
ogs_assert(true == ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
recvmsg, "Invalid NFProfile", NFProfile->nf_instance_id, NULL));
|
||||
|
||||
return false;
|
||||
}
|
||||
ogs_sbi_client_associate(nf_instance);
|
||||
|
||||
/* ---------------------------------------------------------- */
|
||||
|
|
@ -317,8 +333,13 @@ bool nrf_nnrf_handle_nf_update(ogs_sbi_nf_instance_t *nf_instance,
|
|||
CASE(OGS_SBI_PATCH_PATH_PLMN_LIST)
|
||||
/* Ensure the value is not null and is a valid JSON array */
|
||||
if (patch_item->value && patch_item->value->json) {
|
||||
ogs_plmn_id_t new_plmn_id[
|
||||
OGS_ARRAY_SIZE(nf_instance->plmn_id)];
|
||||
int new_num_of_plmn_id = 0;
|
||||
|
||||
/* Set PLMN status to invalid */
|
||||
plmn_valid = false;
|
||||
memset(new_plmn_id, 0, sizeof(new_plmn_id));
|
||||
|
||||
plmn_array = patch_item->value->json;
|
||||
if (!cJSON_IsArray(plmn_array)) {
|
||||
|
|
@ -329,18 +350,13 @@ bool nrf_nnrf_handle_nf_update(ogs_sbi_nf_instance_t *nf_instance,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Clear existing PLMN data in nf_instance */
|
||||
memset(nf_instance->plmn_id, 0,
|
||||
sizeof(nf_instance->plmn_id));
|
||||
nf_instance->num_of_plmn_id = 0;
|
||||
|
||||
/* Iterate through the JSON array of PLMN IDs */
|
||||
cJSON_ArrayForEach(plmn_item, plmn_array) {
|
||||
OpenAPI_plmn_id_t plmn_id;
|
||||
memset(&plmn_id, 0, sizeof(plmn_id));
|
||||
|
||||
if (nf_instance->num_of_plmn_id >=
|
||||
OGS_ARRAY_SIZE(nf_instance->plmn_id)) {
|
||||
if (new_num_of_plmn_id >=
|
||||
OGS_ARRAY_SIZE(new_plmn_id)) {
|
||||
ogs_error("Exceeded maximum number of PLMN IDs");
|
||||
ogs_assert(true == ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
|
|
@ -374,15 +390,11 @@ bool nrf_nnrf_handle_nf_update(ogs_sbi_nf_instance_t *nf_instance,
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert OpenAPI_plmn_id_t to ogs_plmn_id_t
|
||||
* and store in nf_instance
|
||||
*/
|
||||
/* Convert OpenAPI_plmn_id_t to temporary PLMN list */
|
||||
ogs_sbi_parse_plmn_id(
|
||||
&nf_instance->
|
||||
plmn_id[nf_instance->num_of_plmn_id],
|
||||
&new_plmn_id[new_num_of_plmn_id],
|
||||
&plmn_id);
|
||||
nf_instance->num_of_plmn_id++;
|
||||
new_num_of_plmn_id++;
|
||||
|
||||
/* Compare with the serving PLMN list */
|
||||
for (i = 0;
|
||||
|
|
@ -408,6 +420,15 @@ bool nrf_nnrf_handle_nf_update(ogs_sbi_nf_instance_t *nf_instance,
|
|||
"PLMN-ID not allowed", NULL, NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Commit the new PLMN list only after all validation
|
||||
* succeeds. This keeps rejected PATCH requests from
|
||||
* modifying the stored NF instance state.
|
||||
*/
|
||||
memcpy(nf_instance->plmn_id, new_plmn_id,
|
||||
sizeof(new_plmn_id));
|
||||
nf_instance->num_of_plmn_id = new_num_of_plmn_id;
|
||||
}
|
||||
break;
|
||||
DEFAULT
|
||||
|
|
@ -486,7 +507,14 @@ bool nrf_nnrf_handle_nf_status_subscribe(
|
|||
ogs_uuid_format(id, &uuid);
|
||||
|
||||
subscription_data = ogs_sbi_subscription_data_add();
|
||||
ogs_assert(subscription_data);
|
||||
if (!subscription_data) {
|
||||
ogs_error("ogs_sbi_subscription_data_add() failed");
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
recvmsg, "No subscription data available", NULL, NULL));
|
||||
return false;
|
||||
}
|
||||
|
||||
ogs_sbi_subscription_data_set_id(subscription_data, id);
|
||||
ogs_assert(subscription_data->id);
|
||||
|
|
@ -600,7 +628,19 @@ bool nrf_nnrf_handle_nf_status_subscribe(
|
|||
if (!client) {
|
||||
ogs_debug("%s: ogs_sbi_client_add()", OGS_FUNC);
|
||||
client = ogs_sbi_client_add(scheme, fqdn, fqdn_port, addr, addr6);
|
||||
ogs_assert(client);
|
||||
if (!client) {
|
||||
ogs_error("ogs_sbi_client_add() failed");
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR,
|
||||
recvmsg, "No SBI client available",
|
||||
subscription_data->notification_uri, NULL));
|
||||
ogs_free(fqdn);
|
||||
ogs_freeaddrinfo(addr);
|
||||
ogs_freeaddrinfo(addr6);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
OGS_SBI_SETUP_CLIENT(subscription_data, client);
|
||||
|
||||
|
|
@ -1082,6 +1122,54 @@ bool nrf_nnrf_handle_nf_discover(
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TS 33.518 4.2.2.2.1 - NF discovery authorization for specific slice
|
||||
*
|
||||
* When the discovery request carries S-NSSAI filters, verify that the
|
||||
* requesting NF is authorized for those slices. The requester's
|
||||
* registered sNssais (from its NFProfile) define the set of slices
|
||||
* it may legitimately query. If the requester registered with sNssais
|
||||
* and none of the requested slices match, reject with 403 Forbidden.
|
||||
*/
|
||||
if (discovery_option &&
|
||||
discovery_option->num_of_snssais &&
|
||||
discovery_option->requester_nf_instance_id) {
|
||||
|
||||
ogs_sbi_nf_instance_t *requester = NULL;
|
||||
|
||||
requester = ogs_sbi_nf_instance_find(
|
||||
discovery_option->requester_nf_instance_id);
|
||||
if (requester && requester->num_of_s_nssai) {
|
||||
bool authorized = false;
|
||||
int ri, qi;
|
||||
|
||||
for (qi = 0; qi < discovery_option->num_of_snssais; qi++) {
|
||||
for (ri = 0; ri < requester->num_of_s_nssai; ri++) {
|
||||
if (discovery_option->snssais[qi].sst ==
|
||||
requester->s_nssai[ri].sst &&
|
||||
discovery_option->snssais[qi].sd.v ==
|
||||
requester->s_nssai[ri].sd.v) {
|
||||
authorized = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (authorized) break;
|
||||
}
|
||||
|
||||
if (!authorized) {
|
||||
ogs_error("NF [%s] not authorized for requested S-NSSAI",
|
||||
discovery_option->requester_nf_instance_id);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_FORBIDDEN,
|
||||
recvmsg,
|
||||
"Requester not authorized for requested S-NSSAI",
|
||||
discovery_option->requester_nf_instance_id, NULL));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchResult = ogs_calloc(1, sizeof(*SearchResult));
|
||||
ogs_assert(SearchResult);
|
||||
|
||||
|
|
@ -1197,7 +1285,13 @@ bool nrf_nnrf_handle_nf_discover(
|
|||
|
||||
if (!nf_instance) {
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add NRF instance due to insufficient space");
|
||||
ogs_assert(true == ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_PAYLOAD_TOO_LARGE,
|
||||
recvmsg, "Insufficient space", NULL, NULL));
|
||||
goto cleanup;
|
||||
}
|
||||
ogs_sbi_nf_instance_set_type(nf_instance, OpenAPI_nf_type_NRF);
|
||||
|
||||
/*
|
||||
|
|
@ -1229,9 +1323,18 @@ bool nrf_nnrf_handle_nf_discover(
|
|||
rc = ogs_sbi_getaddr_from_uri(
|
||||
&scheme, &fqdn, &fqdn_port, &addr, &addr6,
|
||||
discovery_option->hnrf_uri);
|
||||
if (rc == false || scheme == OpenAPI_uri_scheme_NULL)
|
||||
ogs_error("Invalid URL [%s]", request->h.uri);
|
||||
else {
|
||||
if (rc == false || scheme == OpenAPI_uri_scheme_NULL) {
|
||||
ogs_error("Invalid hnrf-uri [%s]",
|
||||
discovery_option->hnrf_uri);
|
||||
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
|
||||
ogs_assert(true == ogs_sbi_server_send_error(
|
||||
stream, OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
recvmsg, "Invalid hnrf-uri",
|
||||
discovery_option->hnrf_uri, NULL));
|
||||
goto cleanup;
|
||||
} else {
|
||||
/*
|
||||
* If there is an hnrf-uri, this value creates an nf-instance->fqdn,
|
||||
* which in turn creates a client->fqdn from the hnrf-uri.
|
||||
|
|
@ -1360,33 +1463,54 @@ static int discover_handler(
|
|||
int status, ogs_sbi_response_t *response, void *data)
|
||||
{
|
||||
int rv;
|
||||
int ret = OGS_OK;
|
||||
ogs_sbi_message_t message;
|
||||
|
||||
nrf_assoc_t *assoc = data;
|
||||
ogs_sbi_stream_t *stream = NULL;
|
||||
|
||||
ogs_assert(assoc);
|
||||
stream = assoc->stream;
|
||||
ogs_assert(stream);
|
||||
|
||||
/*
|
||||
* Zero-init so the unified cleanup: below can always call
|
||||
* ogs_sbi_message_free() unconditionally. The free function
|
||||
* is fully NULL-guarded per field, and ogs_sbi_parse_response()
|
||||
* (via parse_header()) zeroes the struct itself on entry,
|
||||
* so this single initialization covers every goto cleanup path.
|
||||
*/
|
||||
memset(&message, 0, sizeof(message));
|
||||
|
||||
/*
|
||||
* Resolve the originating inbound stream (GH#4476).
|
||||
*
|
||||
* Per the lib/sbi/client.c callback convention:
|
||||
* status == OGS_OK -> response is non-NULL
|
||||
* OGS_DONE / TIMEUP / ERROR -> response is NULL
|
||||
*
|
||||
* The stream may already be gone if the original inter-PLMN
|
||||
* client RST_STREAM'd or timed out before this (possibly
|
||||
* delayed) Home-NRF reply arrived. All paths converge on
|
||||
* cleanup: below, which dispatches over (stream, response).
|
||||
*/
|
||||
stream = ogs_sbi_stream_find_by_id(assoc->stream_id);
|
||||
|
||||
if (status != OGS_OK) {
|
||||
|
||||
ogs_log_message(
|
||||
status == OGS_DONE ? OGS_LOG_DEBUG : OGS_LOG_WARN, 0,
|
||||
"response_handler() failed [%d]", status);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL,
|
||||
"response_handler() failed", NULL, NULL));
|
||||
|
||||
nrf_assoc_remove(assoc);
|
||||
|
||||
return OGS_ERROR;
|
||||
ret = OGS_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ogs_assert(response);
|
||||
|
||||
if (!stream) {
|
||||
ogs_error("nnrf-disc: originating SBI stream already closed; "
|
||||
"dropping delayed Home-NRF response [assoc_stream_id:%d]",
|
||||
(int)assoc->stream_id);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rv = ogs_sbi_parse_response(&message, response);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_error("cannot parse HTTP response");
|
||||
|
|
@ -1406,11 +1530,28 @@ static int discover_handler(
|
|||
handle_nf_discover_search_result(message.SearchResult);
|
||||
|
||||
cleanup:
|
||||
ogs_expect(true == ogs_sbi_server_send_response(stream, response));
|
||||
nrf_assoc_remove(assoc);
|
||||
ogs_sbi_message_free(&message);
|
||||
/*
|
||||
* Dispatch over (stream, response):
|
||||
* alive + resp -> forward (proxy: parseable or not)
|
||||
* alive + null -> synthesize HTTP 500
|
||||
* gone + resp -> drop orphaned response (#4476)
|
||||
* gone + null -> nothing to dispose
|
||||
*/
|
||||
if (stream && response)
|
||||
ogs_expect(true ==
|
||||
ogs_sbi_server_send_response(stream, response));
|
||||
else if (stream)
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL,
|
||||
"response_handler() failed", NULL, NULL));
|
||||
else if (response)
|
||||
ogs_sbi_response_free(response);
|
||||
|
||||
return OGS_OK;
|
||||
ogs_sbi_message_free(&message);
|
||||
nrf_assoc_remove(assoc);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void handle_nf_discover_search_result(
|
||||
|
|
@ -1423,6 +1564,7 @@ static void handle_nf_discover_search_result(
|
|||
|
||||
OpenAPI_list_for_each(SearchResult->nf_instances, node) {
|
||||
OpenAPI_nf_profile_t *NFProfile = NULL;
|
||||
bool nf_instance_created = false;
|
||||
|
||||
if (!node->data) continue;
|
||||
|
||||
|
|
@ -1451,9 +1593,16 @@ static void handle_nf_discover_search_result(
|
|||
nf_instance = ogs_sbi_nf_instance_find(NFProfile->nf_instance_id);
|
||||
if (!nf_instance) {
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add discovered NF instance [%s:%s] "
|
||||
"due to insufficient space",
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
continue;
|
||||
}
|
||||
|
||||
ogs_sbi_nf_instance_set_id(nf_instance, NFProfile->nf_instance_id);
|
||||
nf_instance_created = true;
|
||||
|
||||
/*
|
||||
* If nrf_nf_fsm_init() is not executed, nf_instance->sm is NULL.
|
||||
|
|
@ -1473,7 +1622,24 @@ static void handle_nf_discover_search_result(
|
|||
}
|
||||
|
||||
if (NF_INSTANCE_ID_IS_OTHERS(nf_instance->id)) {
|
||||
ogs_nnrf_nfm_handle_nf_profile(nf_instance, NFProfile);
|
||||
if (ogs_nnrf_nfm_handle_nf_profile(
|
||||
nf_instance, NFProfile) == false) {
|
||||
ogs_error("[%s] (NF-discover) Invalid NFProfile [type:%s]",
|
||||
NFProfile->nf_instance_id,
|
||||
OpenAPI_nf_type_ToString(NFProfile->nf_type));
|
||||
|
||||
/*
|
||||
* Only roll back nf_instances we just created here. A
|
||||
* pre-existing cache entry will be corrected by the next
|
||||
* inter-NRF discovery cycle; removing it now would cause
|
||||
* a temporary blackhole. (No fsm_fini: this code path
|
||||
* never calls nrf_nf_fsm_init() for newly added entries
|
||||
* per the comment at line 1517-1522, so sm is NULL.)
|
||||
*/
|
||||
if (nf_instance_created)
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
continue;
|
||||
}
|
||||
|
||||
ogs_sbi_client_associate(nf_instance);
|
||||
|
||||
|
|
|
|||
|
|
@ -123,19 +123,26 @@ void nrf_state_operational(ogs_fsm_t *s, nrf_event_t *e)
|
|||
break;
|
||||
|
||||
DEFAULT
|
||||
if (message.h.resource.component[1]) {
|
||||
nf_instance = ogs_sbi_nf_instance_find(
|
||||
message.h.resource.component[1]);
|
||||
if (!message.h.resource.component[1]) {
|
||||
ogs_error("No NFInstanceId");
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST,
|
||||
&message, "No NFInstanceId", NULL, NULL));
|
||||
break;
|
||||
}
|
||||
|
||||
nf_instance = ogs_sbi_nf_instance_find(
|
||||
message.h.resource.component[1]);
|
||||
|
||||
if (!nf_instance) {
|
||||
SWITCH(message.h.method)
|
||||
CASE(OGS_SBI_HTTP_METHOD_PUT)
|
||||
if (ogs_sbi_nf_instance_maximum_number_is_reached())
|
||||
{
|
||||
ogs_warn("Can't add instance [%s] "
|
||||
"due to insufficient space",
|
||||
message.h.resource.component[1]);
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
if (!nf_instance) {
|
||||
ogs_error("Can't add instance [%s] "
|
||||
"due to insufficient space",
|
||||
message.h.resource.component[1]);
|
||||
ogs_assert(
|
||||
true ==
|
||||
ogs_sbi_server_send_error(
|
||||
|
|
@ -145,8 +152,6 @@ void nrf_state_operational(ogs_fsm_t *s, nrf_event_t *e)
|
|||
message.h.resource.component[1], NULL));
|
||||
break;
|
||||
}
|
||||
nf_instance = ogs_sbi_nf_instance_add();
|
||||
ogs_assert(nf_instance);
|
||||
ogs_sbi_nf_instance_set_id(nf_instance,
|
||||
message.h.resource.component[1]);
|
||||
|
||||
|
|
@ -194,6 +199,14 @@ void nrf_state_operational(ogs_fsm_t *s, nrf_event_t *e)
|
|||
ogs_error("[%s] State machine exception",
|
||||
nf_instance->id);
|
||||
|
||||
/*
|
||||
* Handlers invoked from the NF FSM must not free
|
||||
* nf_instance before returning because the FSM
|
||||
* transition writes to &nf_instance->sm. Cleanup
|
||||
* of failed registration/update attempts is
|
||||
* centralized here, after ogs_fsm_dispatch()
|
||||
* completes.
|
||||
*/
|
||||
nrf_nf_fsm_fini(nf_instance);
|
||||
ogs_sbi_nf_instance_remove(nf_instance);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,6 +108,17 @@ bool nssf_nnrf_nsselection_handle_get_from_amf_or_vnssf(
|
|||
nssf_nnssf_nsselection_param_t param;
|
||||
ogs_sbi_discovery_option_t *h_discovery_option = NULL;
|
||||
|
||||
if (ogs_local_conf()->num_of_serving_plmn_id == 0) {
|
||||
status = OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
strerror = ogs_msprintf("No serving PLMN configured: "
|
||||
"cannot build Home-NSSF discovery query "
|
||||
"for PLMN-ID(0x%x) S-NSSAI[SST:%d SD:0x%x]",
|
||||
ogs_plmn_id_hexdump(&recvmsg->param.home_plmn_id),
|
||||
recvmsg->param.home_snssai.sst,
|
||||
recvmsg->param.home_snssai.sd.v);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
h_discovery_option = ogs_sbi_discovery_option_new();
|
||||
ogs_assert(h_discovery_option);
|
||||
|
||||
|
|
@ -115,7 +126,6 @@ bool nssf_nnrf_nsselection_handle_get_from_amf_or_vnssf(
|
|||
h_discovery_option, &home->s_nssai);
|
||||
ogs_sbi_discovery_option_add_target_plmn_list(
|
||||
h_discovery_option, &home->plmn_id);
|
||||
ogs_assert(ogs_local_conf()->num_of_serving_plmn_id);
|
||||
for (i = 0; i < ogs_local_conf()->num_of_serving_plmn_id; i++) {
|
||||
ogs_sbi_discovery_option_add_requester_plmn_list(
|
||||
h_discovery_option,
|
||||
|
|
|
|||
|
|
@ -374,13 +374,19 @@ void nssf_state_operational(ogs_fsm_t *s, nssf_event_t *e)
|
|||
|
||||
ogs_sbi_xact_remove(sbi_xact);
|
||||
|
||||
if (!stream) {
|
||||
ogs_warn("Original NSSelection stream has been removed "
|
||||
"before Home-NSSF response arrived");
|
||||
break;
|
||||
}
|
||||
|
||||
home = nssf_home_find_by_id(sbi_object_id);
|
||||
if (!home) {
|
||||
ogs_error("Home Network Context has already been removed");
|
||||
break;
|
||||
}
|
||||
|
||||
e->h.sbi.message = &message;;
|
||||
e->h.sbi.message = &message;
|
||||
|
||||
nssf_nnrf_nsselection_handle_get_from_hnssf(home, stream, &message);
|
||||
break;
|
||||
|
|
@ -420,24 +426,26 @@ void nssf_state_operational(ogs_fsm_t *s, nssf_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ static OGS_POOL(pcf_app_pool, pcf_app_t);
|
|||
|
||||
static int context_initialized = 0;
|
||||
|
||||
static void pcf_qos_profile_clear(void);
|
||||
static void clear_ipv4addr(pcf_sess_t *sess);
|
||||
static void clear_ipv6prefix(pcf_sess_t *sess);
|
||||
|
||||
|
|
@ -70,6 +71,8 @@ void pcf_context_final(void)
|
|||
pcf_ue_am_remove_all();
|
||||
pcf_ue_sm_remove_all();
|
||||
|
||||
pcf_qos_profile_clear();
|
||||
|
||||
ogs_assert(self.supi_am_hash);
|
||||
ogs_hash_destroy(self.supi_am_hash);
|
||||
ogs_assert(self.supi_sm_hash);
|
||||
|
|
@ -92,8 +95,20 @@ pcf_context_t *pcf_self(void)
|
|||
return &self;
|
||||
}
|
||||
|
||||
static void pcf_qos_profile_clear(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self.num_of_qos_profile; i++)
|
||||
ogs_free(self.qos_profile[i].reference);
|
||||
|
||||
memset(self.qos_profile, 0, sizeof(self.qos_profile));
|
||||
self.num_of_qos_profile = 0;
|
||||
}
|
||||
|
||||
static int pcf_context_prepare(void)
|
||||
{
|
||||
pcf_qos_profile_clear();
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
|
|
@ -204,6 +219,20 @@ static int policy_conf_validation(void)
|
|||
return OGS_OK;
|
||||
}
|
||||
|
||||
static bool pcf_qos_profile_reference_exists(const char *reference)
|
||||
{
|
||||
int i;
|
||||
|
||||
ogs_assert(reference);
|
||||
|
||||
for (i = 0; i < self.num_of_qos_profile; i++) {
|
||||
if (!strcmp(self.qos_profile[i].reference, reference))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int parse_policy_conf(ogs_yaml_iter_t *parent)
|
||||
{
|
||||
int rv;
|
||||
|
|
@ -288,6 +317,75 @@ static int parse_policy_conf(ogs_yaml_iter_t *parent)
|
|||
return OGS_OK;
|
||||
}
|
||||
|
||||
static int parse_qos_profiles_conf(ogs_yaml_iter_t *parent)
|
||||
{
|
||||
ogs_yaml_iter_t profile_array, profile_iter;
|
||||
|
||||
ogs_assert(parent);
|
||||
|
||||
ogs_yaml_iter_recurse(parent, &profile_array);
|
||||
do {
|
||||
const char *reference = NULL;
|
||||
const char *qos_index_string = NULL;
|
||||
char *end = NULL;
|
||||
long qos_index = 0;
|
||||
|
||||
OGS_YAML_ARRAY_NEXT(&profile_array, &profile_iter);
|
||||
while (ogs_yaml_iter_next(&profile_iter)) {
|
||||
const char *profile_key = ogs_yaml_iter_key(&profile_iter);
|
||||
ogs_assert(profile_key);
|
||||
|
||||
if (!strcmp(profile_key, "reference")) {
|
||||
reference = ogs_yaml_iter_value(&profile_iter);
|
||||
} else if (!strcmp(profile_key, "qos_index")) {
|
||||
qos_index_string = ogs_yaml_iter_value(&profile_iter);
|
||||
} else {
|
||||
ogs_warn("unknown qos_profiles key `%s`", profile_key);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reference || !reference[0]) {
|
||||
ogs_warn("Ignore qos_profiles entry without reference");
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (!qos_index_string || !qos_index_string[0]) {
|
||||
ogs_warn("Ignore qos_profiles[%s] without qos_index", reference);
|
||||
goto next;
|
||||
}
|
||||
|
||||
qos_index = strtol(qos_index_string, &end, 10);
|
||||
if (*end || qos_index <= 0 || qos_index > UINT8_MAX) {
|
||||
ogs_warn("Ignore qos_profiles[%s] invalid qos_index [%s]",
|
||||
reference, qos_index_string);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (pcf_qos_profile_reference_exists(reference)) {
|
||||
ogs_warn("Ignore duplicate qos_profiles reference [%s]", reference);
|
||||
goto next;
|
||||
}
|
||||
|
||||
if (self.num_of_qos_profile >= OGS_PCF_MAX_NUM_OF_QOS_PROFILE) {
|
||||
ogs_warn("Ignore qos_profiles[%s] beyond max [%d]",
|
||||
reference, OGS_PCF_MAX_NUM_OF_QOS_PROFILE);
|
||||
goto next;
|
||||
}
|
||||
|
||||
self.qos_profile[self.num_of_qos_profile].reference =
|
||||
ogs_strdup(reference);
|
||||
ogs_assert(self.qos_profile[self.num_of_qos_profile].reference);
|
||||
self.qos_profile[self.num_of_qos_profile].qos_index =
|
||||
(uint8_t)qos_index;
|
||||
self.num_of_qos_profile++;
|
||||
|
||||
next:
|
||||
;
|
||||
} while (ogs_yaml_iter_type(&profile_array) == YAML_SEQUENCE_NODE);
|
||||
|
||||
return OGS_OK;
|
||||
}
|
||||
|
||||
int pcf_context_parse_config(void)
|
||||
{
|
||||
int rv;
|
||||
|
|
@ -326,6 +424,12 @@ int pcf_context_parse_config(void)
|
|||
/* handle config in sbi library */
|
||||
} else if (!strcmp(pcf_key, "metrics")) {
|
||||
/* handle config in metrics library */
|
||||
} else if (!strcmp(pcf_key, "qos_profiles")) {
|
||||
rv = parse_qos_profiles_conf(&pcf_iter);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_error("parse_qos_profiles_conf() failed");
|
||||
return rv;
|
||||
}
|
||||
} else if (!strcmp(pcf_key, OGS_POLICY_STRING)) {
|
||||
rv = parse_policy_conf(&pcf_iter);
|
||||
if (rv != OGS_OK) {
|
||||
|
|
@ -404,7 +508,8 @@ void pcf_ue_am_remove(pcf_ue_am_t *pcf_ue_am)
|
|||
ogs_free(pcf_ue_am->association_id);
|
||||
|
||||
ogs_assert(pcf_ue_am->supi);
|
||||
ogs_hash_set(self.supi_am_hash, pcf_ue_am->supi, strlen(pcf_ue_am->supi), NULL);
|
||||
ogs_hash_unset_if_owner(self.supi_am_hash,
|
||||
pcf_ue_am->supi, strlen(pcf_ue_am->supi), pcf_ue_am);
|
||||
ogs_free(pcf_ue_am->supi);
|
||||
|
||||
if (pcf_ue_am->notification_uri)
|
||||
|
|
@ -467,7 +572,8 @@ void pcf_ue_sm_remove(pcf_ue_sm_t *pcf_ue_sm)
|
|||
pcf_sess_remove_all(pcf_ue_sm);
|
||||
|
||||
ogs_assert(pcf_ue_sm->supi);
|
||||
ogs_hash_set(self.supi_sm_hash, pcf_ue_sm->supi, strlen(pcf_ue_sm->supi), NULL);
|
||||
ogs_hash_unset_if_owner(self.supi_sm_hash,
|
||||
pcf_ue_sm->supi, strlen(pcf_ue_sm->supi), pcf_ue_sm);
|
||||
ogs_free(pcf_ue_sm->supi);
|
||||
|
||||
if (pcf_ue_sm->gpsi)
|
||||
|
|
@ -672,7 +778,12 @@ bool pcf_sess_set_ipv6prefix(pcf_sess_t *sess, char *ipv6prefix_string)
|
|||
return false;
|
||||
}
|
||||
|
||||
ogs_assert(sess->ipv6prefix.len == OGS_IPV6_128_PREFIX_LEN);
|
||||
if (sess->ipv6prefix.len != OGS_IPV6_128_PREFIX_LEN) {
|
||||
ogs_error("Unsupported IPv6 prefix length [%d] in [%s]: "
|
||||
"expected /%d", sess->ipv6prefix.len, ipv6prefix_string,
|
||||
OGS_IPV6_128_PREFIX_LEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
sess->ipv6prefix_string = ogs_strdup(ipv6prefix_string);
|
||||
if (!sess->ipv6prefix_string) {
|
||||
|
|
@ -778,9 +889,17 @@ pcf_sess_t *pcf_sess_find_by_ipv6prefix(char *ipv6prefix_string)
|
|||
|
||||
rv = ogs_ipv6prefix_from_string(
|
||||
ipv6prefix.addr6, &ipv6prefix.len, ipv6prefix_string);
|
||||
ogs_assert(rv == OGS_OK);
|
||||
if (rv != OGS_OK) {
|
||||
ogs_error("ogs_ipv6prefix_from_string() failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ogs_assert(ipv6prefix.len == OGS_IPV6_128_PREFIX_LEN);
|
||||
if (ipv6prefix.len != OGS_IPV6_128_PREFIX_LEN) {
|
||||
ogs_error("Unsupported IPv6 prefix length [%d] in [%s]: "
|
||||
"expected /%d", ipv6prefix.len, ipv6prefix_string,
|
||||
OGS_IPV6_128_PREFIX_LEN);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ogs_hash_get(self.ipv6prefix_hash,
|
||||
&ipv6prefix, (ipv6prefix.len >> 3) + 1);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ extern int __pcf_log_domain;
|
|||
#undef OGS_LOG_DOMAIN
|
||||
#define OGS_LOG_DOMAIN __pcf_log_domain
|
||||
|
||||
#define OGS_PCF_MAX_NUM_OF_QOS_PROFILE 64
|
||||
|
||||
typedef struct pcf_qos_profile_s {
|
||||
char *reference;
|
||||
uint8_t qos_index;
|
||||
} pcf_qos_profile_t;
|
||||
|
||||
typedef struct pcf_context_s {
|
||||
ogs_list_t pcf_ue_am_list;
|
||||
#define PCF_UE_SM_IS_LAST_SESSION(__pCF) \
|
||||
|
|
@ -47,6 +54,9 @@ typedef struct pcf_context_s {
|
|||
|
||||
ogs_hash_t *ipv4addr_hash;
|
||||
ogs_hash_t *ipv6prefix_hash;
|
||||
|
||||
pcf_qos_profile_t qos_profile[OGS_PCF_MAX_NUM_OF_QOS_PROFILE];
|
||||
int num_of_qos_profile;
|
||||
} pcf_context_t;
|
||||
|
||||
struct pcf_ue_am_s {
|
||||
|
|
|
|||
|
|
@ -81,12 +81,14 @@ bool pcf_nbsf_management_handle_register(
|
|||
if (rv != OGS_OK) {
|
||||
strerror = ogs_msprintf("[%s:%d] Cannot parse http.location [%s]",
|
||||
pcf_ue_sm->supi, sess->psi, recvmsg->http.location);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!message.h.resource.component[1]) {
|
||||
strerror = ogs_msprintf("[%s:%d] No Binding ID [%s]",
|
||||
pcf_ue_sm->supi, sess->psi, recvmsg->http.location);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
|
||||
ogs_sbi_header_free(&header);
|
||||
goto cleanup;
|
||||
|
|
@ -97,6 +99,7 @@ bool pcf_nbsf_management_handle_register(
|
|||
if (rc == false || scheme == OpenAPI_uri_scheme_NULL) {
|
||||
strerror = ogs_msprintf("[%s:%d] Invalid URI [%s]",
|
||||
pcf_ue_sm->supi, sess->psi, header.uri);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
ogs_sbi_header_free(&header);
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
@ -108,6 +111,7 @@ bool pcf_nbsf_management_handle_register(
|
|||
if (!client) {
|
||||
strerror = ogs_msprintf("[%s:%d] ogs_sbi_client_add() failed",
|
||||
pcf_ue_sm->supi, sess->psi);
|
||||
status = OGS_SBI_HTTP_STATUS_INTERNAL_SERVER_ERROR;
|
||||
|
||||
ogs_sbi_header_free(&header);
|
||||
ogs_free(fqdn);
|
||||
|
|
|
|||
|
|
@ -101,7 +101,12 @@ void pcf_nnrf_handle_nf_discover(
|
|||
ogs_assert(xact->assoc_stream_id >= OGS_MIN_POOL_ID &&
|
||||
xact->assoc_stream_id <= OGS_MAX_POOL_ID);
|
||||
stream = ogs_sbi_stream_find_by_id(xact->assoc_stream_id);
|
||||
ogs_assert(stream);
|
||||
if (!stream) {
|
||||
ogs_warn("Associated SBI stream has already been removed [%d]",
|
||||
xact->assoc_stream_id);
|
||||
ogs_sbi_xact_remove(xact);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Send Response for SM Policy Association establishment */
|
||||
ogs_expect(true ==
|
||||
|
|
|
|||
|
|
@ -21,6 +21,42 @@
|
|||
|
||||
#include "npcf-handler.h"
|
||||
|
||||
static uint8_t pcf_qos_index_from_media(
|
||||
const char *qos_reference,
|
||||
OpenAPI_media_type_e media_type,
|
||||
const char **err_out)
|
||||
{
|
||||
int i;
|
||||
|
||||
ogs_assert(err_out);
|
||||
*err_out = NULL;
|
||||
|
||||
if (qos_reference) {
|
||||
for (i = 0; i < pcf_self()->num_of_qos_profile; i++) {
|
||||
if (!strcmp(pcf_self()->qos_profile[i].reference, qos_reference))
|
||||
return pcf_self()->qos_profile[i].qos_index;
|
||||
}
|
||||
|
||||
*err_out = "Unknown qosReference - check PCF qos_profiles config";
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (media_type) {
|
||||
case OpenAPI_media_type_AUDIO:
|
||||
return OGS_QOS_INDEX_1;
|
||||
case OpenAPI_media_type_VIDEO:
|
||||
return OGS_QOS_INDEX_2;
|
||||
case OpenAPI_media_type_CONTROL:
|
||||
return OGS_QOS_INDEX_5;
|
||||
case OpenAPI_media_type_NULL:
|
||||
*err_out = "Media-Type is Required";
|
||||
return 0;
|
||||
default:
|
||||
*err_out = "Unknown Media-Type";
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool pcf_npcf_am_policy_control_handle_create(pcf_ue_am_t *pcf_ue_am,
|
||||
ogs_sbi_stream_t *stream, ogs_sbi_message_t *message)
|
||||
{
|
||||
|
|
@ -285,11 +321,8 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
|
|||
|
||||
if (!SmPolicyContextData->ipv4_address &&
|
||||
!SmPolicyContextData->ipv6_address_prefix) {
|
||||
strerror = ogs_msprintf(
|
||||
"[%s:%d] No IPv4 address[%p] or IPv6 prefix[%p]",
|
||||
pcf_ue_sm->supi, sess->psi,
|
||||
SmPolicyContextData->ipv4_address,
|
||||
SmPolicyContextData->ipv6_address_prefix);
|
||||
strerror = ogs_msprintf("[%s:%d] No IPv4 address or IPv6 prefix",
|
||||
pcf_ue_sm->supi, sess->psi);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
@ -452,13 +485,27 @@ bool pcf_npcf_smpolicycontrol_handle_create(pcf_sess_t *sess,
|
|||
ogs_freeaddrinfo(addr);
|
||||
ogs_freeaddrinfo(addr6);
|
||||
|
||||
if (SmPolicyContextData->ipv4_address)
|
||||
ogs_assert(true ==
|
||||
pcf_sess_set_ipv4addr(sess, SmPolicyContextData->ipv4_address));
|
||||
if (SmPolicyContextData->ipv6_address_prefix)
|
||||
ogs_assert(true ==
|
||||
pcf_sess_set_ipv6prefix(
|
||||
sess, SmPolicyContextData->ipv6_address_prefix));
|
||||
if (SmPolicyContextData->ipv4_address) {
|
||||
if (pcf_sess_set_ipv4addr(
|
||||
sess, SmPolicyContextData->ipv4_address) == false) {
|
||||
strerror = ogs_msprintf("[%s:%d] Invalid ipv4Address [%s]",
|
||||
pcf_ue_sm->supi, sess->psi,
|
||||
SmPolicyContextData->ipv4_address);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (SmPolicyContextData->ipv6_address_prefix) {
|
||||
if (pcf_sess_set_ipv6prefix(
|
||||
sess, SmPolicyContextData->ipv6_address_prefix) == false) {
|
||||
strerror = ogs_msprintf("[%s:%d] Invalid ipv6AddressPrefix [%s]",
|
||||
pcf_ue_sm->supi, sess->psi,
|
||||
SmPolicyContextData->ipv6_address_prefix);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (SmPolicyContextData->ipv4_frame_route_list) {
|
||||
OpenAPI_lnode_t *node = NULL;
|
||||
|
|
@ -638,6 +685,7 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
ogs_ims_data_t ims_data;
|
||||
ogs_media_component_t *media_component = NULL;
|
||||
ogs_media_sub_component_t *sub = NULL;
|
||||
const char *qos_reference[OGS_MAX_NUM_OF_MEDIA_COMPONENT] = {0};
|
||||
|
||||
OpenAPI_list_t *MediaComponentList = NULL;
|
||||
OpenAPI_map_t *MediaComponentMap = NULL;
|
||||
|
|
@ -735,6 +783,7 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
if (MediaComponentMap) {
|
||||
MediaComponent = MediaComponentMap->value;
|
||||
if (MediaComponent) {
|
||||
int n;
|
||||
if (ims_data.num_of_media_component >=
|
||||
OGS_ARRAY_SIZE(ims_data.media_component)) {
|
||||
ogs_error("OVERFLOW ims_data.num_of_media_component "
|
||||
|
|
@ -744,8 +793,10 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
(int)OGS_ARRAY_SIZE(ims_data.media_component));
|
||||
break;
|
||||
}
|
||||
media_component = &ims_data.
|
||||
media_component[ims_data.num_of_media_component];
|
||||
n = ims_data.num_of_media_component;
|
||||
|
||||
media_component = &ims_data.media_component[n];
|
||||
qos_reference[n] = MediaComponent->qos_reference;
|
||||
media_component->media_component_number =
|
||||
MediaComponent->med_comp_n;
|
||||
media_component->media_type = MediaComponent->med_type;
|
||||
|
|
@ -871,28 +922,17 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
ogs_pcc_rule_t *db_pcc_rule = NULL;
|
||||
uint8_t qos_index = 0;
|
||||
ogs_media_component_t *media_component = &ims_data.media_component[i];
|
||||
const char *reference = qos_reference[i];
|
||||
const char *err_str = NULL;
|
||||
|
||||
if (media_component->media_type == OpenAPI_media_type_NULL) {
|
||||
strerror = ogs_msprintf("[%s:%d] Media-Type is Required",
|
||||
pcf_ue_sm->supi, sess->psi);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch(media_component->media_type) {
|
||||
case OpenAPI_media_type_AUDIO:
|
||||
qos_index = OGS_QOS_INDEX_1;
|
||||
break;
|
||||
case OpenAPI_media_type_VIDEO:
|
||||
qos_index = OGS_QOS_INDEX_2;
|
||||
break;
|
||||
case OpenAPI_media_type_CONTROL:
|
||||
qos_index = OGS_QOS_INDEX_5;
|
||||
break;
|
||||
default:
|
||||
strerror = ogs_msprintf("[%s:%d] Unknown Media-Type [%d]",
|
||||
pcf_ue_sm->supi, sess->psi, media_component->media_type);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
qos_index = pcf_qos_index_from_media(
|
||||
reference, media_component->media_type, &err_str);
|
||||
if (qos_index == 0) {
|
||||
strerror = ogs_msprintf("[%s:%d] %s",
|
||||
pcf_ue_sm->supi, sess->psi, err_str);
|
||||
status = reference ?
|
||||
OGS_SBI_HTTP_STATUS_FORBIDDEN :
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -903,7 +943,7 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
}
|
||||
}
|
||||
|
||||
if (!db_pcc_rule &&
|
||||
if (!db_pcc_rule && !reference &&
|
||||
(media_component->media_type == OpenAPI_media_type_CONTROL)) {
|
||||
/*
|
||||
* Check for default bearer for IMS signalling
|
||||
|
|
@ -1094,7 +1134,14 @@ bool pcf_npcf_policyauthorization_handle_create(pcf_sess_t *sess,
|
|||
cleanup:
|
||||
ogs_assert(status);
|
||||
ogs_assert(strerror);
|
||||
ogs_error("%s", strerror);
|
||||
|
||||
/*
|
||||
* Unknown qosReference is rejected with 403 in a negative test case.
|
||||
* Log it as warning to avoid treating the expected rejection as an error.
|
||||
*/
|
||||
ogs_log_message(
|
||||
status == OGS_SBI_HTTP_STATUS_FORBIDDEN ?
|
||||
OGS_LOG_WARN : OGS_LOG_ERROR, 0, "%s", strerror);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream, status, recvmsg, strerror, NULL,
|
||||
NULL));
|
||||
|
|
@ -1125,6 +1172,9 @@ cleanup:
|
|||
ogs_ims_data_free(&ims_data);
|
||||
OGS_SESSION_DATA_FREE(&session_data);
|
||||
|
||||
if (app_session)
|
||||
pcf_app_remove(app_session);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1148,6 +1198,7 @@ bool pcf_npcf_policyauthorization_handle_update(
|
|||
ogs_ims_data_t ims_data;
|
||||
ogs_media_component_t *media_component = NULL;
|
||||
ogs_media_sub_component_t *sub = NULL;
|
||||
const char *qos_reference[OGS_MAX_NUM_OF_MEDIA_COMPONENT] = {0};
|
||||
|
||||
OpenAPI_list_t *MediaComponentList = NULL;
|
||||
OpenAPI_map_t *MediaComponentMap = NULL;
|
||||
|
|
@ -1210,6 +1261,7 @@ bool pcf_npcf_policyauthorization_handle_update(
|
|||
if (MediaComponentMap) {
|
||||
MediaComponent = MediaComponentMap->value;
|
||||
if (MediaComponent) {
|
||||
int n;
|
||||
if (ims_data.num_of_media_component >=
|
||||
OGS_ARRAY_SIZE(ims_data.media_component)) {
|
||||
ogs_error("OVERFLOW ims_data.num_of_media_component "
|
||||
|
|
@ -1219,9 +1271,10 @@ bool pcf_npcf_policyauthorization_handle_update(
|
|||
(int)OGS_ARRAY_SIZE(ims_data.media_component));
|
||||
break;
|
||||
}
|
||||
media_component = &ims_data.
|
||||
media_component[ims_data.num_of_media_component];
|
||||
n = ims_data.num_of_media_component;
|
||||
|
||||
media_component = &ims_data.media_component[n];
|
||||
qos_reference[n] = MediaComponent->qos_reference;
|
||||
media_component->media_component_number =
|
||||
MediaComponent->med_comp_n;
|
||||
media_component->media_type = MediaComponent->med_type;
|
||||
|
|
@ -1321,28 +1374,17 @@ bool pcf_npcf_policyauthorization_handle_update(
|
|||
ogs_pcc_rule_t *db_pcc_rule = NULL;
|
||||
uint8_t qos_index = 0;
|
||||
ogs_media_component_t *media_component = &ims_data.media_component[i];
|
||||
const char *reference = qos_reference[i];
|
||||
const char *err_str = NULL;
|
||||
|
||||
if (media_component->media_type == OpenAPI_media_type_NULL) {
|
||||
strerror = ogs_msprintf("[%s:%d] Media-Type is Required",
|
||||
pcf_ue_sm->supi, sess->psi);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch(media_component->media_type) {
|
||||
case OpenAPI_media_type_AUDIO:
|
||||
qos_index = OGS_QOS_INDEX_1;
|
||||
break;
|
||||
case OpenAPI_media_type_VIDEO:
|
||||
qos_index = OGS_QOS_INDEX_2;
|
||||
break;
|
||||
case OpenAPI_media_type_CONTROL:
|
||||
qos_index = OGS_QOS_INDEX_5;
|
||||
break;
|
||||
default:
|
||||
strerror = ogs_msprintf("[%s:%d] Unknown Media-Type [%d]",
|
||||
pcf_ue_sm->supi, sess->psi, media_component->media_type);
|
||||
status = OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
qos_index = pcf_qos_index_from_media(
|
||||
reference, media_component->media_type, &err_str);
|
||||
if (qos_index == 0) {
|
||||
strerror = ogs_msprintf("[%s:%d] %s",
|
||||
pcf_ue_sm->supi, sess->psi, err_str);
|
||||
status = reference ?
|
||||
OGS_SBI_HTTP_STATUS_FORBIDDEN :
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
|
@ -1353,7 +1395,7 @@ bool pcf_npcf_policyauthorization_handle_update(
|
|||
}
|
||||
}
|
||||
|
||||
if (!db_pcc_rule &&
|
||||
if (!db_pcc_rule && !reference &&
|
||||
(media_component->media_type == OpenAPI_media_type_CONTROL)) {
|
||||
/*
|
||||
* Check for default bearer for IMS signalling
|
||||
|
|
|
|||
|
|
@ -711,24 +711,26 @@ void pcf_state_operational(ogs_fsm_t *s, pcf_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
|
|||
|
|
@ -93,14 +93,28 @@ void pcf_sm_state_operational(ogs_fsm_t *s, pcf_event_t *e)
|
|||
} else {
|
||||
SWITCH(message->h.resource.component[2])
|
||||
CASE(OGS_SBI_RESOURCE_NAME_DELETE)
|
||||
handled = pcf_npcf_smpolicycontrol_handle_delete(
|
||||
sess, stream, message);
|
||||
if (!handled) {
|
||||
ogs_error("[%s:%d] "
|
||||
"pcf_npcf_smpolicycontrol_handle_delete() failed",
|
||||
pcf_ue_sm->supi, sess->psi);
|
||||
OGS_FSM_TRAN(s, pcf_sm_state_exception);
|
||||
}
|
||||
SWITCH(message->h.method)
|
||||
CASE(OGS_SBI_HTTP_METHOD_POST)
|
||||
handled = pcf_npcf_smpolicycontrol_handle_delete(
|
||||
sess, stream, message);
|
||||
if (!handled) {
|
||||
ogs_error("[%s:%d] "
|
||||
"pcf_npcf_smpolicycontrol_handle_delete() "
|
||||
"failed", pcf_ue_sm->supi, sess->psi);
|
||||
OGS_FSM_TRAN(s, pcf_sm_state_exception);
|
||||
}
|
||||
break;
|
||||
|
||||
DEFAULT
|
||||
ogs_error("[%s:%d] Invalid HTTP method [%s] for "
|
||||
"/delete", pcf_ue_sm->supi, sess->psi,
|
||||
message->h.method);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_METHOD_NOT_ALLOWED,
|
||||
message, "Invalid HTTP method",
|
||||
message->h.uri, NULL));
|
||||
END
|
||||
break;
|
||||
|
||||
DEFAULT
|
||||
|
|
@ -119,8 +133,22 @@ void pcf_sm_state_operational(ogs_fsm_t *s, pcf_event_t *e)
|
|||
if (message->h.resource.component[2]) {
|
||||
SWITCH(message->h.resource.component[2])
|
||||
CASE(OGS_SBI_RESOURCE_NAME_DELETE)
|
||||
handled = pcf_npcf_policyauthorization_handle_delete(
|
||||
sess, e->app, stream, message);
|
||||
SWITCH(message->h.method)
|
||||
CASE(OGS_SBI_HTTP_METHOD_POST)
|
||||
handled =
|
||||
pcf_npcf_policyauthorization_handle_delete(
|
||||
sess, e->app, stream, message);
|
||||
break;
|
||||
DEFAULT
|
||||
ogs_error("[%s:%d] Invalid HTTP method [%s] for "
|
||||
"/delete", pcf_ue_sm->supi, sess->psi,
|
||||
message->h.method);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_METHOD_NOT_ALLOWED,
|
||||
message, "Invalid HTTP method",
|
||||
message->h.uri, NULL));
|
||||
END
|
||||
break;
|
||||
DEFAULT
|
||||
ogs_error("[%s:%d] Invalid resource name [%s]",
|
||||
|
|
|
|||
|
|
@ -248,9 +248,18 @@ static int request_handler(ogs_sbi_request_t *request, void *data)
|
|||
ogs_sbi_discovery_option_set_requester_nf_instance_id(
|
||||
discovery_option, val);
|
||||
} else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_SERVICE_NAMES)) {
|
||||
if (val)
|
||||
ogs_sbi_discovery_option_parse_service_names(
|
||||
discovery_option, val);
|
||||
if (val) {
|
||||
if (ogs_sbi_discovery_option_parse_service_names(
|
||||
discovery_option, val) != OGS_OK) {
|
||||
ogs_error("Invalid service-names [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid service-names", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* So, we'll use the first item in service-names list.
|
||||
|
|
@ -273,32 +282,79 @@ static int request_handler(ogs_sbi_request_t *request, void *data)
|
|||
service_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);
|
||||
if (val) {
|
||||
if (ogs_sbi_discovery_option_parse_snssais(
|
||||
discovery_option, val) != OGS_OK) {
|
||||
ogs_error("Invalid snssais [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid snssais", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_GUAMI)) {
|
||||
if (val)
|
||||
ogs_sbi_discovery_option_parse_guami(discovery_option, val);
|
||||
if (val) {
|
||||
if (ogs_sbi_discovery_option_parse_guami(
|
||||
discovery_option, val) != OGS_OK) {
|
||||
ogs_error("Invalid guami [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid guami", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
} 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_GUAMI)) {
|
||||
if (val)
|
||||
ogs_sbi_discovery_option_parse_guami(discovery_option, val);
|
||||
if (val) {
|
||||
if (ogs_sbi_discovery_option_parse_tai(
|
||||
discovery_option, val) != OGS_OK) {
|
||||
ogs_error("Invalid tai [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid tai", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
}
|
||||
} else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_TARGET_PLMN_LIST)) {
|
||||
if (val)
|
||||
discovery_option->num_of_target_plmn_list =
|
||||
ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->target_plmn_list, val);
|
||||
if (val) {
|
||||
int n = ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->target_plmn_list, val);
|
||||
if (n < 0) {
|
||||
ogs_error("Invalid target-plmn-list [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid target-plmn-list", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option->num_of_target_plmn_list = n;
|
||||
}
|
||||
} else if (!strcasecmp(key, OGS_SBI_CUSTOM_DISCOVERY_HNRF_URI)) {
|
||||
ogs_sbi_discovery_option_set_hnrf_uri(discovery_option, val);
|
||||
} else if (!strcasecmp(key,
|
||||
OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_PLMN_LIST)) {
|
||||
if (val)
|
||||
discovery_option->num_of_requester_plmn_list =
|
||||
ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->requester_plmn_list, val);
|
||||
if (val) {
|
||||
int n = ogs_sbi_discovery_option_parse_plmn_list(
|
||||
discovery_option->requester_plmn_list, val);
|
||||
if (n < 0) {
|
||||
ogs_error("Invalid requester-plmn-list [%s]", val);
|
||||
scp_assoc_remove(assoc);
|
||||
ogs_assert(true ==
|
||||
ogs_sbi_server_send_error(stream,
|
||||
OGS_SBI_HTTP_STATUS_BAD_REQUEST, NULL,
|
||||
"Invalid requester-plmn-list", val, NULL));
|
||||
return OGS_ERROR;
|
||||
}
|
||||
discovery_option->num_of_requester_plmn_list = n;
|
||||
}
|
||||
} else if (!strcasecmp(key,
|
||||
OGS_SBI_CUSTOM_DISCOVERY_REQUESTER_FEATURES)) {
|
||||
if (val)
|
||||
|
|
|
|||
|
|
@ -289,24 +289,26 @@ void scp_state_operational(ogs_fsm_t *s, scp_event_t *e)
|
|||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_subscribe(
|
||||
ogs_sbi_self()->nf_instance->nf_type,
|
||||
subscription_data->req_nf_instance_id,
|
||||
subscription_data->subscr_cond.nf_type,
|
||||
subscription_data->subscr_cond.service_name));
|
||||
|
||||
ogs_error("[%s] Subscription validity expired",
|
||||
subscription_data->id);
|
||||
ogs_sbi_subscription_data_remove(subscription_data);
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
/*
|
||||
* Helper strdup-s the fields we need, removes the old
|
||||
* subscription so the pool slot is freed, then resubscribes.
|
||||
*/
|
||||
(void)ogs_nnrf_nfm_send_nf_status_subscribe_renew(
|
||||
subscription_data);
|
||||
break;
|
||||
|
||||
case OGS_TIMER_SUBSCRIPTION_PATCH:
|
||||
subscription_data = e->h.sbi.data;
|
||||
ogs_assert(subscription_data);
|
||||
|
||||
ogs_assert(true ==
|
||||
ogs_nnrf_nfm_send_nf_status_update(subscription_data));
|
||||
if (ogs_nnrf_nfm_send_nf_status_update(subscription_data) != true)
|
||||
ogs_error("[%s] NF status subscription update failed",
|
||||
subscription_data->id ?
|
||||
subscription_data->id : "Unknown");
|
||||
|
||||
ogs_info("[%s] Need to update Subscription",
|
||||
subscription_data->id);
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue