From 1bed0d587275ad105c5fe6f0d9ca4ef1508a74e0 Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Tue, 20 Oct 2020 20:00:02 -0400 Subject: [PATCH] [#568] Add GTP-U Error Indication Handling So far, no operation was performed when Error Indication was received from eNodeB. For that reason, I solved #568 issues by controlling the MME to prevent this from happening. Now, when GTP-U Error Indication is received, MME and SGW are implemented to do what they have to do. I hope that the network can be restored by responding appropriately even if Error Indication occurs. --- lib/core/ogs-pkbuf.c | 10 +- lib/core/ogs-pkbuf.h | 14 ++ lib/core/ogs-sockaddr.c | 2 +- lib/gtp/build.c | 53 +++++ lib/gtp/build.h | 3 + lib/gtp/message.c | 2 +- lib/gtp/message.h | 10 +- lib/gtp/path.c | 108 +++++++++ lib/gtp/path.h | 5 + lib/gtp/support/gtp-tlv.py | 8 +- lib/gtp/types.h | 14 +- lib/pfcp/build.c | 16 +- lib/pfcp/context.c | 168 +++++++++++--- lib/pfcp/context.h | 24 +- lib/pfcp/handler.c | 32 +++ lib/pfcp/handler.h | 2 + lib/pfcp/path.c | 140 ++---------- lib/pfcp/xact.h | 1 + lib/sctp/meson.build | 10 +- src/mme/esm-handler.c | 5 +- src/mme/mme-context.c | 4 +- src/mme/mme-context.h | 1 + src/mme/mme-s11-handler.c | 98 +++++--- src/mme/mme-sm.c | 17 -- src/mme/s1ap-handler.c | 19 +- src/sgwc/context.c | 65 ++++++ src/sgwc/context.h | 3 + src/sgwc/gtp-path.c | 9 +- src/sgwc/gtp-path.h | 2 +- src/sgwc/pfcp-path.c | 3 - src/sgwc/s11-build.c | 18 +- src/sgwc/s11-build.h | 3 +- src/sgwc/s11-handler.c | 19 +- src/sgwc/sxa-handler.c | 134 +++++++---- src/sgwu/gtp-path.c | 87 ++++---- src/sgwu/sxa-handler.c | 3 + src/upf/gtp-path.c | 118 +++++----- src/upf/n4-handler.c | 3 + tests/app/5gc-init.c | 1 + tests/app/app-init.c | 1 + tests/app/epc-init.c | 1 + tests/attach/idle-test.c | 446 +++++++++++++++++++++++++++++++++++++ tests/common/gtpu.c | 322 +++++++++++++------------- tests/common/gtpu.h | 6 + 44 files changed, 1428 insertions(+), 582 deletions(-) diff --git a/lib/core/ogs-pkbuf.c b/lib/core/ogs-pkbuf.c index a925bfaa0..48c43eaa4 100644 --- a/lib/core/ogs-pkbuf.c +++ b/lib/core/ogs-pkbuf.c @@ -339,13 +339,16 @@ static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster) ogs_pool_free(&pool->cluster_512, (ogs_cluster_512_t*)cluster->buffer); break; case OGS_CLUSTER_1024_SIZE: - ogs_pool_free(&pool->cluster_1024, (ogs_cluster_1024_t*)cluster->buffer); + ogs_pool_free( + &pool->cluster_1024, (ogs_cluster_1024_t*)cluster->buffer); break; case OGS_CLUSTER_2048_SIZE: - ogs_pool_free(&pool->cluster_2048, (ogs_cluster_2048_t*)cluster->buffer); + ogs_pool_free( + &pool->cluster_2048, (ogs_cluster_2048_t*)cluster->buffer); break; case OGS_CLUSTER_8192_SIZE: - ogs_pool_free(&pool->cluster_8192, (ogs_cluster_8192_t*)cluster->buffer); + ogs_pool_free( + &pool->cluster_8192, (ogs_cluster_8192_t*)cluster->buffer); break; case OGS_CLUSTER_BIG_SIZE: ogs_pool_free(&pool->cluster_big, (ogs_cluster_big_t*)cluster->buffer); @@ -356,4 +359,3 @@ static void cluster_free(ogs_pkbuf_pool_t *pool, ogs_cluster_t *cluster) ogs_pool_free(&pool->cluster, cluster); } - diff --git a/lib/core/ogs-pkbuf.h b/lib/core/ogs-pkbuf.h index e6b443482..d6396615f 100644 --- a/lib/core/ogs-pkbuf.h +++ b/lib/core/ogs-pkbuf.h @@ -110,6 +110,20 @@ static ogs_inline void ogs_pkbuf_put_u8(ogs_pkbuf_t *pkbuf, uint8_t val) *(uint8_t *)ogs_pkbuf_put(pkbuf, 1) = val; } +static ogs_inline void ogs_pkbuf_put_u16(ogs_pkbuf_t *pkbuf, uint16_t val) +{ + uint8_t *p = ogs_pkbuf_put(pkbuf, 2); + uint16_t tmp = htobe16(val); + memcpy(p, &tmp, 2); +} + +static ogs_inline void ogs_pkbuf_put_u32(ogs_pkbuf_t *pkbuf, uint32_t val) +{ + uint8_t *p = ogs_pkbuf_put(pkbuf, 4); + uint32_t tmp = htobe32(val); + memcpy(p, &tmp, 4); +} + static ogs_inline void *ogs_pkbuf_push(ogs_pkbuf_t *pkbuf, unsigned int len) { pkbuf->data -= len; diff --git a/lib/core/ogs-sockaddr.c b/lib/core/ogs-sockaddr.c index 2c02945a0..bf302b43e 100644 --- a/lib/core/ogs-sockaddr.c +++ b/lib/core/ogs-sockaddr.c @@ -300,7 +300,7 @@ const char *ogs_inet_ntop(void *sa, char *buf, int buflen) ogs_assert(buflen >= OGS_ADDRSTRLEN); family = sockaddr->ogs_sa_family; - switch(family) { + switch (family) { case AF_INET: return inet_ntop(family, &sockaddr->sin.sin_addr, buf, INET_ADDRSTRLEN); diff --git a/lib/gtp/build.c b/lib/gtp/build.c index 0a467f076..7fa6799f7 100644 --- a/lib/gtp/build.c +++ b/lib/gtp/build.c @@ -56,3 +56,56 @@ ogs_pkbuf_t *ogs_gtp_build_echo_response( gtp_message.h.type = type; return ogs_gtp_build_msg(>p_message); } + +ogs_pkbuf_t *ogs_gtp_build_error_indication( + uint32_t teid, ogs_sockaddr_t *addr) +{ + ogs_pkbuf_t *pkbuf = NULL; + unsigned char *p = NULL; + int family; + + ogs_assert(addr); + + pkbuf = ogs_pkbuf_alloc( + NULL, 100 /* enough for Error Indiciation; use smaller buffer */); + ogs_assert(pkbuf); + ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + + /* + * 8.3 Tunnel Endpoint Identifier Data I + * + * Octet 1 : Type = 16 (Decimal) + * Octet 2-5 : Tunnel Endpoint Identitifer Data I + */ + ogs_pkbuf_put_u8(pkbuf, 16); + ogs_pkbuf_put_u32(pkbuf, teid); + + /* + * 8.4 GTP-U Peer Address + * + * Octet 1 : Type = 133 (Decimal) + * Octet 2-3 : Length + * Octet 4-n : IPv4 or IPv6 Address + */ + ogs_pkbuf_put_u8(pkbuf, 133); + + family = addr->ogs_sa_family; + switch(family) { + case AF_INET: + ogs_pkbuf_put_u16(pkbuf, OGS_IPV4_LEN); + p = ogs_pkbuf_put(pkbuf, OGS_IPV4_LEN); + memcpy(p, &addr->sin.sin_addr, OGS_IPV4_LEN); + break; + case AF_INET6: + ogs_pkbuf_put_u16(pkbuf, OGS_IPV6_LEN); + p = ogs_pkbuf_put(pkbuf, OGS_IPV6_LEN); + memcpy(p, &addr->sin6.sin6_addr, OGS_IPV6_LEN); + break; + default: + ogs_fatal("Unknown family(%d)", family); + ogs_abort(); + return NULL; + } + + return pkbuf; +} diff --git a/lib/gtp/build.h b/lib/gtp/build.h index 43ed0fbbe..ed6378f5a 100644 --- a/lib/gtp/build.h +++ b/lib/gtp/build.h @@ -33,6 +33,9 @@ ogs_pkbuf_t *ogs_gtp_build_echo_request( ogs_pkbuf_t *ogs_gtp_build_echo_response( uint8_t type, uint8_t recovery, uint8_t features); +ogs_pkbuf_t *ogs_gtp_build_error_indication( + uint32_t teid, ogs_sockaddr_t *addr); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/message.c b/lib/gtp/message.c index 506de6f4d..8194bf154 100644 --- a/lib/gtp/message.c +++ b/lib/gtp/message.c @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by gtp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-08-11 20:17:53.518172 by acetcom + * Created on: 2020-10-14 13:48:14.476575 by acetcom * from 29274-g30.docx ******************************************************************************/ diff --git a/lib/gtp/message.h b/lib/gtp/message.h index a6c3d5002..7021f4675 100644 --- a/lib/gtp/message.h +++ b/lib/gtp/message.h @@ -20,7 +20,7 @@ /******************************************************************************* * This file had been created by gtp-tlv.py script v0.1.0 * Please do not modify this file but regenerate it via script. - * Created on: 2020-08-11 20:17:53.511069 by acetcom + * Created on: 2020-10-14 13:48:14.468022 by acetcom * from 29274-g30.docx ******************************************************************************/ @@ -50,9 +50,11 @@ typedef struct ogs_gtp_header_s { uint8_t spare1:3;) }; /* GTU-U flags */ -#define OGS_GTPU_FLAGS_PN 0x1 -#define OGS_GTPU_FLAGS_S 0x2 -#define OGS_GTPU_FLAGS_E 0x4 +#define OGS_GTPU_FLAGS_V 0x20 +#define OGS_GTPU_FLAGS_PT 0x10 +#define OGS_GTPU_FLAGS_E 0x04 +#define OGS_GTPU_FLAGS_S 0x02 +#define OGS_GTPU_FLAGS_PN 0x01 uint8_t flags; }; /* GTP-U message type, defined in 3GPP TS 29.281 Release 11 */ diff --git a/lib/gtp/path.c b/lib/gtp/path.c index e4f3bbab9..823aca90f 100644 --- a/lib/gtp/path.c +++ b/lib/gtp/path.c @@ -123,6 +123,114 @@ int ogs_gtp_sendto(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf) return OGS_OK; } +int ogs_gtp_send_user_plane( + ogs_gtp_node_t *gnode, + ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf) +{ + char buf[OGS_ADDRSTRLEN]; + int rv; + + ogs_gtp_header_t *gtp_h = NULL; + ogs_gtp_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_gtp_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_gtp_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_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = + OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION; + ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier; + ext_h->next_type = + OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } else { + /* EPC */ + ext_h->type = ext_hdesc->type; + ext_h->len = 1; + ext_h->next_type = + OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } + } + + ogs_debug("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) { + ogs_error("SEND GTP-U[%d] to Peer[%s] : TEID[0x%x]", + gtp_hdesc->type, OGS_ADDR(&gnode->addr, buf), gtp_hdesc->teid); + } + } + + ogs_pkbuf_free(pkbuf); + + return rv; +} + ogs_pkbuf_t *ogs_gtp_handle_echo_req(ogs_pkbuf_t *pkb) { ogs_gtp_header_t *gtph = NULL; diff --git a/lib/gtp/path.h b/lib/gtp/path.h index 0f0a97453..2c01ebcdf 100644 --- a/lib/gtp/path.h +++ b/lib/gtp/path.h @@ -36,6 +36,11 @@ int ogs_gtp_connect(ogs_sock_t *ipv4, ogs_sock_t *ipv6, ogs_gtp_node_t *gnode); int ogs_gtp_send(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf); int ogs_gtp_sendto(ogs_gtp_node_t *gnode, ogs_pkbuf_t *pkbuf); +int ogs_gtp_send_user_plane( + ogs_gtp_node_t *gnode, + ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf); + ogs_pkbuf_t *ogs_gtp_handle_echo_req(ogs_pkbuf_t *pkt); void ogs_gtp_send_error_message( ogs_gtp_xact_t *xact, uint32_t teid, uint8_t type, uint8_t cause_value); diff --git a/lib/gtp/support/gtp-tlv.py b/lib/gtp/support/gtp-tlv.py index 29a4eea88..c201180dd 100644 --- a/lib/gtp/support/gtp-tlv.py +++ b/lib/gtp/support/gtp-tlv.py @@ -411,9 +411,11 @@ typedef struct ogs_gtp_header_s { uint8_t spare1:3;) }; /* GTU-U flags */ -#define OGS_GTPU_FLAGS_PN 0x1 -#define OGS_GTPU_FLAGS_S 0x2 -#define OGS_GTPU_FLAGS_E 0x4 +#define OGS_GTPU_FLAGS_V 0x20 +#define OGS_GTPU_FLAGS_PT 0x10 +#define OGS_GTPU_FLAGS_E 0x04 +#define OGS_GTPU_FLAGS_S 0x02 +#define OGS_GTPU_FLAGS_PN 0x01 uint8_t flags; }; /* GTP-U message type, defined in 3GPP TS 29.281 Release 11 */ diff --git a/lib/gtp/types.h b/lib/gtp/types.h index e63c57495..d35b31d59 100644 --- a/lib/gtp/types.h +++ b/lib/gtp/types.h @@ -51,6 +51,7 @@ extern "C" { #define OGS_GTPV1U_EXTENSION_HEADER_LEN 4 typedef struct ogs_gtp_extension_header_s { +#define OGS_GTP_EXTENSION_HEADER_TYPE_UDP_PORT 0x40 #define OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER 0x85 #define OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS 0x0 uint16_t sequence_number; @@ -68,12 +69,13 @@ typedef struct ogs_gtp_extension_header_s { } __attribute__ ((packed)) ogs_gtp_extension_header_t; /* 8.4 Cause */ -#define OGS_GTP_CAUSE_LOCAL_DETACH 2 +#define OGS_GTP_CAUSE_INVALID_VALUE 0 +#define OGS_GTP_CAUSE_LOCAL_DETACH 2 #define OGS_GTP_CAUSE_COMPLETE_DETACH_3 #define OGS_GTP_CAUSE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP 4 -#define OGS_GTP_CAUSE_ISR_DEACTIVATION 5 -#define OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED_FROM_RNC_ENODEB_S4_SGSN_MME 6 -#define OGS_GTP_CAUSE_IMSI_DETACH_ONLY 7 +#define OGS_GTP_CAUSE_ISR_DEACTIVATION 5 +#define OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED 6 +#define OGS_GTP_CAUSE_IMSI_DETACH_ONLY 7 #define OGS_GTP_CAUSE_REACTIVATION_REQUESTED 8 #define OGS_GTP_CAUSE_PDN_RECONNECTION_TO_THIS_APN_DISALLOWED 9 #define OGS_GTP_CAUSE_ACCESS_CHANGED_FROM_NON_3GPP_TO_3GPP 10 @@ -81,7 +83,7 @@ typedef struct ogs_gtp_extension_header_s { #define OGS_GTP_CAUSE_PGW_NOT_RESPONDING 12 #define OGS_GTP_CAUSE_NETWORK_FAILURE 13 #define OGS_GTP_CAUSE_QOS_PARAMETER_MISMATCH 14 -#define OGS_GTP_CAUSE_REQUEST_ACCEPTED 16 +#define OGS_GTP_CAUSE_REQUEST_ACCEPTED 16 #define OGS_GTP_CAUSE_REQUEST_ACCEPTED_PARTIALLY 17 #define OGS_GTP_CAUSE_NEW_PDN_TYPE_DUE_TO_NETWORK_PREFERENCE 18 #define OGS_GTP_CAUSE_NEW_PDN_TYPE_DUE_TO_SINGLE_ADDRESS_BEARER_ONLY 19 @@ -127,7 +129,7 @@ typedef struct ogs_gtp_extension_header_s { #define OGS_GTP_CAUSE_DATA_FORWARDING_NOT_SUPPORTED 106 #define OGS_GTP_CAUSE_INVALID_REPLY_FROM_REMOTE_PEER 107 #define OGS_GTP_CAUSE_FALLBACK_TO_GTPV1 108 -#define OGS_GTP_CAUSE_INVALID_PEER 109 +#define OGS_GTP_CAUSE_INVALID_PEER 109 #define OGS_GTP_CAUSE_TEMPORARILY_REJECTED_DUE_TO_HANDOVER_IN_PROGRESS 110 #define OGS_GTP_CAUSE_MODIFICATIONS_NOT_LIMITED_TO_S1_U_BEARERS 111 #define OGS_GTP_CAUSE_REQUEST_REJECTED_FOR_A_PMIPV6_REASON 112 diff --git a/lib/pfcp/build.c b/lib/pfcp/build.c index 26a29a08a..5fa056954 100644 --- a/lib/pfcp/build.c +++ b/lib/pfcp/build.c @@ -253,7 +253,6 @@ void ogs_pfcp_build_create_pdr( ogs_pfcp_tlv_create_pdr_t *message, int i, ogs_pfcp_pdr_t *pdr) { ogs_pfcp_far_t *far = NULL; - ogs_pfcp_sess_t *pfcp_sess = NULL; ogs_pfcp_sdf_filter_t pfcp_sdf_filter[OGS_MAX_NUM_OF_RULE]; int j = 0; int len = 0; @@ -261,8 +260,6 @@ void ogs_pfcp_build_create_pdr( ogs_assert(message); ogs_assert(pdr); - pfcp_sess = pdr->sess; - ogs_assert(pfcp_sess); far = pdr->far; ogs_assert(far); @@ -412,12 +409,8 @@ static struct { void ogs_pfcp_build_create_far( ogs_pfcp_tlv_create_far_t *message, int i, ogs_pfcp_far_t *far) { - ogs_pfcp_sess_t *pfcp_sess = NULL; - ogs_assert(message); ogs_assert(far); - pfcp_sess = far->sess; - ogs_assert(pfcp_sess); message->presence = 1; message->far_id.presence = 1; @@ -637,6 +630,15 @@ ogs_pkbuf_t *ogs_pfcp_build_session_report_request( } } + if (report->error_indication.remote_f_teid_len) { + req->error_indication_report.presence = 1; + req->error_indication_report.remote_f_teid.presence = 1; + req->error_indication_report.remote_f_teid.data = + &report->error_indication.remote_f_teid; + req->error_indication_report.remote_f_teid.len = + report->error_indication.remote_f_teid_len; + } + pfcp_message.h.type = type; return ogs_pfcp_build_msg(&pfcp_message); } diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index e8ed7ed61..00890d657 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -92,6 +92,7 @@ void ogs_pfcp_context_init(int num_of_gtpu_resource) ogs_pool_init(&ogs_pfcp_subnet_pool, OGS_MAX_NUM_OF_SUBNET); self.pdr_hash = ogs_hash_make(); + self.far_hash = ogs_hash_make(); context_initialized = 1; } @@ -102,6 +103,8 @@ void ogs_pfcp_context_final(void) ogs_assert(self.pdr_hash); ogs_hash_destroy(self.pdr_hash); + ogs_assert(self.far_hash); + ogs_hash_destroy(self.far_hash); ogs_pfcp_dev_remove_all(); ogs_pfcp_subnet_remove_all(); @@ -857,32 +860,6 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find( return NULL; } -static uint64_t pdr_hash_keygen(uint32_t teid, uint8_t qfi) -{ - uint64_t hashkey = (teid << 8) + qfi; - return hashkey; -} - -void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr) -{ - ogs_assert(pdr); - - if (pdr->hashkey) - ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, - sizeof(pdr->hashkey), NULL); - - pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, pdr->qfi); - ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, - sizeof(pdr->hashkey), pdr); -} - -ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi) -{ - uint64_t hashkey = pdr_hash_keygen(teid, qfi); - return (ogs_pfcp_pdr_t *)ogs_hash_get(self.pdr_hash, - &hashkey, sizeof(hashkey)); -} - ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id) { @@ -900,6 +877,32 @@ ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_or_add( return pdr; } +static uint64_t pdr_hash_keygen(uint32_t teid, uint8_t qfi) +{ + uint64_t hashkey = (teid << 8) + qfi; + return hashkey; +} + +void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr) +{ + ogs_assert(pdr); + + if (pdr->hashkey) + ogs_hash_set(ogs_pfcp_self()->pdr_hash, + &pdr->hashkey, sizeof(pdr->hashkey), NULL); + + pdr->hashkey = pdr_hash_keygen(pdr->f_teid.teid, pdr->qfi); + ogs_hash_set(ogs_pfcp_self()->pdr_hash, + &pdr->hashkey, sizeof(pdr->hashkey), pdr); +} + +ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi) +{ + uint64_t hashkey = pdr_hash_keygen(teid, qfi); + return (ogs_pfcp_pdr_t *)ogs_hash_get(self.pdr_hash, + &hashkey, sizeof(hashkey)); +} + void ogs_pfcp_pdr_reorder_by_precedence( ogs_pfcp_pdr_t *pdr, ogs_pfcp_precedence_t precedence) { @@ -947,8 +950,8 @@ void ogs_pfcp_pdr_remove(ogs_pfcp_pdr_t *pdr) ogs_pfcp_rule_remove_all(pdr); if (pdr->hashkey) - ogs_hash_set(ogs_pfcp_self()->pdr_hash, &pdr->hashkey, - sizeof(pdr->hashkey), NULL); + ogs_hash_set(ogs_pfcp_self()->pdr_hash, + &pdr->hashkey, sizeof(pdr->hashkey), NULL); if (pdr->dnn) ogs_free(pdr->dnn); @@ -1021,6 +1024,107 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_or_add( return far; } +void ogs_pfcp_far_hash_set(ogs_pfcp_far_t *far) +{ + int family; + + ogs_gtp_node_t *gnode = NULL; + ogs_sockaddr_t *addr = NULL; + + ogs_assert(far); + gnode = far->gnode; + ogs_assert(gnode); + addr = &gnode->addr; + ogs_assert(addr); + + if (far->hashkey_len) + ogs_hash_set(ogs_pfcp_self()->far_hash, + &far->hashkey, far->hashkey_len, NULL); + + far->hashkey.teid = far->outer_header_creation.teid; + far->hashkey_len = sizeof(far->hashkey.teid); + + family = addr->ogs_sa_family; + switch (family) { + case AF_INET: + memcpy(far->hashkey.addr, &addr->sin.sin_addr, OGS_IPV4_LEN); + far->hashkey_len += OGS_IPV4_LEN; + break; + case AF_INET6: + memcpy(far->hashkey.addr, &addr->sin6.sin6_addr, OGS_IPV6_LEN); + far->hashkey_len += OGS_IPV6_LEN; + break; + default: + ogs_fatal("Unknown family(%d)", family); + ogs_abort(); + return; + } + + ogs_hash_set(ogs_pfcp_self()->far_hash, + &far->hashkey, far->hashkey_len, far); +} + +ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf) +{ + ogs_pfcp_far_hashkey_t hashkey; + int hashkey_len; + + uint32_t teid; + uint16_t len; + unsigned char *p = NULL; + + ogs_assert(pkbuf); + + p = pkbuf->data; + ogs_assert(p); + + /* + * 8.3 Tunnel Endpoint Identifier Data I + * + * Octet 1 : Type = 16 (Decimal) + * Octet 2-5 : Tunnel Endpoint Identitifer Data I + */ + if (*p != 16) { + ogs_error("Unknown Type [%d]", *p); + return NULL; + } + p += 1; + + memcpy(&teid, p, 4); + teid = be32toh(teid); + p += 4; + + /* + * 8.4 GTP-U Peer Address + * + * Octet 1 : Type = 133 (Decimal) + * Octet 2-3 : Length + * Octet 4-n : IPv4 or IPv6 Address + */ + if (*p != 133) { + ogs_error("Unknown Type [%d]", *p); + return NULL; + } + p += 1; + + memcpy(&len, p, 2); + len = be16toh(len); + p += 2; + + if (len == OGS_IPV4_LEN) { + } else if (len == OGS_IPV6_LEN) { + } else { + ogs_error("Invalid Length [%d]", len); + return NULL; + } + + hashkey.teid = teid; + memcpy(hashkey.addr, p, len); + hashkey_len = 4 + len; + + return (ogs_pfcp_far_t *)ogs_hash_get(self.far_hash, &hashkey, hashkey_len); +} + void ogs_pfcp_far_remove(ogs_pfcp_far_t *far) { int i; @@ -1030,11 +1134,15 @@ void ogs_pfcp_far_remove(ogs_pfcp_far_t *far) sess = far->sess; ogs_assert(sess); + ogs_list_remove(&sess->far_list, far); + + if (far->hashkey_len) + ogs_hash_set(ogs_pfcp_self()->far_hash, + &far->hashkey, far->hashkey_len, NULL); + for (i = 0; i < far->num_of_buffered_packet; i++) ogs_pkbuf_free(far->buffered_packet[i]); - ogs_list_remove(&sess->far_list, far); - if (far->id_node) ogs_pool_free(&far->sess->far_id_pool, far->id_node); diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index c0aa74114..2d991cea5 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -60,7 +60,8 @@ typedef struct ogs_pfcp_context_s { ogs_list_t dev_list; /* Tun Device List */ ogs_list_t subnet_list; /* UE Subnet List */ - ogs_hash_t *pdr_hash; /* hash table (UPF-N3-TEID) */ + ogs_hash_t *pdr_hash; /* hash table for PDR(TEID+QFI) */ + ogs_hash_t *far_hash; /* hash table for FAR(TEID+ADDR) */ } ogs_pfcp_context_t; #define OGS_SETUP_PFCP_NODE(__cTX, __pNODE) \ @@ -152,10 +153,18 @@ typedef struct ogs_pfcp_pdr_s { ogs_pfcp_sess_t *sess; } ogs_pfcp_pdr_t; +typedef struct ogs_pfcp_far_hashkey_s { + uint32_t teid; + uint32_t addr[4]; +} ogs_pfcp_far_hashkey_t; + typedef struct ogs_pfcp_far_s { ogs_lnode_t lnode; uint32_t index; + int hashkey_len; + ogs_pfcp_far_hashkey_t hashkey; + uint8_t *id_node; /* Pool-Node for ID */ ogs_pfcp_far_id_t id; ogs_pfcp_apply_action_t apply_action; @@ -166,7 +175,7 @@ typedef struct ogs_pfcp_far_s { ogs_pfcp_smreq_flags_t smreq_flags; uint32_t num_of_buffered_packet; - ogs_pkbuf_t* buffered_packet[OGS_MAX_NUM_OF_PACKET_BUFFER]; + ogs_pkbuf_t *buffered_packet[OGS_MAX_NUM_OF_PACKET_BUFFER]; /* Related Context */ ogs_pfcp_sess_t *sess; @@ -304,13 +313,14 @@ ogs_pfcp_pdr_t *ogs_pfcp_sess_default_pdr( void ogs_pfcp_sess_clear(ogs_pfcp_sess_t *sess); ogs_pfcp_pdr_t *ogs_pfcp_pdr_add(ogs_pfcp_sess_t *sess); -void ogs_pfcp_pdr_hash_set(ogs_pfcp_pdr_t *pdr); - ogs_pfcp_pdr_t *ogs_pfcp_pdr_find( ogs_pfcp_sess_t *sess, ogs_pfcp_pdr_id_t id); -ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi); 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_hash_set(ogs_pfcp_pdr_t *pdr); +ogs_pfcp_pdr_t *ogs_pfcp_pdr_find_by_teid_and_qfi(uint32_t teid, uint8_t qfi); + void ogs_pfcp_pdr_reorder_by_precedence( ogs_pfcp_pdr_t *pdr, ogs_pfcp_precedence_t precedence); void ogs_pfcp_pdr_associate_far(ogs_pfcp_pdr_t *pdr, ogs_pfcp_far_t *far); @@ -324,6 +334,10 @@ ogs_pfcp_far_t *ogs_pfcp_far_find( ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id); 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_hash_set(ogs_pfcp_far_t *far); +ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf); + void ogs_pfcp_far_remove(ogs_pfcp_far_t *far); void ogs_pfcp_far_remove_all(ogs_pfcp_sess_t *sess); diff --git a/lib/pfcp/handler.c b/lib/pfcp/handler.c index 2f56917d5..b4485bfcd 100644 --- a/lib/pfcp/handler.c +++ b/lib/pfcp/handler.c @@ -172,6 +172,38 @@ void ogs_pfcp_up_handle_pdr( } } +void ogs_pfcp_up_handle_error_indication( + ogs_pfcp_far_t *far, ogs_pfcp_user_plane_report_t *report) +{ + uint16_t len; + + ogs_assert(far); + ogs_assert(far->hashkey_len); + + ogs_assert(report); + + memset(report, 0, sizeof(*report)); + + len = far->hashkey_len - 4; /* Remove TEID size, Only use ADDR size */ + + report->error_indication.remote_f_teid_len = 5 + len; + report->error_indication.remote_f_teid.teid = htobe32(far->hashkey.teid); + if (len == OGS_IPV4_LEN) { + report->error_indication.remote_f_teid.ipv4 = 1; + memcpy(&report->error_indication.remote_f_teid.addr, + far->hashkey.addr, len); + } else if (len == OGS_IPV6_LEN) { + report->error_indication.remote_f_teid.ipv6 = 1; + memcpy(report->error_indication.remote_f_teid.addr6, + far->hashkey.addr, len); + } else { + ogs_error("Invalid Length [%d]", len); + return; + } + + report->type.error_indication_report = 1; +} + ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, ogs_pfcp_tlv_create_pdr_t *message, uint8_t *cause_value, uint8_t *offending_ie_value) diff --git a/lib/pfcp/handler.h b/lib/pfcp/handler.h index e5c16b474..6fb8cbf14 100644 --- a/lib/pfcp/handler.h +++ b/lib/pfcp/handler.h @@ -48,6 +48,8 @@ void ogs_pfcp_up_handle_association_setup_response( void ogs_pfcp_up_handle_pdr( ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *recvbuf, ogs_pfcp_user_plane_report_t *report); +void ogs_pfcp_up_handle_error_indication( + ogs_pfcp_far_t *far, ogs_pfcp_user_plane_report_t *report); ogs_pfcp_pdr_t *ogs_pfcp_handle_create_pdr(ogs_pfcp_sess_t *sess, ogs_pfcp_tlv_create_pdr_t *message, diff --git a/lib/pfcp/path.c b/lib/pfcp/path.c index 6a4bd1922..592356f61 100644 --- a/lib/pfcp/path.c +++ b/lib/pfcp/path.c @@ -262,14 +262,11 @@ void ogs_pfcp_up_send_association_setup_response(ogs_pfcp_xact_t *xact, void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf) { - char buf[OGS_ADDRSTRLEN]; - int rv; - ogs_gtp_header_t *gtp_h = NULL; - ogs_gtp_extension_header_t *ext_h = NULL; ogs_gtp_node_t *gnode = NULL; - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_qer_t *qer = NULL; + + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; ogs_assert(pdr); ogs_assert(sendbuf); @@ -285,73 +282,26 @@ void ogs_pfcp_send_g_pdu(ogs_pfcp_pdr_t *pdr, ogs_pkbuf_t *sendbuf) ogs_assert(gnode); ogs_assert(gnode->sock); - qer = pdr->qer; + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); - /* Add GTP-U header */ - if (qer && qer->qfi) { - ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN)); - gtp_h = (ogs_gtp_header_t *)sendbuf->data; - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 1 0 0 - */ - gtp_h->flags = 0x34; - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; - gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); - gtp_h->teid = htobe32(far->outer_header_creation.teid); + gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU; + gtp_hdesc.teid = far->outer_header_creation.teid; + if (pdr->qer && pdr->qer->qfi) + ext_hdesc.qos_flow_identifier = pdr->qer->qfi; - ext_h = (ogs_gtp_extension_header_t *)( - sendbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = qer->qfi; - ext_h->next_type = - OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } else { - ogs_assert(ogs_pkbuf_push(sendbuf, OGS_GTPV1U_HEADER_LEN)); - gtp_h = (ogs_gtp_header_t *)sendbuf->data; - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 0 0 0 - */ - gtp_h->flags = 0x30; - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; - gtp_h->length = htobe16(sendbuf->len - OGS_GTPV1U_HEADER_LEN); - gtp_h->teid = htobe32(far->outer_header_creation.teid); - } - - /* Send G-PDU */ - ogs_debug("SEND G-PDU to Peer[%s] : TEID[0x%x]", - OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); - rv = ogs_gtp_sendto(gnode, sendbuf); - if (rv != OGS_OK) { - if (ogs_socket_errno != OGS_EAGAIN) { - ogs_error("SEND G-PDU to Peer[%s] : TEID[0x%x]", - OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); - } - } - - ogs_pkbuf_free(sendbuf); + ogs_gtp_send_user_plane(gnode, >p_hdesc, &ext_hdesc, sendbuf); } void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr) { - char buf[OGS_ADDRSTRLEN]; - int rv; + ogs_gtp_node_t *gnode = NULL; + ogs_pfcp_far_t *far = NULL; + ogs_pkbuf_t *sendbuf = NULL; - ogs_gtp_header_t *gtp_h = NULL; - ogs_gtp_extension_header_t *ext_h = NULL; - ogs_gtp_node_t *gnode = NULL; - - ogs_pfcp_far_t *far = NULL; - ogs_pfcp_qer_t *qer = NULL; + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; ogs_assert(pdr); far = pdr->far; @@ -367,61 +317,19 @@ void ogs_pfcp_send_end_marker(ogs_pfcp_pdr_t *pdr) return; } - sendbuf = ogs_pkbuf_alloc(NULL, - 100 /* enough for END_MARKER; use smaller buffer */); + sendbuf = ogs_pkbuf_alloc(NULL, OGS_GTPV1U_5GC_HEADER_LEN); ogs_assert(sendbuf); - ogs_pkbuf_put(sendbuf, 100); - memset(sendbuf->data, 0, sendbuf->len); + ogs_pkbuf_reserve(sendbuf, OGS_GTPV1U_5GC_HEADER_LEN); - gtp_h = (ogs_gtp_header_t *)sendbuf->data; + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); - qer = pdr->qer; + gtp_hdesc.type = OGS_GTPU_MSGTYPE_END_MARKER; + gtp_hdesc.teid = far->outer_header_creation.teid; + if (pdr->qer && pdr->qer->qfi) + ext_hdesc.qos_flow_identifier = pdr->qer->qfi; - /* Add GTP-U header */ - if (qer && qer->qfi) { - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 1 0 0 - */ - gtp_h->flags = 0x34; - gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER; - gtp_h->teid = htobe32(far->outer_header_creation.teid); - - ext_h = (ogs_gtp_extension_header_t *)( - sendbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_DL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = qer->qfi; - ext_h->next_type = - OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } else { - /* Bits 8 7 6 5 4 3 2 1 - * +--+--+--+--+--+--+--+--+ - * |version |PT| 1| E| S|PN| - * +--+--+--+--+--+--+--+--+ - * 0 0 1 1 0 0 0 0 - */ - gtp_h->flags = 0x30; - gtp_h->type = OGS_GTPU_MSGTYPE_END_MARKER; - gtp_h->teid = htobe32(far->outer_header_creation.teid); - } - - /* Send End Marker */ - ogs_debug("SEND End Marker to Peer[%s] : TEID[0x%x]", - OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); - rv = ogs_gtp_sendto(gnode, sendbuf); - if (rv != OGS_OK) { - if (ogs_socket_errno != OGS_EAGAIN) { - ogs_error("SEND End Marker to Peer[%s] : TEID[0x%x]", - OGS_ADDR(&gnode->addr, buf), far->outer_header_creation.teid); - } - } - - ogs_pkbuf_free(sendbuf); + ogs_gtp_send_user_plane(gnode, >p_hdesc, &ext_hdesc, sendbuf); } void ogs_pfcp_send_buffered_packet(ogs_pfcp_pdr_t *pdr) diff --git a/lib/pfcp/xact.h b/lib/pfcp/xact.h index 1d14836c6..94894a508 100644 --- a/lib/pfcp/xact.h +++ b/lib/pfcp/xact.h @@ -79,6 +79,7 @@ typedef struct ogs_pfcp_xact_s { #define OGS_PFCP_MODIFY_ACTIVATE ((uint64_t)1<<8) #define OGS_PFCP_MODIFY_DEACTIVATE ((uint64_t)1<<9) #define OGS_PFCP_MODIFY_END_MARKER ((uint64_t)1<<10) +#define OGS_PFCP_MODIFY_ERROR_INDICATION ((uint64_t)1<<11) uint64_t modify_flags; #define OGS_PFCP_DELETE_TRIGGER_UE_REQUESTED 1 diff --git a/lib/sctp/meson.build b/lib/sctp/meson.build index 3e31ee029..fe62680fa 100644 --- a/lib/sctp/meson.build +++ b/lib/sctp/meson.build @@ -34,11 +34,11 @@ libsctp_sources = files(''' ogs-sctp.c '''.split()) -libsctp_dep = cc.find_library('sctp', required : false) -if libsctp_dep.found() +sctp_dep = cc.find_library('sctp', required : false) +if sctp_dep.found() libsctp_sources += files('ogs-lksctp.c') else - libsctp_dep = dependency('usrsctp', + sctp_dep = dependency('usrsctp', version: ['>=1.0.0', '<2'], fallback: ['usrsctp', 'usrsctp_dep'], default_options: [ @@ -59,10 +59,10 @@ libsctp = library('ogssctp', version : libogslib_version, c_args : '-DOGS_SCTP_COMPILATION', include_directories : [libsctp_inc, libinc], - dependencies : [libcore_dep, libsctp_dep], + dependencies : [libcore_dep, sctp_dep], install : true) libsctp_dep = declare_dependency( link_with : libsctp, include_directories : [libsctp_inc, libinc], - dependencies : [libcore_dep, libsctp_dep]) + dependencies : [libcore_dep, sctp_dep]) diff --git a/src/mme/esm-handler.c b/src/mme/esm-handler.c index 4526c099d..95696c38b 100644 --- a/src/mme/esm-handler.c +++ b/src/mme/esm-handler.c @@ -145,7 +145,10 @@ int esm_handle_information_response(mme_sess_t *sess, } else if (sess->pdn->paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { /* Nothing */ } else { - ogs_error("Unknown PDN Type %u", sess->pdn->paa.pdn_type); + ogs_error("Unknown PDN[%s] Type %u:%u", + sess->pdn->apn, + sess->pdn->pdn_type, + sess->pdn->paa.pdn_type); nas_eps_send_pdn_connectivity_reject( sess, ESM_CAUSE_UNKNOWN_PDN_TYPE); return OGS_ERROR; diff --git a/src/mme/mme-context.c b/src/mme/mme-context.c index b206c0e07..565e4551e 100644 --- a/src/mme/mme-context.c +++ b/src/mme/mme-context.c @@ -2533,8 +2533,8 @@ int mme_ue_set_imsi(mme_ue_t *mme_ue, char *imsi_bcd) ogs_warn("[%s] OLD UE Context Release", mme_ue->imsi_bcd); if (ECM_CONNECTED(old_mme_ue)) { /* Implcit S1 release */ - ogs_debug("[%s] Implicit S1 release", mme_ue->imsi_bcd); - ogs_debug("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", + ogs_info("[%s] Implicit S1 release", mme_ue->imsi_bcd); + ogs_info("[%s] ENB_UE_S1AP_ID[%d] MME_UE_S1AP_ID[%d]", old_mme_ue->imsi_bcd, old_mme_ue->enb_ue->enb_ue_s1ap_id, old_mme_ue->enb_ue->mme_ue_s1ap_id); diff --git a/src/mme/mme-context.h b/src/mme/mme-context.h index efc60ddea..120d4f1d6 100644 --- a/src/mme/mme-context.h +++ b/src/mme/mme-context.h @@ -264,6 +264,7 @@ struct enb_ue_s { #define S1AP_UE_CTX_REL_S1_REMOVE_AND_UNLINK 2 #define S1AP_UE_CTX_REL_UE_CONTEXT_REMOVE 3 #define S1AP_UE_CTX_REL_DELETE_INDIRECT_TUNNEL 4 +#define S1AP_UE_CTX_REL_S1_PAGING 5 uint8_t ue_ctx_rel_action; /* Related Context */ diff --git a/src/mme/mme-s11-handler.c b/src/mme/mme-s11-handler.c index 093f8d78e..55639fb2e 100644 --- a/src/mme/mme-s11-handler.c +++ b/src/mme/mme-s11-handler.c @@ -100,31 +100,14 @@ void mme_s11_handle_create_session_response( cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; } - /* CHECK PDN_TYPE */ - if (rsp->pdn_address_allocation.presence) { - ogs_paa_t paa; + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) + ogs_warn("Cause[%d] : No Accepted", cause_value); - memcpy(&paa, rsp->pdn_address_allocation.data, - ogs_min(sizeof(paa), rsp->pdn_address_allocation.len)); - if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4) { - /* Nothing */ - } else if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV6) { - /* Nothing */ - } else if (paa.pdn_type == OGS_GTP_PDN_TYPE_IPV4V6) { - /* Nothing */ - } else { - ogs_error("Unknown PDN Type %u", paa.pdn_type); - cause_value = OGS_GTP_CAUSE_MANDATORY_IE_INCORRECT; - } - - } else { + if (rsp->pdn_address_allocation.presence == 0) { ogs_error("No PDN Address Allocation"); cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; } - if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) - ogs_warn("Cause[%d] : No Accepted", cause_value); - if (rsp->sender_f_teid_for_control_plane.presence == 0) { ogs_error("No S11 TEID"); cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; @@ -728,7 +711,7 @@ void mme_s11_handle_downlink_data_notification( ogs_gtp_downlink_data_notification_t *noti) { int rv; - ogs_gtp_cause_t cause; + uint8_t cause_value = 0; ogs_gtp_header_t h; ogs_pkbuf_t *s11buf = NULL; @@ -737,21 +720,15 @@ void mme_s11_handle_downlink_data_notification( ogs_debug("[MME] Downlink Data Notification"); - memset(&cause, 0, sizeof(cause)); - cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; - if (!mme_ue) { ogs_warn("OGS_GTP_CAUSE_CONTEXT_NOT_FOUND"); - cause.value = OGS_GTP_CAUSE_CONTEXT_NOT_FOUND; - } - - if (cause.value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { ogs_gtp_send_error_message(xact, mme_ue ? mme_ue->sgw_s11_teid : 0, OGS_GTP_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE_TYPE, - cause.value); + OGS_GTP_CAUSE_CONTEXT_NOT_FOUND); return; } + ogs_assert(mme_ue); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", mme_ue->mme_s11_teid, mme_ue->sgw_s11_teid); @@ -768,6 +745,69 @@ void mme_s11_handle_downlink_data_notification( rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); + + if (noti->cause.presence) { + ogs_gtp_cause_t *cause = noti->cause.data; + ogs_assert(cause); + + cause_value = cause->value; + } +/* + * 5.3.4.2 in Spec 23.401 + * Under certain conditions, the current UE triggered Service Request + * procedure can cause unnecessary Downlink Packet Notification messages + * which increase the load of the MME. + * + * This can occur when uplink data sent in step 6 causes a response + * on the downlink which arrives at the Serving GW before the Modify Bearer + * Request message, step 8. This data cannot be forwarded from the Serving GW + * to the eNodeB and hence it triggers a Downlink Data Notification message. + * + * If the MME receives a Downlink Data Notification after step 2 and + * before step 9, the MME shall not send S1 interface paging messages + */ + if (ECM_IDLE(mme_ue)) { + s1ap_send_paging(mme_ue, S1AP_CNDomain_ps); + + } else if (ECM_CONNECTED(mme_ue)) { + if (cause_value == OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED) { + +/* + * TS23.007 22. Downlink Data Notification Handling at MME/S4 SGSN + * + * If the MME/S4 SGSN receives a Downlink Data Notification message from + * the SGW as a result of the SGW having received an Error Indication message + * from the eNodeB/RNC or S4-SGSN over S4 User Plane, the MME/S4 SGSN should + * perform the following: + * + * - If the UE is in IDLE state, upon receipt of the Downlink Data + * Notification message, the MME/S4 SGSN shall perform the Network + * Triggered Service Request procedure as specified in 3GPP TS 23.060[5] + * and 3GPP TS 23.401 [15]. + * - If the UE is in CONNECTED state, upon receipt of the Downlink Data + * Notification message, the MME shall perform S1 Release procedure and + * perform Network Triggered Service Request procedure as specified + * in 3GPP TS 23.401 [15]. + * - If the UE is in CONNECTED state, upon receipt of the Downlink Data + * Notification message and Direct Tunnel is used, the S4-SGSN shall + * perform Iu Release procedure and perform Network Triggered Service + * Request procedure as specified in 3GPP TS 23.060 [5] + * if the cause value included in Downlink Data Notification is + * "Error Indication received from RNC/eNodeB/S4-SGSN", + * - If the UE is in CONNECTED state, upon receipt of the Downlink Data + * Notification message and Direct Tunnel is not used, the S4-SGSN should + * re-establish all of the S4-U bearers of this UE if the cause value + * included in Downlink Data Notification is "Error Indication received + * from RNC/eNodeB/S4-SGSN" + */ + enb_ue_t *enb_ue = mme_ue->enb_ue; + ogs_assert(enb_ue); + + s1ap_send_ue_context_release_command(enb_ue, + S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, + S1AP_UE_CTX_REL_S1_PAGING, 0); + } + } } void mme_s11_handle_create_indirect_data_forwarding_tunnel_response( diff --git a/src/mme/mme-sm.c b/src/mme/mme-sm.c index 24140c64f..b3f5ec1a9 100644 --- a/src/mme/mme-sm.c +++ b/src/mme/mme-sm.c @@ -609,23 +609,6 @@ void mme_state_operational(ogs_fsm_t *s, mme_event_t *e) } mme_s11_handle_downlink_data_notification( xact, mme_ue, >p_message.downlink_data_notification); - -/* -* 5.3.4.2 in Spec 23.401 -* Under certain conditions, the current UE triggered Service Request -* procedure can cause unnecessary Downlink Packet Notification messages -* which increase the load of the MME. -* -* This can occur when uplink data sent in step 6 causes a response -* on the downlink which arrives at the Serving GW before the Modify Bearer -* Request message, step 8. This data cannot be forwarded from the Serving GW -* to the eNodeB and hence it triggers a Downlink Data Notification message. -* -* If the MME receives a Downlink Data Notification after step 2 and -* before step 9, the MME shall not send S1 interface paging messages -*/ - if (ECM_IDLE(mme_ue)) - s1ap_send_paging(mme_ue, S1AP_CNDomain_ps); break; case OGS_GTP_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE: mme_s11_handle_create_indirect_data_forwarding_tunnel_response( diff --git a/src/mme/s1ap-handler.c b/src/mme/s1ap-handler.c index 848784472..f4afe8ae4 100644 --- a/src/mme/s1ap-handler.c +++ b/src/mme/s1ap-handler.c @@ -684,18 +684,7 @@ void s1ap_handle_initial_context_setup_failure( * may in principle be adopted. The eNB should ensure * that no hanging resources remain at the eNB. */ -#if 0 - if (mme_ue && SESSION_CONTEXT_IS_AVAILABLE(mme_ue)) { - mme_gtp_send_delete_all_sessions(mme_ue); - } else { - s1ap_send_ue_context_release_command(enb_ue, - S1AP_Cause_PR_nas, S1AP_CauseNas_normal_release, - S1AP_UE_CTX_REL_S1_CONTEXT_REMOVE, 0); - } -#else - /* In Issues #568, v2.0.12 is not working. */ mme_send_release_access_bearer_or_ue_context_release(enb_ue); -#endif } void s1ap_handle_ue_context_modification_response( @@ -1166,6 +1155,14 @@ void s1ap_handle_ue_context_release_action(enb_ue_t *enb_ue) ogs_expect(rv == OGS_OK); } break; + case S1AP_UE_CTX_REL_S1_PAGING: + ogs_debug(" Action: S1 paging"); + enb_ue_remove(enb_ue); + ogs_expect_or_return(mme_ue); + mme_ue_deassociate(mme_ue); + + s1ap_send_paging(mme_ue, S1AP_CNDomain_ps); + break; default: ogs_error("Invalid Action[%d]", enb_ue->ue_ctx_rel_action); break; diff --git a/src/sgwc/context.c b/src/sgwc/context.c index 754dfde7f..158b9ea44 100644 --- a/src/sgwc/context.c +++ b/src/sgwc/context.c @@ -677,6 +677,71 @@ 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; + 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); diff --git a/src/sgwc/context.h b/src/sgwc/context.h index 96dc47578..68d13b42f 100644 --- a/src/sgwc/context.h +++ b/src/sgwc/context.h @@ -185,6 +185,9 @@ 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_tunnel_t *sgwc_tunnel_add( diff --git a/src/sgwc/gtp-path.c b/src/sgwc/gtp-path.c index a32801609..bc0711650 100644 --- a/src/sgwc/gtp-path.c +++ b/src/sgwc/gtp-path.c @@ -140,7 +140,7 @@ void sgwc_gtp_close(void) } void sgwc_gtp_send_downlink_data_notification( - sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact) + uint8_t cause_value, sgwc_bearer_t *bearer) { int rv; @@ -153,7 +153,6 @@ void sgwc_gtp_send_downlink_data_notification( ogs_gtp_header_t h; ogs_assert(bearer); - ogs_assert(pfcp_xact); sess = bearer->sess; ogs_assert(sess); @@ -169,13 +168,11 @@ void sgwc_gtp_send_downlink_data_notification( h.type = OGS_GTP_DOWNLINK_DATA_NOTIFICATION_TYPE; h.teid = sgwc_ue->mme_s11_teid; - pkbuf = sgwc_s11_build_downlink_data_notification(bearer); + pkbuf = sgwc_s11_build_downlink_data_notification(cause_value, bearer); ogs_expect_or_return(pkbuf); - gtp_xact = ogs_gtp_xact_local_create( - sgwc_ue->gnode, &h, pkbuf, NULL, sess); + gtp_xact = ogs_gtp_xact_local_create(sgwc_ue->gnode, &h, pkbuf, NULL, sess); ogs_expect_or_return(gtp_xact); - gtp_xact->pfcp_xact = pfcp_xact; rv = ogs_gtp_xact_commit(gtp_xact); ogs_expect(rv == OGS_OK); diff --git a/src/sgwc/gtp-path.h b/src/sgwc/gtp-path.h index b9c5cf483..a6b35323c 100644 --- a/src/sgwc/gtp-path.h +++ b/src/sgwc/gtp-path.h @@ -30,7 +30,7 @@ int sgwc_gtp_open(void); void sgwc_gtp_close(void); void sgwc_gtp_send_downlink_data_notification( - sgwc_bearer_t *bearer, ogs_pfcp_xact_t *pfcp_xact); + uint8_t cause_value, sgwc_bearer_t *bearer); #ifdef __cplusplus } diff --git a/src/sgwc/pfcp-path.c b/src/sgwc/pfcp-path.c index bd3df5b7b..a2ecf92e4 100644 --- a/src/sgwc/pfcp-path.c +++ b/src/sgwc/pfcp-path.c @@ -18,9 +18,6 @@ */ #include "pfcp-path.h" -#if 0 -#include "n4-build.h" -#endif static void pfcp_node_fsm_init(ogs_pfcp_node_t *node, bool try_to_assoicate) { diff --git a/src/sgwc/s11-build.c b/src/sgwc/s11-build.c index fa962fa4e..6e1001e28 100644 --- a/src/sgwc/s11-build.c +++ b/src/sgwc/s11-build.c @@ -19,10 +19,12 @@ #include "s11-build.h" -ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer) +ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification( + uint8_t cause_value, sgwc_bearer_t *bearer) { ogs_gtp_message_t message; ogs_gtp_downlink_data_notification_t *noti = NULL; + ogs_gtp_cause_t cause; ogs_gtp_arp_t arp; sgwc_sess_t *sess = NULL; @@ -34,6 +36,20 @@ ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer) noti = &message.downlink_data_notification; memset(&message, 0, sizeof(ogs_gtp_message_t)); + /* + * TS29.274 8.4 Cause Value + * + * 0 : Reserved. Shall not be sent and + * if received the Cause shall be treated as an invalid IE + */ + if (cause_value != OGS_GTP_CAUSE_INVALID_VALUE) { + memset(&cause, 0, sizeof(cause)); + cause.value = cause_value; + noti->cause.presence = 1; + noti->cause.len = sizeof(cause); + noti->cause.data = &cause; + } + noti->eps_bearer_id.presence = 1; noti->eps_bearer_id.u8 = bearer->ebi; diff --git a/src/sgwc/s11-build.h b/src/sgwc/s11-build.h index 7fc7f86ca..54c9c3f9b 100644 --- a/src/sgwc/s11-build.h +++ b/src/sgwc/s11-build.h @@ -26,7 +26,8 @@ extern "C" { #endif -ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification(sgwc_bearer_t *bearer); +ogs_pkbuf_t *sgwc_s11_build_downlink_data_notification( + uint8_t cause_value, sgwc_bearer_t *bearer); #ifdef __cplusplus } diff --git a/src/sgwc/s11-handler.c b/src/sgwc/s11-handler.c index 960bef16b..344a12f54 100644 --- a/src/sgwc/s11-handler.c +++ b/src/sgwc/s11-handler.c @@ -39,6 +39,8 @@ static void timeout(ogs_gtp_xact_t *xact, void *data) sgwc_ue->imsi_bcd, type); } +/* This code was created in case it will be used later, + * and is currently not being used. */ static uint8_t pfcp_cause_from_gtp(uint8_t gtp_cause) { switch (gtp_cause) { @@ -820,7 +822,6 @@ void sgwc_s11_handle_downlink_data_notification_ack( uint8_t cause_value; sgwc_sess_t *sess = NULL; - ogs_pfcp_xact_t *pfcp_xact; ogs_gtp_downlink_data_notification_acknowledge_t *ack = NULL; @@ -829,8 +830,6 @@ void sgwc_s11_handle_downlink_data_notification_ack( ack = &message->downlink_data_notification_acknowledge; ogs_assert(ack); - pfcp_xact = s11_xact->pfcp_xact; - ogs_assert(pfcp_xact); sess = s11_xact->data; ogs_assert(sess); sgwc_ue = sess->sgwc_ue; @@ -844,24 +843,16 @@ void sgwc_s11_handle_downlink_data_notification_ack( ogs_assert(cause); cause_value = cause->value; + if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) + ogs_warn("GTP Failed [CAUSE:%d] - PFCP_CAUSE[%d]", + cause_value, pfcp_cause_from_gtp(cause_value)); } else { ogs_error("No Cause"); - cause_value = OGS_GTP_CAUSE_MANDATORY_IE_MISSING; - } - - if (cause_value != OGS_GTP_CAUSE_REQUEST_ACCEPTED) { - ogs_pfcp_send_error_message(pfcp_xact, sess ? sess->sgwu_sxa_seid : 0, - OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, - pfcp_cause_from_gtp(cause_value), 0); - return; } ogs_debug("Downlink Data Notification Acknowledge"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); - - sgwc_pfcp_send_session_report_response( - pfcp_xact, sess, pfcp_cause_from_gtp(cause_value)); } void sgwc_s11_handle_create_indirect_data_forwarding_tunnel_request( diff --git a/src/sgwc/sxa-handler.c b/src/sgwc/sxa-handler.c index 74f65d661..da13c859f 100644 --- a/src/sgwc/sxa-handler.c +++ b/src/sgwc/sxa-handler.c @@ -244,7 +244,6 @@ void sgwc_sxa_handle_session_modification_response( ogs_assert(pfcp_xact); ogs_assert(pfcp_rsp); - ogs_assert(recv_message); flags = pfcp_xact->modify_flags; ogs_assert(flags); @@ -343,6 +342,7 @@ void sgwc_sxa_handle_session_modification_response( s5c_xact = pfcp_xact->assoc_xact; ogs_assert(s5c_xact); + ogs_assert(recv_message); gtp_req = &recv_message->create_bearer_request; ogs_assert(gtp_req); @@ -384,6 +384,7 @@ void sgwc_sxa_handle_session_modification_response( s5c_xact = pfcp_xact->assoc_xact; ogs_assert(s5c_xact); + ogs_assert(recv_message); gtp_rsp = &recv_message->create_bearer_response; ogs_assert(gtp_rsp); @@ -451,6 +452,7 @@ void sgwc_sxa_handle_session_modification_response( ogs_gtp_f_teid_t rsp_dl_teid[OGS_GTP_MAX_INDIRECT_TUNNEL]; ogs_gtp_f_teid_t rsp_ul_teid[OGS_GTP_MAX_INDIRECT_TUNNEL]; + ogs_assert(recv_message); gtp_req = &recv_message-> create_indirect_data_forwarding_tunnel_request; ogs_assert(gtp_req); @@ -616,6 +618,7 @@ void sgwc_sxa_handle_session_modification_response( s5c_xact = pfcp_xact->assoc_xact; ogs_assert(s5c_xact); + ogs_assert(recv_message); recv_message->h.type = OGS_GTP_DELETE_BEARER_RESPONSE_TYPE; recv_message->h.teid = sess->pgw_s5c_teid; @@ -640,6 +643,7 @@ void sgwc_sxa_handle_session_modification_response( ogs_gtp_f_teid_t sgw_s11_teid; ogs_gtp_f_teid_t sgw_s1u_teid; + ogs_assert(recv_message); gtp_rsp = &recv_message->create_session_response; ogs_assert(gtp_rsp); @@ -684,6 +688,7 @@ void sgwc_sxa_handle_session_modification_response( ogs_gtp_modify_bearer_request_t *gtp_req = NULL; ogs_gtp_modify_bearer_response_t *gtp_rsp = NULL; + ogs_assert(recv_message); gtp_req = &recv_message->modify_bearer_request; ogs_assert(gtp_req); @@ -745,9 +750,6 @@ void sgwc_sxa_handle_session_modification_response( } else if (flags & OGS_PFCP_MODIFY_DEACTIVATE) { bool release_access_bearers_is_done; - s11_xact = pfcp_xact->assoc_xact; - ogs_assert(s11_xact); - sess->state.release_access_bearers = true; release_access_bearers_is_done = true; @@ -757,31 +759,45 @@ void sgwc_sxa_handle_session_modification_response( } if (release_access_bearers_is_done == true) { - ogs_gtp_release_access_bearers_response_t *gtp_rsp = NULL; + if (flags & OGS_PFCP_MODIFY_ERROR_INDICATION) { + /* It's faked method for receiving `bearer` context */ + bearer = pfcp_xact->assoc_xact; + ogs_assert(bearer); - gtp_rsp = &send_message.release_access_bearers_response; - ogs_assert(gtp_rsp); + sgwc_gtp_send_downlink_data_notification( + OGS_GTP_CAUSE_ERROR_INDICATION_RECEIVED, bearer); - memset(&send_message, 0, sizeof(ogs_gtp_message_t)); + } else { + ogs_gtp_release_access_bearers_response_t *gtp_rsp = NULL; - memset(&cause, 0, sizeof(cause)); - cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; + s11_xact = pfcp_xact->assoc_xact; + ogs_assert(s11_xact); - gtp_rsp->cause.presence = 1; - gtp_rsp->cause.data = &cause; - gtp_rsp->cause.len = sizeof(cause); + gtp_rsp = &send_message.release_access_bearers_response; + ogs_assert(gtp_rsp); - send_message.h.type = OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE; - send_message.h.teid = sgwc_ue->mme_s11_teid; + memset(&send_message, 0, sizeof(ogs_gtp_message_t)); - pkbuf = ogs_gtp_build_msg(&send_message); - ogs_expect_or_return(pkbuf); + memset(&cause, 0, sizeof(cause)); + cause.value = OGS_GTP_CAUSE_REQUEST_ACCEPTED; - rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); - ogs_expect_or_return(rv == OGS_OK); + gtp_rsp->cause.presence = 1; + gtp_rsp->cause.data = &cause; + gtp_rsp->cause.len = sizeof(cause); - rv = ogs_gtp_xact_commit(s11_xact); - ogs_expect(rv == OGS_OK); + send_message.h.type = + OGS_GTP_RELEASE_ACCESS_BEARERS_RESPONSE_TYPE; + send_message.h.teid = sgwc_ue->mme_s11_teid; + + pkbuf = ogs_gtp_build_msg(&send_message); + ogs_expect_or_return(pkbuf); + + rv = ogs_gtp_xact_update_tx(s11_xact, &send_message.h, pkbuf); + ogs_expect_or_return(rv == OGS_OK); + + rv = ogs_gtp_xact_commit(s11_xact); + ogs_expect(rv == OGS_OK); + } } } else { ogs_fatal("Invalid modify_flags[0x%llx]", (long long)flags); @@ -894,31 +910,65 @@ void sgwc_sxa_handle_session_report_request( sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); - if (sgwc_ue->gnode) { - report_type.value = pfcp_req->report_type.u8; - if (report_type.downlink_data_report) { - if (pfcp_req->downlink_data_report.presence && - pfcp_req->downlink_data_report.pdr_id.presence) { + if (!sgwc_ue->gnode) { + ogs_error("No SGWC-UE GTP Node"); + ogs_pfcp_send_error_message(pfcp_xact, sess ? sess->sgwu_sxa_seid : 0, + OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, + OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); + return; + } - pdr_id = pfcp_req->downlink_data_report.pdr_id.u16; + sgwc_pfcp_send_session_report_response( + pfcp_xact, sess, OGS_PFCP_CAUSE_REQUEST_ACCEPTED); - ogs_list_for_each(&sess->bearer_list, bearer) { - ogs_list_for_each(&bearer->tunnel_list, tunnel) { - ogs_assert(tunnel->pdr); - if (tunnel->pdr->id == pdr_id) { - sgwc_gtp_send_downlink_data_notification( - bearer, pfcp_xact); - return; - } - } + report_type.value = pfcp_req->report_type.u8; + + if (report_type.downlink_data_report) { + if (pfcp_req->downlink_data_report.presence == 0) { + ogs_error("No Downlink Data Report"); + return; + } + + if (pfcp_req->downlink_data_report.pdr_id.presence == 0) { + ogs_error("No PDR-ID"); + return; + } + + pdr_id = pfcp_req->downlink_data_report.pdr_id.u16; + + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + ogs_assert(tunnel->pdr); + if (tunnel->pdr->id == pdr_id) { + sgwc_gtp_send_downlink_data_notification( + OGS_GTP_CAUSE_INVALID_VALUE, bearer); + return; } } } + + 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) { + + sess->state.release_access_bearers = false; + + sgwc_pfcp_send_sess_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 { + ogs_error("Not supported Report Type[%d]", report_type.value); } - - ogs_error("No Bearer"); - ogs_pfcp_send_error_message(pfcp_xact, 0, - OGS_PFCP_SESSION_REPORT_RESPONSE_TYPE, - OGS_PFCP_CAUSE_SESSION_CONTEXT_NOT_FOUND, 0); - } diff --git a/src/sgwu/gtp-path.c b/src/sgwu/gtp-path.c index 584dcad9c..634651c7d 100644 --- a/src/sgwu/gtp-path.c +++ b/src/sgwu/gtp-path.c @@ -30,16 +30,16 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) ssize_t size; char buf[OGS_ADDRSTRLEN]; + sgwu_sess_t *sess = NULL; + ogs_pkbuf_t *pkbuf = NULL; ogs_sockaddr_t from; ogs_gtp_header_t *gtp_h = NULL; - struct ip *ip_h = NULL; + ogs_pfcp_user_plane_report_t report; uint32_t teid; uint8_t qfi; - ogs_pfcp_pdr_t *pdr = NULL; - ogs_pfcp_user_plane_report_t report; ogs_assert(fd != INVALID_SOCKET); @@ -89,25 +89,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); - if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) { - ogs_debug("[RECV] End Marker from [%s] : TEID[0x%x]", - OGS_ADDR(&from, buf), teid); - goto cleanup; - } - - if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { - ogs_warn("[RECV] Error Indication from [%s]", OGS_ADDR(&from, buf)); - goto cleanup; - } - - if (gtp_h->type != OGS_GTPU_MSGTYPE_GPDU) { - ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); - ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); - goto cleanup; - } - - ogs_debug("[RECV] GPU-U from [%s] : TEID[0x%x]", - OGS_ADDR(&from, buf), teid); + ogs_debug("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", + gtp_h->type, OGS_ADDR(&from, buf), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -142,31 +125,53 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } ogs_assert(ogs_pkbuf_pull(pkbuf, len)); - ip_h = (struct ip *)pkbuf->data; - ogs_assert(ip_h); + if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) { + /* Nothing */ - pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi); - if (!pdr) { -#if 0 /* It's redundant log message */ - ogs_warn("[DROP] Cannot find PDR : TEID[0x%x] QFI[%d]", - teid, qfi); -#endif - goto cleanup; - } + } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { + ogs_pfcp_far_t *far = NULL; - ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report); + far = ogs_pfcp_far_find_by_error_indication(pkbuf); + if (far) { + ogs_pfcp_up_handle_error_indication(far, &report); - if (report.type.downlink_data_report) { - sgwu_sess_t *sess = NULL; + if (report.type.error_indication_report) { + ogs_assert(far->sess); + sess = SGWU_SESS(far->sess); + ogs_assert(sess); - ogs_assert(pdr->sess); - sess = SGWU_SESS(pdr->sess); - ogs_assert(sess); + sgwu_pfcp_send_session_report_request(sess, &report); + } - report.downlink_data.pdr_id = pdr->id; - report.downlink_data.qfi = qfi; /* for 5GC */ + } else { + ogs_error("[DROP] Cannot decode Error-Indication"); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + } + } else if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU) { + struct ip *ip_h = NULL; + ogs_pfcp_pdr_t *pdr = NULL; - sgwu_pfcp_send_session_report_request(sess, &report); + ip_h = (struct ip *)pkbuf->data; + ogs_assert(ip_h); + + pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi); + if (pdr) { + ogs_pfcp_up_handle_pdr(pdr, pkbuf, &report); + + if (report.type.downlink_data_report) { + ogs_assert(pdr->sess); + sess = SGWU_SESS(pdr->sess); + ogs_assert(sess); + + report.downlink_data.pdr_id = pdr->id; + report.downlink_data.qfi = qfi; /* for 5GC */ + + sgwu_pfcp_send_session_report_request(sess, &report); + } + } + } else { + ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); } cleanup: diff --git a/src/sgwu/sxa-handler.c b/src/sgwu/sxa-handler.c index f2ca43114..b8784c7ca 100644 --- a/src/sgwu/sxa-handler.c +++ b/src/sgwu/sxa-handler.c @@ -47,7 +47,10 @@ static void setup_gtp_node(ogs_pfcp_far_t *far) sgwu_self()->gtpu_sock, sgwu_self()->gtpu_sock6, gnode); ogs_assert(rv == OGS_OK); } + OGS_SETUP_GTP_NODE(far, gnode); + + ogs_pfcp_far_hash_set(far); } void sgwu_sxa_handle_session_establishment_request( diff --git a/src/upf/gtp-path.c b/src/upf/gtp-path.c index eda0b3639..9f70705f7 100644 --- a/src/upf/gtp-path.c +++ b/src/upf/gtp-path.c @@ -85,7 +85,7 @@ static void _gtpv1_tun_recv_cb(short when, ogs_socket_t fd, void *data) static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) { - int rv, len; + int len; ssize_t size; char buf[OGS_ADDRSTRLEN]; @@ -93,14 +93,12 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) ogs_sockaddr_t from; ogs_gtp_header_t *gtp_h = NULL; - struct ip *ip_h = NULL; +#if 0 + ogs_pfcp_user_plane_report_t report; +#endif uint32_t teid; uint8_t qfi; - ogs_pfcp_pdr_t *pdr = NULL; - upf_sess_t *sess = NULL; - ogs_pfcp_subnet_t *subnet = NULL; - ogs_pfcp_dev_t *dev = NULL; ogs_assert(fd != INVALID_SOCKET); @@ -150,25 +148,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); - if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) { - ogs_debug("[RECV] End Marker from [%s] : TEID[0x%x]", - OGS_ADDR(&from, buf), teid); - goto cleanup; - } - - if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { - ogs_warn("[RECV] Error Indication from [%s]", OGS_ADDR(&from, buf)); - goto cleanup; - } - - if (gtp_h->type != OGS_GTPU_MSGTYPE_GPDU) { - ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); - ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); - goto cleanup; - } - - ogs_debug("[RECV] GPU-U from [%s] : TEID[0x%x]", - OGS_ADDR(&from, buf), teid); + ogs_debug("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", + gtp_h->type, OGS_ADDR(&from, buf), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -203,48 +184,65 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } ogs_assert(ogs_pkbuf_pull(pkbuf, len)); - ip_h = (struct ip *)pkbuf->data; - ogs_assert(ip_h); + if (gtp_h->type == OGS_GTPU_MSGTYPE_END_MARKER) { + /* Nothing */ - pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi); - if (!pdr) { -#if 0 /* It's redundant log message */ - ogs_warn("[DROP] Cannot find PDR : UPF-N3-TEID[0x%x] QFI[%d]", - teid, qfi); -#endif - goto cleanup; - } - ogs_assert(pdr->sess); - sess = UPF_SESS(pdr->sess); - ogs_assert(sess); + } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { + /* TODO */ - if (ip_h->ip_v == 4 && sess->ipv4) - subnet = sess->ipv4->subnet; - else if (ip_h->ip_v == 6 && sess->ipv6) - subnet = sess->ipv6->subnet; + } else if (gtp_h->type == OGS_GTPU_MSGTYPE_GPDU) { + int rv; - if (!subnet) { -#if 0 /* It's redundant log message */ - ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p", - ip_h->ip_v, sess->ipv4, sess->ipv6); - ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); -#endif - goto cleanup; - } + struct ip *ip_h = NULL; + ogs_pfcp_pdr_t *pdr = NULL; - /* Check IPv6 */ - if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) { - rv = upf_gtp_handle_slaac(sess, pkbuf); - if (rv == UPF_GTP_HANDLED) { + upf_sess_t *sess = NULL; + ogs_pfcp_subnet_t *subnet = NULL; + ogs_pfcp_dev_t *dev = NULL; + + ip_h = (struct ip *)pkbuf->data; + ogs_assert(ip_h); + + pdr = ogs_pfcp_pdr_find_by_teid_and_qfi(teid, qfi); + if (!pdr) { + /* TODO : Send Error Indication */ goto cleanup; } - ogs_assert(rv == OGS_OK); - } + ogs_assert(pdr->sess); + sess = UPF_SESS(pdr->sess); + ogs_assert(sess); - dev = subnet->dev; - ogs_assert(dev); - if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0) - ogs_error("ogs_write() failed"); + if (ip_h->ip_v == 4 && sess->ipv4) + subnet = sess->ipv4->subnet; + else if (ip_h->ip_v == 6 && sess->ipv6) + subnet = sess->ipv6->subnet; + + if (!subnet) { +#if 0 /* It's redundant log message */ + ogs_error("[DROP] Cannot find subnet V:%d, IPv4:%p, IPv6:%p", + ip_h->ip_v, sess->ipv4, sess->ipv6); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); +#endif + goto cleanup; + } + + /* Check IPv6 */ + if (ogs_app()->parameter.no_slaac == 0 && ip_h->ip_v == 6) { + rv = upf_gtp_handle_slaac(sess, pkbuf); + if (rv == UPF_GTP_HANDLED) { + goto cleanup; + } + ogs_assert(rv == OGS_OK); + } + + dev = subnet->dev; + ogs_assert(dev); + if (ogs_write(dev->fd, pkbuf->data, pkbuf->len) <= 0) + ogs_error("ogs_write() failed"); + } else { + ogs_error("[DROP] Invalid GTPU Type [%d]", gtp_h->type); + ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); + } cleanup: ogs_pkbuf_free(pkbuf); diff --git a/src/upf/n4-handler.c b/src/upf/n4-handler.c index e5c3a77df..e8a82a9e6 100644 --- a/src/upf/n4-handler.c +++ b/src/upf/n4-handler.c @@ -48,7 +48,10 @@ static void setup_gtp_node(ogs_pfcp_far_t *far) upf_self()->gtpu_sock, upf_self()->gtpu_sock6, gnode); ogs_assert(rv == OGS_OK); } + OGS_SETUP_GTP_NODE(far, gnode); + + ogs_pfcp_far_hash_set(far); } void upf_n4_handle_session_establishment_request( diff --git a/tests/app/5gc-init.c b/tests/app/5gc-init.c index 42a07c88d..d4b1febc7 100644 --- a/tests/app/5gc-init.c +++ b/tests/app/5gc-init.c @@ -95,6 +95,7 @@ void test_5gc_init(void) ogs_log_install_domain(&__ogs_ngap_domain, "ngap", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR); ogs_sctp_init(ogs_app()->usrsctp.udp_port); ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK); diff --git a/tests/app/app-init.c b/tests/app/app-init.c index 419820559..489dc1f7c 100644 --- a/tests/app/app-init.c +++ b/tests/app/app-init.c @@ -119,6 +119,7 @@ void test_app_init(void) ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR); ogs_sctp_init(ogs_app()->usrsctp.udp_port); ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK); diff --git a/tests/app/epc-init.c b/tests/app/epc-init.c index 2d118f60c..e05eed04a 100644 --- a/tests/app/epc-init.c +++ b/tests/app/epc-init.c @@ -100,6 +100,7 @@ void test_epc_init(void) ogs_log_install_domain(&__ogs_diam_domain, "diam", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_dbi_domain, "dbi", OGS_LOG_ERROR); ogs_log_install_domain(&__ogs_nas_domain, "nas", OGS_LOG_ERROR); + ogs_log_install_domain(&__ogs_gtp_domain, "gtp", OGS_LOG_ERROR); ogs_sctp_init(ogs_app()->usrsctp.udp_port); ogs_assert(ogs_dbi_init(ogs_app()->db_uri) == OGS_OK); diff --git a/tests/attach/idle-test.c b/tests/attach/idle-test.c index 35c7d75b4..a0dabb6db 100644 --- a/tests/attach/idle-test.c +++ b/tests/attach/idle-test.c @@ -798,12 +798,458 @@ static void test2_func(abts_case *tc, void *data) test_ue_remove(test_ue); } +static void test3_func(abts_case *tc, void *data) +{ + int rv; + ogs_socknode_t *s1ap; + ogs_socknode_t *gtpu; + ogs_pkbuf_t *emmbuf; + ogs_pkbuf_t *esmbuf; + ogs_pkbuf_t *sendbuf; + ogs_pkbuf_t *recvbuf; + ogs_s1ap_message_t message; + + ogs_nas_5gs_mobile_identity_suci_t mobile_identity_suci; + test_ue_t *test_ue = NULL; + test_sess_t *sess = NULL; + test_bearer_t *bearer = NULL; + + uint32_t enb_ue_s1ap_id; + uint64_t mme_ue_s1ap_id; + + const char *_k_string = "465b5ce8b199b49faa5f0a2ee238a6bc"; + uint8_t k[OGS_KEY_LEN]; + const char *_opc_string = "e8ed289deba952e4283b54e88e6183ca"; + uint8_t opc[OGS_KEY_LEN]; + + mongoc_collection_t *collection = NULL; + bson_t *doc = NULL; + int64_t count = 0; + bson_error_t error; + const char *json = + "{" + "\"_id\" : { \"$oid\" : \"310014158b8861d7605378c6\" }, " + "\"imsi\" : \"901707364000060\", " + "\"pdn\" : [" + "{" + "\"apn\" : \"internet\", " + "\"_id\" : { \"$oid\" : \"310014158b8861d7605378c7\" }, " + "\"ambr\" : {" + "\"uplink\" : { \"$numberLong\" : \"1000000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1000000\" } " + "}," + "\"qos\" : { " + "\"qci\" : 9, " + "\"arp\" : { " + "\"priority_level\" : 15," + "\"pre_emption_vulnerability\" : 1, " + "\"pre_emption_capability\" : 1" + "} " + "}, " + "\"type\" : 2" + "}" + "]," + "\"ambr\" : { " + "\"uplink\" : { \"$numberLong\" : \"1000000\" }, " + "\"downlink\" : { \"$numberLong\" : \"1000000\" } " + "}," + "\"subscribed_rau_tau_timer\" : 12," + "\"network_access_mode\" : 2, " + "\"subscriber_status\" : 0, " + "\"access_restriction_data\" : 32, " + "\"security\" : { " + "\"k\" : \"465B5CE8 B199B49F AA5F0A2E E238A6BC\", " + "\"opc\" : \"E8ED289D EBA952E4 283B54E8 8E6183CA\", " + "\"amf\" : \"8000\", " + "\"sqn\" : { \"$numberLong\" : \"64\" } " + "}, " + "\"__v\" : 0 " + "}"; + + /* Setup Test UE & Session Context */ + memset(&mobile_identity_suci, 0, sizeof(mobile_identity_suci)); + + mobile_identity_suci.h.supi_format = OGS_NAS_5GS_SUPI_FORMAT_IMSI; + mobile_identity_suci.h.type = OGS_NAS_5GS_MOBILE_IDENTITY_SUCI; + mobile_identity_suci.routing_indicator1 = 0; + mobile_identity_suci.routing_indicator2 = 0xf; + mobile_identity_suci.routing_indicator3 = 0xf; + mobile_identity_suci.routing_indicator4 = 0xf; + mobile_identity_suci.protection_scheme_id = OGS_NAS_5GS_NULL_SCHEME; + mobile_identity_suci.home_network_pki_value = 0; + mobile_identity_suci.scheme_output[0] = 0x37; + mobile_identity_suci.scheme_output[1] = 0x46; + mobile_identity_suci.scheme_output[2] = 0; + mobile_identity_suci.scheme_output[3] = 0; + mobile_identity_suci.scheme_output[4] = 0x06; + + test_ue = test_ue_add_by_suci(&mobile_identity_suci, 13); + ogs_assert(test_ue); + + test_ue->e_cgi.cell_id = 0x54f6401; + test_ue->nas.ksi = OGS_NAS_KSI_NO_KEY_IS_AVAILABLE; + test_ue->nas.value = OGS_NAS_ATTACH_TYPE_EPS_ATTACH; + + OGS_HEX(_k_string, strlen(_k_string), test_ue->k); + OGS_HEX(_opc_string, strlen(_opc_string), test_ue->opc); + + sess = test_sess_add_by_apn(test_ue, "internet"); + ogs_assert(sess); + + /* eNB connects to MME */ + s1ap = tests1ap_client(AF_INET); + ABTS_PTR_NOTNULL(tc, s1ap); + + /* eNB connects to SGW */ + gtpu = test_gtpu_server(1, AF_INET); + ABTS_PTR_NOTNULL(tc, gtpu); + + /* Send S1-Setup Reqeust */ + sendbuf = test_s1ap_build_s1_setup_request( + S1AP_ENB_ID_PR_macroENB_ID, 0x54f64); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Setup Response */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(NULL, recvbuf); + + /********** Insert Subscriber in Database */ + collection = mongoc_client_get_collection( + ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); + ABTS_PTR_NOTNULL(tc, collection); + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + if (count) { + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + } + bson_destroy(doc); + + doc = bson_new_from_json((const uint8_t *)json, -1, &error);; + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_insert(collection, + MONGOC_INSERT_NONE, doc, NULL, &error)); + bson_destroy(doc); + + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + do { + count = mongoc_collection_count ( + collection, MONGOC_QUERY_NONE, doc, 0, 0, NULL, &error); + } while (count == 0); + bson_destroy(doc); + + collection = mongoc_client_get_collection( + ogs_mongoc()->client, ogs_mongoc()->name, "subscribers"); + ABTS_PTR_NOTNULL(tc, collection); + + /* Send Attach Request */ + memset(&sess->pdn_connectivity_param, + 0, sizeof(sess->pdn_connectivity_param)); + sess->pdn_connectivity_param.eit = 1; + sess->pdn_connectivity_param.pco = 1; + esmbuf = testesm_build_pdn_connectivity_request(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + + memset(&test_ue->attach_request_param, + 0, sizeof(test_ue->attach_request_param)); + test_ue->attach_request_param.ms_network_feature_support = 1; + emmbuf = testemm_build_attach_request(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, emmbuf); + + memset(&test_ue->initial_ue_param, 0, sizeof(test_ue->initial_ue_param)); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Signalling, false); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Authentication Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Authentication response */ + emmbuf = testemm_build_authentication_response(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Security mode Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Security mode complete */ + test_ue->mobile_identity_imeisv_presence = true; + emmbuf = testemm_build_security_mode_complete(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive ESM Information Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send ESM Information Response */ + esmbuf = testesm_build_esm_information_response(sess); + ABTS_PTR_NOTNULL(tc, esmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request + + * Attach Accept + + * Activate Default Bearer Context Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Capability Info Indication */ + sendbuf = tests1ap_build_ue_radio_capability_info_indication(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Attach Complete + Activate default EPS bearer cotext accept */ + test_ue->nr_cgi.cell_id = 0x1234502; + bearer = test_bearer_find_by_ue_ebi(test_ue, 5); + ogs_assert(bearer); + esmbuf = testesm_build_activate_default_eps_bearer_context_accept( + bearer, false); + ABTS_PTR_NOTNULL(tc, esmbuf); + emmbuf = testemm_build_attach_complete(test_ue, esmbuf); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_uplink_nas_transport(test_ue, emmbuf); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive EMM information */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Request */ + sendbuf = test_s1ap_build_ue_context_release_request(test_ue, + S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UE Context Release Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Paging */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Service Request */ + emmbuf = testemm_build_service_request(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send Error Indication */ + rv = test_gtpu_send_error_indication(gtpu, bearer); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UE Context Release Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Paging */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Service Request */ + emmbuf = testemm_build_service_request(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send UE Context Release Request */ + sendbuf = test_s1ap_build_ue_context_release_request(test_ue, + S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UE Context Release Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send Error Indication */ + rv = test_gtpu_send_error_indication(gtpu, bearer); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive S1-Paging */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Service Request */ + emmbuf = testemm_build_service_request(test_ue); + ABTS_PTR_NOTNULL(tc, emmbuf); + sendbuf = test_s1ap_build_initial_ue_message( + test_ue, emmbuf, S1AP_RRC_Establishment_Cause_mo_Data, true); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive Initial Context Setup Request */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send Initial Context Setup Response */ + sendbuf = test_s1ap_build_initial_context_setup_response(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Send GTP-U ICMP Packet */ + rv = test_gtpu_send_ping(gtpu, bearer, TEST_PING_IPV4); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive GTP-U ICMP Packet */ + recvbuf = test_gtpu_read(gtpu); + ABTS_PTR_NOTNULL(tc, recvbuf); + ogs_pkbuf_free(recvbuf); + + /* Send UE Context Release Request */ + sendbuf = test_s1ap_build_ue_context_release_request(test_ue, + S1AP_Cause_PR_radioNetwork, S1AP_CauseRadioNetwork_user_inactivity); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + /* Receive UE Context Release Command */ + recvbuf = testenb_s1ap_read(s1ap); + ABTS_PTR_NOTNULL(tc, recvbuf); + tests1ap_recv(test_ue, recvbuf); + + /* Send UE Context Release Complete */ + sendbuf = test_s1ap_build_ue_context_release_complete(test_ue); + ABTS_PTR_NOTNULL(tc, sendbuf); + rv = testenb_s1ap_send(s1ap, sendbuf); + ABTS_INT_EQUAL(tc, OGS_OK, rv); + + ogs_msleep(300); + + /********** Remove Subscriber in Database */ + doc = BCON_NEW("imsi", BCON_UTF8(test_ue->imsi)); + ABTS_PTR_NOTNULL(tc, doc); + ABTS_TRUE(tc, mongoc_collection_remove(collection, + MONGOC_REMOVE_SINGLE_REMOVE, doc, NULL, &error)) + bson_destroy(doc); + + mongoc_collection_destroy(collection); + + /* eNB disonncect from MME */ + testenb_s1ap_close(s1ap); + + /* eNB disonncect from SGW */ + test_gtpu_close(gtpu); + + test_ue_remove(test_ue); +} + abts_suite *test_idle(abts_suite *suite) { suite = ADD_SUITE(suite) abts_run_test(suite, test1_func, NULL); abts_run_test(suite, test2_func, NULL); + abts_run_test(suite, test3_func, NULL); return suite; } diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c index adc78b916..6fdd806df 100644 --- a/tests/common/gtpu.c +++ b/tests/common/gtpu.c @@ -97,18 +97,62 @@ void test_gtpu_close(ogs_socknode_t *node) #include #endif +int test_gtpu_send( + ogs_socknode_t *node, test_bearer_t *bearer, + ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf) +{ + ogs_gtp_node_t gnode; + test_sess_t *sess = NULL; + + ogs_assert(gtp_hdesc); + ogs_assert(ext_hdesc); + ogs_assert(pkbuf); + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + memset(&gnode, 0, sizeof(ogs_gtp_node_t)); + + gnode.addr.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT); + gnode.sock = node->sock; + + if (bearer->qfi) { + if (sess->upf_n3_ip.ipv4) { + gnode.addr.ogs_sa_family = AF_INET; + gnode.addr.sin.sin_addr.s_addr = sess->upf_n3_ip.addr; + } else { + ogs_fatal("Not implemented"); + ogs_assert_if_reached(); + } + + } else if (bearer->ebi) { + if (bearer->sgw_s1u_ip.ipv4) { + gnode.addr.ogs_sa_family = AF_INET; + gnode.addr.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr; + } else { + ogs_fatal("Not implemented"); + ogs_assert_if_reached(); + } + } else { + ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); + ogs_assert_if_reached(); + } + + return ogs_gtp_send_user_plane(&gnode, gtp_hdesc, ext_hdesc, pkbuf); +} + int test_gtpu_send_ping( ogs_socknode_t *node, test_bearer_t *bearer, const char *dst_ip) { int rv; - ssize_t sent; - test_sess_t *sess = NULL; - ogs_sockaddr_t upf; + + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; ogs_pkbuf_t *pkbuf = NULL; - ogs_gtp_header_t *gtp_h = NULL; - ogs_gtp_extension_header_t *ext_h = NULL; ogs_ipsubnet_t dst_ipsub; ogs_assert(bearer); @@ -119,79 +163,21 @@ int test_gtpu_send_ping( rv = ogs_ipsubnet(&dst_ipsub, dst_ip, NULL); ogs_assert(rv == OGS_OK); - memset(&upf, 0, sizeof(ogs_sockaddr_t)); - upf.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT); - pkbuf = ogs_pkbuf_alloc( NULL, 200 /* enough for ICMP; use smaller buffer */); ogs_assert(pkbuf); - ogs_pkbuf_put(pkbuf, 200); + ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + ogs_pkbuf_put(pkbuf, 200-OGS_GTPV1U_5GC_HEADER_LEN); memset(pkbuf->data, 0, pkbuf->len); - gtp_h = (ogs_gtp_header_t *)pkbuf->data; - if (bearer->qfi) { - /* 5G Core */ - gtp_h->flags = 0x34; - gtp_h->teid = htobe32(sess->upf_n3_teid); - - if (sess->upf_n3_ip.ipv4) { - upf.ogs_sa_family = AF_INET; - upf.sin.sin_addr.s_addr = sess->upf_n3_ip.addr; - } else { - ogs_fatal("Not implemented"); - ogs_assert_if_reached(); - } - - } else if (bearer->ebi) { - /* EPC */ - gtp_h->flags = 0x30; - gtp_h->teid = htobe32(bearer->sgw_s1u_teid); - - if (bearer->sgw_s1u_ip.ipv4) { - upf.ogs_sa_family = AF_INET; - upf.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr; - } else { - ogs_fatal("Not implemented"); - ogs_assert_if_reached(); - } - } else { - ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); - ogs_assert_if_reached(); - } - - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; - - if (bearer->qfi) { - ext_h = (ogs_gtp_extension_header_t *) - (pkbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = bearer->qfi; - ext_h->next_type = - OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } - if (dst_ipsub.family == AF_INET) { struct ip *ip_h = NULL; struct icmp *icmp_h = NULL; - if (bearer->qfi) { - gtp_h->length = htobe16( - sizeof *ip_h + ICMP_MINLEN + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); + ogs_pkbuf_trim(pkbuf, sizeof *ip_h + ICMP_MINLEN); - ip_h = (struct ip *)(pkbuf->data + - OGS_GTPV1U_HEADER_LEN + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); - icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); - } else { - gtp_h->length = htobe16(sizeof *ip_h + ICMP_MINLEN); - - ip_h = (struct ip *)(pkbuf->data + OGS_GTPV1U_HEADER_LEN); - icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); - } + ip_h = (struct ip *)pkbuf->data; + icmp_h = (struct icmp *)((uint8_t *)ip_h + sizeof *ip_h); ip_h->ip_v = 4; ip_h->ip_hl = 5; @@ -217,15 +203,9 @@ int test_gtpu_send_ping( uint8_t nxt = 0; uint8_t *p = NULL; - if (bearer->qfi) { - gtp_h->length = htobe16(sizeof *ip6_h + sizeof *icmp6_h) + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4; - p = (uint8_t *)pkbuf->data + OGS_GTPV1U_HEADER_LEN + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4; - } else { - gtp_h->length = htobe16(sizeof *ip6_h + sizeof *icmp6_h); - p = (uint8_t *)pkbuf->data + OGS_GTPV1U_HEADER_LEN; - } + ogs_pkbuf_trim(pkbuf, sizeof *ip6_h + sizeof *icmp6_h); + + p = (uint8_t *)pkbuf->data; plen = htobe16(sizeof *icmp6_h); nxt = IPPROTO_ICMPV6; @@ -246,7 +226,7 @@ int test_gtpu_send_ping( icmp6_h->icmp6_cksum = ogs_in_cksum( (uint16_t *)ip6_h, sizeof *ip6_h + sizeof *icmp6_h); - ip6_h->ip6_flow = htonl(0x60000001); + ip6_h->ip6_flow = htobe32(0x60000001); ip6_h->ip6_plen = plen; ip6_h->ip6_nxt = nxt;; ip6_h->ip6_hlim = 0xff; @@ -258,117 +238,117 @@ int test_gtpu_send_ping( ogs_assert_if_reached(); } - ogs_assert(node); - ogs_assert(node->sock); + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); - sent = ogs_sendto(node->sock->fd, pkbuf->data, pkbuf->len, 0, &upf); - ogs_pkbuf_free(pkbuf); - if (sent < 0 || sent != pkbuf->len) - return OGS_ERROR; + gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU; - return OGS_OK; -} - -int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer) -{ - int rv; - ssize_t sent; - - test_sess_t *sess = NULL; - ogs_sockaddr_t upf; - - ogs_pkbuf_t *pkbuf = NULL; - ogs_gtp_header_t *gtp_h = NULL; - ogs_gtp_extension_header_t *ext_h = NULL; - - unsigned char *ip_h = NULL; - - const char *payload = - "6000000000083aff fe80000000000000 0000000000000002" - "ff02000000000000 0000000000000002 85007d3500000000"; - unsigned char tmp[OGS_MAX_SDU_LEN]; - - ogs_assert(bearer); - sess = bearer->sess; - ogs_assert(sess); - - memset(&upf, 0, sizeof(ogs_sockaddr_t)); - upf.ogs_sin_port = htobe16(OGS_GTPV1_U_UDP_PORT); - - pkbuf = ogs_pkbuf_alloc( - NULL, 200 /* enough for ICMP; use smaller buffer */); - ogs_assert(pkbuf); - ogs_pkbuf_put(pkbuf, 200); - memset(pkbuf->data, 0, pkbuf->len); - - gtp_h = (ogs_gtp_header_t *)pkbuf->data; if (bearer->qfi) { - /* 5G Core */ - gtp_h->flags = 0x36; - gtp_h->teid = htobe32(sess->upf_n3_teid); - - if (sess->upf_n3_ip.ipv4) { - upf.ogs_sa_family = AF_INET; - upf.sin.sin_addr.s_addr = sess->upf_n3_ip.addr; - } else { - ogs_fatal("Not implemented"); - ogs_assert_if_reached(); - } + gtp_hdesc.teid = sess->upf_n3_teid; + ext_hdesc.qos_flow_identifier = bearer->qfi; } else if (bearer->ebi) { - /* EPC */ - gtp_h->flags = 0x32; - gtp_h->teid = htobe32(bearer->sgw_s1u_teid); + gtp_hdesc.teid = bearer->sgw_s1u_teid; - if (bearer->sgw_s1u_ip.ipv4) { - upf.ogs_sa_family = AF_INET; - upf.sin.sin_addr.s_addr = bearer->sgw_s1u_ip.addr; - } else { - ogs_fatal("Not implemented"); - ogs_assert_if_reached(); - } } else { ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); ogs_assert_if_reached(); } - gtp_h->type = OGS_GTPU_MSGTYPE_GPDU; + return test_gtpu_send(node, bearer, >p_hdesc, &ext_hdesc, pkbuf); +} - if (bearer->qfi) { - ext_h = (ogs_gtp_extension_header_t *) - (pkbuf->data + OGS_GTPV1U_HEADER_LEN); - ext_h->type = OGS_GTP_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = - OGS_GTP_EXTENSION_HEADER_PDU_TYPE_UL_PDU_SESSION_INFORMATION; - ext_h->qos_flow_identifier = bearer->qfi; - ext_h->next_type = - OGS_GTP_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } +int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer) +{ + test_sess_t *sess = NULL; - if (bearer->qfi) { - gtp_h->length = htobe16(52 + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; - ip_h = (pkbuf->data + - OGS_GTPV1U_HEADER_LEN + 4 + - OGS_GTPV1U_EXTENSION_HEADER_LEN + ext_h->len * 4); - } else { - gtp_h->length = htobe16(52); + ogs_pkbuf_t *pkbuf = NULL; - ip_h = (pkbuf->data + OGS_GTPV1U_HEADER_LEN + 4); - } + const char *payload = + "6000000000083aff fe80000000000000 0000000000000002" + "ff02000000000000 0000000000000002 85007d3500000000"; + unsigned char tmp[OGS_MAX_SDU_LEN]; + int payload_len = 48; - ogs_assert(node); - ogs_assert(node->sock); + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + pkbuf = ogs_pkbuf_alloc( + NULL, 200 /* enough for ICMP; use smaller buffer */); + ogs_assert(pkbuf); + ogs_pkbuf_reserve(pkbuf, OGS_GTPV1U_5GC_HEADER_LEN); + ogs_pkbuf_put(pkbuf, 200-OGS_GTPV1U_5GC_HEADER_LEN); + memset(pkbuf->data, 0, pkbuf->len); OGS_HEX(payload, strlen(payload), tmp); - memcpy(ip_h, tmp, 48); + memcpy(pkbuf->data, tmp, payload_len); - sent = ogs_sendto(node->sock->fd, pkbuf->data, pkbuf->len, 0, &upf); - ogs_pkbuf_free(pkbuf); - if (sent < 0 || sent != pkbuf->len) - return OGS_ERROR; + ogs_pkbuf_trim(pkbuf, payload_len); - return OGS_OK; + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); + + gtp_hdesc.type = OGS_GTPU_MSGTYPE_GPDU; + gtp_hdesc.flags = OGS_GTPU_FLAGS_S; + + if (bearer->qfi) { + gtp_hdesc.teid = sess->upf_n3_teid; + ext_hdesc.qos_flow_identifier = bearer->qfi; + + } else if (bearer->ebi) { + gtp_hdesc.teid = bearer->sgw_s1u_teid; + + } else { + ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); + ogs_assert_if_reached(); + } + + return test_gtpu_send(node, bearer, >p_hdesc, &ext_hdesc, pkbuf); +} + +int test_gtpu_send_error_indication( + ogs_socknode_t *node, test_bearer_t *bearer) +{ + test_sess_t *sess = NULL; + uint32_t teid = 0; + + ogs_gtp_header_t gtp_hdesc; + ogs_gtp_extension_header_t ext_hdesc; + + ogs_pkbuf_t *pkbuf = NULL; + + ogs_assert(bearer); + sess = bearer->sess; + ogs_assert(sess); + + ogs_assert(bearer); + + if (bearer->qfi) { + /* 5GC */ + teid = sess->gnb_n3_teid; + + } else if (bearer->ebi) { + /* EPC */ + teid = bearer->enb_s1u_teid; + + } else { + ogs_fatal("No QFI[%d] and EBI[%d]", bearer->qfi, bearer->ebi); + ogs_assert_if_reached(); + } + + pkbuf = ogs_gtp_build_error_indication(teid, node->addr); + ogs_assert(pkbuf); + + 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_GTP_EXTENSION_HEADER_TYPE_UDP_PORT; + + return test_gtpu_send(node, bearer, >p_hdesc, &ext_hdesc, pkbuf); } diff --git a/tests/common/gtpu.h b/tests/common/gtpu.h index bf11da6af..36896aa8d 100644 --- a/tests/common/gtpu.h +++ b/tests/common/gtpu.h @@ -31,9 +31,15 @@ ogs_socknode_t *test_gtpu_server(int index, int family); ogs_pkbuf_t *test_gtpu_read(ogs_socknode_t *node); void test_gtpu_close(ogs_socknode_t *node); +int test_gtpu_send( + ogs_socknode_t *node, test_bearer_t *bearer, + ogs_gtp_header_t *gtp_hdesc, ogs_gtp_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf); int test_gtpu_send_ping( ogs_socknode_t *node, test_bearer_t *bearer, const char *dst_ip); int test_gtpu_send_slacc_rs(ogs_socknode_t *node, test_bearer_t *bearer); +int test_gtpu_send_error_indication( + ogs_socknode_t *node, test_bearer_t *bearer); #ifdef __cplusplus }