diff --git a/lib/core/ogs-core.c b/lib/core/ogs-core.c index 3341df616..4b76be5d5 100644 --- a/lib/core/ogs-core.c +++ b/lib/core/ogs-core.c @@ -38,9 +38,9 @@ static ogs_core_context_t self = { void ogs_core_initialize(void) { + ogs_mem_init(); ogs_log_init(); ogs_pkbuf_init(); - ogs_mem_init(); ogs_socket_init(); ogs_tlv_init(); @@ -56,9 +56,9 @@ void ogs_core_terminate(void) { ogs_tlv_final(); ogs_socket_final(); - ogs_mem_final(); ogs_pkbuf_final(); ogs_log_final(); + ogs_mem_final(); } ogs_core_context_t *ogs_core(void) diff --git a/lib/core/ogs-memory.h b/lib/core/ogs-memory.h index 0dc909d0b..1570b3bf7 100644 --- a/lib/core/ogs-memory.h +++ b/lib/core/ogs-memory.h @@ -58,7 +58,7 @@ void *ogs_realloc_debug( void *ptr, size_t size, const char *file_line); int ogs_free_debug(void *ptr); -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 /***************************************** * Memory Pool - Use talloc library diff --git a/lib/core/ogs-pkbuf.c b/lib/core/ogs-pkbuf.c index 562088f1f..6d3cbe76a 100644 --- a/lib/core/ogs-pkbuf.c +++ b/lib/core/ogs-pkbuf.c @@ -99,6 +99,7 @@ void ogs_pkbuf_init(void) { #if OGS_USE_TALLOC == 0 ogs_pool_init(&pkbuf_pool, ogs_core()->pkbuf.pool); + #endif } @@ -219,7 +220,7 @@ void ogs_pkbuf_pool_destroy(ogs_pkbuf_pool_t *pool) ogs_pkbuf_t *ogs_pkbuf_alloc_debug( ogs_pkbuf_pool_t *pool, unsigned int size, const char *file_line) { -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 ogs_pkbuf_t *pkbuf = NULL; pkbuf = ogs_talloc_zero_size(pool, sizeof(*pkbuf) + size, file_line); @@ -287,7 +288,7 @@ ogs_pkbuf_t *ogs_pkbuf_alloc_debug( void ogs_pkbuf_free(ogs_pkbuf_t *pkbuf) { -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 ogs_talloc_free(pkbuf, OGS_FILE_LINE); #else ogs_pkbuf_pool_t *pool = NULL; @@ -315,7 +316,7 @@ void ogs_pkbuf_free(ogs_pkbuf_t *pkbuf) ogs_pkbuf_t *ogs_pkbuf_copy_debug(ogs_pkbuf_t *pkbuf, const char *file_line) { -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 ogs_pkbuf_t *newbuf; #else ogs_pkbuf_pool_t *pool = NULL; @@ -331,7 +332,7 @@ ogs_pkbuf_t *ogs_pkbuf_copy_debug(ogs_pkbuf_t *pkbuf, const char *file_line) return NULL; } -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 newbuf = ogs_pkbuf_alloc_debug(NULL, size, file_line); if (!newbuf) { ogs_error("ogs_pkbuf_alloc() failed [size=%d]", size); diff --git a/lib/core/ogs-pkbuf.h b/lib/core/ogs-pkbuf.h index ba81c2622..5dc065557 100644 --- a/lib/core/ogs-pkbuf.h +++ b/lib/core/ogs-pkbuf.h @@ -35,7 +35,7 @@ typedef struct ogs_cluster_s { unsigned int reference_count; } ogs_cluster_t; -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 typedef void ogs_pkbuf_pool_t; #else typedef struct ogs_pkbuf_pool_s ogs_pkbuf_pool_t; diff --git a/lib/core/ogs-pool.h b/lib/core/ogs-pool.h index 94ded03a8..e5f3c8c4f 100644 --- a/lib/core/ogs-pool.h +++ b/lib/core/ogs-pool.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -28,7 +28,15 @@ extern "C" { #endif -typedef unsigned int ogs_index_t; +#if OGS_USE_TALLOC == 1 +#define ogs_pool_create ogs_malloc +#define ogs_pool_destroy ogs_free +#else +#define ogs_pool_create malloc +#define ogs_pool_destroy free +#endif + +typedef uint32_t ogs_pool_id_t; #define OGS_POOL(pool, type) \ struct { \ @@ -41,11 +49,11 @@ typedef unsigned int ogs_index_t; #define ogs_pool_init(pool, _size) do { \ int i; \ (pool)->name = #pool; \ - (pool)->free = malloc(sizeof(*(pool)->free) * _size); \ + (pool)->free = ogs_pool_create(sizeof(*(pool)->free) * _size); \ ogs_assert((pool)->free); \ - (pool)->array = malloc(sizeof(*(pool)->array) * _size); \ + (pool)->array = ogs_pool_create(sizeof(*(pool)->array) * _size); \ ogs_assert((pool)->array); \ - (pool)->index = malloc(sizeof(*(pool)->index) * _size); \ + (pool)->index = ogs_pool_create(sizeof(*(pool)->index) * _size); \ ogs_assert((pool)->index); \ (pool)->size = (pool)->avail = _size; \ (pool)->head = (pool)->tail = 0; \ @@ -59,9 +67,9 @@ typedef unsigned int ogs_index_t; if (((pool)->size != (pool)->avail)) \ ogs_error("%d in '%s[%d]' were not released.", \ (pool)->size - (pool)->avail, (pool)->name, (pool)->size); \ - free((pool)->free); \ - free((pool)->array); \ - free((pool)->index); \ + ogs_pool_destroy((pool)->free); \ + ogs_pool_destroy((pool)->array); \ + ogs_pool_destroy((pool)->index); \ } while (0) #define ogs_pool_index(pool, node) (((node) - (pool)->array)+1) @@ -93,30 +101,23 @@ typedef unsigned int ogs_index_t; #define ogs_pool_size(pool) ((pool)->size) #define ogs_pool_avail(pool) ((pool)->avail) -#define ogs_index_init(pool, _size) do { \ +#define ogs_pool_sequence_id_generate(pool) do { \ int i; \ - (pool)->name = #pool; \ - (pool)->free = ogs_malloc(sizeof(*(pool)->free) * _size); \ - ogs_assert((pool)->free); \ - (pool)->array = ogs_malloc(sizeof(*(pool)->array) * _size); \ - ogs_assert((pool)->array); \ - (pool)->index = ogs_malloc(sizeof(*(pool)->index) * _size); \ - ogs_assert((pool)->index); \ - (pool)->size = (pool)->avail = _size; \ - (pool)->head = (pool)->tail = 0; \ - for (i = 0; i < _size; i++) { \ - (pool)->free[i] = &((pool)->array[i]); \ - (pool)->index[i] = NULL; \ - } \ + for (i = 0; i < (pool)->size; i++) \ + (pool)->array[i] = i+1; \ } while (0) -#define ogs_index_final(pool) do { \ - if (((pool)->size != (pool)->avail)) \ - ogs_error("%d in '%s[%d]' were not released.", \ - (pool)->size - (pool)->avail, (pool)->name, (pool)->size); \ - ogs_free((pool)->free); \ - ogs_free((pool)->array); \ - ogs_free((pool)->index); \ +#define ogs_pool_random_id_generate(pool) do { \ + int i, j; \ + ogs_pool_id_t temp; \ + for (i = 0; i < (pool)->size; i++) \ + (pool)->array[i] = i+1; \ + for (i = (pool)->size - 1; i > 0; i--) { \ + j = ogs_random32() % (i + 1); \ + temp = (pool)->array[i]; \ + (pool)->array[i] = (pool)->array[j]; \ + (pool)->array[j] = temp; \ + } \ } while (0) #ifdef __cplusplus diff --git a/lib/core/ogs-strings.h b/lib/core/ogs-strings.h index 21eecf455..aa2207bdf 100644 --- a/lib/core/ogs-strings.h +++ b/lib/core/ogs-strings.h @@ -110,7 +110,7 @@ char *ogs_mstrcatf_debug( char *source, const char *file_line, const char *message, ...) OGS_GNUC_PRINTF(3, 4); -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 /***************************************** * Memory Pool - Use talloc library diff --git a/lib/gtp/v1/build.c b/lib/gtp/v1/build.c index 4b849e741..85b95140b 100644 --- a/lib/gtp/v1/build.c +++ b/lib/gtp/v1/build.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 Sukchan Lee * * This file is part of Open5GS. * diff --git a/lib/gtp/v1/build.h b/lib/gtp/v1/build.h index 742d0380e..084068042 100644 --- a/lib/gtp/v1/build.h +++ b/lib/gtp/v1/build.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -31,6 +32,7 @@ extern "C" { ogs_pkbuf_t *ogs_gtp1_build_echo_request(uint8_t type); ogs_pkbuf_t *ogs_gtp1_build_echo_response(uint8_t type, uint8_t recovery); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/v1/path.c b/lib/gtp/v1/path.c index 55433d30a..d13145fa1 100644 --- a/lib/gtp/v1/path.c +++ b/lib/gtp/v1/path.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -143,8 +144,7 @@ void ogs_gtp1_send_error_message( ogs_expect(rv == OGS_OK); } -void ogs_gtp1_send_echo_request( - ogs_gtp_node_t *gnode) +void ogs_gtp1_send_echo_request(ogs_gtp_node_t *gnode) { int rv; ogs_pkbuf_t *pkbuf = NULL; diff --git a/lib/gtp/v2/build.c b/lib/gtp/v2/build.c index 1c44fa6bc..90ce1e7c5 100644 --- a/lib/gtp/v2/build.c +++ b/lib/gtp/v2/build.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -57,7 +57,7 @@ ogs_pkbuf_t *ogs_gtp2_build_echo_response( return ogs_gtp2_build_msg(>p_message); } -ogs_pkbuf_t *ogs_gtp2_build_error_indication( +ogs_pkbuf_t *ogs_gtp1_build_error_indication( uint32_t teid, ogs_sockaddr_t *addr) { ogs_pkbuf_t *pkbuf = NULL; @@ -112,3 +112,91 @@ ogs_pkbuf_t *ogs_gtp2_build_error_indication( return pkbuf; } + +void ogs_gtp2_fill_header( + ogs_gtp2_header_t *gtp_hdesc, ogs_gtp2_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf) +{ + ogs_gtp2_header_t *gtp_h = NULL; + ogs_gtp2_extension_header_t *ext_h = NULL; + uint8_t flags; + uint8_t gtp_hlen = 0; + + ogs_assert(gtp_hdesc); + ogs_assert(ext_hdesc); + ogs_assert(pkbuf); + + /* Processing GTP Flags */ + flags = gtp_hdesc->flags; + flags |= OGS_GTPU_FLAGS_V | OGS_GTPU_FLAGS_PT; + if (ext_hdesc->qos_flow_identifier) flags |= OGS_GTPU_FLAGS_E; + + /* Define GTP Header Size */ + if (flags & OGS_GTPU_FLAGS_E) + gtp_hlen = OGS_GTPV1U_HEADER_LEN+8; + else if (flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) + gtp_hlen = OGS_GTPV1U_HEADER_LEN+4; + else + gtp_hlen = OGS_GTPV1U_HEADER_LEN; + + ogs_pkbuf_push(pkbuf, gtp_hlen); + + /* Fill GTP Header */ + gtp_h = (ogs_gtp2_header_t *)pkbuf->data; + ogs_assert(gtp_h); + memset(gtp_h, 0, gtp_hlen); + + gtp_h->flags = flags; + gtp_h->type = gtp_hdesc->type; + + if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ || + gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_RSP || + gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { + /* + * TS29.281 5.1 General format in GTP-U header + * + * - The Echo Request/Response and Supported Extension Headers + * notification messages, where the Tunnel Endpoint Identifier + * shall be set to all zeroes. + * - The Error Indication message where the Tunnel Endpoint Identifier + * shall be set to all zeros. + */ + ogs_assert(gtp_hdesc->teid == 0); + } + + gtp_h->teid = htobe32(gtp_hdesc->teid); + + /* + * TS29.281 5.1 General format in GTP-U header + * + * Length: This field indicates the length in octets of the payload, + * i.e. the rest of the packet following the mandatory part of + * the GTP header (that is the first 8 octets). The Sequence Number, + * the N-PDU Number or any Extension headers shall be considered + * to be part of the payload, i.e. included in the length count. + */ + gtp_h->length = htobe16(pkbuf->len - OGS_GTPV1U_HEADER_LEN); + + /* Fill Extention Header */ + if (gtp_h->flags & OGS_GTPU_FLAGS_E) { + ext_h = (ogs_gtp2_extension_header_t *) + (pkbuf->data + OGS_GTPV1U_HEADER_LEN); + ogs_assert(ext_h); + + if (ext_hdesc->qos_flow_identifier) { + /* 5G Core */ + ext_h->type = OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = ext_hdesc->pdu_type; + ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier; + ext_h->next_type = + OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } else { + /* EPC */ + ext_h->type = ext_hdesc->type; + ext_h->len = 1; + ext_h->next_type = + OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } + } +} diff --git a/lib/gtp/v2/build.h b/lib/gtp/v2/build.h index be06b2aef..0e61359f0 100644 --- a/lib/gtp/v2/build.h +++ b/lib/gtp/v2/build.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -33,9 +33,13 @@ ogs_pkbuf_t *ogs_gtp2_build_echo_request( ogs_pkbuf_t *ogs_gtp2_build_echo_response( uint8_t type, uint8_t recovery, uint8_t features); -ogs_pkbuf_t *ogs_gtp2_build_error_indication( +ogs_pkbuf_t *ogs_gtp1_build_error_indication( uint32_t teid, ogs_sockaddr_t *addr); +void ogs_gtp2_fill_header( + ogs_gtp2_header_t *gtp_hdesc, ogs_gtp2_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/v2/path.c b/lib/gtp/v2/path.c index 578960590..790279ce9 100644 --- a/lib/gtp/v2/path.c +++ b/lib/gtp/v2/path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -27,92 +27,11 @@ int ogs_gtp2_send_user_plane( char buf[OGS_ADDRSTRLEN]; int rv; - ogs_gtp2_header_t *gtp_h = NULL; - ogs_gtp2_extension_header_t *ext_h = NULL; - uint8_t flags; - uint8_t gtp_hlen = 0; - - ogs_assert(gnode); - ogs_assert(gtp_hdesc); - ogs_assert(ext_hdesc); - ogs_assert(pkbuf); - - /* Processing GTP Flags */ - flags = gtp_hdesc->flags; - flags |= OGS_GTPU_FLAGS_V | OGS_GTPU_FLAGS_PT; - if (ext_hdesc->qos_flow_identifier) flags |= OGS_GTPU_FLAGS_E; - - /* Define GTP Header Size */ - if (flags & OGS_GTPU_FLAGS_E) - gtp_hlen = OGS_GTPV1U_HEADER_LEN+8; - else if (flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) - gtp_hlen = OGS_GTPV1U_HEADER_LEN+4; - else - gtp_hlen = OGS_GTPV1U_HEADER_LEN; - - ogs_pkbuf_push(pkbuf, gtp_hlen); - - /* Fill GTP Header */ - gtp_h = (ogs_gtp2_header_t *)pkbuf->data; - ogs_assert(gtp_h); - memset(gtp_h, 0, gtp_hlen); - - gtp_h->flags = flags; - gtp_h->type = gtp_hdesc->type; - - if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ || - gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_RSP || - gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { - /* - * TS29.281 5.1 General format in GTP-U header - * - * - The Echo Request/Response and Supported Extension Headers - * notification messages, where the Tunnel Endpoint Identifier - * shall be set to all zeroes. - * - The Error Indication message where the Tunnel Endpoint Identifier - * shall be set to all zeros. - */ - ogs_assert(gtp_hdesc->teid == 0); - } - - gtp_h->teid = htobe32(gtp_hdesc->teid); - - /* - * TS29.281 5.1 General format in GTP-U header - * - * Length: This field indicates the length in octets of the payload, - * i.e. the rest of the packet following the mandatory part of - * the GTP header (that is the first 8 octets). The Sequence Number, - * the N-PDU Number or any Extension headers shall be considered - * to be part of the payload, i.e. included in the length count. - */ - gtp_h->length = htobe16(pkbuf->len - OGS_GTPV1U_HEADER_LEN); - - /* Fill Extention Header */ - if (gtp_h->flags & OGS_GTPU_FLAGS_E) { - ext_h = (ogs_gtp2_extension_header_t *) - (pkbuf->data + OGS_GTPV1U_HEADER_LEN); - ogs_assert(ext_h); - - if (ext_hdesc->qos_flow_identifier) { - /* 5G Core */ - ext_h->type = OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = ext_hdesc->pdu_type; - ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier; - ext_h->next_type = - OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } else { - /* EPC */ - ext_h->type = ext_hdesc->type; - ext_h->len = 1; - ext_h->next_type = - OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } - } + ogs_gtp2_fill_header(gtp_hdesc, ext_hdesc, pkbuf); ogs_trace("SEND GTP-U[%d] to Peer[%s] : TEID[0x%x]", gtp_hdesc->type, OGS_ADDR(&gnode->addr, buf), gtp_hdesc->teid); + rv = ogs_gtp_sendto(gnode, pkbuf); if (rv != OGS_OK) { if (ogs_socket_errno != OGS_EAGAIN) { @@ -342,3 +261,39 @@ void ogs_gtp2_send_echo_response(ogs_gtp_xact_t *xact, rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); } + +void ogs_gtp1_send_error_indication( + ogs_sock_t *sock, uint32_t teid, uint8_t qfi, const ogs_sockaddr_t *to) +{ + ssize_t sent; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_gtp2_header_t gtp_hdesc; + ogs_gtp2_extension_header_t ext_hdesc; + + ogs_assert(sock); + ogs_assert(to); + + pkbuf = ogs_gtp1_build_error_indication(teid, &sock->local_addr); + if (!pkbuf) { + ogs_error("ogs_gtp1_build_error_indication() failed"); + return; + } + + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); + + gtp_hdesc.type = OGS_GTPU_MSGTYPE_ERR_IND; + gtp_hdesc.flags = OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_E; + ext_hdesc.type = OGS_GTP2_EXTENSION_HEADER_TYPE_UDP_PORT; + ext_hdesc.qos_flow_identifier = qfi; + + ogs_gtp2_fill_header(>p_hdesc, &ext_hdesc, pkbuf); + + sent = ogs_sendto(sock->fd, pkbuf->data, pkbuf->len, 0, to); + if (sent < 0 || sent != pkbuf->len) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_sendto() failed"); + } + ogs_pkbuf_free(pkbuf); +} diff --git a/lib/gtp/v2/path.h b/lib/gtp/v2/path.h index b9bd26ad1..72e99bfbe 100644 --- a/lib/gtp/v2/path.h +++ b/lib/gtp/v2/path.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -44,6 +44,9 @@ void ogs_gtp2_send_echo_request( void ogs_gtp2_send_echo_response(ogs_gtp_xact_t *xact, uint8_t recovery, uint8_t features); +void ogs_gtp1_send_error_indication( + ogs_sock_t *sock, uint32_t teid, uint8_t qfi, const ogs_sockaddr_t *to); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/xact.h b/lib/gtp/xact.h index 216cb04f1..5bb25242b 100644 --- a/lib/gtp/xact.h +++ b/lib/gtp/xact.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -57,7 +58,7 @@ extern "C" { */ typedef struct ogs_gtp_xact_s { ogs_lnode_t node; /**< A node of list */ - ogs_index_t index; + ogs_pool_id_t index; uint8_t gtp_version; /**< 1 or 2 */ diff --git a/lib/pfcp/build.c b/lib/pfcp/build.c index a1b3e15fb..56899bd76 100644 --- a/lib/pfcp/build.c +++ b/lib/pfcp/build.c @@ -446,17 +446,15 @@ bool ogs_pfcp_build_created_pdr( ogs_assert(pdr); - if (ogs_pfcp_self()->up_function_features.ftup) { - if (pdr->f_teid_len) { - memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, pdr->f_teid_len); - pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); + if (pdr->f_teid_len) { + memcpy(&pdrbuf[i].f_teid, &pdr->f_teid, pdr->f_teid_len); + pdrbuf[i].f_teid.teid = htobe32(pdr->f_teid.teid); - message->local_f_teid.presence = 1; - message->local_f_teid.data = &pdrbuf[i].f_teid; - message->local_f_teid.len = pdr->f_teid_len; + message->local_f_teid.presence = 1; + message->local_f_teid.data = &pdrbuf[i].f_teid; + message->local_f_teid.len = pdr->f_teid_len; - pdr_presence = true; - } + pdr_presence = true; } if (pdr_presence == true) { diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index a76560307..8964b49dd 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -27,18 +27,23 @@ static int context_initialized = 0; static OGS_POOL(ogs_pfcp_node_pool, ogs_pfcp_node_t); static OGS_POOL(ogs_pfcp_sess_pool, ogs_pfcp_sess_t); -static OGS_POOL(ogs_pfcp_pdr_pool, ogs_pfcp_pdr_t); static OGS_POOL(ogs_pfcp_far_pool, ogs_pfcp_far_t); static OGS_POOL(ogs_pfcp_urr_pool, ogs_pfcp_urr_t); static OGS_POOL(ogs_pfcp_qer_pool, ogs_pfcp_qer_t); static OGS_POOL(ogs_pfcp_bar_pool, ogs_pfcp_bar_t); +static OGS_POOL(ogs_pfcp_pdr_pool, ogs_pfcp_pdr_t); +static OGS_POOL(ogs_pfcp_pdr_teid_pool, ogs_pool_id_t); +static ogs_pool_id_t *pdr_random_to_index; + +static OGS_POOL(ogs_pfcp_rule_pool, ogs_pfcp_rule_t); + static OGS_POOL(ogs_pfcp_dev_pool, ogs_pfcp_dev_t); static OGS_POOL(ogs_pfcp_subnet_pool, ogs_pfcp_subnet_t); -static OGS_POOL(ogs_pfcp_rule_pool, ogs_pfcp_rule_t); void ogs_pfcp_context_init(void) { + int i; ogs_assert(context_initialized == 0); /* Initialize SMF context */ @@ -52,8 +57,6 @@ void ogs_pfcp_context_init(void) ogs_pool_init(&ogs_pfcp_sess_pool, ogs_app()->pool.sess); - ogs_pool_init(&ogs_pfcp_pdr_pool, - ogs_app()->pool.sess * OGS_MAX_NUM_OF_PDR); ogs_pool_init(&ogs_pfcp_far_pool, ogs_app()->pool.sess * OGS_MAX_NUM_OF_FAR); ogs_pool_init(&ogs_pfcp_urr_pool, @@ -63,6 +66,17 @@ void ogs_pfcp_context_init(void) ogs_pool_init(&ogs_pfcp_bar_pool, ogs_app()->pool.sess * OGS_MAX_NUM_OF_BAR); + ogs_pool_init(&ogs_pfcp_pdr_pool, + ogs_app()->pool.sess * OGS_MAX_NUM_OF_PDR); + ogs_pool_init(&ogs_pfcp_pdr_teid_pool, ogs_pfcp_pdr_pool.size); + ogs_pool_random_id_generate(&ogs_pfcp_pdr_teid_pool); + + pdr_random_to_index = ogs_calloc( + sizeof(ogs_pool_id_t), ogs_pfcp_pdr_pool.size); + ogs_assert(pdr_random_to_index); + for (i = 0; i < ogs_pfcp_pdr_pool.size; i++) + pdr_random_to_index[ogs_pfcp_pdr_teid_pool.array[i]] = i; + ogs_pool_init(&ogs_pfcp_rule_pool, ogs_app()->pool.sess * OGS_MAX_NUM_OF_PDR * OGS_MAX_NUM_OF_FLOW_IN_PDR); @@ -98,8 +112,11 @@ void ogs_pfcp_context_final(void) ogs_pool_final(&ogs_pfcp_subnet_pool); ogs_pool_final(&ogs_pfcp_rule_pool); - ogs_pool_final(&ogs_pfcp_sess_pool); ogs_pool_final(&ogs_pfcp_pdr_pool); + ogs_pool_final(&ogs_pfcp_pdr_teid_pool); + ogs_free(pdr_random_to_index); + + ogs_pool_final(&ogs_pfcp_sess_pool); ogs_pool_final(&ogs_pfcp_far_pool); ogs_pool_final(&ogs_pfcp_urr_pool); ogs_pool_final(&ogs_pfcp_qer_pool); @@ -894,6 +911,16 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_add(ogs_pfcp_sess_t *sess) } memset(pdr, 0, sizeof *pdr); + pdr->obj.type = OGS_PFCP_OBJ_PDR_TYPE; + pdr->src_if = OGS_PFCP_INTERFACE_UNKNOWN; + + /* Set TEID */ + ogs_pool_alloc(&ogs_pfcp_pdr_teid_pool, &pdr->teid_node); + ogs_assert(pdr->teid_node); + + pdr->teid = *(pdr->teid_node); + + /* Set PDR-ID */ ogs_pool_alloc(&sess->pdr_id_pool, &pdr->id_node); if (pdr->id_node == NULL) { ogs_error("pdr_id_pool() failed"); @@ -901,17 +928,9 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_add(ogs_pfcp_sess_t *sess) return NULL; } - pdr->obj.type = OGS_PFCP_OBJ_PDR_TYPE; - - pdr->index = ogs_pool_index(&ogs_pfcp_pdr_pool, pdr); - ogs_assert(pdr->index > 0 && - pdr->index <= ogs_app()->pool.sess * OGS_MAX_NUM_OF_PDR); - pdr->id = *(pdr->id_node); ogs_assert(pdr->id > 0 && pdr->id <= OGS_MAX_NUM_OF_PDR); - pdr->src_if = OGS_PFCP_INTERFACE_UNKNOWN; - pdr->sess = sess; ogs_list_add(&sess->pdr_list, pdr); @@ -948,12 +967,88 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( return pdr; } +void ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr) +{ + int i = 0; + + ogs_assert(pdr); + ogs_assert(pdr->f_teid.teid > 0 && + pdr->f_teid.teid <= ogs_pfcp_pdr_teid_pool.size); + + /* Find out the Array Index for the restored TEID. */ + i = pdr_random_to_index[pdr->f_teid.teid]; + ogs_assert(i < ogs_pfcp_pdr_teid_pool.size); + + ogs_assert(pdr->teid_node); + /* + * If SWAP has already done this, it will not try this again. + * This situation can occur when multiple PDRs are restored + * with the same TEID. + */ + if (pdr->f_teid.teid == ogs_pfcp_pdr_teid_pool.array[i]) { + ogs_pfcp_pdr_teid_pool.array[i] = *(pdr->teid_node); + *(pdr->teid_node) = pdr->f_teid.teid; + } +} + void ogs_pfcp_object_teid_hash_set( - ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr) + ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr, + bool restoration_indication) { ogs_assert(type); ogs_assert(pdr); + if (ogs_pfcp_self()->up_function_features.ftup && pdr->f_teid.ch) { + + ogs_pfcp_pdr_t *choosed_pdr = NULL; + + if (pdr->f_teid.chid) { + choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( + pdr->sess, pdr->f_teid.choose_id); + if (!choosed_pdr) { + pdr->chid = true; + pdr->choose_id = pdr->f_teid.choose_id; + } + } + + if (choosed_pdr) { + pdr->f_teid_len = choosed_pdr->f_teid_len; + memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); + + } else { + ogs_gtpu_resource_t *resource = NULL; + resource = ogs_pfcp_find_gtpu_resource( + &ogs_gtp_self()->gtpu_resource_list, + pdr->dnn, OGS_PFCP_INTERFACE_ACCESS); + if (resource) { + ogs_assert( + (resource->info.v4 && pdr->f_teid.ipv4) || + (resource->info.v6 && pdr->f_teid.ipv6)); + ogs_assert(OGS_OK == + ogs_pfcp_user_plane_ip_resource_info_to_f_teid( + &resource->info, &pdr->f_teid, &pdr->f_teid_len)); + if (resource->info.teidri) + pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( + pdr->teid, resource->info.teidri, + resource->info.teid_range); + else + pdr->f_teid.teid = pdr->teid; + } else { + ogs_assert( + (ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || + (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6)); + ogs_assert(OGS_OK == + ogs_pfcp_sockaddr_to_f_teid( + pdr->f_teid.ipv4 ? + ogs_gtp_self()->gtpu_addr : NULL, + pdr->f_teid.ipv6 ? + ogs_gtp_self()->gtpu_addr6 : NULL, + &pdr->f_teid, &pdr->f_teid_len)); + pdr->f_teid.teid = pdr->teid; + } + } + } + if (pdr->hash.teid.len) ogs_hash_set(self.object_teid_hash, &pdr->hash.teid.key, pdr->hash.teid.len, NULL); @@ -1107,6 +1202,7 @@ void ogs_pfcp_pdr_remove(ogs_pfcp_pdr_t *pdr) ogs_free(pdr->ipv6_framed_routes); } + ogs_pool_free(&ogs_pfcp_pdr_teid_pool, pdr->teid_node); ogs_pool_free(&ogs_pfcp_pdr_pool, pdr); } @@ -1220,7 +1316,7 @@ void ogs_pfcp_far_f_teid_hash_set(ogs_pfcp_far_t *far) &far->hash.f_teid.key, far->hash.f_teid.len, far); } -ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf) +ogs_pfcp_far_t *ogs_pfcp_far_find_by_gtpu_error_indication(ogs_pkbuf_t *pkbuf) { ogs_pfcp_far_hash_f_teid_t hashkey; int hashkey_len; @@ -1282,6 +1378,61 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf) self.far_f_teid_hash, &hashkey, hashkey_len); } +ogs_pfcp_far_t *ogs_pfcp_far_find_by_pfcp_session_report( + ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_error_indication_report_t *error_indication_report) +{ + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_f_teid_t *remote_f_teid = NULL; + + uint32_t teid; + uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ + uint32_t addr[4]; + + ogs_assert(sess); + ogs_assert(error_indication_report); + + if (error_indication_report->presence == 0) { + ogs_error("No Error Indication Report"); + return NULL; + } + + if (error_indication_report->remote_f_teid.presence == 0) { + ogs_error("No Remote F-TEID"); + return NULL; + } + + remote_f_teid = error_indication_report->remote_f_teid.data; + ogs_assert(remote_f_teid); + + teid = be32toh(remote_f_teid->teid); + if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { + ogs_error("User plane should not set both IPv4 and IPv6"); + return NULL; + } else if (remote_f_teid->ipv4) { + len = OGS_IPV4_LEN; + memcpy(addr, &remote_f_teid->addr, len); + } else if (remote_f_teid->ipv6) { + len = OGS_IPV6_LEN; + memcpy(addr, remote_f_teid->addr6, len); + } else { + ogs_error("No IPv4 and IPv6"); + return NULL; + } + + ogs_list_for_each(&sess->far_list, far) { + if (teid == far->outer_header_creation.teid) + return far; + } + + ogs_error("Cannot find the session context " + "[TEID:0x%x,LEN:%d,ADDR:%08x %08x %08x %08x]", + teid, len, be32toh(addr[0]), be32toh(addr[1]), + be32toh(addr[2]), be32toh(addr[3])); + + return NULL; +} + void ogs_pfcp_far_teid_hash_set(ogs_pfcp_far_t *far) { ogs_assert(far); @@ -1929,41 +2080,29 @@ ogs_pfcp_subnet_t *ogs_pfcp_find_subnet_by_dnn(int family, const char *dnn) void ogs_pfcp_pool_init(ogs_pfcp_sess_t *sess) { - int i; - ogs_assert(sess); sess->obj.type = OGS_PFCP_OBJ_SESS_TYPE; - ogs_index_init(&sess->pdr_id_pool, OGS_MAX_NUM_OF_PDR); - ogs_index_init(&sess->far_id_pool, OGS_MAX_NUM_OF_FAR); - ogs_index_init(&sess->urr_id_pool, OGS_MAX_NUM_OF_URR); - ogs_index_init(&sess->qer_id_pool, OGS_MAX_NUM_OF_QER); - ogs_index_init(&sess->bar_id_pool, OGS_MAX_NUM_OF_BAR); + ogs_pool_init(&sess->pdr_id_pool, OGS_MAX_NUM_OF_PDR); + ogs_pool_init(&sess->far_id_pool, OGS_MAX_NUM_OF_FAR); + ogs_pool_init(&sess->urr_id_pool, OGS_MAX_NUM_OF_URR); + ogs_pool_init(&sess->qer_id_pool, OGS_MAX_NUM_OF_QER); + ogs_pool_init(&sess->bar_id_pool, OGS_MAX_NUM_OF_BAR); - for (i = 1; i <= OGS_MAX_NUM_OF_PDR; i++) { - sess->pdr_id_pool.array[i-1] = i; - } - for (i = 1; i <= OGS_MAX_NUM_OF_FAR; i++) { - sess->far_id_pool.array[i-1] = i; - } - for (i = 1; i <= OGS_MAX_NUM_OF_URR; i++) { - sess->urr_id_pool.array[i-1] = i; - } - for (i = 1; i <= OGS_MAX_NUM_OF_QER; i++) { - sess->qer_id_pool.array[i-1] = i; - } - for (i = 1; i <= OGS_MAX_NUM_OF_BAR; i++) { - sess->bar_id_pool.array[i-1] = i; - } + ogs_pool_sequence_id_generate(&sess->pdr_id_pool); + ogs_pool_sequence_id_generate(&sess->far_id_pool); + ogs_pool_sequence_id_generate(&sess->urr_id_pool); + ogs_pool_sequence_id_generate(&sess->qer_id_pool); + ogs_pool_sequence_id_generate(&sess->bar_id_pool); } void ogs_pfcp_pool_final(ogs_pfcp_sess_t *sess) { ogs_assert(sess); - ogs_index_final(&sess->pdr_id_pool); - ogs_index_final(&sess->far_id_pool); - ogs_index_final(&sess->urr_id_pool); - ogs_index_final(&sess->qer_id_pool); - ogs_index_final(&sess->bar_id_pool); + ogs_pool_final(&sess->pdr_id_pool); + ogs_pool_final(&sess->far_id_pool); + ogs_pool_final(&sess->urr_id_pool); + ogs_pool_final(&sess->qer_id_pool); + ogs_pool_final(&sess->bar_id_pool); } diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index a14f0e3c4..0dcd98e4b 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -137,7 +137,9 @@ typedef struct ogs_pfcp_bar_s ogs_pfcp_bar_t; typedef struct ogs_pfcp_pdr_s { ogs_pfcp_object_t obj; - uint32_t index; + + ogs_pool_id_t *teid_node; /* A node of TEID */ + ogs_pool_id_t teid; ogs_lnode_t to_create_node; ogs_lnode_t to_modify_node; @@ -399,8 +401,11 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find( ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id); +void ogs_pfcp_pdr_swap_teid(ogs_pfcp_pdr_t *pdr); + void ogs_pfcp_object_teid_hash_set( - ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr); + ogs_pfcp_object_type_e type, ogs_pfcp_pdr_t *pdr, + bool restoration_indication); ogs_pfcp_object_t *ogs_pfcp_object_find_by_teid(uint32_t teid); int ogs_pfcp_object_count_by_teid(ogs_pfcp_sess_t *sess, uint32_t teid); @@ -422,7 +427,11 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_or_add( ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id); void ogs_pfcp_far_f_teid_hash_set(ogs_pfcp_far_t *far); -ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf); +ogs_pfcp_far_t *ogs_pfcp_far_find_by_gtpu_error_indication( + ogs_pkbuf_t *pkbuf); +ogs_pfcp_far_t *ogs_pfcp_far_find_by_pfcp_session_report( + ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_error_indication_report_t *error_indication_report); void ogs_pfcp_far_teid_hash_set(ogs_pfcp_far_t *far); ogs_pfcp_far_t *ogs_pfcp_far_find_by_teid(uint32_t teid); diff --git a/lib/pfcp/handler.c b/lib/pfcp/handler.c index 356bd8195..26377faa7 100644 --- a/lib/pfcp/handler.c +++ b/lib/pfcp/handler.c @@ -320,6 +320,7 @@ bool ogs_pfcp_up_handle_error_indication( ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, ogs_pfcp_tlv_create_pdr_t *message, + ogs_pfcp_sereq_flags_t *sereq_flags, uint8_t *cause_value, uint8_t *offending_ie_value) { ogs_pfcp_pdr_t *pdr = NULL; @@ -376,6 +377,18 @@ ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, *offending_ie_value = OGS_PFCP_F_TEID_TYPE; return NULL; } + + if (f_teid.ch == 0) { + if (sereq_flags && sereq_flags->restoration_indication == 1) { + f_teid.teid = be32toh(f_teid.teid); + if (ogs_pfcp_object_find_by_teid(f_teid.teid)) { + ogs_error("TEID:%x had already been allocated", f_teid.teid); + *cause_value = OGS_PFCP_CAUSE_INVALID_F_TEID_ALLOCATION_OPTION; + *offending_ie_value = OGS_PFCP_F_TEID_TYPE; + return NULL; + } + } + } } pdr->src_if = message->pdi.source_interface.u8; @@ -398,7 +411,6 @@ ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, if (sdf_filter.bid) { oppsite_direction_rule = ogs_pfcp_rule_find_by_sdf_filter_id( sess, sdf_filter.sdf_filter_id); - } if (!oppsite_direction_rule && !sdf_filter.fd) { diff --git a/lib/pfcp/handler.h b/lib/pfcp/handler.h index 9ad357dca..7ae62f30b 100644 --- a/lib/pfcp/handler.h +++ b/lib/pfcp/handler.h @@ -53,6 +53,7 @@ bool ogs_pfcp_up_handle_error_indication( ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, ogs_pfcp_tlv_create_pdr_t *message, + ogs_pfcp_sereq_flags_t *sereq_flags, uint8_t *cause_value, uint8_t *offending_ie_value); ogs_pfcp_pdr_t *ogs_pfcp_handle_created_pdr(ogs_pfcp_sess_t *sess, ogs_pfcp_tlv_created_pdr_t *message, diff --git a/lib/pfcp/message.c b/lib/pfcp/message.c index c8aee5f7e..63fc780cd 100644 --- a/lib/pfcp/message.c +++ b/lib/pfcp/message.c @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by pfcp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2023-03-05 22:26:11.716006 by acetcom + * Created on: 2023-04-09 20:37:00.518388 by acetcom * from 29244-h71-modified.docx ******************************************************************************/ @@ -1546,10 +1546,10 @@ ogs_tlv_desc_t ogs_pfcp_tlv_desc_cp_pfcp_entity_ip_address = ogs_tlv_desc_t ogs_pfcp_tlv_desc_pfcpsereq_flags = { - OGS_TLV_VAR_STR, + OGS_TLV_UINT8, "PFCPSEReq-Flags", OGS_PFCP_PFCPSEREQ_FLAGS_TYPE, - 0, + 1, 0, sizeof(ogs_pfcp_tlv_pfcpsereq_flags_t), { NULL } diff --git a/lib/pfcp/message.h b/lib/pfcp/message.h index 69adde349..e31e26702 100644 --- a/lib/pfcp/message.h +++ b/lib/pfcp/message.h @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by pfcp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2023-03-05 22:26:11.698310 by acetcom + * Created on: 2023-04-09 20:37:00.506639 by acetcom * from 29244-h71-modified.docx ******************************************************************************/ @@ -903,7 +903,7 @@ typedef ogs_tlv_uint32_t ogs_pfcp_tlv_quota_validity_time_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_number_of_reports_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpasrsp_flags_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_cp_pfcp_entity_ip_address_t; -typedef ogs_tlv_octet_t ogs_pfcp_tlv_pfcpsereq_flags_t; +typedef ogs_tlv_uint8_t ogs_pfcp_tlv_pfcpsereq_flags_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_ip_multicast_address_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_source_ip_address_t; typedef ogs_tlv_octet_t ogs_pfcp_tlv_packet_rate_status_t; diff --git a/lib/pfcp/support/pfcp-tlv.py b/lib/pfcp/support/pfcp-tlv.py index d180392f1..bbeac762b 100644 --- a/lib/pfcp/support/pfcp-tlv.py +++ b/lib/pfcp/support/pfcp-tlv.py @@ -519,6 +519,7 @@ type_list["Paging Policy Indicator"]["size"] = 1 # Type 158 type_list["PFCPSRReq-Flags"]["size"] = 1 # Type 161 type_list["PFCPAUReq-Flags"]["size"] = 1 # Type 162 type_list["Quota Validity Time"]["size"] = 4 # Type 181 +type_list["PFCPSEReq-Flags"]["size"] = 1 # Type 186 type_list["Data Status"]["size"] = 1 # Type 260 f = open(outdir + 'message.h', 'w') diff --git a/lib/pfcp/types.h b/lib/pfcp/types.h index 481832409..895a7efd2 100644 --- a/lib/pfcp/types.h +++ b/lib/pfcp/types.h @@ -1651,6 +1651,28 @@ int16_t ogs_pfcp_build_user_id( ogs_tlv_octet_t *octet, ogs_pfcp_user_id_t *user_id, void *data, int data_len); +/* + * 8.2.136 PFCPSEReq-Flags + * + * The following bits within Octet 5 shall indicate: + * - Bit 1 – RESTI (Restoration Indication): if this bit is set to "1", + * it indicates to the UP function that the PFCP session to be established is + * to restore an existing PFCP session. + * - Bit 2 – SUMPC (Stop of Usage Measurement to Pause Charging): + * if this bit is set to "1", it indicates that the UP function shall + * stop the usage measurement for all URRs with the "ASPOC" flag set to "1". + */ +typedef struct ogs_pfcp_sereq_flags_s { + union { + struct { +ED3(uint8_t spare:6;, + uint8_t stop_of_usage_measurement_to_pause_charging:1;, + uint8_t restoration_indication:1;) + }; + uint8_t value; + }; +} __attribute__ ((packed)) ogs_pfcp_sereq_flags_t; + #ifdef __cplusplus } #endif diff --git a/lib/pfcp/xact.h b/lib/pfcp/xact.h index bc6a85803..580b80b46 100644 --- a/lib/pfcp/xact.h +++ b/lib/pfcp/xact.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -35,7 +35,7 @@ typedef struct ogs_pfcp_xact_s { ogs_lnode_t lnode; /**< A node of list */ ogs_lnode_t tmpnode; /**< A node of temp-list */ - ogs_index_t index; + ogs_pool_id_t index; #define OGS_PFCP_LOCAL_ORIGINATOR 0 #define OGS_PFCP_REMOTE_ORIGINATOR 1 @@ -78,6 +78,9 @@ typedef struct ogs_pfcp_xact_s { bool epc; /**< EPC or 5GC */ +#define OGS_PFCP_CREATE_RESTORATION_INDICATION ((uint64_t)1<<0) + uint64_t create_flags; + #define OGS_PFCP_MODIFY_SESSION ((uint64_t)1<<0) #define OGS_PFCP_MODIFY_DL_ONLY ((uint64_t)1<<1) #define OGS_PFCP_MODIFY_UL_ONLY ((uint64_t)1<<2) @@ -110,7 +113,6 @@ typedef struct ogs_pfcp_xact_s { #define OGS_PFCP_MODIFY_URR_TIME_QUOTA ((uint64_t)1<<29) #define OGS_PFCP_MODIFY_URR_VOLUME_THRESH ((uint64_t)1<<30) #define OGS_PFCP_MODIFY_URR_TIME_THRESH ((uint64_t)1<<31) - uint64_t modify_flags; #define OGS_PFCP_DELETE_TRIGGER_LOCAL_INITIATED 1 diff --git a/misc/db/open5gs-dbctl b/misc/db/open5gs-dbctl index 5f179322e..f543bc365 100755 --- a/misc/db/open5gs-dbctl +++ b/misc/db/open5gs-dbctl @@ -1,6 +1,6 @@ #!/bin/bash -version=0.10.2 +version=0.10.3 display_help() { echo "open5gs-dbctl: Open5GS Database Configuration Tool ($version)" @@ -24,6 +24,7 @@ display_help() { echo " showall: shows the list of subscriber in the db" echo " showpretty: shows the list of subscriber in the db in a pretty json tree format" echo " showfiltered: shows {imsi key opc apn ip} information of subscriber" + echo " ambr_speed {imsi dl_value dl_unit ul_value ul_unit}: Change AMBR speed from a specific user and the unit values are \"[0=bps 1=Kbps 2=Mbps 3=Gbps 4=Tbps ]\"" } @@ -785,4 +786,44 @@ if [ "$1" = "showfiltered" ]; then exit $? fi +if [ "$1" = "ambr_speed" ]; then + if [ "$#" -eq 6 ]; then + IMSI=$2 + DL_VALUE=$3 + DL_UNIT=$4 + UL_VALUE=$5 + UL_UNIT=$6 + mongosh --eval "db.subscribers.updateOne({\"imsi\": \"$IMSI\"}, + {\$set: { + \"ambr\" : { + \"downlink\" : { + \"value\" : NumberInt($DL_VALUE), + \"unit\" : NumberInt($DL_UNIT) + }, + \"uplink\" :{ + \"value\": NumberInt($UL_VALUE), + \"unit\" : NumberInt($UL_UNIT) + } + }, + \"slice.0.session.0.ambr\": { + \"downlink\" : { + \"value\" : NumberInt($DL_VALUE), + \"unit\" : NumberInt($DL_UNIT) + }, + \"uplink\" :{ + \"value\": NumberInt($UL_VALUE), + \"unit\" : NumberInt($UL_UNIT) + } + } + } + });" $DB_URI + + + exit $? + fi + echo "open5gs-dbctl: incorrect number of args, format is \"open5gs-dbctl ambr_speed imsi dl_value dl_unit ul_value ul_unit dl is for download and ul is for upload and the unit values are[0=bps 1=Kbps 2=Mbps 3=Gbps 4=Tbps ] \"" + exit 1 +fi + + display_help diff --git a/src/amf/context.c b/src/amf/context.c index ad5260a85..e13600083 100644 --- a/src/amf/context.c +++ b/src/amf/context.c @@ -29,6 +29,8 @@ static OGS_POOL(amf_ue_pool, amf_ue_t); static OGS_POOL(ran_ue_pool, ran_ue_t); static OGS_POOL(amf_sess_pool, amf_sess_t); +static OGS_POOL(m_tmsi_pool, amf_m_tmsi_t); + static int context_initialized = 0; static int num_of_ran_ue = 0; @@ -60,7 +62,8 @@ void amf_context_init(void) ogs_pool_init(&amf_ue_pool, ogs_app()->max.ue); ogs_pool_init(&ran_ue_pool, ogs_app()->max.ue); ogs_pool_init(&amf_sess_pool, ogs_app()->pool.sess); - ogs_pool_init(&self.m_tmsi, ogs_app()->max.ue*2); + ogs_pool_init(&m_tmsi_pool, ogs_app()->max.ue*2); + ogs_pool_random_id_generate(&m_tmsi_pool); ogs_list_init(&self.gnb_list); ogs_list_init(&self.amf_ue_list); @@ -98,7 +101,7 @@ void amf_context_final(void) ogs_assert(self.supi_hash); ogs_hash_destroy(self.supi_hash); - ogs_pool_final(&self.m_tmsi); + ogs_pool_final(&m_tmsi_pool); ogs_pool_final(&amf_sess_pool); ogs_pool_final(&amf_ue_pool); ogs_pool_final(&ran_ue_pool); @@ -1697,11 +1700,6 @@ amf_ue_t *amf_ue_find_by_guti(ogs_nas_5gs_guti_t *guti) self.guti_ue_hash, guti, sizeof(ogs_nas_5gs_guti_t)); } -amf_ue_t *amf_ue_find_by_teid(uint32_t teid) -{ - return ogs_pool_find(&amf_ue_pool, teid); -} - amf_ue_t *amf_ue_find_by_suci(char *suci) { ogs_assert(suci); @@ -2368,6 +2366,7 @@ ogs_s_nssai_t *amf_find_s_nssai( return NULL; } +#if 0 /* DEPRECATED */ int amf_m_tmsi_pool_generate(void) { int j; @@ -2378,7 +2377,7 @@ int amf_m_tmsi_pool_generate(void) amf_m_tmsi_t *m_tmsi = NULL; int conflict = 0; - m_tmsi = &self.m_tmsi.array[index]; + m_tmsi = &m_tmsi_pool.array[index]; ogs_assert(m_tmsi); *m_tmsi = ogs_random32(); @@ -2387,10 +2386,10 @@ int amf_m_tmsi_pool_generate(void) *m_tmsi &= 0xff00ffff; for (j = 0; j < index; j++) { - if (*m_tmsi == self.m_tmsi.array[j]) { + if (*m_tmsi == m_tmsi_pool.array[j]) { conflict = 1; ogs_trace("[M-TMSI CONFLICT] %d:0x%x == %d:0x%x", - index, *m_tmsi, j, self.m_tmsi.array[j]); + index, *m_tmsi, j, m_tmsi_pool.array[j]); break; } } @@ -2400,26 +2399,48 @@ int amf_m_tmsi_pool_generate(void) index++; } - self.m_tmsi.size = index; + m_tmsi_pool.size = index; ogs_trace("M-TMSI Pool generate...done"); return OGS_OK; } +#endif amf_m_tmsi_t *amf_m_tmsi_alloc(void) { amf_m_tmsi_t *m_tmsi = NULL; - ogs_pool_alloc(&self.m_tmsi, &m_tmsi); + ogs_pool_alloc(&m_tmsi_pool, &m_tmsi); ogs_assert(m_tmsi); + /* TS23.003 + * 2.8.2.1.2 Mapping in the UE + * + * E-UTRAN maps as follows: + * - 6 bits of the E-UTRAN starting at bit 29 and down to bit 24 + * are mapped into bit 29 and down to bit 24 of the GERAN/UTRAN ; + * - 16 bits of the E-UTRAN starting at bit 15 and down to bit 0 + * are mapped into bit 15 and down to bit 0 of the GERAN/UTRAN ; + * - and the remaining 8 bits of the E-UTRAN are + * mapped into the 8 Most Significant Bits of the field. + * + * The UE shall fill the remaining 2 octets of the + * according to clauses 9.1.1, 9.4.1, 10.2.1, or 10.5.1 + * of 3GPP TS.33.401 [89] , as appropriate, for RAU/Attach procedures + */ + + ogs_assert(*m_tmsi <= 0x003fffff); + + *m_tmsi = ((*m_tmsi & 0xffff) | ((*m_tmsi & 0x003f0000) << 8)); + *m_tmsi |= 0xc0000000; + return m_tmsi; } int amf_m_tmsi_free(amf_m_tmsi_t *m_tmsi) { ogs_assert(m_tmsi); - ogs_pool_free(&self.m_tmsi, m_tmsi); + ogs_pool_free(&m_tmsi_pool, m_tmsi); return OGS_OK; } diff --git a/src/amf/context.h b/src/amf/context.h index e95db3d4d..1479f00d2 100644 --- a/src/amf/context.h +++ b/src/amf/context.h @@ -111,8 +111,6 @@ typedef struct amf_context_s { ogs_hash_t *suci_hash; /* hash table (SUCI) */ ogs_hash_t *supi_hash; /* hash table (SUPI) */ - OGS_POOL(m_tmsi, amf_m_tmsi_t); /* M-TMSI Pool */ - uint16_t ngap_port; /* Default NGAP Port */ ogs_list_t ngap_list; /* AMF NGAP IPv4 Server List */ @@ -697,7 +695,6 @@ void amf_ue_fsm_init(amf_ue_t *amf_ue); void amf_ue_fsm_fini(amf_ue_t *amf_ue); amf_ue_t *amf_ue_find_by_guti(ogs_nas_5gs_guti_t *nas_guti); -amf_ue_t *amf_ue_find_by_teid(uint32_t teid); amf_ue_t *amf_ue_find_by_suci(char *suci); amf_ue_t *amf_ue_find_by_supi(char *supi); @@ -823,7 +820,6 @@ int amf_find_served_tai(ogs_5gs_tai_t *nr_tai); ogs_s_nssai_t *amf_find_s_nssai( ogs_plmn_id_t *served_plmn_id, ogs_s_nssai_t *s_nssai); -int amf_m_tmsi_pool_generate(void); amf_m_tmsi_t *amf_m_tmsi_alloc(void); int amf_m_tmsi_free(amf_m_tmsi_t *tmsi); diff --git a/src/amf/init.c b/src/amf/init.c index 18b3dbda3..9c485d646 100644 --- a/src/amf/init.c +++ b/src/amf/init.c @@ -46,9 +46,6 @@ int amf_initialize(void) rv = amf_context_nf_info(); if (rv != OGS_OK) return rv; - rv = amf_m_tmsi_pool_generate(); - if (rv != OGS_OK) return rv; - rv = ogs_log_config_domain( ogs_app()->logger.domain, ogs_app()->logger.level); if (rv != OGS_OK) return rv; diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index 61eca1073..8013aab10 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -44,11 +44,14 @@ static OGS_POOL(mme_csmap_pool, mme_csmap_t); static OGS_POOL(mme_enb_pool, mme_enb_t); static OGS_POOL(mme_ue_pool, mme_ue_t); +static OGS_POOL(mme_s11_teid_pool, ogs_pool_id_t); static OGS_POOL(enb_ue_pool, enb_ue_t); static OGS_POOL(sgw_ue_pool, sgw_ue_t); static OGS_POOL(mme_sess_pool, mme_sess_t); static OGS_POOL(mme_bearer_pool, mme_bearer_t); +static OGS_POOL(m_tmsi_pool, mme_m_tmsi_t); + static int context_initialized = 0; static int num_of_enb_ue = 0; @@ -100,11 +103,15 @@ void mme_context_init(void) ogs_pool_init(&mme_enb_pool, ogs_app()->max.peer*2); ogs_pool_init(&mme_ue_pool, ogs_app()->max.ue); + ogs_pool_init(&mme_s11_teid_pool, ogs_app()->max.ue); + ogs_pool_random_id_generate(&mme_s11_teid_pool); + ogs_pool_init(&enb_ue_pool, ogs_app()->max.ue); ogs_pool_init(&sgw_ue_pool, ogs_app()->max.ue); ogs_pool_init(&mme_sess_pool, ogs_app()->pool.sess); ogs_pool_init(&mme_bearer_pool, ogs_app()->pool.bearer); - ogs_pool_init(&self.m_tmsi, ogs_app()->max.ue*2); + ogs_pool_init(&m_tmsi_pool, ogs_app()->max.ue*2); + ogs_pool_random_id_generate(&m_tmsi_pool); self.enb_addr_hash = ogs_hash_make(); ogs_assert(self.enb_addr_hash); @@ -114,6 +121,8 @@ void mme_context_init(void) ogs_assert(self.imsi_ue_hash); self.guti_ue_hash = ogs_hash_make(); ogs_assert(self.guti_ue_hash); + self.mme_s11_teid_hash = ogs_hash_make(); + ogs_assert(self.mme_s11_teid_hash); ogs_list_init(&self.mme_ue_list); @@ -141,11 +150,14 @@ void mme_context_final(void) ogs_hash_destroy(self.imsi_ue_hash); ogs_assert(self.guti_ue_hash); ogs_hash_destroy(self.guti_ue_hash); + ogs_assert(self.mme_s11_teid_hash); + ogs_hash_destroy(self.mme_s11_teid_hash); - ogs_pool_final(&self.m_tmsi); + ogs_pool_final(&m_tmsi_pool); ogs_pool_final(&mme_bearer_pool); ogs_pool_final(&mme_sess_pool); ogs_pool_final(&mme_ue_pool); + ogs_pool_final(&mme_s11_teid_pool); ogs_pool_final(&enb_ue_pool); ogs_pool_final(&sgw_ue_pool); @@ -2327,11 +2339,6 @@ void sgw_ue_switch_to_sgw(sgw_ue_t *sgw_ue, mme_sgw_t *new_sgw) sgw_ue->sgw = new_sgw; } -sgw_ue_t *sgw_ue_find(uint32_t index) -{ - return ogs_pool_find(&sgw_ue_pool, index); -} - sgw_ue_t *sgw_ue_cycle(sgw_ue_t *sgw_ue) { return ogs_pool_cycle(&sgw_ue_pool, sgw_ue); @@ -2564,9 +2571,14 @@ mme_ue_t *mme_ue_add(enb_ue_t *enb_ue) ogs_list_init(&mme_ue->sess_list); - mme_ue->mme_s11_teid = ogs_pool_index(&mme_ue_pool, mme_ue); - ogs_assert(mme_ue->mme_s11_teid > 0 && - mme_ue->mme_s11_teid <= ogs_app()->max.ue); + /* Set MME-S11_TEID */ + ogs_pool_alloc(&mme_s11_teid_pool, &mme_ue->mme_s11_teid_node); + ogs_assert(mme_ue->mme_s11_teid_node); + + mme_ue->mme_s11_teid = *(mme_ue->mme_s11_teid_node); + + ogs_hash_set(self.mme_s11_teid_hash, + &mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), mme_ue); /* * When used for the first time, if last node is set, @@ -2609,6 +2621,9 @@ void mme_ue_remove(mme_ue_t *mme_ue) mme_ue_fsm_fini(mme_ue); + ogs_hash_set(self.mme_s11_teid_hash, + &mme_ue->mme_s11_teid, sizeof(mme_ue->mme_s11_teid), NULL); + ogs_assert(mme_ue->sgw_ue); sgw_ue_remove(mme_ue->sgw_ue); @@ -2654,6 +2669,7 @@ void mme_ue_remove(mme_ue_t *mme_ue) mme_ebi_pool_final(mme_ue); + ogs_pool_free(&mme_s11_teid_pool, mme_ue->mme_s11_teid_node); ogs_pool_free(&mme_ue_pool, mme_ue); ogs_info("[Removed] Number of MME-UEs is now %d", @@ -2729,7 +2745,7 @@ mme_ue_t *mme_ue_find_by_guti(ogs_nas_eps_guti_t *guti) mme_ue_t *mme_ue_find_by_teid(uint32_t teid) { - return ogs_pool_find(&mme_ue_pool, teid); + return ogs_hash_get(self.mme_s11_teid_hash, &teid, sizeof(teid)); } mme_ue_t *mme_ue_find_by_message(ogs_nas_eps_message_t *message) @@ -3743,6 +3759,7 @@ int mme_find_served_tai(ogs_eps_tai_t *tai) return -1; } +#if 0 /* DEPRECATED */ int mme_m_tmsi_pool_generate(void) { int j; @@ -3753,7 +3770,7 @@ int mme_m_tmsi_pool_generate(void) mme_m_tmsi_t *m_tmsi = NULL; int conflict = 0; - m_tmsi = &self.m_tmsi.array[index]; + m_tmsi = &m_tmsi_pool.array[index]; ogs_assert(m_tmsi); *m_tmsi = ogs_random32(); @@ -3762,10 +3779,10 @@ int mme_m_tmsi_pool_generate(void) *m_tmsi &= 0xff00ffff; for (j = 0; j < index; j++) { - if (*m_tmsi == self.m_tmsi.array[j]) { + if (*m_tmsi == m_tmsi_pool.array[j]) { conflict = 1; ogs_trace("[M-TMSI CONFLICT] %d:0x%x == %d:0x%x", - index, *m_tmsi, j, self.m_tmsi.array[j]); + index, *m_tmsi, j, m_tmsi_pool.array[j]); break; } } @@ -3775,26 +3792,48 @@ int mme_m_tmsi_pool_generate(void) index++; } - self.m_tmsi.size = index; + m_tmsi_pool.size = index; ogs_trace("M-TMSI Pool generate...done"); return OGS_OK; } +#endif mme_m_tmsi_t *mme_m_tmsi_alloc(void) { mme_m_tmsi_t *m_tmsi = NULL; - ogs_pool_alloc(&self.m_tmsi, &m_tmsi); + ogs_pool_alloc(&m_tmsi_pool, &m_tmsi); ogs_assert(m_tmsi); + /* TS23.003 + * 2.8.2.1.2 Mapping in the UE + * + * E-UTRAN maps as follows: + * - 6 bits of the E-UTRAN starting at bit 29 and down to bit 24 + * are mapped into bit 29 and down to bit 24 of the GERAN/UTRAN ; + * - 16 bits of the E-UTRAN starting at bit 15 and down to bit 0 + * are mapped into bit 15 and down to bit 0 of the GERAN/UTRAN ; + * - and the remaining 8 bits of the E-UTRAN are + * mapped into the 8 Most Significant Bits of the field. + * + * The UE shall fill the remaining 2 octets of the + * according to clauses 9.1.1, 9.4.1, 10.2.1, or 10.5.1 + * of 3GPP TS.33.401 [89] , as appropriate, for RAU/Attach procedures + */ + + ogs_assert(*m_tmsi <= 0x003fffff); + + *m_tmsi = ((*m_tmsi & 0xffff) | ((*m_tmsi & 0x003f0000) << 8)); + *m_tmsi |= 0xc0000000; + return m_tmsi; } int mme_m_tmsi_free(mme_m_tmsi_t *m_tmsi) { ogs_assert(m_tmsi); - ogs_pool_free(&self.m_tmsi, m_tmsi); + ogs_pool_free(&m_tmsi_pool, m_tmsi); return OGS_OK; } @@ -3805,7 +3844,7 @@ void mme_ebi_pool_init(mme_ue_t *mme_ue) ogs_assert(mme_ue); - ogs_index_init(&mme_ue->ebi_pool, MAX_EPS_BEARER_ID-MIN_EPS_BEARER_ID+1); + ogs_pool_init(&mme_ue->ebi_pool, MAX_EPS_BEARER_ID-MIN_EPS_BEARER_ID+1); for (i = MIN_EPS_BEARER_ID, index = 0; i <= MAX_EPS_BEARER_ID; i++, index++) { @@ -3817,17 +3856,17 @@ void mme_ebi_pool_final(mme_ue_t *mme_ue) { ogs_assert(mme_ue); - ogs_index_final(&mme_ue->ebi_pool); + ogs_pool_final(&mme_ue->ebi_pool); } void mme_ebi_pool_clear(mme_ue_t *mme_ue) { ogs_assert(mme_ue); - ogs_free(mme_ue->ebi_pool.free); - ogs_free(mme_ue->ebi_pool.array); - ogs_free(mme_ue->ebi_pool.index); + /* Suppress log message (mme_ue->ebi_pool.avail != mme_ue->ebi_pool.size) */ + mme_ue->ebi_pool.avail = mme_ue->ebi_pool.size; + mme_ebi_pool_final(mme_ue); mme_ebi_pool_init(mme_ue); } diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index 9c705939f..6e44c0ba9 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -145,15 +145,14 @@ typedef struct mme_context_s { /* Generator for unique identification */ uint32_t mme_ue_s1ap_id; /* mme_ue_s1ap_id generator */ - /* M-TMSI Pool */ - OGS_POOL(m_tmsi, mme_m_tmsi_t); - ogs_list_t mme_ue_list; - ogs_hash_t *enb_addr_hash; /* hash table for ENB Address */ - ogs_hash_t *enb_id_hash; /* hash table for ENB-ID */ - ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : MME_UE) */ - ogs_hash_t *guti_ue_hash; /* hash table (GUTI : MME_UE) */ + ogs_hash_t *enb_addr_hash; /* hash table for ENB Address */ + ogs_hash_t *enb_id_hash; /* hash table for ENB-ID */ + ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : MME_UE) */ + ogs_hash_t *guti_ue_hash; /* hash table (GUTI : MME_UE) */ + + ogs_hash_t *mme_s11_teid_hash; /* hash table (MME-S11-TEID : MME_UE) */ struct { struct { @@ -286,7 +285,6 @@ struct enb_ue_s { struct sgw_ue_s { ogs_lnode_t lnode; - uint32_t index; sgw_ue_t *source_ue; sgw_ue_t *target_ue; @@ -371,7 +369,8 @@ struct mme_ue_s { ogs_nas_eps_guti_t guti; } current, next; - uint32_t mme_s11_teid; /* MME-S11-TEID is derived from INDEX */ + ogs_pool_id_t *mme_s11_teid_node; /* A node of MME-S11-TEID */ + uint32_t mme_s11_teid; /* MME-S11-TEID is derived from NODE */ uint16_t vlr_ostream_id; /* SCTP output stream id for VLR */ @@ -685,7 +684,6 @@ typedef struct mme_bearer_s { ogs_lnode_t lnode; ogs_lnode_t to_modify_node; - uint32_t index; ogs_fsm_t sm; /* State Machine */ uint8_t *ebi_node; /* Pool-Node for EPS Bearer ID */ @@ -794,7 +792,6 @@ enb_ue_t *enb_ue_cycle(enb_ue_t *enb_ue); sgw_ue_t *sgw_ue_add(mme_sgw_t *sgw); void sgw_ue_remove(sgw_ue_t *sgw_ue); void sgw_ue_switch_to_sgw(sgw_ue_t *sgw_ue, mme_sgw_t *new_sgw); -sgw_ue_t *sgw_ue_find(uint32_t index); sgw_ue_t *sgw_ue_cycle(sgw_ue_t *sgw_ue); typedef enum { @@ -923,7 +920,6 @@ ogs_session_t *mme_default_session(mme_ue_t *mme_ue); int mme_find_served_tai(ogs_eps_tai_t *tai); -int mme_m_tmsi_pool_generate(void); mme_m_tmsi_t *mme_m_tmsi_alloc(void); int mme_m_tmsi_free(mme_m_tmsi_t *tmsi); diff --git a/src/mme/mme-init.c b/src/mme/mme-init.c index 72adbf9cf..3c2571a28 100644 --- a/src/mme/mme-init.c +++ b/src/mme/mme-init.c @@ -61,9 +61,6 @@ int mme_initialize(void) ogs_app()->logger.domain, ogs_app()->logger.level); if (rv != OGS_OK) return rv; - rv = mme_m_tmsi_pool_generate(); - if (rv != OGS_OK) return rv; - ogs_metrics_context_open(ogs_metrics_self()); rv = mme_fd_init(); diff --git a/src/sgwc/context.c b/src/sgwc/context.c index 079a6a0ab..47e84e2d5 100644 --- a/src/sgwc/context.c +++ b/src/sgwc/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -25,11 +25,15 @@ static sgwc_context_t self; int __sgwc_log_domain; -static OGS_POOL(sgwc_ue_pool, sgwc_ue_t); -static OGS_POOL(sgwc_sess_pool, sgwc_sess_t); static OGS_POOL(sgwc_bearer_pool, sgwc_bearer_t); static OGS_POOL(sgwc_tunnel_pool, sgwc_tunnel_t); +static OGS_POOL(sgwc_ue_pool, sgwc_ue_t); +static OGS_POOL(sgwc_s11_teid_pool, ogs_pool_id_t); + +static OGS_POOL(sgwc_sess_pool, sgwc_sess_t); +static OGS_POOL(sgwc_sxa_seid_pool, ogs_pool_id_t); + static int context_initialized = 0; static int num_of_sgwc_sess = 0; @@ -45,13 +49,23 @@ void sgwc_context_init(void) ogs_log_install_domain(&__sgwc_log_domain, "sgwc", ogs_core()->log.level); - ogs_pool_init(&sgwc_ue_pool, ogs_app()->max.ue); - ogs_pool_init(&sgwc_sess_pool, ogs_app()->pool.sess); ogs_pool_init(&sgwc_bearer_pool, ogs_app()->pool.bearer); ogs_pool_init(&sgwc_tunnel_pool, ogs_app()->pool.tunnel); + ogs_pool_init(&sgwc_ue_pool, ogs_app()->max.ue); + ogs_pool_init(&sgwc_s11_teid_pool, ogs_app()->max.ue); + ogs_pool_random_id_generate(&sgwc_s11_teid_pool); + + ogs_pool_init(&sgwc_sess_pool, ogs_app()->pool.sess); + ogs_pool_init(&sgwc_sxa_seid_pool, ogs_app()->pool.sess); + ogs_pool_random_id_generate(&sgwc_sxa_seid_pool); + self.imsi_ue_hash = ogs_hash_make(); ogs_assert(self.imsi_ue_hash); + self.sgw_s11_teid_hash = ogs_hash_make(); + ogs_assert(self.sgw_s11_teid_hash); + self.sgwc_sxa_seid_hash = ogs_hash_make(); + ogs_assert(self.sgwc_sxa_seid_hash); ogs_list_init(&self.sgw_ue_list); @@ -66,11 +80,19 @@ void sgwc_context_final(void) ogs_assert(self.imsi_ue_hash); ogs_hash_destroy(self.imsi_ue_hash); + ogs_assert(self.sgw_s11_teid_hash); + ogs_hash_destroy(self.sgw_s11_teid_hash); + ogs_assert(self.sgwc_sxa_seid_hash); + ogs_hash_destroy(self.sgwc_sxa_seid_hash); ogs_pool_final(&sgwc_tunnel_pool); ogs_pool_final(&sgwc_bearer_pool); - ogs_pool_final(&sgwc_sess_pool); + ogs_pool_final(&sgwc_ue_pool); + ogs_pool_final(&sgwc_s11_teid_pool); + + ogs_pool_final(&sgwc_sess_pool); + ogs_pool_final(&sgwc_sxa_seid_pool); ogs_gtp_node_remove_all(&self.mme_s11_list); ogs_gtp_node_remove_all(&self.pgw_s5c_list); @@ -192,9 +214,14 @@ sgwc_ue_t *sgwc_ue_add(uint8_t *imsi, int imsi_len) ogs_assert(sgwc_ue); memset(sgwc_ue, 0, sizeof *sgwc_ue); - sgwc_ue->sgw_s11_teid = ogs_pool_index(&sgwc_ue_pool, sgwc_ue); - ogs_assert(sgwc_ue->sgw_s11_teid > 0 && - sgwc_ue->sgw_s11_teid <= ogs_app()->max.ue); + /* Set SGW-S11-TEID */ + ogs_pool_alloc(&sgwc_s11_teid_pool, &sgwc_ue->sgw_s11_teid_node); + ogs_assert(sgwc_ue->sgw_s11_teid_node); + + sgwc_ue->sgw_s11_teid = *(sgwc_ue->sgw_s11_teid_node); + + ogs_hash_set(self.sgw_s11_teid_hash, + &sgwc_ue->sgw_s11_teid, sizeof(sgwc_ue->sgw_s11_teid), sgwc_ue); /* Set IMSI */ sgwc_ue->imsi_len = imsi_len; @@ -219,10 +246,13 @@ int sgwc_ue_remove(sgwc_ue_t *sgwc_ue) ogs_list_remove(&self.sgw_ue_list, sgwc_ue); + ogs_hash_set(self.sgw_s11_teid_hash, + &sgwc_ue->sgw_s11_teid, sizeof(sgwc_ue->sgw_s11_teid), NULL); ogs_hash_set(self.imsi_ue_hash, sgwc_ue->imsi, sgwc_ue->imsi_len, NULL); sgwc_sess_remove_all(sgwc_ue); + ogs_pool_free(&sgwc_s11_teid_pool, sgwc_ue->sgw_s11_teid_node); ogs_pool_free(&sgwc_ue_pool, sgwc_ue); ogs_info("[Removed] Number of SGWC-UEs is now %d", @@ -255,12 +285,12 @@ sgwc_ue_t *sgwc_ue_find_by_imsi(uint8_t *imsi, int imsi_len) { ogs_assert(imsi && imsi_len); - return (sgwc_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); + return ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); } sgwc_ue_t *sgwc_ue_find_by_teid(uint32_t teid) { - return ogs_pool_find(&sgwc_ue_pool, teid); + return ogs_hash_get(self.sgw_s11_teid_hash, &teid, sizeof(teid)); } sgwc_sess_t *sgwc_sess_add(sgwc_ue_t *sgwc_ue, char *apn) @@ -279,12 +309,15 @@ sgwc_sess_t *sgwc_sess_add(sgwc_ue_t *sgwc_ue, char *apn) ogs_pfcp_pool_init(&sess->pfcp); - sess->index = ogs_pool_index(&sgwc_sess_pool, sess); - ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); - /* Set TEID & SEID */ - sess->sgw_s5c_teid = sess->index; - sess->sgwc_sxa_seid = sess->index; + ogs_pool_alloc(&sgwc_sxa_seid_pool, &sess->sgwc_sxa_seid_node); + ogs_assert(sess->sgwc_sxa_seid_node); + + sess->sgw_s5c_teid = *(sess->sgwc_sxa_seid_node); + sess->sgwc_sxa_seid = *(sess->sgwc_sxa_seid_node); + + ogs_hash_set(self.sgwc_sxa_seid_hash, + &sess->sgwc_sxa_seid, sizeof(sess->sgwc_sxa_seid), sess); /* Create BAR in PFCP Session */ ogs_pfcp_bar_new(&sess->pfcp); @@ -400,6 +433,9 @@ int sgwc_sess_remove(sgwc_sess_t *sess) ogs_list_remove(&sgwc_ue->sess_list, sess); + ogs_hash_set(self.sgwc_sxa_seid_hash, &sess->sgwc_sxa_seid, + sizeof(sess->sgwc_sxa_seid), NULL); + sgwc_bearer_remove_all(sess); ogs_assert(sess->pfcp.bar); @@ -410,6 +446,7 @@ int sgwc_sess_remove(sgwc_sess_t *sess) ogs_assert(sess->session.name); ogs_free(sess->session.name); + ogs_pool_free(&sgwc_sxa_seid_pool, sess->sgwc_sxa_seid_node); ogs_pool_free(&sgwc_sess_pool, sess); stats_remove_sgwc_session(); @@ -426,19 +463,14 @@ void sgwc_sess_remove_all(sgwc_ue_t *sgwc_ue) sgwc_sess_remove(sess); } -sgwc_sess_t *sgwc_sess_find(uint32_t index) -{ - return ogs_pool_find(&sgwc_sess_pool, index); -} - sgwc_sess_t* sgwc_sess_find_by_teid(uint32_t teid) { - return ogs_pool_find(&sgwc_sess_pool, teid); + return sgwc_sess_find_by_seid(teid); } sgwc_sess_t *sgwc_sess_find_by_seid(uint64_t seid) { - return sgwc_sess_find(seid); + return ogs_hash_get(self.sgwc_sxa_seid_hash, &seid, sizeof(seid)); } sgwc_sess_t* sgwc_sess_find_by_apn(sgwc_ue_t *sgwc_ue, char *apn) @@ -578,71 +610,6 @@ sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) return NULL; } -sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report( - sgwc_sess_t *sess, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report) -{ - ogs_pfcp_f_teid_t *remote_f_teid = NULL; - sgwc_bearer_t *bearer = NULL; - sgwc_tunnel_t *tunnel = NULL; - - uint32_t teid; - uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ - uint32_t addr[4]; - - ogs_assert(sess); - ogs_assert(error_indication_report); - - if (error_indication_report->presence == 0) { - ogs_error("No Error Indication Report"); - return NULL; - } - - if (error_indication_report->remote_f_teid.presence == 0) { - ogs_error("No Remote F-TEID"); - return NULL; - } - - remote_f_teid = error_indication_report->remote_f_teid.data; - ogs_assert(remote_f_teid); - - teid = be32toh(remote_f_teid->teid); - if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { - ogs_error("User plane should not set both IPv4 and IPv6"); - return NULL; - } else if (remote_f_teid->ipv4) { - len = OGS_IPV4_LEN; - memcpy(addr, &remote_f_teid->addr, len); - } else if (remote_f_teid->ipv6) { - len = OGS_IPV6_LEN; - memcpy(addr, remote_f_teid->addr6, len); - } else { - ogs_error("No IPv4 and IPv6"); - return NULL; - } - - ogs_list_for_each(&sess->bearer_list, bearer) { - ogs_list_for_each(&bearer->tunnel_list, tunnel) { - if (teid == tunnel->remote_teid) { - if (len == OGS_IPV4_LEN && tunnel->remote_ip.ipv4 && - memcmp(addr, &tunnel->remote_ip.addr, len) == 0) { - return bearer; - } else if (len == OGS_IPV6_LEN && tunnel->remote_ip.ipv6 && - memcmp(addr, tunnel->remote_ip.addr6, len) == 0) { - return bearer; - } - } - } - } - - ogs_error("Cannot find the bearer context " - "[TEID:%d,LEN:%d,ADDR:%08x %08x %08x %08x]", - teid, len, be32toh(addr[0]), be32toh(addr[1]), - be32toh(addr[2]), be32toh(addr[3])); - - return NULL; -} - sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess) { ogs_assert(sess); @@ -690,6 +657,7 @@ sgwc_tunnel_t *sgwc_tunnel_add( break; default: ogs_fatal("Invalid interface type = %d", interface_type); + ogs_assert_if_reached(); } ogs_pool_alloc(&sgwc_tunnel_pool, &tunnel); @@ -697,8 +665,6 @@ sgwc_tunnel_t *sgwc_tunnel_add( memset(tunnel, 0, sizeof *tunnel); tunnel->interface_type = interface_type; - tunnel->index = ogs_pool_index(&sgwc_tunnel_pool, tunnel); - ogs_assert(tunnel->index > 0 && tunnel->index <= ogs_app()->pool.tunnel); pdr = ogs_pfcp_pdr_add(&sess->pfcp); ogs_assert(pdr); @@ -753,10 +719,10 @@ sgwc_tunnel_t *sgwc_tunnel_add( &tunnel->local_addr, &tunnel->local_addr6); if (resource->info.teidri) tunnel->local_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, + pdr->teid, resource->info.teidri, resource->info.teid_range); else - tunnel->local_teid = pdr->index; + tunnel->local_teid = pdr->teid; } else { if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) ogs_assert(OGS_OK == @@ -769,7 +735,7 @@ sgwc_tunnel_t *sgwc_tunnel_add( else ogs_assert_if_reached(); - tunnel->local_teid = pdr->index; + tunnel->local_teid = pdr->teid; } ogs_assert(OGS_OK == @@ -872,6 +838,28 @@ sgwc_tunnel_t *sgwc_tunnel_find_by_pdr_id( return NULL; } +sgwc_tunnel_t *sgwc_tunnel_find_by_far_id( + sgwc_sess_t *sess, ogs_pfcp_far_id_t far_id) +{ + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *tunnel = NULL; + + ogs_pfcp_far_t *far = NULL; + + ogs_assert(sess); + + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + far = tunnel->far; + ogs_assert(far); + + if (far->id == far_id) return tunnel; + } + } + + return NULL; +} + sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer) { ogs_assert(bearer); diff --git a/src/sgwc/context.h b/src/sgwc/context.h index efa3a6461..eca5e72d8 100644 --- a/src/sgwc/context.h +++ b/src/sgwc/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -39,18 +39,21 @@ extern int __sgwc_log_domain; typedef struct sgwc_tunnel_s sgwc_tunnel_t; typedef struct sgwc_context_s { - ogs_list_t mme_s11_list; /* MME GTPC Node List */ - ogs_list_t pgw_s5c_list; /* PGW GTPC Node List */ + ogs_list_t mme_s11_list; /* MME GTPC Node List */ + ogs_list_t pgw_s5c_list; /* PGW GTPC Node List */ - ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : SGW_UE) */ + ogs_hash_t *imsi_ue_hash; /* hash table (IMSI : SGW_UE) */ + ogs_hash_t *sgw_s11_teid_hash; /* hash table (SGW-S11-TEID : SGW_UE) */ + ogs_hash_t *sgwc_sxa_seid_hash; /* hash table (SGWC-SXA-SEID : Session) */ - ogs_list_t sgw_ue_list; /* SGW_UE List */ + ogs_list_t sgw_ue_list; /* SGW_UE List */ } sgwc_context_t; typedef struct sgwc_ue_s { ogs_lnode_t lnode; + ogs_pool_id_t *sgw_s11_teid_node; /* A node of SGW-S11-TEID */ - uint32_t sgw_s11_teid; /* SGW-S11-TEID is derived from INDEX */ + uint32_t sgw_s11_teid; /* SGW-S11-TEID is derived from NODE */ uint32_t mme_s11_teid; /* MME-S11-TEID is received from MME */ /* UE identity */ @@ -70,15 +73,15 @@ typedef struct sgwc_ue_s { #define SGWC_SESS(pfcp_sess) ogs_container_of(pfcp_sess, sgwc_sess_t, pfcp) typedef struct sgwc_sess_s { - ogs_lnode_t lnode; /* A node of list_t */ - uint32_t index; /**< An index of this node */ + ogs_lnode_t lnode; /* A node of list_t */ + ogs_pool_id_t *sgwc_sxa_seid_node; /* A node of SGWC-SXA-SEID */ ogs_pfcp_sess_t pfcp; /* PFCP session context */ - uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from INDEX */ + uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is derived from NODE */ uint32_t pgw_s5c_teid; /* PGW-S5C-TEID is received from PGW */ - uint64_t sgwc_sxa_seid; /* SGW-C SEID is dervied from INDEX */ + uint64_t sgwc_sxa_seid; /* SGW-C SEID is dervied from NODE */ uint64_t sgwu_sxa_seid; /* SGW-U SEID is received from Peer */ /* APN Configuration */ @@ -106,7 +109,6 @@ typedef struct sgwc_bearer_s { typedef struct sgwc_tunnel_s { ogs_lnode_t lnode; - uint32_t index; /**< An index of this node */ uint8_t interface_type; @@ -147,7 +149,6 @@ void sgwc_sess_select_sgwu(sgwc_sess_t *sess); int sgwc_sess_remove(sgwc_sess_t *sess); void sgwc_sess_remove_all(sgwc_ue_t *sgwc_ue); -sgwc_sess_t *sgwc_sess_find(uint32_t index); sgwc_sess_t *sgwc_sess_find_by_teid(uint32_t teid); sgwc_sess_t *sgwc_sess_find_by_seid(uint64_t seid); @@ -167,9 +168,6 @@ sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi( sgwc_sess_t *sess, uint8_t ebi); sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi( sgwc_ue_t *sgwc_ue, uint8_t ebi); -sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report( - sgwc_sess_t *sess, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report); sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess); sgwc_bearer_t *sgwc_bearer_cycle(sgwc_bearer_t *bearer); @@ -182,6 +180,8 @@ sgwc_tunnel_t *sgwc_tunnel_find_by_interface_type( sgwc_bearer_t *bearer, uint8_t interface_type); sgwc_tunnel_t *sgwc_tunnel_find_by_pdr_id( sgwc_sess_t *sess, ogs_pfcp_pdr_id_t pdr_id); +sgwc_tunnel_t *sgwc_tunnel_find_by_far_id( + sgwc_sess_t *sess, ogs_pfcp_far_id_t far_id); sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer); sgwc_tunnel_t *sgwc_ul_tunnel_in_bearer(sgwc_bearer_t *bearer); diff --git a/src/sgwc/gtp-path.c b/src/sgwc/gtp-path.c index 906ab4436..85f0edec5 100644 --- a/src/sgwc/gtp-path.c +++ b/src/sgwc/gtp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/sgwc/pfcp-path.c b/src/sgwc/pfcp-path.c index 24d27cb6b..e737a4442 100644 --- a/src/sgwc/pfcp-path.c +++ b/src/sgwc/pfcp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -238,7 +238,8 @@ int sgwc_pfcp_send_bearer_to_modify_list( } int sgwc_pfcp_send_session_establishment_request( - sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf) + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf, + uint64_t flags) { int rv; ogs_pkbuf_t *sxabuf = NULL; @@ -262,9 +263,40 @@ int sgwc_pfcp_send_session_establishment_request( } } xact->local_seid = sess->sgwc_sxa_seid; + xact->create_flags = flags; memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE; + +/* + * 7.2.2.4.2 Conditions for Sending SEID=0 in PFCP Header + * + * If a peer's SEID is not available, the SEID field shall still be present + * in the header and its value shall be set to "0" in the following messages: + * + * - PFCP Session Establishment Request message on Sxa/Sxb/Sxc/N4; + * + * - If a node receives a message for which it has no session, i.e. + * if SEID in the PFCP header is not known, it shall respond + * with "Session context not found" cause in the corresponding + * response message to the sender, the SEID used in the PFCP header + * in the response message shall be then set to "0"; + * + * - If a node receives a request message containing protocol error, + * e.g. Mandatory IE missing, which requires the receiver + * to reject the message as specified in clause 7.6, it shall reject + * the request message. For the response message, the node should look up + * the remote peer's SEID and accordingly set SEID in the PFCP header + * and the message cause code. As an implementation option, + * the node may not look up the remote peer's SEID and + * set the PFCP header SEID to "0" in the response message. + * However in this case, the cause value shall not be set + * to "Session not found". + * + * - When the UP function sends PFCP Session Report Request message + * over N4 towards another SMF or another PFCP entity in the SMF + * as specified in clause 5.22.2 and clause 5.22.3. + */ h.seid = sess->sgwu_sxa_seid; sxabuf = sgwc_sxa_build_session_establishment_request(h.type, sess); diff --git a/src/sgwc/pfcp-path.h b/src/sgwc/pfcp-path.h index 82bbbb0c8..ae3314da2 100644 --- a/src/sgwc/pfcp-path.h +++ b/src/sgwc/pfcp-path.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -33,7 +33,8 @@ int sgwc_pfcp_send_bearer_to_modify_list( sgwc_sess_t *sess, ogs_pfcp_xact_t *xact); int sgwc_pfcp_send_session_establishment_request( - sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf); + sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, ogs_pkbuf_t *gtpbuf, + uint64_t flags); int sgwc_pfcp_send_session_modification_request( sgwc_sess_t *sess, ogs_gtp_xact_t *gtp_xact, diff --git a/src/sgwc/pfcp-sm.c b/src/sgwc/pfcp-sm.c index af099860e..fb9a17736 100644 --- a/src/sgwc/pfcp-sm.c +++ b/src/sgwc/pfcp-sm.c @@ -20,6 +20,7 @@ #include "pfcp-path.h" #include "sxa-handler.h" +static void pfcp_restoration(ogs_pfcp_node_t *node); static void node_timeout(ogs_pfcp_xact_t *xact, void *data); void sgwc_pfcp_state_initial(ogs_fsm_t *s, sgwc_event_t *e) @@ -188,6 +189,7 @@ void sgwc_pfcp_state_associated(ogs_fsm_t *s, sgwc_event_t *e) ogs_pfcp_send_heartbeat_request(node, node_timeout)); if (node->restoration_required == true) { + pfcp_restoration(node); node->restoration_required = false; ogs_error("PFCP restoration"); } @@ -220,11 +222,56 @@ void sgwc_pfcp_state_associated(ogs_fsm_t *s, sgwc_event_t *e) ogs_expect(true == ogs_pfcp_handle_heartbeat_request(node, xact, &message->pfcp_heartbeat_request)); + if (node->restoration_required == true) { + if (node->t_association) { + /* + * node->t_association that the PFCP entity attempts an association. + * + * In this case, even if Remote PFCP entity is restarted, + * PFCP restoration must be performed after PFCP association. + * + * Otherwise, Session related PFCP cannot be initiated + * because the peer PFCP entity is in a de-associated state. + */ + OGS_FSM_TRAN(s, sgwc_pfcp_state_will_associate); + } else { + + /* + * If the peer PFCP entity is performing the association, + * Restoration can be performed immediately. + */ + pfcp_restoration(node); + node->restoration_required = false; + ogs_error("PFCP restoration"); + } + } break; case OGS_PFCP_HEARTBEAT_RESPONSE_TYPE: ogs_expect(true == ogs_pfcp_handle_heartbeat_response(node, xact, &message->pfcp_heartbeat_response)); + if (node->restoration_required == true) { + /* + * node->t_association that the PFCP entity attempts an association. + * + * In this case, even if Remote PFCP entity is restarted, + * PFCP restoration must be performed after PFCP association. + * + * Otherwise, Session related PFCP cannot be initiated + * because the peer PFCP entity is in a de-associated state. + */ + if (node->t_association) { + OGS_FSM_TRAN(s, sgwc_pfcp_state_will_associate); + } else { + /* + * If the peer PFCP entity is performing the association, + * Restoration can be performed immediately. + */ + pfcp_restoration(node); + node->restoration_required = false; + ogs_error("PFCP restoration"); + } + } break; case OGS_PFCP_ASSOCIATION_SETUP_REQUEST_TYPE: ogs_warn("PFCP[REQ] has already been associated [%s]:%d", @@ -243,9 +290,24 @@ void sgwc_pfcp_state_associated(ogs_fsm_t *s, sgwc_event_t *e) case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: if (!message->h.seid_presence) ogs_error("No SEID"); - sgwc_sxa_handle_session_establishment_response( - sess, xact, e->gtp_message, - &message->pfcp_session_establishment_response); + if ((xact->create_flags & + OGS_PFCP_CREATE_RESTORATION_INDICATION)) { + ogs_pfcp_session_establishment_response_t *rsp = NULL; + ogs_pfcp_f_seid_t *up_f_seid = NULL; + + rsp = &message->pfcp_session_establishment_response; + if (rsp->up_f_seid.presence == 0) { + ogs_error("No UP F-SEID"); + break; + } + up_f_seid = rsp->up_f_seid.data; + ogs_assert(up_f_seid); + sess->sgwu_sxa_seid = be64toh(up_f_seid->seid); + } else { + sgwc_sxa_handle_session_establishment_response( + sess, xact, e->gtp_message, + &message->pfcp_session_establishment_response); + } break; case OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE: @@ -326,6 +388,29 @@ void sgwc_pfcp_state_exception(ogs_fsm_t *s, sgwc_event_t *e) } } +static void pfcp_restoration(ogs_pfcp_node_t *node) +{ + sgwc_ue_t *sgwc_ue = NULL; + + ogs_list_for_each(&sgwc_self()->sgw_ue_list, sgwc_ue) { + sgwc_sess_t *sess = NULL; + ogs_assert(sgwc_ue); + + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_assert(sess); + + if (node == sess->pfcp_node) { + ogs_info("UE IMSI[%s] APN[%s]", + sgwc_ue->imsi_bcd, sess->session.name); + ogs_assert(OGS_OK == + sgwc_pfcp_send_session_establishment_request( + sess, NULL, NULL, + OGS_PFCP_CREATE_RESTORATION_INDICATION)); + } + } + } +} + static void node_timeout(ogs_pfcp_xact_t *xact, void *data) { int rv; diff --git a/src/sgwc/s11-handler.c b/src/sgwc/s11-handler.c index e4363659d..f6fe9c319 100644 --- a/src/sgwc/s11-handler.c +++ b/src/sgwc/s11-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -234,6 +234,8 @@ void sgwc_s11_handle_create_session_request( sess = sgwc_sess_add(sgwc_ue, apn); ogs_assert(sess); + ogs_info("UE IMSI[%s] APN[%s]", sgwc_ue->imsi_bcd, sess->session.name); + /* Set User Location Information */ if (req->user_location_information.presence == 1) { decoded = ogs_gtp2_parse_uli(&uli, &req->user_location_information); @@ -376,7 +378,8 @@ void sgwc_s11_handle_create_session_request( sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); ogs_assert(OGS_OK == - sgwc_pfcp_send_session_establishment_request(sess, s11_xact, gtpbuf)); + sgwc_pfcp_send_session_establishment_request( + sess, s11_xact, gtpbuf, 0)); } void sgwc_s11_handle_modify_bearer_request( diff --git a/src/sgwc/sxa-handler.c b/src/sgwc/sxa-handler.c index 9bd17f163..14fb667cb 100644 --- a/src/sgwc/sxa-handler.c +++ b/src/sgwc/sxa-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -1297,10 +1297,7 @@ void sgwc_sxa_handle_session_deletion_response( ogs_pfcp_xact_commit(pfcp_xact); - if (!gtp_message) { - ogs_error("No GTP Message"); - goto cleanup; - } + if (!gtp_message) goto cleanup; if (gtp_message->h.type == OGS_GTP2_DELETE_SESSION_REQUEST_TYPE) { /* @@ -1380,7 +1377,10 @@ void sgwc_sxa_handle_session_deletion_response( } cleanup: - sgwc_sess_remove(sess); + if (sgwc_sess_cycle(sess)) + sgwc_sess_remove(sess); + else + ogs_error("Session has already been removed"); } void sgwc_sxa_handle_session_report_request( @@ -1390,6 +1390,7 @@ void sgwc_sxa_handle_session_report_request( sgwc_ue_t *sgwc_ue = NULL; sgwc_bearer_t *bearer = NULL; sgwc_tunnel_t *tunnel = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_report_type_t report_type; uint8_t cause_value = 0; @@ -1470,22 +1471,44 @@ void sgwc_sxa_handle_session_report_request( ogs_error("Cannot find the PDR-ID[%d]", pdr_id); } else if (report_type.error_indication_report) { - bearer = sgwc_bearer_find_by_error_indication_report( - sess, &pfcp_req->error_indication_report); - - if (!bearer) return; - - ogs_list_for_each(&sgwc_ue->sess_list, sess) { - - ogs_assert(OGS_OK == - sgwc_pfcp_send_session_modification_request(sess, - /* We only use the `assoc_xact` parameter temporarily here - * to pass the `bearer` context. */ - (ogs_gtp_xact_t *)bearer, - NULL, - OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| - OGS_PFCP_MODIFY_ERROR_INDICATION)); - } + far = ogs_pfcp_far_find_by_pfcp_session_report( + &sess->pfcp, &pfcp_req->error_indication_report); + if (far) { + tunnel = sgwc_tunnel_find_by_far_id(sess, far->id); + ogs_assert(tunnel); + bearer = tunnel->bearer; + ogs_assert(bearer); + if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + ogs_warn("[%s] Error Indication from eNB", sgwc_ue->imsi_bcd); + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_assert(OGS_OK == + sgwc_pfcp_send_session_modification_request(sess, + /* We only use the `assoc_xact` parameter temporarily here + * to pass the `bearer` context. */ + (ogs_gtp_xact_t *)bearer, + NULL, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| + OGS_PFCP_MODIFY_ERROR_INDICATION)); + } + } else if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { + if (sgwc_default_bearer_in_sess(sess) == bearer) { + ogs_error("[%s] Error Indication(Default Bearer) from SMF", + sgwc_ue->imsi_bcd); + ogs_assert(OGS_OK == + sgwc_pfcp_send_session_deletion_request( + sess, NULL, NULL)); + } else { + ogs_error("[%s] Error Indication(Dedicated Bearer) " + "from SMF", sgwc_ue->imsi_bcd); + ogs_assert(OGS_OK == + sgwc_pfcp_send_bearer_modification_request( + bearer, NULL, NULL, OGS_PFCP_MODIFY_REMOVE)); + } + } else { + ogs_error("Error Indication Ignored for Indirect Tunnel"); + } + } else + ogs_error("Cannot find Session in Error Indication"); } else { ogs_error("Not supported Report Type[%d]", report_type.value); diff --git a/src/sgwu/context.c b/src/sgwu/context.c index 2f1fd4a60..87f97ef2f 100644 --- a/src/sgwu/context.c +++ b/src/sgwu/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -24,6 +24,7 @@ static sgwu_context_t self; int __sgwu_log_domain; static OGS_POOL(sgwu_sess_pool, sgwu_sess_t); +static OGS_POOL(sgwu_sxa_seid_pool, ogs_pool_id_t); static int context_initialized = 0; @@ -43,11 +44,15 @@ void sgwu_context_init(void) ogs_list_init(&self.sess_list); ogs_pool_init(&sgwu_sess_pool, ogs_app()->pool.sess); + ogs_pool_init(&sgwu_sxa_seid_pool, ogs_app()->pool.sess); + ogs_pool_random_id_generate(&sgwu_sxa_seid_pool); - self.seid_hash = ogs_hash_make(); - ogs_assert(self.seid_hash); - self.f_seid_hash = ogs_hash_make(); - ogs_assert(self.f_seid_hash); + self.sgwu_sxa_seid_hash = ogs_hash_make(); + ogs_assert(self.sgwu_sxa_seid_hash); + self.sgwc_sxa_seid_hash = ogs_hash_make(); + ogs_assert(self.sgwc_sxa_seid_hash); + self.sgwc_sxa_f_seid_hash = ogs_hash_make(); + ogs_assert(self.sgwc_sxa_f_seid_hash); context_initialized = 1; } @@ -58,12 +63,15 @@ void sgwu_context_final(void) sgwu_sess_remove_all(); - ogs_assert(self.seid_hash); - ogs_hash_destroy(self.seid_hash); - ogs_assert(self.f_seid_hash); - ogs_hash_destroy(self.f_seid_hash); + ogs_assert(self.sgwu_sxa_seid_hash); + ogs_hash_destroy(self.sgwu_sxa_seid_hash); + ogs_assert(self.sgwc_sxa_seid_hash); + ogs_hash_destroy(self.sgwc_sxa_seid_hash); + ogs_assert(self.sgwc_sxa_f_seid_hash); + ogs_hash_destroy(self.sgwc_sxa_f_seid_hash); ogs_pool_final(&sgwu_sess_pool); + ogs_pool_final(&sgwu_sxa_seid_pool); context_initialized = 0; } @@ -137,10 +145,14 @@ sgwu_sess_t *sgwu_sess_add(ogs_pfcp_f_seid_t *cp_f_seid) ogs_pfcp_pool_init(&sess->pfcp); - sess->index = ogs_pool_index(&sgwu_sess_pool, sess); - ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); + /* Set SEID */ + ogs_pool_alloc(&sgwu_sxa_seid_pool, &sess->sgwu_sxa_seid_node); + ogs_assert(sess->sgwu_sxa_seid_node); - sess->sgwu_sxa_seid = sess->index; + sess->sgwu_sxa_seid = *(sess->sgwu_sxa_seid_node); + + ogs_hash_set(self.sgwu_sxa_seid_hash, &sess->sgwu_sxa_seid, + sizeof(sess->sgwu_sxa_seid), sess); /* Since F-SEID is composed of ogs_ip_t and uint64-seid, * all these values must be put into the structure-sgwc_sxa_f_eid @@ -149,9 +161,9 @@ sgwu_sess_t *sgwu_sess_add(ogs_pfcp_f_seid_t *cp_f_seid) ogs_assert(OGS_OK == ogs_pfcp_f_seid_to_ip(cp_f_seid, &sess->sgwc_sxa_f_seid.ip)); - ogs_hash_set(self.f_seid_hash, &sess->sgwc_sxa_f_seid, + ogs_hash_set(self.sgwc_sxa_f_seid_hash, &sess->sgwc_sxa_f_seid, sizeof(sess->sgwc_sxa_f_seid), sess); - ogs_hash_set(self.seid_hash, &sess->sgwc_sxa_f_seid.seid, + ogs_hash_set(self.sgwc_sxa_seid_hash, &sess->sgwc_sxa_f_seid.seid, sizeof(sess->sgwc_sxa_f_seid.seid), sess); ogs_info("UE F-SEID[UP:0x%lx CP:0x%lx]", @@ -172,13 +184,17 @@ int sgwu_sess_remove(sgwu_sess_t *sess) ogs_list_remove(&self.sess_list, sess); ogs_pfcp_sess_clear(&sess->pfcp); - ogs_hash_set(self.seid_hash, &sess->sgwc_sxa_f_seid.seid, + ogs_hash_set(self.sgwu_sxa_seid_hash, &sess->sgwu_sxa_seid, + sizeof(sess->sgwu_sxa_seid), NULL); + + ogs_hash_set(self.sgwc_sxa_seid_hash, &sess->sgwc_sxa_f_seid.seid, sizeof(sess->sgwc_sxa_f_seid.seid), NULL); - ogs_hash_set(self.f_seid_hash, &sess->sgwc_sxa_f_seid, + ogs_hash_set(self.sgwc_sxa_f_seid_hash, &sess->sgwc_sxa_f_seid, sizeof(sess->sgwc_sxa_f_seid), NULL); ogs_pfcp_pool_final(&sess->pfcp); + ogs_pool_free(&sgwu_sxa_seid_pool, sess->sgwu_sxa_seid_node); ogs_pool_free(&sgwu_sess_pool, sess); ogs_info("[Removed] Number of SGWU-sessions is now %d", @@ -196,14 +212,9 @@ void sgwu_sess_remove_all(void) } } -sgwu_sess_t *sgwu_sess_find(uint32_t index) -{ - return ogs_pool_find(&sgwu_sess_pool, index); -} - sgwu_sess_t *sgwu_sess_find_by_sgwc_sxa_seid(uint64_t seid) { - return (sgwu_sess_t *)ogs_hash_get(self.seid_hash, &seid, sizeof(seid)); + return ogs_hash_get(self.sgwc_sxa_seid_hash, &seid, sizeof(seid)); } sgwu_sess_t *sgwu_sess_find_by_sgwc_sxa_f_seid(ogs_pfcp_f_seid_t *f_seid) @@ -217,12 +228,12 @@ sgwu_sess_t *sgwu_sess_find_by_sgwc_sxa_f_seid(ogs_pfcp_f_seid_t *f_seid) ogs_assert(OGS_OK == ogs_pfcp_f_seid_to_ip(f_seid, &key.ip)); key.seid = f_seid->seid; - return (sgwu_sess_t *)ogs_hash_get(self.f_seid_hash, &key, sizeof(key)); + return ogs_hash_get(self.sgwc_sxa_f_seid_hash, &key, sizeof(key)); } sgwu_sess_t *sgwu_sess_find_by_sgwu_sxa_seid(uint64_t seid) { - return sgwu_sess_find(seid); + return ogs_hash_get(self.sgwu_sxa_seid_hash, &seid, sizeof(seid)); } sgwu_sess_t *sgwu_sess_add_by_message(ogs_pfcp_message_t *message) @@ -244,7 +255,10 @@ sgwu_sess_t *sgwu_sess_add_by_message(ogs_pfcp_message_t *message) sess = sgwu_sess_find_by_sgwc_sxa_f_seid(f_seid); if (!sess) { sess = sgwu_sess_add(f_seid); - if (!sess) return NULL; + if (!sess) { + ogs_error("No Session Context"); + return NULL; + } } ogs_assert(sess); diff --git a/src/sgwu/context.h b/src/sgwu/context.h index 723adc71b..81928498b 100644 --- a/src/sgwu/context.h +++ b/src/sgwu/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -37,16 +37,17 @@ extern int __sgwu_log_domain; #define OGS_LOG_DOMAIN __sgwu_log_domain typedef struct sgwu_context_s { - ogs_hash_t *seid_hash; /* hash table (SEID) */ - ogs_hash_t *f_seid_hash; /* hash table (F-SEID) */ + ogs_hash_t *sgwu_sxa_seid_hash; /* hash table (SGWU-SXA-SEID) */ + ogs_hash_t *sgwc_sxa_seid_hash; /* hash table (SGWC-SXA-SEID) */ + ogs_hash_t *sgwc_sxa_f_seid_hash; /* hash table (SGWC-SXA-F-SEID) */ - ogs_list_t sess_list; + ogs_list_t sess_list; } sgwu_context_t; #define SGWU_SESS(pfcp_sess) ogs_container_of(pfcp_sess, sgwu_sess_t, pfcp) typedef struct sgwu_sess_s { ogs_lnode_t lnode; - uint32_t index; /**< An index of this node */ + ogs_pool_id_t *sgwu_sxa_seid_node; /* A node of SGWU-SXA-SEID */ ogs_pfcp_sess_t pfcp; @@ -70,7 +71,6 @@ sgwu_sess_t *sgwu_sess_add_by_message(ogs_pfcp_message_t *message); sgwu_sess_t *sgwu_sess_add(ogs_pfcp_f_seid_t *f_seid); int sgwu_sess_remove(sgwu_sess_t *sess); void sgwu_sess_remove_all(void); -sgwu_sess_t *sgwu_sess_find(uint32_t index); sgwu_sess_t *sgwu_sess_find_by_sgwc_sxa_seid(uint64_t seid); sgwu_sess_t *sgwu_sess_find_by_sgwc_sxa_f_seid(ogs_pfcp_f_seid_t *f_seid); sgwu_sess_t *sgwu_sess_find_by_sgwu_sxa_seid(uint64_t seid); diff --git a/src/sgwu/gtp-path.c b/src/sgwu/gtp-path.c index 91484bef9..b3868b49a 100644 --- a/src/sgwu/gtp-path.c +++ b/src/sgwu/gtp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -28,11 +28,13 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) { int len; ssize_t size; - char buf[OGS_ADDRSTRLEN]; + char buf1[OGS_ADDRSTRLEN]; + char buf2[OGS_ADDRSTRLEN]; sgwu_sess_t *sess = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_sock_t *sock = NULL; ogs_sockaddr_t from; ogs_gtp2_header_t *gtp_h = NULL; @@ -42,6 +44,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) uint8_t qfi; ogs_assert(fd != INVALID_SOCKET); + sock = data; + ogs_assert(sock); pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_PKT_LEN); ogs_assert(pkbuf); @@ -69,14 +73,14 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) { ogs_pkbuf_t *echo_rsp; - ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf1)); echo_rsp = ogs_gtp2_handle_echo_req(pkbuf); ogs_expect(echo_rsp); if (echo_rsp) { ssize_t sent; /* Echo reply */ - ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf1)); sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from); if (sent < 0 || sent != echo_rsp->len) { @@ -91,7 +95,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); ogs_trace("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", - gtp_h->type, OGS_ADDR(&from, buf), teid); + gtp_h->type, OGS_ADDR(&from, buf1), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -139,7 +143,27 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * Refer to the following 5G standard + * + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, 0, &from); + } goto cleanup; } @@ -164,7 +188,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { ogs_pfcp_far_t *far = NULL; - far = ogs_pfcp_far_find_by_error_indication(pkbuf); + far = ogs_pfcp_far_find_by_gtpu_error_indication(pkbuf); if (far) { ogs_assert(true == ogs_pfcp_up_handle_error_indication(far, &report)); @@ -177,7 +201,6 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) ogs_assert(OGS_OK == sgwu_pfcp_send_session_report_request(sess, &report)); } - } else { ogs_error("[DROP] Cannot find FAR by Error-Indication"); ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); @@ -193,7 +216,27 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * Refer to the following 5G standard + * + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, 0, &from); + } goto cleanup; } @@ -203,6 +246,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) ogs_assert(pdr); break; case OGS_PFCP_OBJ_SESS_TYPE: + /* SGWU does not use SESS TYPE */ + ogs_assert_if_reached(); pfcp_sess = (ogs_pfcp_sess_t *)pfcp_object; ogs_assert(pfcp_sess); @@ -266,7 +311,7 @@ int sgwu_gtp_init(void) config.cluster_2048_pool = ogs_app()->pool.packet; -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 /* allocate a talloc pool for GTP to ensure it doesn't have to go back * to the libc malloc all the time */ packet_pool = talloc_pool(__ogs_talloc_core, 1000*1024); diff --git a/src/sgwu/pfcp-path.c b/src/sgwu/pfcp-path.c index 1a63fbacc..f3a08c072 100644 --- a/src/sgwu/pfcp-path.c +++ b/src/sgwu/pfcp-path.c @@ -205,7 +205,6 @@ int sgwu_pfcp_send_session_modification_response( ogs_pfcp_header_t h; ogs_assert(xact); - ogs_assert(created_pdr); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE; diff --git a/src/sgwu/pfcp-sm.c b/src/sgwu/pfcp-sm.c index c28385307..3393eaecf 100644 --- a/src/sgwu/pfcp-sm.c +++ b/src/sgwu/pfcp-sm.c @@ -277,12 +277,9 @@ void sgwu_pfcp_state_associated(ogs_fsm_t *s, sgwu_event_t *e) &message->pfcp_association_setup_response); break; case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: - if (message->h.seid_presence && message->h.seid == 0) { - ogs_expect(!sess); - sess = sgwu_sess_add_by_message(message); - if (sess) - OGS_SETUP_PFCP_NODE(sess, node); - } + sess = sgwu_sess_add_by_message(message); + if (sess) + OGS_SETUP_PFCP_NODE(sess, node); sgwu_sxa_handle_session_establishment_request( sess, xact, &message->pfcp_session_establishment_request); break; diff --git a/src/sgwu/sxa-handler.c b/src/sgwu/sxa-handler.c index b000626d6..7950ef941 100644 --- a/src/sgwu/sxa-handler.c +++ b/src/sgwu/sxa-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -33,6 +33,9 @@ void sgwu_sxa_handle_session_establishment_request( uint8_t offending_ie_value = 0; int i; + ogs_pfcp_sereq_flags_t sereq_flags; + bool restoration_indication = false; + ogs_assert(xact); ogs_assert(req); @@ -48,9 +51,16 @@ void sgwu_sxa_handle_session_establishment_request( return; } + /* PFCPSEReq-Flags */ + memset(&sereq_flags, 0, sizeof(sereq_flags)); + if (req->pfcpsereq_flags.presence == 1) { + sereq_flags.value = req->pfcpsereq_flags.u8; + } + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, - &req->create_pdr[i], &cause_value, &offending_ie_value); + &req->create_pdr[i], &sereq_flags, + &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; } @@ -79,6 +89,18 @@ void sgwu_sxa_handle_session_establishment_request( if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) goto cleanup; + /* PFCPSEReq-Flags */ + if (sereq_flags.restoration_indication == 1) { + for (i = 0; i < num_of_created_pdr; i++) { + pdr = created_pdr[i]; + ogs_assert(pdr); + + if (pdr->f_teid_len) + ogs_pfcp_pdr_swap_teid(pdr); + } + restoration_indication = true; + } + /* Setup GTP Node */ ogs_list_for_each(&sess->pfcp.far_list, far) { ogs_assert(OGS_ERROR != ogs_pfcp_setup_far_gtpu_node(far)); @@ -86,70 +108,14 @@ void sgwu_sxa_handle_session_establishment_request( ogs_pfcp_far_f_teid_hash_set(far); } - /* Setup TEID Hash */ for (i = 0; i < num_of_created_pdr; i++) { pdr = created_pdr[i]; ogs_assert(pdr); - if (pdr->f_teid_len) { - ogs_pfcp_object_type_e type = OGS_PFCP_OBJ_PDR_TYPE; - - if (ogs_pfcp_self()->up_function_features.ftup && - pdr->f_teid.ch) { - - ogs_pfcp_pdr_t *choosed_pdr = NULL; - - if (pdr->f_teid.chid) { - type = OGS_PFCP_OBJ_SESS_TYPE; - - choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( - &sess->pfcp, pdr->f_teid.choose_id); - if (!choosed_pdr) { - pdr->chid = true; - pdr->choose_id = pdr->f_teid.choose_id; - } - } - - if (choosed_pdr) { - pdr->f_teid_len = choosed_pdr->f_teid_len; - memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); - - } else { - ogs_gtpu_resource_t *resource = NULL; - resource = ogs_pfcp_find_gtpu_resource( - &ogs_gtp_self()->gtpu_resource_list, - pdr->apn, OGS_PFCP_INTERFACE_ACCESS); - if (resource) { - ogs_assert( - (resource->info.v4 && pdr->f_teid.ipv4) || - (resource->info.v6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_user_plane_ip_resource_info_to_f_teid( - &resource->info, &pdr->f_teid, &pdr->f_teid_len)); - if (resource->info.teidri) - pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, - resource->info.teid_range); - else - pdr->f_teid.teid = pdr->index; - } else { - ogs_assert( - (ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || - (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_sockaddr_to_f_teid( - pdr->f_teid.ipv4 ? - ogs_gtp_self()->gtpu_addr : NULL, - pdr->f_teid.ipv6 ? - ogs_gtp_self()->gtpu_addr6 : NULL, - &pdr->f_teid, &pdr->f_teid_len)); - pdr->f_teid.teid = pdr->index; - } - } - } - - ogs_pfcp_object_teid_hash_set(type, pdr); - } + /* Setup TEID Hash */ + if (pdr->f_teid_len) + ogs_pfcp_object_teid_hash_set( + OGS_PFCP_OBJ_PDR_TYPE, pdr, restoration_indication); } /* Send Buffered Packet to gNB */ @@ -159,9 +125,15 @@ void sgwu_sxa_handle_session_establishment_request( } } - ogs_assert(OGS_OK == - sgwu_pfcp_send_session_establishment_response( - xact, sess, created_pdr, num_of_created_pdr)); + if (restoration_indication == true || + ogs_pfcp_self()->up_function_features.ftup == 0) + ogs_assert(OGS_OK == + sgwu_pfcp_send_session_establishment_response( + xact, sess, NULL, 0)); + else + ogs_assert(OGS_OK == + sgwu_pfcp_send_session_establishment_response( + xact, sess, created_pdr, num_of_created_pdr)); return; cleanup: @@ -200,7 +172,7 @@ void sgwu_sxa_handle_session_modification_request( for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, - &req->create_pdr[i], &cause_value, &offending_ie_value); + &req->create_pdr[i], NULL, &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; } @@ -301,70 +273,13 @@ void sgwu_sxa_handle_session_modification_request( ogs_pfcp_far_f_teid_hash_set(far); } - /* Setup TEID Hash */ for (i = 0; i < num_of_created_pdr; i++) { pdr = created_pdr[i]; ogs_assert(pdr); - if (pdr->f_teid_len) { - ogs_pfcp_object_type_e type = OGS_PFCP_OBJ_PDR_TYPE; - - if (ogs_pfcp_self()->up_function_features.ftup && - pdr->f_teid.ch) { - - ogs_pfcp_pdr_t *choosed_pdr = NULL; - - if (pdr->f_teid.chid) { - type = OGS_PFCP_OBJ_SESS_TYPE; - - choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( - &sess->pfcp, pdr->f_teid.choose_id); - if (!choosed_pdr) { - pdr->chid = true; - pdr->choose_id = pdr->f_teid.choose_id; - } - } - - if (choosed_pdr) { - pdr->f_teid_len = choosed_pdr->f_teid_len; - memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); - - } else { - ogs_gtpu_resource_t *resource = NULL; - resource = ogs_pfcp_find_gtpu_resource( - &ogs_gtp_self()->gtpu_resource_list, - pdr->apn, OGS_PFCP_INTERFACE_ACCESS); - if (resource) { - ogs_assert( - (resource->info.v4 && pdr->f_teid.ipv4) || - (resource->info.v6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_user_plane_ip_resource_info_to_f_teid( - &resource->info, &pdr->f_teid, &pdr->f_teid_len)); - if (resource->info.teidri) - pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, - resource->info.teid_range); - else - pdr->f_teid.teid = pdr->index; - } else { - ogs_assert( - (ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || - (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_sockaddr_to_f_teid( - pdr->f_teid.ipv4 ? - ogs_gtp_self()->gtpu_addr : NULL, - pdr->f_teid.ipv6 ? - ogs_gtp_self()->gtpu_addr6 : NULL, - &pdr->f_teid, &pdr->f_teid_len)); - pdr->f_teid.teid = pdr->index; - } - } - } - - ogs_pfcp_object_teid_hash_set(type, pdr); - } + /* Setup TEID Hash */ + if (pdr->f_teid_len) + ogs_pfcp_object_teid_hash_set(OGS_PFCP_OBJ_PDR_TYPE, pdr, false); } /* Send Buffered Packet to gNB */ @@ -374,9 +289,14 @@ void sgwu_sxa_handle_session_modification_request( } } - ogs_assert(OGS_OK == - sgwu_pfcp_send_session_modification_response( - xact, sess, created_pdr, num_of_created_pdr)); + if (ogs_pfcp_self()->up_function_features.ftup == 0) + ogs_assert(OGS_OK == + sgwu_pfcp_send_session_modification_response( + xact, sess, NULL, 0)); + else + ogs_assert(OGS_OK == + sgwu_pfcp_send_session_modification_response( + xact, sess, created_pdr, num_of_created_pdr)); return; cleanup: diff --git a/src/smf/binding.c b/src/smf/binding.c index 7a27fb52d..bd254b903 100644 --- a/src/smf/binding.c +++ b/src/smf/binding.c @@ -197,10 +197,10 @@ void smf_bearer_binding(smf_sess_t *sess) &bearer->pgw_s5u_addr, &bearer->pgw_s5u_addr6); if (resource->info.teidri) bearer->pgw_s5u_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - ul_pdr->index, resource->info.teidri, + ul_pdr->teid, resource->info.teidri, resource->info.teid_range); else - bearer->pgw_s5u_teid = ul_pdr->index; + bearer->pgw_s5u_teid = ul_pdr->teid; } else { if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) ogs_assert(OGS_OK == @@ -214,7 +214,7 @@ void smf_bearer_binding(smf_sess_t *sess) else ogs_assert_if_reached(); - bearer->pgw_s5u_teid = ul_pdr->index; + bearer->pgw_s5u_teid = ul_pdr->teid; } ogs_assert(OGS_OK == diff --git a/src/smf/context.c b/src/smf/context.c index f4a9b6a75..867c11480 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -29,11 +29,12 @@ int __gsm_log_domain; static OGS_POOL(smf_gtp_node_pool, smf_gtp_node_t); static OGS_POOL(smf_ue_pool, smf_ue_t); -static OGS_POOL(smf_sess_pool, smf_sess_t); static OGS_POOL(smf_bearer_pool, smf_bearer_t); - static OGS_POOL(smf_pf_pool, smf_pf_t); +static OGS_POOL(smf_sess_pool, smf_sess_t); +static OGS_POOL(smf_n4_seid_pool, ogs_pool_id_t); + static int context_initialized = 0; static int num_of_smf_sess = 0; @@ -82,16 +83,20 @@ void smf_context_init(void) ogs_pool_init(&smf_gtp_node_pool, ogs_app()->pool.nf); ogs_pool_init(&smf_ue_pool, ogs_app()->max.ue); - ogs_pool_init(&smf_sess_pool, ogs_app()->pool.sess); ogs_pool_init(&smf_bearer_pool, ogs_app()->pool.bearer); - ogs_pool_init(&smf_pf_pool, ogs_app()->pool.bearer * OGS_MAX_NUM_OF_FLOW_IN_BEARER); + ogs_pool_init(&smf_sess_pool, ogs_app()->pool.sess); + ogs_pool_init(&smf_n4_seid_pool, ogs_app()->pool.sess); + ogs_pool_random_id_generate(&smf_n4_seid_pool); + self.supi_hash = ogs_hash_make(); ogs_assert(self.supi_hash); self.imsi_hash = ogs_hash_make(); ogs_assert(self.imsi_hash); + self.smf_n4_seid_hash = ogs_hash_make(); + ogs_assert(self.smf_n4_seid_hash); self.ipv4_hash = ogs_hash_make(); ogs_assert(self.ipv4_hash); self.ipv6_hash = ogs_hash_make(); @@ -99,7 +104,6 @@ void smf_context_init(void) self.n1n2message_hash = ogs_hash_make(); ogs_assert(self.n1n2message_hash); - context_initialized = 1; } @@ -114,6 +118,8 @@ void smf_context_final(void) ogs_hash_destroy(self.supi_hash); ogs_assert(self.imsi_hash); ogs_hash_destroy(self.imsi_hash); + ogs_assert(self.smf_n4_seid_hash); + ogs_hash_destroy(self.smf_n4_seid_hash); ogs_assert(self.ipv4_hash); ogs_hash_destroy(self.ipv4_hash); ogs_assert(self.ipv6_hash); @@ -123,10 +129,11 @@ void smf_context_final(void) ogs_pool_final(&smf_ue_pool); ogs_pool_final(&smf_bearer_pool); - ogs_pool_final(&smf_sess_pool); - ogs_pool_final(&smf_pf_pool); + ogs_pool_final(&smf_sess_pool); + ogs_pool_final(&smf_n4_seid_pool); + ogs_list_for_each_entry_safe(&self.sgw_s5c_list, next_gnode, gnode, node) { smf_gtp_node_t *smf_gnode = gnode->data_ptr; ogs_assert(smf_gnode); @@ -1198,8 +1205,14 @@ smf_sess_t *smf_sess_add_by_apn(smf_ue_t *smf_ue, char *apn, uint8_t rat_type) ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); /* Set TEID & SEID */ - sess->smf_n4_teid = sess->index; - sess->smf_n4_seid = sess->index; + ogs_pool_alloc(&smf_n4_seid_pool, &sess->smf_n4_seid_node); + ogs_assert(sess->smf_n4_seid_node); + + sess->smf_n4_teid = *(sess->smf_n4_seid_node); + sess->smf_n4_seid = *(sess->smf_n4_seid_node); + + ogs_hash_set(self.smf_n4_seid_hash, &sess->smf_n4_seid, + sizeof(sess->smf_n4_seid), sess); /* Set Charging ID */ sess->charging.id = sess->index; @@ -1403,9 +1416,18 @@ smf_sess_t *smf_sess_add_by_psi(smf_ue_t *smf_ue, uint8_t psi) sess->index = ogs_pool_index(&smf_sess_pool, sess); ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); + /* Set TEID & SEID */ + ogs_pool_alloc(&smf_n4_seid_pool, &sess->smf_n4_seid_node); + ogs_assert(sess->smf_n4_seid_node); + + sess->smf_n4_teid = *(sess->smf_n4_seid_node); + sess->smf_n4_seid = *(sess->smf_n4_seid_node); + + ogs_hash_set(self.smf_n4_seid_hash, &sess->smf_n4_seid, + sizeof(sess->smf_n4_seid), sess); + /* Set SmContextRef in 5GC */ - sess->sm_context_ref = ogs_msprintf("%d", - (int)ogs_pool_index(&smf_sess_pool, sess)); + sess->sm_context_ref = ogs_msprintf("%d", sess->index); ogs_assert(sess->sm_context_ref); /* Create BAR in PFCP Session */ @@ -1420,10 +1442,6 @@ smf_sess_t *smf_sess_add_by_psi(smf_ue_t *smf_ue, uint8_t psi) sess->mapped_hplmn.sst = 0; sess->mapped_hplmn.sd.v = OGS_S_NSSAI_NO_SD_VALUE; - /* Set TEID & SEID */ - sess->smf_n4_teid = sess->index; - sess->smf_n4_seid = sess->index; - /* Set Charging Id */ sess->charging.id = sess->index; @@ -1623,68 +1641,6 @@ void smf_sess_set_paging_n1n2message_location( sess); } -smf_sess_t *smf_sess_find_by_error_indication_report( - smf_ue_t *smf_ue, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report) -{ - smf_sess_t *sess = NULL; - ogs_pfcp_f_teid_t *remote_f_teid = NULL; - - uint32_t teid; - uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ - uint32_t addr[4]; - - ogs_assert(smf_ue); - ogs_assert(error_indication_report); - - if (error_indication_report->presence == 0) { - ogs_error("No Error Indication Report"); - return NULL; - } - - if (error_indication_report->remote_f_teid.presence == 0) { - ogs_error("No Remote F-TEID"); - return NULL; - } - - remote_f_teid = error_indication_report->remote_f_teid.data; - ogs_assert(remote_f_teid); - - teid = be32toh(remote_f_teid->teid); - if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { - ogs_error("User plane should not set both IPv4 and IPv6"); - return NULL; - } else if (remote_f_teid->ipv4) { - len = OGS_IPV4_LEN; - memcpy(addr, &remote_f_teid->addr, len); - } else if (remote_f_teid->ipv6) { - len = OGS_IPV6_LEN; - memcpy(addr, remote_f_teid->addr6, len); - } else { - ogs_error("No IPv4 and IPv6"); - return NULL; - } - - ogs_list_reverse_for_each(&smf_ue->sess_list, sess) { - if (teid == sess->gnb_n3_teid) { - if (len == OGS_IPV4_LEN && sess->gnb_n3_ip.ipv4 && - memcmp(addr, &sess->gnb_n3_ip.addr, len) == 0) { - return sess; - } else if (len == OGS_IPV6_LEN && sess->gnb_n3_ip.ipv6 && - memcmp(addr, sess->gnb_n3_ip.addr6, len) == 0) { - return sess; - } - } - } - - ogs_error("Cannot find the session context " - "[TEID:%d,LEN:%d,ADDR:%08x %08x %08x %08x]", - teid, len, be32toh(addr[0]), be32toh(addr[1]), - be32toh(addr[2]), be32toh(addr[3])); - - return NULL; -} - void smf_sess_remove(smf_sess_t *sess) { int i; @@ -1722,6 +1678,9 @@ void smf_sess_remove(smf_sess_t *sess) OGS_PCC_RULE_FREE(&sess->policy.pcc_rule[i]); sess->policy.num_of_pcc_rule = 0; + ogs_hash_set(self.smf_n4_seid_hash, &sess->smf_n4_seid, + sizeof(sess->smf_n4_seid), NULL); + if (sess->ipv4) { ogs_hash_set(self.ipv4_hash, sess->ipv4->addr, OGS_IPV4_LEN, NULL); ogs_pfcp_ue_ip_free(sess->ipv4); @@ -1809,6 +1768,8 @@ void smf_sess_remove(smf_sess_t *sess) break; } stats_remove_smf_session(sess); + + ogs_pool_free(&smf_n4_seid_pool, sess->smf_n4_seid_node); ogs_pool_free(&smf_sess_pool, sess); } @@ -1822,19 +1783,14 @@ void smf_sess_remove_all(smf_ue_t *smf_ue) smf_sess_remove(sess); } -smf_sess_t *smf_sess_find(uint32_t index) -{ - return ogs_pool_find(&smf_sess_pool, index); -} - smf_sess_t *smf_sess_find_by_teid(uint32_t teid) { - return smf_sess_find(teid); + return smf_sess_find_by_seid(teid); } smf_sess_t *smf_sess_find_by_seid(uint64_t seid) { - return smf_sess_find(seid); + return ogs_hash_get(self.smf_n4_seid_hash, &seid, sizeof(seid)); } smf_sess_t *smf_sess_find_by_apn(smf_ue_t *smf_ue, char *apn, uint8_t rat_type) @@ -1868,6 +1824,11 @@ smf_sess_t *smf_sess_find_by_psi(smf_ue_t *smf_ue, uint8_t psi) return NULL; } +smf_sess_t *smf_sess_find(uint32_t index) +{ + return ogs_pool_find(&smf_sess_pool, index); +} + smf_sess_t *smf_sess_find_by_charging_id(uint32_t charging_id) { ogs_assert(charging_id); @@ -1939,10 +1900,6 @@ smf_bearer_t *smf_qos_flow_add(smf_sess_t *sess) smf_pf_identifier_pool_init(qos_flow); - qos_flow->index = ogs_pool_index(&smf_bearer_pool, qos_flow); - ogs_assert(qos_flow->index > 0 && qos_flow->index <= - ogs_app()->pool.bearer); - ogs_list_init(&qos_flow->pf_list); /* PDR */ @@ -2123,37 +2080,51 @@ void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess) pdr->f_teid.choose_id = OGS_PFCP_INDIRECT_DATA_FORWARDING_CHOOSE_ID; pdr->f_teid_len = 2; } else { - ogs_gtpu_resource_t *resource = NULL; + /* + * CHOOSE_ID is set in INDIRECT so that all PDRs must be set + * to the same TEID. + * + * If sess->handover.upf_dl_teid is set in the PDR of + * the first QoS flow, the PDRs of the remaining QoS flows use + * the same TEID. + */ + if (ogs_list_first(&sess->bearer_list) == qos_flow) { + ogs_gtpu_resource_t *resource = NULL; - if (sess->handover.upf_dl_addr) - ogs_freeaddrinfo(sess->handover.upf_dl_addr); - if (sess->handover.upf_dl_addr6) - ogs_freeaddrinfo(sess->handover.upf_dl_addr6); + if (sess->handover.upf_dl_addr) + ogs_freeaddrinfo(sess->handover.upf_dl_addr); + if (sess->handover.upf_dl_addr6) + ogs_freeaddrinfo(sess->handover.upf_dl_addr6); - resource = ogs_pfcp_find_gtpu_resource( - &sess->pfcp_node->gtpu_resource_list, - sess->session.name, OGS_PFCP_INTERFACE_ACCESS); + resource = ogs_pfcp_find_gtpu_resource( + &sess->pfcp_node->gtpu_resource_list, + sess->session.name, OGS_PFCP_INTERFACE_ACCESS); - if (resource) { - ogs_user_plane_ip_resource_info_to_sockaddr(&resource->info, - &sess->handover.upf_dl_addr, &sess->handover.upf_dl_addr6); - if (resource->info.teidri) - sess->handover.upf_dl_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, - resource->info.teid_range); - else - sess->handover.upf_dl_teid = pdr->index; - } else { - if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) - ogs_assert(OGS_OK == ogs_copyaddrinfo( - &sess->handover.upf_dl_addr, &sess->pfcp_node->addr)); - else if (sess->pfcp_node->addr.ogs_sa_family == AF_INET6) - ogs_assert(OGS_OK == ogs_copyaddrinfo( - &sess->handover.upf_dl_addr6, &sess->pfcp_node->addr)); - else - ogs_assert_if_reached(); + if (resource) { + ogs_user_plane_ip_resource_info_to_sockaddr(&resource->info, + &sess->handover.upf_dl_addr, + &sess->handover.upf_dl_addr6); + if (resource->info.teidri) + sess->handover.upf_dl_teid = + OGS_PFCP_GTPU_INDEX_TO_TEID( + pdr->teid, resource->info.teidri, + resource->info.teid_range); + else + sess->handover.upf_dl_teid = pdr->teid; + } else { + if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) + ogs_assert(OGS_OK == ogs_copyaddrinfo( + &sess->handover.upf_dl_addr, + &sess->pfcp_node->addr)); + else if (sess->pfcp_node->addr.ogs_sa_family == AF_INET6) + ogs_assert(OGS_OK == ogs_copyaddrinfo( + &sess->handover.upf_dl_addr6, + &sess->pfcp_node->addr)); + else + ogs_assert_if_reached(); - sess->handover.upf_dl_teid = pdr->index; + sess->handover.upf_dl_teid = pdr->teid; + } } ogs_assert(OGS_OK == @@ -2370,10 +2341,6 @@ smf_bearer_t *smf_bearer_add(smf_sess_t *sess) smf_pf_identifier_pool_init(bearer); - bearer->index = ogs_pool_index(&smf_bearer_pool, bearer); - ogs_assert(bearer->index > 0 && bearer->index <= - ogs_app()->pool.bearer); - ogs_list_init(&bearer->pf_list); /* PDR */ @@ -3033,64 +3000,48 @@ int smf_pco_build(uint8_t *pco_buf, uint8_t *buffer, int length) void smf_qfi_pool_init(smf_sess_t *sess) { - int i; - ogs_assert(sess); - ogs_index_init(&sess->qfi_pool, OGS_MAX_QOS_FLOW_ID); - - for (i = 1; i <= OGS_MAX_QOS_FLOW_ID; i++) { - sess->qfi_pool.array[i-1] = i; - } + ogs_pool_init(&sess->qfi_pool, OGS_MAX_QOS_FLOW_ID); + ogs_pool_sequence_id_generate(&sess->qfi_pool); } void smf_qfi_pool_final(smf_sess_t *sess) { ogs_assert(sess); - ogs_index_final(&sess->qfi_pool); + ogs_pool_final(&sess->qfi_pool); } void smf_pf_identifier_pool_init(smf_bearer_t *bearer) { - int i; - ogs_assert(bearer); - ogs_index_init(&bearer->pf_identifier_pool, OGS_MAX_NUM_OF_FLOW_IN_BEARER); - - for (i = 1; i <= OGS_MAX_NUM_OF_FLOW_IN_BEARER; i++) { - bearer->pf_identifier_pool.array[i-1] = i; - } + ogs_pool_init(&bearer->pf_identifier_pool, OGS_MAX_NUM_OF_FLOW_IN_BEARER); + ogs_pool_sequence_id_generate(&bearer->pf_identifier_pool); } void smf_pf_identifier_pool_final(smf_bearer_t *bearer) { ogs_assert(bearer); - ogs_index_final(&bearer->pf_identifier_pool); + ogs_pool_final(&bearer->pf_identifier_pool); } void smf_pf_precedence_pool_init(smf_sess_t *sess) { - int i; - ogs_assert(sess); - ogs_index_init(&sess->pf_precedence_pool, + ogs_pool_init(&sess->pf_precedence_pool, OGS_MAX_NUM_OF_BEARER * OGS_MAX_NUM_OF_FLOW_IN_BEARER); - - for (i = 1; i <= - OGS_MAX_NUM_OF_BEARER * OGS_MAX_NUM_OF_FLOW_IN_BEARER; i++) { - sess->pf_precedence_pool.array[i-1] = i; - } + ogs_pool_sequence_id_generate(&sess->pf_precedence_pool); } void smf_pf_precedence_pool_final(smf_sess_t *sess) { ogs_assert(sess); - ogs_index_final(&sess->pf_precedence_pool); + ogs_pool_final(&sess->pf_precedence_pool); } static void stats_add_smf_session(void) diff --git a/src/smf/context.h b/src/smf/context.h index 9fbd98b8e..40eebb76f 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2022 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -88,6 +88,7 @@ typedef struct smf_context_s { ogs_hash_t *imsi_hash; /* hash table (IMSI) */ ogs_hash_t *ipv4_hash; /* hash table (IPv4 Address) */ ogs_hash_t *ipv6_hash; /* hash table (IPv6 Address) */ + ogs_hash_t *smf_n4_seid_hash; /* hash table (SMF-N4-SEID) */ ogs_hash_t *n1n2message_hash; /* hash table (N1N2Message Location) */ uint16_t mtu; /* MTU to advertise in PCO */ @@ -173,8 +174,6 @@ typedef struct smf_bearer_s { ogs_lnode_t to_modify_node; ogs_lnode_t to_delete_node; - uint32_t index; - ogs_pfcp_pdr_t *dl_pdr; ogs_pfcp_pdr_t *ul_pdr; ogs_pfcp_far_t *dl_far; @@ -215,7 +214,10 @@ typedef struct smf_bearer_s { #define SMF_SESS(pfcp_sess) ogs_container_of(pfcp_sess, smf_sess_t, pfcp) typedef struct smf_sess_s { ogs_sbi_object_t sbi; - uint32_t index; /**< An index of this node */ + + uint32_t index; /* An index of this node */ + ogs_pool_id_t *smf_n4_seid_node; /* A node of SMF-N4-SEID */ + ogs_fsm_t sm; /* A state machine */ struct { bool gx_ccr_init_in_flight; /* Waiting for Gx CCA */ @@ -236,12 +238,12 @@ typedef struct smf_sess_s { uint64_t smpolicycontrol_features; /* SBI features */ - uint32_t smf_n4_teid; /* SMF-N4-TEID is derived from INDEX */ + uint32_t smf_n4_teid; /* SMF-N4-TEID is derived from NODE */ uint32_t sgw_s5c_teid; /* SGW-S5C-TEID is received from SGW */ ogs_ip_t sgw_s5c_ip; /* SGW-S5C IPv4/IPv6 */ - uint64_t smf_n4_seid; /* SMF SEID is dervied from INDEX */ + uint64_t smf_n4_seid; /* SMF SEID is dervied from NODE */ uint64_t upf_n4_seid; /* UPF SEID is received from Peer */ uint32_t upf_n3_teid; /* UPF-N3 TEID */ @@ -463,9 +465,6 @@ smf_sess_t *smf_sess_find_by_ipv4(uint32_t addr); smf_sess_t *smf_sess_find_by_ipv6(uint32_t *addr6); smf_sess_t *smf_sess_find_by_paging_n1n2message_location( char *n1n2message_location); -smf_sess_t *smf_sess_find_by_error_indication_report( - smf_ue_t *smf_ue, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report); void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess); bool smf_sess_have_indirect_data_forwarding(smf_sess_t *sess); diff --git a/src/smf/fd-path.c b/src/smf/fd-path.c index 885776f3e..17acea9bc 100644 --- a/src/smf/fd-path.c +++ b/src/smf/fd-path.c @@ -62,6 +62,7 @@ void smf_fd_final(void) } smf_gx_final(); + smf_gy_final(); smf_s6b_final(); ogs_diam_final(); diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index b43bc9414..63b8aee47 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019-2022 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -452,7 +452,7 @@ test_can_proceed: OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_establishment); ogs_assert(OGS_OK == smf_epc_pfcp_send_session_establishment_request( - sess, e->gtp_xact)); + sess, e->gtp_xact, 0)); } else { /* FIXME: tear down Gx/Gy session * if its sm_data.*init_err == ER_DIAMETER_SUCCESS */ @@ -671,21 +671,28 @@ void smf_gsm_state_wait_pfcp_establishment(ogs_fsm_t *s, smf_event_t *e) send_gtp_create_err_msg(sess, e->gtp_xact, gtp_cause); return; } - switch (gtp_xact->gtp_version) { - case 1: - rv = smf_gtp1_send_create_pdp_context_response(sess, gtp_xact); - break; - case 2: - rv = smf_gtp2_send_create_session_response(sess, gtp_xact); - break; - default: - rv = OGS_ERROR; - break; - } - /* If no CreatePDPCtxResp can be sent, then tear down the session: */ - if (rv != OGS_OK) { - OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_deletion); - return; + + gtp_xact = pfcp_xact->assoc_xact; + if (gtp_xact) { + switch (gtp_xact->gtp_version) { + case 1: + rv = smf_gtp1_send_create_pdp_context_response( + sess, gtp_xact); + break; + case 2: + rv = smf_gtp2_send_create_session_response( + sess, gtp_xact); + break; + default: + rv = OGS_ERROR; + break; + } + /* If no CreatePDPCtxResp can be sent, + * then tear down the session: */ + if (rv != OGS_OK) { + OGS_FSM_TRAN(s, &smf_gsm_state_wait_pfcp_deletion); + return; + } } if (sess->gtp_rat_type == OGS_GTP2_RAT_TYPE_WLAN) { @@ -739,6 +746,9 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) smf_sess_t *sess = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_pfcp_xact_t *pfcp_xact = NULL; + ogs_pfcp_message_t *pfcp_message = NULL; + ogs_nas_5gs_message_t *nas_message = NULL; ogs_sbi_stream_t *stream = NULL; @@ -815,6 +825,43 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) } break; + case SMF_EVT_N4_MESSAGE: + pfcp_xact = e->pfcp_xact; + ogs_assert(pfcp_xact); + pfcp_message = e->pfcp_message; + ogs_assert(pfcp_message); + + switch (pfcp_message->h.type) { + case OGS_PFCP_SESSION_ESTABLISHMENT_RESPONSE_TYPE: + if ((pfcp_xact->create_flags & + OGS_PFCP_CREATE_RESTORATION_INDICATION)) { + ogs_pfcp_session_establishment_response_t *rsp = NULL; + ogs_pfcp_f_seid_t *up_f_seid = NULL; + + rsp = &pfcp_message->pfcp_session_establishment_response; + if (rsp->up_f_seid.presence == 0) { + ogs_error("No UP F-SEID"); + break; + } + up_f_seid = rsp->up_f_seid.data; + ogs_assert(up_f_seid); + sess->upf_n4_seid = be64toh(up_f_seid->seid); + } else { + ogs_error("cannot handle PFCP Session Establishment Response"); + } + break; + + case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: + ogs_error("Session Released by Error Indication"); + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + break; + + default: + ogs_error("cannot handle PFCP message type[%d]", + pfcp_message->h.type); + } + break; + case OGS_EVENT_SBI_SERVER: sbi_message = e->h.sbi.message; ogs_assert(sbi_message); diff --git a/src/smf/gx-handler.c b/src/smf/gx-handler.c index a3e4a4fce..3b4177bd2 100644 --- a/src/smf/gx-handler.c +++ b/src/smf/gx-handler.c @@ -202,10 +202,10 @@ uint32_t smf_gx_handle_cca_initial_request( &bearer->pgw_s5u_addr, &bearer->pgw_s5u_addr6); if (resource->info.teidri) bearer->pgw_s5u_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - ul_pdr->index, resource->info.teidri, + ul_pdr->teid, resource->info.teidri, resource->info.teid_range); else - bearer->pgw_s5u_teid = ul_pdr->index; + bearer->pgw_s5u_teid = ul_pdr->teid; } else { if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) ogs_assert(OGS_OK == @@ -218,7 +218,7 @@ uint32_t smf_gx_handle_cca_initial_request( else ogs_assert_if_reached(); - bearer->pgw_s5u_teid = ul_pdr->index; + bearer->pgw_s5u_teid = ul_pdr->teid; } ogs_assert(OGS_OK == @@ -231,7 +231,7 @@ uint32_t smf_gx_handle_cca_initial_request( ogs_pfcp_sockaddr_to_f_teid( bearer->pgw_s5u_addr, bearer->pgw_s5u_addr6, &cp2up_pdr->f_teid, &cp2up_pdr->f_teid_len)); - cp2up_pdr->f_teid.teid = cp2up_pdr->index; + cp2up_pdr->f_teid.teid = cp2up_pdr->teid; ogs_assert(OGS_OK == ogs_pfcp_sockaddr_to_f_teid( diff --git a/src/smf/n4-build.c b/src/smf/n4-build.c index 4ce171cd8..b37003d4e 100644 --- a/src/smf/n4-build.c +++ b/src/smf/n4-build.c @@ -21,7 +21,7 @@ #include "n4-build.h" ogs_pkbuf_t *smf_n4_build_session_establishment_request( - uint8_t type, smf_sess_t *sess) + uint8_t type, smf_sess_t *sess, ogs_pfcp_xact_t *xact) { ogs_pfcp_message_t *pfcp_message = NULL; ogs_pfcp_session_establishment_request_t *req = NULL; @@ -46,6 +46,7 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( ogs_assert(sess); smf_ue = sess->smf_ue; ogs_assert(smf_ue); + ogs_assert(xact); pfcp_message = ogs_calloc(1, sizeof(*pfcp_message)); if (!pfcp_message) { @@ -159,6 +160,16 @@ ogs_pkbuf_t *smf_n4_build_session_establishment_request( req->s_nssai.data = &sess->s_nssai; } + /* Restoration Indication */ + if (xact->create_flags & OGS_PFCP_CREATE_RESTORATION_INDICATION) { + ogs_pfcp_sereq_flags_t sereq_flags; + sereq_flags.value = 0; + + sereq_flags.restoration_indication = 1; + req->pfcpsereq_flags.presence = 1; + req->pfcpsereq_flags.u8 = sereq_flags.value; + } + pfcp_message->h.type = type; pkbuf = ogs_pfcp_build_msg(pfcp_message); ogs_expect(pkbuf); diff --git a/src/smf/n4-build.h b/src/smf/n4-build.h index 560a4a0f3..49e9c89df 100644 --- a/src/smf/n4-build.h +++ b/src/smf/n4-build.h @@ -27,7 +27,7 @@ extern "C" { #endif ogs_pkbuf_t *smf_n4_build_session_establishment_request( - uint8_t type, smf_sess_t *sess); + uint8_t type, smf_sess_t *sess, ogs_pfcp_xact_t *xact); ogs_pkbuf_t *smf_n4_build_pdr_to_modify_list( uint8_t type, smf_sess_t *sess, ogs_pfcp_xact_t *xact); ogs_pkbuf_t *smf_n4_build_qos_flow_to_modify_list( diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index ef3e2b553..c92409304 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -148,8 +148,6 @@ uint8_t smf_5gc_n4_handle_session_establishment_response( { int i; - ogs_sbi_stream_t *stream = NULL; - uint8_t cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; uint8_t offending_ie_value = 0; @@ -164,9 +162,6 @@ uint8_t smf_5gc_n4_handle_session_establishment_response( ogs_debug("Session Establishment Response [5gc]"); - stream = xact->assoc_stream; - ogs_assert(stream); - ogs_pfcp_xact_commit(xact); if (rsp->up_f_seid.presence == 0) { @@ -732,7 +727,6 @@ uint8_t smf_epc_n4_handle_session_establishment_response( uint8_t cause_value = OGS_PFCP_CAUSE_REQUEST_ACCEPTED; smf_bearer_t *bearer = NULL; - ogs_gtp_xact_t *gtp_xact = NULL; ogs_pfcp_f_seid_t *up_f_seid = NULL; @@ -742,9 +736,6 @@ uint8_t smf_epc_n4_handle_session_establishment_response( ogs_debug("Session Establishment Response [epc]"); - gtp_xact = xact->assoc_xact; - ogs_assert(gtp_xact); - ogs_pfcp_xact_commit(xact); if (rsp->up_f_seid.presence == 0) { @@ -1157,9 +1148,11 @@ void smf_n4_handle_session_report_request( smf_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, ogs_pfcp_session_report_request_t *pfcp_req) { + smf_ue_t *smf_ue = NULL; smf_bearer_t *qos_flow = NULL; smf_bearer_t *bearer = NULL; ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_report_type_t report_type; uint8_t cause_value = 0; @@ -1193,6 +1186,9 @@ void smf_n4_handle_session_report_request( } ogs_assert(sess); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + report_type.value = pfcp_req->report_type.u8; if (report_type.downlink_data_report) { @@ -1279,21 +1275,10 @@ void smf_n4_handle_session_report_request( } if (report_type.error_indication_report) { - smf_ue_t *smf_ue = sess->smf_ue; - smf_sess_t *error_indication_session = NULL; - ogs_assert(smf_ue); - - error_indication_session = smf_sess_find_by_error_indication_report( - smf_ue, &pfcp_req->error_indication_report); - - if (error_indication_session) { - ogs_assert(OGS_OK == - smf_5gc_pfcp_send_all_pdr_modification_request( - error_indication_session, NULL, - OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| - OGS_PFCP_MODIFY_ERROR_INDICATION, - 0)); - } + far = ogs_pfcp_far_find_by_pfcp_session_report( + &sess->pfcp, &pfcp_req->error_indication_report); + if (!far) + ogs_error("Cannot find Session in Error Indication"); } if (report_type.usage_report) { @@ -1350,4 +1335,24 @@ void smf_n4_handle_session_report_request( smf_pfcp_send_session_report_response( pfcp_xact, sess, OGS_PFCP_CAUSE_SYSTEM_FAILURE)); } + + /* Error Indication is handled last */ + if (report_type.error_indication_report && far) { + if (sess->epc == true) { + ogs_error("[%s:%s] Error Indication from SGW-C", + smf_ue->imsi_bcd, sess->session.name); + ogs_assert(OGS_OK == + smf_epc_pfcp_send_session_deletion_request( + sess, NULL)); + } else { + ogs_warn("[%s:%s] Error Indication from gNB", + smf_ue->supi, sess->session.name); + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_all_pdr_modification_request( + sess, NULL, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| + OGS_PFCP_MODIFY_ERROR_INDICATION, + 0)); + } + } } diff --git a/src/smf/npcf-handler.c b/src/smf/npcf-handler.c index c035346dd..d126cb3b3 100644 --- a/src/smf/npcf-handler.c +++ b/src/smf/npcf-handler.c @@ -585,10 +585,10 @@ bool smf_npcf_smpolicycontrol_handle_create( &sess->upf_n3_addr, &sess->upf_n3_addr6); if (resource->info.teidri) sess->upf_n3_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - ul_pdr->index, resource->info.teidri, + ul_pdr->teid, resource->info.teidri, resource->info.teid_range); else - sess->upf_n3_teid = ul_pdr->index; + sess->upf_n3_teid = ul_pdr->teid; } else { if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) ogs_assert(OGS_OK == @@ -601,7 +601,7 @@ bool smf_npcf_smpolicycontrol_handle_create( else ogs_assert_if_reached(); - sess->upf_n3_teid = ul_pdr->index; + sess->upf_n3_teid = ul_pdr->teid; } ogs_assert(OGS_OK == @@ -614,7 +614,7 @@ bool smf_npcf_smpolicycontrol_handle_create( ogs_pfcp_sockaddr_to_f_teid( sess->upf_n3_addr, sess->upf_n3_addr6, &cp2up_pdr->f_teid, &cp2up_pdr->f_teid_len)); - cp2up_pdr->f_teid.teid = cp2up_pdr->index; + cp2up_pdr->f_teid.teid = cp2up_pdr->teid; ogs_assert(OGS_OK == ogs_pfcp_sockaddr_to_f_teid( @@ -630,7 +630,7 @@ bool smf_npcf_smpolicycontrol_handle_create( up2cp_pdr->precedence = OGS_PFCP_UP2CP_PDR_PRECEDENCE; ogs_assert(OGS_OK == - smf_5gc_pfcp_send_session_establishment_request(sess, stream)); + smf_5gc_pfcp_send_session_establishment_request(sess, 0)); return true; diff --git a/src/smf/pfcp-path.c b/src/smf/pfcp-path.c index a636f18c3..e146ce2f6 100644 --- a/src/smf/pfcp-path.c +++ b/src/smf/pfcp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -362,7 +362,7 @@ int smf_pfcp_send_modify_list( } int smf_5gc_pfcp_send_session_establishment_request( - smf_sess_t *sess, ogs_sbi_stream_t *stream) + smf_sess_t *sess, uint64_t flags) { int rv; ogs_pkbuf_t *n4buf = NULL; @@ -370,7 +370,6 @@ int smf_5gc_pfcp_send_session_establishment_request( ogs_pfcp_xact_t *xact = NULL; ogs_assert(sess); - ogs_assert(stream); xact = ogs_pfcp_xact_local_create(sess->pfcp_node, sess_5gc_timeout, sess); if (!xact) { @@ -378,14 +377,44 @@ int smf_5gc_pfcp_send_session_establishment_request( return OGS_ERROR; } - xact->assoc_stream = stream; xact->local_seid = sess->smf_n4_seid; + xact->create_flags = flags; memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE; + +/* + * 7.2.2.4.2 Conditions for Sending SEID=0 in PFCP Header + * + * If a peer's SEID is not available, the SEID field shall still be present + * in the header and its value shall be set to "0" in the following messages: + * + * - PFCP Session Establishment Request message on Sxa/Sxb/Sxc/N4; + * + * - If a node receives a message for which it has no session, i.e. + * if SEID in the PFCP header is not known, it shall respond + * with "Session context not found" cause in the corresponding + * response message to the sender, the SEID used in the PFCP header + * in the response message shall be then set to "0"; + * + * - If a node receives a request message containing protocol error, + * e.g. Mandatory IE missing, which requires the receiver + * to reject the message as specified in clause 7.6, it shall reject + * the request message. For the response message, the node should look up + * the remote peer's SEID and accordingly set SEID in the PFCP header + * and the message cause code. As an implementation option, + * the node may not look up the remote peer's SEID and + * set the PFCP header SEID to "0" in the response message. + * However in this case, the cause value shall not be set + * to "Session not found". + * + * - When the UP function sends PFCP Session Report Request message + * over N4 towards another SMF or another PFCP entity in the SMF + * as specified in clause 5.22.2 and clause 5.22.3. + */ h.seid = sess->upf_n4_seid; - n4buf = smf_n4_build_session_establishment_request(h.type, sess); + n4buf = smf_n4_build_session_establishment_request(h.type, sess, xact); if (!n4buf) { ogs_error("smf_n4_build_session_establishment_request() failed"); return OGS_ERROR; @@ -506,7 +535,7 @@ int smf_5gc_pfcp_send_session_deletion_request( } int smf_epc_pfcp_send_session_establishment_request( - smf_sess_t *sess, void *gtp_xact) + smf_sess_t *sess, void *gtp_xact, uint64_t flags) { int rv; ogs_pkbuf_t *n4buf = NULL; @@ -524,12 +553,43 @@ int smf_epc_pfcp_send_session_establishment_request( xact->epc = true; /* EPC PFCP transaction */ xact->assoc_xact = gtp_xact; xact->local_seid = sess->smf_n4_seid; + xact->create_flags = flags; memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE; + +/* + * 7.2.2.4.2 Conditions for Sending SEID=0 in PFCP Header + * + * If a peer's SEID is not available, the SEID field shall still be present + * in the header and its value shall be set to "0" in the following messages: + * + * - PFCP Session Establishment Request message on Sxa/Sxb/Sxc/N4; + * + * - If a node receives a message for which it has no session, i.e. + * if SEID in the PFCP header is not known, it shall respond + * with "Session context not found" cause in the corresponding + * response message to the sender, the SEID used in the PFCP header + * in the response message shall be then set to "0"; + * + * - If a node receives a request message containing protocol error, + * e.g. Mandatory IE missing, which requires the receiver + * to reject the message as specified in clause 7.6, it shall reject + * the request message. For the response message, the node should look up + * the remote peer's SEID and accordingly set SEID in the PFCP header + * and the message cause code. As an implementation option, + * the node may not look up the remote peer's SEID and + * set the PFCP header SEID to "0" in the response message. + * However in this case, the cause value shall not be set + * to "Session not found". + * + * - When the UP function sends PFCP Session Report Request message + * over N4 towards another SMF or another PFCP entity in the SMF + * as specified in clause 5.22.2 and clause 5.22.3. + */ h.seid = sess->upf_n4_seid; - n4buf = smf_n4_build_session_establishment_request(h.type, sess); + n4buf = smf_n4_build_session_establishment_request(h.type, sess, xact); if (!n4buf) { ogs_error("smf_n4_build_session_establishment_request() failed"); return OGS_ERROR; diff --git a/src/smf/pfcp-path.h b/src/smf/pfcp-path.h index a7a70180e..d05145f01 100644 --- a/src/smf/pfcp-path.h +++ b/src/smf/pfcp-path.h @@ -36,7 +36,7 @@ int smf_pfcp_send_modify_list( ogs_pfcp_xact_t *xact, ogs_time_t duration); int smf_5gc_pfcp_send_session_establishment_request( - smf_sess_t *sess, ogs_sbi_stream_t *stream); + smf_sess_t *sess, uint64_t flags); int smf_5gc_pfcp_send_all_pdr_modification_request( smf_sess_t *sess, ogs_sbi_stream_t *stream, uint64_t flags, ogs_time_t duration); @@ -47,7 +47,7 @@ int smf_5gc_pfcp_send_session_deletion_request( smf_sess_t *sess, ogs_sbi_stream_t *stream, int trigger); int smf_epc_pfcp_send_session_establishment_request( - smf_sess_t *sess, void *gtp_xact); + smf_sess_t *sess, void *gtp_xact, uint64_t flags); int smf_epc_pfcp_send_all_pdr_modification_request( smf_sess_t *sess, void *gtp_xact, ogs_pkbuf_t *gtpbuf, uint64_t flags, uint8_t gtp_pti, uint8_t gtp_cause); diff --git a/src/smf/pfcp-sm.c b/src/smf/pfcp-sm.c index 4dab934be..5b637dd08 100644 --- a/src/smf/pfcp-sm.c +++ b/src/smf/pfcp-sm.c @@ -22,6 +22,7 @@ #include "n4-handler.h" +static void pfcp_restoration(ogs_pfcp_node_t *node); static void reselect_upf(ogs_pfcp_node_t *node); static void node_timeout(ogs_pfcp_xact_t *xact, void *data); @@ -191,7 +192,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) ogs_pfcp_send_heartbeat_request(node, node_timeout)); if (node->restoration_required == true) { - /* PFCP Restoration is being performed after PFCP association */ + pfcp_restoration(node); node->restoration_required = false; ogs_error("PFCP restoration"); } @@ -244,6 +245,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) * If the peer PFCP entity is performing the association, * Restoration can be performed immediately. */ + pfcp_restoration(node); node->restoration_required = false; ogs_error("PFCP restoration"); } @@ -270,6 +272,7 @@ void smf_pfcp_state_associated(ogs_fsm_t *s, smf_event_t *e) * If the peer PFCP entity is performing the association, * Restoration can be performed immediately. */ + pfcp_restoration(node); node->restoration_required = false; ogs_error("PFCP restoration"); } @@ -409,10 +412,52 @@ void smf_pfcp_state_exception(ogs_fsm_t *s, smf_event_t *e) } } +static void pfcp_restoration(ogs_pfcp_node_t *node) +{ + smf_ue_t *smf_ue = NULL; + + char buf1[OGS_ADDRSTRLEN]; + char buf2[OGS_ADDRSTRLEN]; + + ogs_list_for_each(&smf_self()->smf_ue_list, smf_ue) { + smf_sess_t *sess = NULL; + ogs_assert(smf_ue); + + ogs_list_for_each(&smf_ue->sess_list, sess) { + ogs_assert(sess); + + if (node == sess->pfcp_node) { + if (sess->epc) { + ogs_info("UE IMSI[%s] APN[%s] IPv4[%s] IPv6[%s]", + smf_ue->imsi_bcd, sess->session.name, + sess->ipv4 ? + OGS_INET_NTOP(&sess->ipv4->addr, buf1) : "", + sess->ipv6 ? + OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : ""); + ogs_assert(OGS_OK == + smf_epc_pfcp_send_session_establishment_request( + sess, NULL, + OGS_PFCP_CREATE_RESTORATION_INDICATION)); + } else { + ogs_info("UE SUPI[%s] DNN[%s] IPv4[%s] IPv6[%s]", + smf_ue->supi, sess->session.name, + sess->ipv4 ? + OGS_INET_NTOP(&sess->ipv4->addr, buf1) : "", + sess->ipv6 ? + OGS_INET6_NTOP(&sess->ipv6->addr, buf2) : ""); + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_session_establishment_request( + sess, OGS_PFCP_CREATE_RESTORATION_INDICATION)); + } + } + } + } +} + static void reselect_upf(ogs_pfcp_node_t *node) { int r; - smf_ue_t *smf_ue = NULL, *next_ue = NULL;; + smf_ue_t *smf_ue = NULL; ogs_pfcp_node_t *iter = NULL; ogs_assert(node); @@ -434,19 +479,18 @@ static void reselect_upf(ogs_pfcp_node_t *node) return; } - ogs_list_for_each_safe(&smf_self()->smf_ue_list, next_ue, smf_ue) { - smf_sess_t *sess = NULL, *next_sess = NULL;; + ogs_list_for_each(&smf_self()->smf_ue_list, smf_ue) { + smf_sess_t *sess = NULL; ogs_assert(smf_ue); - ogs_list_for_each_safe(&smf_ue->sess_list, next_sess, sess) { + ogs_list_for_each(&smf_ue->sess_list, sess) { ogs_assert(sess); - if (sess->epc) { - ogs_error("[%s:%s] EPC restoration is not implemented", - smf_ue->imsi_bcd, sess->session.name); - } else { - ogs_assert(sess->sm_context_ref); - if (node == sess->pfcp_node) { + if (node == sess->pfcp_node) { + if (sess->epc) { + ogs_error("[%s:%s] EPC restoration is not implemented", + smf_ue->imsi_bcd, sess->session.name); + } else { smf_npcf_smpolicycontrol_param_t param; ogs_info("[%s:%d] SMF-initiated Deletion", @@ -479,18 +523,6 @@ static void node_timeout(ogs_pfcp_xact_t *xact, void *data) switch (type) { case OGS_PFCP_HEARTBEAT_REQUEST_TYPE: ogs_assert(data); - - - /* - * The code below is not secure. - * Session does not differentiate between EPC and 5GC. - * And, it does not check whether there are other PFCP Nodes. - * - * So, UPF redundancy will be implemented later. - * - * We plan to do this again after testing with restoration first - * in case peer PFCP restarts. - */ reselect_upf(data); e = smf_event_new(SMF_EVT_N4_NO_HEARTBEAT); diff --git a/src/upf/context.c b/src/upf/context.c index 8ca55390d..61bf58cb1 100644 --- a/src/upf/context.c +++ b/src/upf/context.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -25,6 +25,7 @@ static upf_context_t self; int __upf_log_domain; static OGS_POOL(upf_sess_pool, upf_sess_t); +static OGS_POOL(upf_n4_seid_pool, ogs_pool_id_t); static int context_initialized = 0; @@ -49,11 +50,15 @@ void upf_context_init(void) ogs_list_init(&self.sess_list); ogs_pool_init(&upf_sess_pool, ogs_app()->pool.sess); + ogs_pool_init(&upf_n4_seid_pool, ogs_app()->pool.sess); + ogs_pool_random_id_generate(&upf_n4_seid_pool); - self.seid_hash = ogs_hash_make(); - ogs_assert(self.seid_hash); - self.f_seid_hash = ogs_hash_make(); - ogs_assert(self.f_seid_hash); + self.upf_n4_seid_hash = ogs_hash_make(); + ogs_assert(self.upf_n4_seid_hash); + self.smf_n4_seid_hash = ogs_hash_make(); + ogs_assert(self.smf_n4_seid_hash); + self.smf_n4_f_seid_hash = ogs_hash_make(); + ogs_assert(self.smf_n4_f_seid_hash); self.ipv4_hash = ogs_hash_make(); ogs_assert(self.ipv4_hash); self.ipv6_hash = ogs_hash_make(); @@ -77,10 +82,12 @@ void upf_context_final(void) upf_sess_remove_all(); - ogs_assert(self.seid_hash); - ogs_hash_destroy(self.seid_hash); - ogs_assert(self.f_seid_hash); - ogs_hash_destroy(self.f_seid_hash); + ogs_assert(self.upf_n4_seid_hash); + ogs_hash_destroy(self.upf_n4_seid_hash); + ogs_assert(self.smf_n4_seid_hash); + ogs_hash_destroy(self.smf_n4_seid_hash); + ogs_assert(self.smf_n4_f_seid_hash); + ogs_hash_destroy(self.smf_n4_f_seid_hash); ogs_assert(self.ipv4_hash); ogs_hash_destroy(self.ipv4_hash); ogs_assert(self.ipv6_hash); @@ -90,6 +97,7 @@ void upf_context_final(void) free_upf_route_trie_node(self.ipv6_framed_routes); ogs_pool_final(&upf_sess_pool); + ogs_pool_final(&upf_n4_seid_pool); context_initialized = 0; } @@ -171,10 +179,14 @@ upf_sess_t *upf_sess_add(ogs_pfcp_f_seid_t *cp_f_seid) ogs_pfcp_pool_init(&sess->pfcp); - sess->index = ogs_pool_index(&upf_sess_pool, sess); - ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); + /* Set UPF-N4-SEID */ + ogs_pool_alloc(&upf_n4_seid_pool, &sess->upf_n4_seid_node); + ogs_assert(sess->upf_n4_seid_node); - sess->upf_n4_seid = sess->index; + sess->upf_n4_seid = *(sess->upf_n4_seid_node); + + ogs_hash_set(self.upf_n4_seid_hash, &sess->upf_n4_seid, + sizeof(sess->upf_n4_seid), sess); /* Since F-SEID is composed of ogs_ip_t and uint64-seid, * all these values must be put into the structure-smf_n4_f_seid @@ -183,9 +195,9 @@ upf_sess_t *upf_sess_add(ogs_pfcp_f_seid_t *cp_f_seid) ogs_assert(OGS_OK == ogs_pfcp_f_seid_to_ip(cp_f_seid, &sess->smf_n4_f_seid.ip)); - ogs_hash_set(self.f_seid_hash, &sess->smf_n4_f_seid, + ogs_hash_set(self.smf_n4_f_seid_hash, &sess->smf_n4_f_seid, sizeof(sess->smf_n4_f_seid), sess); - ogs_hash_set(self.seid_hash, &sess->smf_n4_f_seid.seid, + ogs_hash_set(self.smf_n4_seid_hash, &sess->smf_n4_f_seid.seid, sizeof(sess->smf_n4_f_seid.seid), sess); ogs_list_add(&self.sess_list, sess); @@ -206,9 +218,12 @@ int upf_sess_remove(upf_sess_t *sess) ogs_list_remove(&self.sess_list, sess); ogs_pfcp_sess_clear(&sess->pfcp); - ogs_hash_set(self.seid_hash, &sess->smf_n4_f_seid.seid, + ogs_hash_set(self.upf_n4_seid_hash, &sess->upf_n4_seid, + sizeof(sess->upf_n4_seid), NULL); + + ogs_hash_set(self.smf_n4_seid_hash, &sess->smf_n4_f_seid.seid, sizeof(sess->smf_n4_f_seid.seid), NULL); - ogs_hash_set(self.f_seid_hash, &sess->smf_n4_f_seid, + ogs_hash_set(self.smf_n4_f_seid_hash, &sess->smf_n4_f_seid, sizeof(sess->smf_n4_f_seid), NULL); if (sess->ipv4) { @@ -226,6 +241,7 @@ int upf_sess_remove(upf_sess_t *sess) ogs_pfcp_pool_final(&sess->pfcp); + ogs_pool_free(&upf_n4_seid_pool, sess->upf_n4_seid_node); ogs_pool_free(&upf_sess_pool, sess); if (sess->apn_dnn) ogs_free(sess->apn_dnn); @@ -246,14 +262,9 @@ void upf_sess_remove_all(void) } } -upf_sess_t *upf_sess_find(uint32_t index) -{ - return ogs_pool_find(&upf_sess_pool, index); -} - upf_sess_t *upf_sess_find_by_smf_n4_seid(uint64_t seid) { - return (upf_sess_t *)ogs_hash_get(self.seid_hash, &seid, sizeof(seid)); + return ogs_hash_get(self.smf_n4_seid_hash, &seid, sizeof(seid)); } upf_sess_t *upf_sess_find_by_smf_n4_f_seid(ogs_pfcp_f_seid_t *f_seid) @@ -267,12 +278,12 @@ upf_sess_t *upf_sess_find_by_smf_n4_f_seid(ogs_pfcp_f_seid_t *f_seid) ogs_assert(OGS_OK == ogs_pfcp_f_seid_to_ip(f_seid, &key.ip)); key.seid = f_seid->seid; - return (upf_sess_t *)ogs_hash_get(self.f_seid_hash, &key, sizeof(key)); + return ogs_hash_get(self.smf_n4_f_seid_hash, &key, sizeof(key)); } upf_sess_t *upf_sess_find_by_upf_n4_seid(uint64_t seid) { - return upf_sess_find(seid); + return ogs_hash_get(self.upf_n4_seid_hash, &seid, sizeof(seid)); } upf_sess_t *upf_sess_find_by_ipv4(uint32_t addr) @@ -357,7 +368,10 @@ upf_sess_t *upf_sess_add_by_message(ogs_pfcp_message_t *message) sess = upf_sess_find_by_smf_n4_f_seid(f_seid); if (!sess) { sess = upf_sess_add(f_seid); - if (!sess) return NULL; + if (!sess) { + ogs_error("No Session Context"); + return NULL; + } } ogs_assert(sess); diff --git a/src/upf/context.h b/src/upf/context.h index dde8e6e5e..4dbb2a065 100644 --- a/src/upf/context.h +++ b/src/upf/context.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -48,14 +48,18 @@ extern int __upf_log_domain; struct upf_route_trie_node; typedef struct upf_context_s { - ogs_hash_t *seid_hash; /* hash table (SEID) */ - ogs_hash_t *f_seid_hash; /* hash table (F-SEID) */ - ogs_hash_t *ipv4_hash; /* hash table (IPv4 Address) */ - ogs_hash_t *ipv6_hash; /* hash table (IPv6 Address) */ - struct upf_route_trie_node *ipv4_framed_routes; /* IPv4 framed routes trie */ - struct upf_route_trie_node *ipv6_framed_routes; /* IPv6 framed routes trie */ + ogs_hash_t *upf_n4_seid_hash; /* hash table (UPF-N4-SEID) */ + ogs_hash_t *smf_n4_seid_hash; /* hash table (SMF-N4-SEID) */ + ogs_hash_t *smf_n4_f_seid_hash; /* hash table (SMF-N4-F-SEID) */ + ogs_hash_t *ipv4_hash; /* hash table (IPv4 Address) */ + ogs_hash_t *ipv6_hash; /* hash table (IPv6 Address) */ - ogs_list_t sess_list; + /* IPv4 framed routes trie */ + struct upf_route_trie_node *ipv4_framed_routes; + /* IPv6 framed routes trie */ + struct upf_route_trie_node *ipv6_framed_routes; + + ogs_list_t sess_list; } upf_context_t; /* trie mapping from IP framed routes to session. */ @@ -96,11 +100,11 @@ typedef struct upf_sess_urr_acc_s { #define UPF_SESS(pfcp_sess) ogs_container_of(pfcp_sess, upf_sess_t, pfcp) typedef struct upf_sess_s { ogs_lnode_t lnode; - uint32_t index; /**< An index of this node */ + ogs_pool_id_t *upf_n4_seid_node; /* A node of UPF-N4-SEID */ ogs_pfcp_sess_t pfcp; - uint64_t upf_n4_seid; /* UPF SEID is dervied from INDEX */ + uint64_t upf_n4_seid; /* UPF SEID is dervied from NODE */ struct { uint64_t seid; ogs_ip_t ip; @@ -132,7 +136,6 @@ upf_sess_t *upf_sess_add_by_message(ogs_pfcp_message_t *message); upf_sess_t *upf_sess_add(ogs_pfcp_f_seid_t *f_seid); int upf_sess_remove(upf_sess_t *sess); void upf_sess_remove_all(void); -upf_sess_t *upf_sess_find(uint32_t index); upf_sess_t *upf_sess_find_by_smf_n4_seid(uint64_t seid); upf_sess_t *upf_sess_find_by_smf_n4_f_seid(ogs_pfcp_f_seid_t *f_seid); upf_sess_t *upf_sess_find_by_upf_n4_seid(uint64_t seid); diff --git a/src/upf/gtp-path.c b/src/upf/gtp-path.c index 17fe69cf1..9edce8ed7 100644 --- a/src/upf/gtp-path.c +++ b/src/upf/gtp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -258,11 +258,13 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) { int len; ssize_t size; - char buf[OGS_ADDRSTRLEN]; + char buf1[OGS_ADDRSTRLEN]; + char buf2[OGS_ADDRSTRLEN]; upf_sess_t *sess = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_sock_t *sock = NULL; ogs_sockaddr_t from; ogs_gtp2_header_t *gtp_h = NULL; @@ -272,6 +274,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) uint8_t qfi; ogs_assert(fd != INVALID_SOCKET); + sock = data; + ogs_assert(sock); pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_PKT_LEN); ogs_assert(pkbuf); @@ -300,14 +304,14 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) { ogs_pkbuf_t *echo_rsp; - ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf1)); echo_rsp = ogs_gtp2_handle_echo_req(pkbuf); ogs_expect(echo_rsp); if (echo_rsp) { ssize_t sent; /* Echo reply */ - ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf1)); sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from); if (sent < 0 || sent != echo_rsp->len) { @@ -322,7 +326,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); ogs_trace("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", - gtp_h->type, OGS_ADDR(&from, buf), teid); + gtp_h->type, OGS_ADDR(&from, buf1), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -369,7 +373,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { ogs_pfcp_far_t *far = NULL; - far = ogs_pfcp_far_find_by_error_indication(pkbuf); + far = ogs_pfcp_far_find_by_gtpu_error_indication(pkbuf); if (far) { ogs_assert(true == ogs_pfcp_up_handle_error_indication(far, &report)); @@ -419,12 +423,32 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, qfi, &from); + } goto cleanup; } switch(pfcp_object->type) { case OGS_PFCP_OBJ_PDR_TYPE: + /* UPF does not use PDR TYPE */ + ogs_assert_if_reached(); pdr = (ogs_pfcp_pdr_t *)pfcp_object; ogs_assert(pdr); break; @@ -456,7 +480,26 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } if (!pdr) { - /* TODO : Send Error Indication */ + /* + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error( + "[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, qfi, &from); + } goto cleanup; } @@ -684,7 +727,7 @@ int upf_gtp_init(void) config.cluster_2048_pool = ogs_app()->pool.packet; -#if OGS_USE_TALLOC +#if OGS_USE_TALLOC == 1 /* allocate a talloc pool for GTP to ensure it doesn't have to go back * to the libc malloc all the time */ packet_pool = talloc_pool(__ogs_talloc_core, 1000*1024); diff --git a/src/upf/n4-handler.c b/src/upf/n4-handler.c index 1b05d794b..378a0bd73 100644 --- a/src/upf/n4-handler.c +++ b/src/upf/n4-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -55,6 +55,9 @@ void upf_n4_handle_session_establishment_request( uint8_t offending_ie_value = 0; int i; + ogs_pfcp_sereq_flags_t sereq_flags; + bool restoration_indication = false; + upf_metrics_inst_global_inc(UPF_METR_GLOB_CTR_SM_N4SESSIONESTABREQ); ogs_assert(xact); @@ -74,9 +77,14 @@ void upf_n4_handle_session_establishment_request( return; } + memset(&sereq_flags, 0, sizeof(sereq_flags)); + if (req->pfcpsereq_flags.presence == 1) + sereq_flags.value = req->pfcpsereq_flags.u8; + for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, - &req->create_pdr[i], &cause_value, &offending_ie_value); + &req->create_pdr[i], &sereq_flags, + &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; } @@ -123,6 +131,18 @@ void upf_n4_handle_session_establishment_request( if (cause_value != OGS_PFCP_CAUSE_REQUEST_ACCEPTED) goto cleanup; + /* PFCPSEReq-Flags */ + if (sereq_flags.restoration_indication == 1) { + for (i = 0; i < num_of_created_pdr; i++) { + pdr = created_pdr[i]; + ogs_assert(pdr); + + if (pdr->f_teid_len) + ogs_pfcp_pdr_swap_teid(pdr); + } + restoration_indication = true; + } + /* Setup GTP Node */ ogs_list_for_each(&sess->pfcp.far_list, far) { ogs_assert(OGS_ERROR != ogs_pfcp_setup_far_gtpu_node(far)); @@ -162,65 +182,9 @@ void upf_n4_handle_session_establishment_request( } /* Setup UPF-N3-TEID & QFI Hash */ - if (pdr->f_teid_len) { - ogs_pfcp_object_type_e type = OGS_PFCP_OBJ_SESS_TYPE; - - if (ogs_pfcp_self()->up_function_features.ftup && - pdr->f_teid.ch) { - - ogs_pfcp_pdr_t *choosed_pdr = NULL; - - if (pdr->f_teid.chid) { - choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( - &sess->pfcp, pdr->f_teid.choose_id); - if (!choosed_pdr) { - pdr->chid = true; - pdr->choose_id = pdr->f_teid.choose_id; - } - } else { - type = OGS_PFCP_OBJ_PDR_TYPE; - } - - if (choosed_pdr) { - pdr->f_teid_len = choosed_pdr->f_teid_len; - memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); - - } else { - ogs_gtpu_resource_t *resource = NULL; - resource = ogs_pfcp_find_gtpu_resource( - &ogs_gtp_self()->gtpu_resource_list, - pdr->dnn, OGS_PFCP_INTERFACE_ACCESS); - if (resource) { - ogs_assert( - (resource->info.v4 && pdr->f_teid.ipv4) || - (resource->info.v6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_user_plane_ip_resource_info_to_f_teid( - &resource->info, &pdr->f_teid, &pdr->f_teid_len)); - if (resource->info.teidri) - pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, - resource->info.teid_range); - else - pdr->f_teid.teid = pdr->index; - } else { - ogs_assert( - (ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || - (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_sockaddr_to_f_teid( - pdr->f_teid.ipv4 ? - ogs_gtp_self()->gtpu_addr : NULL, - pdr->f_teid.ipv6 ? - ogs_gtp_self()->gtpu_addr6 : NULL, - &pdr->f_teid, &pdr->f_teid_len)); - pdr->f_teid.teid = pdr->index; - } - } - } - - ogs_pfcp_object_teid_hash_set(type, pdr); - } + if (pdr->f_teid_len) + ogs_pfcp_object_teid_hash_set( + OGS_PFCP_OBJ_SESS_TYPE, pdr, restoration_indication); } /* Send Buffered Packet to gNB/SGW */ @@ -230,9 +194,16 @@ void upf_n4_handle_session_establishment_request( } } - ogs_assert(OGS_OK == - upf_pfcp_send_session_establishment_response( - xact, sess, created_pdr, num_of_created_pdr)); + if (restoration_indication == true || + ogs_pfcp_self()->up_function_features.ftup == 0) + ogs_assert(OGS_OK == + upf_pfcp_send_session_establishment_response( + xact, sess, NULL, 0)); + else + ogs_assert(OGS_OK == + upf_pfcp_send_session_establishment_response( + xact, sess, created_pdr, num_of_created_pdr)); + return; cleanup: @@ -273,7 +244,7 @@ void upf_n4_handle_session_modification_request( for (i = 0; i < OGS_MAX_NUM_OF_PDR; i++) { created_pdr[i] = ogs_pfcp_handle_create_pdr(&sess->pfcp, - &req->create_pdr[i], &cause_value, &offending_ie_value); + &req->create_pdr[i], NULL, &cause_value, &offending_ie_value); if (created_pdr[i] == NULL) break; } @@ -406,70 +377,13 @@ void upf_n4_handle_session_modification_request( ogs_pfcp_far_f_teid_hash_set(far); } - /* Setup UPF-N3-TEID & QFI Hash */ for (i = 0; i < num_of_created_pdr; i++) { pdr = created_pdr[i]; ogs_assert(pdr); - if (pdr->f_teid_len) { - ogs_pfcp_object_type_e type = OGS_PFCP_OBJ_SESS_TYPE; - - if (ogs_pfcp_self()->up_function_features.ftup && - pdr->f_teid.ch) { - - ogs_pfcp_pdr_t *choosed_pdr = NULL; - - if (pdr->f_teid.chid) { - choosed_pdr = ogs_pfcp_pdr_find_by_choose_id( - &sess->pfcp, pdr->f_teid.choose_id); - if (!choosed_pdr) { - pdr->chid = true; - pdr->choose_id = pdr->f_teid.choose_id; - } - } else { - type = OGS_PFCP_OBJ_PDR_TYPE; - } - - if (choosed_pdr) { - pdr->f_teid_len = choosed_pdr->f_teid_len; - memcpy(&pdr->f_teid, &choosed_pdr->f_teid, pdr->f_teid_len); - - } else { - ogs_gtpu_resource_t *resource = NULL; - resource = ogs_pfcp_find_gtpu_resource( - &ogs_gtp_self()->gtpu_resource_list, - pdr->dnn, OGS_PFCP_INTERFACE_ACCESS); - if (resource) { - ogs_assert( - (resource->info.v4 && pdr->f_teid.ipv4) || - (resource->info.v6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_user_plane_ip_resource_info_to_f_teid( - &resource->info, &pdr->f_teid, &pdr->f_teid_len)); - if (resource->info.teidri) - pdr->f_teid.teid = OGS_PFCP_GTPU_INDEX_TO_TEID( - pdr->index, resource->info.teidri, - resource->info.teid_range); - else - pdr->f_teid.teid = pdr->index; - } else { - ogs_assert( - (ogs_gtp_self()->gtpu_addr && pdr->f_teid.ipv4) || - (ogs_gtp_self()->gtpu_addr6 && pdr->f_teid.ipv6)); - ogs_assert(OGS_OK == - ogs_pfcp_sockaddr_to_f_teid( - pdr->f_teid.ipv4 ? - ogs_gtp_self()->gtpu_addr : NULL, - pdr->f_teid.ipv6 ? - ogs_gtp_self()->gtpu_addr6 : NULL, - &pdr->f_teid, &pdr->f_teid_len)); - pdr->f_teid.teid = pdr->index; - } - } - } - - ogs_pfcp_object_teid_hash_set(type, pdr); - } + /* Setup UPF-N3-TEID & QFI Hash */ + if (pdr->f_teid_len) + ogs_pfcp_object_teid_hash_set(OGS_PFCP_OBJ_SESS_TYPE, pdr, false); } /* Send Buffered Packet to gNB/SGW */ @@ -479,9 +393,14 @@ void upf_n4_handle_session_modification_request( } } - ogs_assert(OGS_OK == - upf_pfcp_send_session_modification_response( - xact, sess, created_pdr, num_of_created_pdr)); + if (ogs_pfcp_self()->up_function_features.ftup == 0) + ogs_assert(OGS_OK == + upf_pfcp_send_session_modification_response( + xact, sess, NULL, 0)); + else + ogs_assert(OGS_OK == + upf_pfcp_send_session_modification_response( + xact, sess, created_pdr, num_of_created_pdr)); return; cleanup: diff --git a/src/upf/pfcp-path.c b/src/upf/pfcp-path.c index 4e64d6b74..16c9c0544 100644 --- a/src/upf/pfcp-path.c +++ b/src/upf/pfcp-path.c @@ -208,7 +208,6 @@ int upf_pfcp_send_session_modification_response( ogs_pfcp_header_t h; ogs_assert(xact); - ogs_assert(created_pdr); memset(&h, 0, sizeof(ogs_pfcp_header_t)); h.type = OGS_PFCP_SESSION_MODIFICATION_RESPONSE_TYPE; diff --git a/src/upf/pfcp-sm.c b/src/upf/pfcp-sm.c index 787ac2dca..980bcb60d 100644 --- a/src/upf/pfcp-sm.c +++ b/src/upf/pfcp-sm.c @@ -281,12 +281,9 @@ void upf_pfcp_state_associated(ogs_fsm_t *s, upf_event_t *e) &message->pfcp_association_setup_response); break; case OGS_PFCP_SESSION_ESTABLISHMENT_REQUEST_TYPE: - if (message->h.seid_presence && message->h.seid == 0) { - ogs_expect(!sess); - sess = upf_sess_add_by_message(message); - if (sess) - OGS_SETUP_PFCP_NODE(sess, node); - } + sess = upf_sess_add_by_message(message); + if (sess) + OGS_SETUP_PFCP_NODE(sess, node); upf_n4_handle_session_establishment_request( sess, xact, &message->pfcp_session_establishment_request); break; diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c index 1a93390fa..a5c7ce2d3 100644 --- a/tests/common/gtpu.c +++ b/tests/common/gtpu.c @@ -499,7 +499,7 @@ int test_gtpu_send_error_indication( ogs_assert_if_reached(); } - pkbuf = ogs_gtp2_build_error_indication(teid, node->addr); + pkbuf = ogs_gtp1_build_error_indication(teid, node->addr); ogs_assert(pkbuf); memset(>p_hdesc, 0, sizeof(gtp_hdesc)); diff --git a/tests/core/memory-test.c b/tests/core/memory-test.c index 7d316ac20..6792234ed 100644 --- a/tests/core/memory-test.c +++ b/tests/core/memory-test.c @@ -41,7 +41,7 @@ static void test2_func(abts_case *tc, void *data) static void test3_func(abts_case *tc, void *data) { -#if OGS_USE_TALLOC != 1 +#if OGS_USE_TALLOC == 0 char *ptr = ogs_realloc(0, 10); ABTS_PTR_NOTNULL(tc, ptr); ogs_free(ptr); @@ -54,7 +54,7 @@ static void test3_func(abts_case *tc, void *data) static void test4_func(abts_case *tc, void *data) { -#if OGS_USE_TALLOC != 1 +#if OGS_USE_TALLOC == 0 char *p, *q; p = ogs_malloc(10); diff --git a/tests/handover/5gc-n2-test.c b/tests/handover/5gc-n2-test.c index e560c8353..f984b08ad 100644 --- a/tests/handover/5gc-n2-test.c +++ b/tests/handover/5gc-n2-test.c @@ -806,9 +806,6 @@ static void direct_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -926,9 +923,6 @@ static void direct_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1844,9 +1838,6 @@ static void indirect_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1916,6 +1907,9 @@ static void indirect_complete_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); + /* Waiting for removing Indirect Data Forwarding */ + ogs_msleep(100); + /* Send HandoverRequired */ sendbuf = testngap_build_handover_required( test_ue, NGAP_HandoverType_intra5gs, @@ -1992,9 +1986,6 @@ static void indirect_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -2500,9 +2491,6 @@ static void indirect_cancel_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf);