diff --git a/lib/gtp/v1/build.c b/lib/gtp/v1/build.c index 4b849e741..85b95140b 100644 --- a/lib/gtp/v1/build.c +++ b/lib/gtp/v1/build.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 Sukchan Lee * * This file is part of Open5GS. * diff --git a/lib/gtp/v1/build.h b/lib/gtp/v1/build.h index 742d0380e..084068042 100644 --- a/lib/gtp/v1/build.h +++ b/lib/gtp/v1/build.h @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -31,6 +32,7 @@ extern "C" { ogs_pkbuf_t *ogs_gtp1_build_echo_request(uint8_t type); ogs_pkbuf_t *ogs_gtp1_build_echo_response(uint8_t type, uint8_t recovery); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/v1/path.c b/lib/gtp/v1/path.c index 55433d30a..d13145fa1 100644 --- a/lib/gtp/v1/path.c +++ b/lib/gtp/v1/path.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2019 by Sukchan Lee * Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH + * Copyright (C) 2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -143,8 +144,7 @@ void ogs_gtp1_send_error_message( ogs_expect(rv == OGS_OK); } -void ogs_gtp1_send_echo_request( - ogs_gtp_node_t *gnode) +void ogs_gtp1_send_echo_request(ogs_gtp_node_t *gnode) { int rv; ogs_pkbuf_t *pkbuf = NULL; diff --git a/lib/gtp/v2/build.c b/lib/gtp/v2/build.c index 1c44fa6bc..90ce1e7c5 100644 --- a/lib/gtp/v2/build.c +++ b/lib/gtp/v2/build.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -57,7 +57,7 @@ ogs_pkbuf_t *ogs_gtp2_build_echo_response( return ogs_gtp2_build_msg(>p_message); } -ogs_pkbuf_t *ogs_gtp2_build_error_indication( +ogs_pkbuf_t *ogs_gtp1_build_error_indication( uint32_t teid, ogs_sockaddr_t *addr) { ogs_pkbuf_t *pkbuf = NULL; @@ -112,3 +112,91 @@ ogs_pkbuf_t *ogs_gtp2_build_error_indication( return pkbuf; } + +void ogs_gtp2_fill_header( + ogs_gtp2_header_t *gtp_hdesc, ogs_gtp2_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf) +{ + ogs_gtp2_header_t *gtp_h = NULL; + ogs_gtp2_extension_header_t *ext_h = NULL; + uint8_t flags; + uint8_t gtp_hlen = 0; + + ogs_assert(gtp_hdesc); + ogs_assert(ext_hdesc); + ogs_assert(pkbuf); + + /* Processing GTP Flags */ + flags = gtp_hdesc->flags; + flags |= OGS_GTPU_FLAGS_V | OGS_GTPU_FLAGS_PT; + if (ext_hdesc->qos_flow_identifier) flags |= OGS_GTPU_FLAGS_E; + + /* Define GTP Header Size */ + if (flags & OGS_GTPU_FLAGS_E) + gtp_hlen = OGS_GTPV1U_HEADER_LEN+8; + else if (flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) + gtp_hlen = OGS_GTPV1U_HEADER_LEN+4; + else + gtp_hlen = OGS_GTPV1U_HEADER_LEN; + + ogs_pkbuf_push(pkbuf, gtp_hlen); + + /* Fill GTP Header */ + gtp_h = (ogs_gtp2_header_t *)pkbuf->data; + ogs_assert(gtp_h); + memset(gtp_h, 0, gtp_hlen); + + gtp_h->flags = flags; + gtp_h->type = gtp_hdesc->type; + + if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ || + gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_RSP || + gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { + /* + * TS29.281 5.1 General format in GTP-U header + * + * - The Echo Request/Response and Supported Extension Headers + * notification messages, where the Tunnel Endpoint Identifier + * shall be set to all zeroes. + * - The Error Indication message where the Tunnel Endpoint Identifier + * shall be set to all zeros. + */ + ogs_assert(gtp_hdesc->teid == 0); + } + + gtp_h->teid = htobe32(gtp_hdesc->teid); + + /* + * TS29.281 5.1 General format in GTP-U header + * + * Length: This field indicates the length in octets of the payload, + * i.e. the rest of the packet following the mandatory part of + * the GTP header (that is the first 8 octets). The Sequence Number, + * the N-PDU Number or any Extension headers shall be considered + * to be part of the payload, i.e. included in the length count. + */ + gtp_h->length = htobe16(pkbuf->len - OGS_GTPV1U_HEADER_LEN); + + /* Fill Extention Header */ + if (gtp_h->flags & OGS_GTPU_FLAGS_E) { + ext_h = (ogs_gtp2_extension_header_t *) + (pkbuf->data + OGS_GTPV1U_HEADER_LEN); + ogs_assert(ext_h); + + if (ext_hdesc->qos_flow_identifier) { + /* 5G Core */ + ext_h->type = OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; + ext_h->len = 1; + ext_h->pdu_type = ext_hdesc->pdu_type; + ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier; + ext_h->next_type = + OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } else { + /* EPC */ + ext_h->type = ext_hdesc->type; + ext_h->len = 1; + ext_h->next_type = + OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; + } + } +} diff --git a/lib/gtp/v2/build.h b/lib/gtp/v2/build.h index be06b2aef..0e61359f0 100644 --- a/lib/gtp/v2/build.h +++ b/lib/gtp/v2/build.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -33,9 +33,13 @@ ogs_pkbuf_t *ogs_gtp2_build_echo_request( ogs_pkbuf_t *ogs_gtp2_build_echo_response( uint8_t type, uint8_t recovery, uint8_t features); -ogs_pkbuf_t *ogs_gtp2_build_error_indication( +ogs_pkbuf_t *ogs_gtp1_build_error_indication( uint32_t teid, ogs_sockaddr_t *addr); +void ogs_gtp2_fill_header( + ogs_gtp2_header_t *gtp_hdesc, ogs_gtp2_extension_header_t *ext_hdesc, + ogs_pkbuf_t *pkbuf); + #ifdef __cplusplus } #endif diff --git a/lib/gtp/v2/path.c b/lib/gtp/v2/path.c index 578960590..790279ce9 100644 --- a/lib/gtp/v2/path.c +++ b/lib/gtp/v2/path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -27,92 +27,11 @@ int ogs_gtp2_send_user_plane( char buf[OGS_ADDRSTRLEN]; int rv; - ogs_gtp2_header_t *gtp_h = NULL; - ogs_gtp2_extension_header_t *ext_h = NULL; - uint8_t flags; - uint8_t gtp_hlen = 0; - - ogs_assert(gnode); - ogs_assert(gtp_hdesc); - ogs_assert(ext_hdesc); - ogs_assert(pkbuf); - - /* Processing GTP Flags */ - flags = gtp_hdesc->flags; - flags |= OGS_GTPU_FLAGS_V | OGS_GTPU_FLAGS_PT; - if (ext_hdesc->qos_flow_identifier) flags |= OGS_GTPU_FLAGS_E; - - /* Define GTP Header Size */ - if (flags & OGS_GTPU_FLAGS_E) - gtp_hlen = OGS_GTPV1U_HEADER_LEN+8; - else if (flags & (OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_PN)) - gtp_hlen = OGS_GTPV1U_HEADER_LEN+4; - else - gtp_hlen = OGS_GTPV1U_HEADER_LEN; - - ogs_pkbuf_push(pkbuf, gtp_hlen); - - /* Fill GTP Header */ - gtp_h = (ogs_gtp2_header_t *)pkbuf->data; - ogs_assert(gtp_h); - memset(gtp_h, 0, gtp_hlen); - - gtp_h->flags = flags; - gtp_h->type = gtp_hdesc->type; - - if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ || - gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_RSP || - gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { - /* - * TS29.281 5.1 General format in GTP-U header - * - * - The Echo Request/Response and Supported Extension Headers - * notification messages, where the Tunnel Endpoint Identifier - * shall be set to all zeroes. - * - The Error Indication message where the Tunnel Endpoint Identifier - * shall be set to all zeros. - */ - ogs_assert(gtp_hdesc->teid == 0); - } - - gtp_h->teid = htobe32(gtp_hdesc->teid); - - /* - * TS29.281 5.1 General format in GTP-U header - * - * Length: This field indicates the length in octets of the payload, - * i.e. the rest of the packet following the mandatory part of - * the GTP header (that is the first 8 octets). The Sequence Number, - * the N-PDU Number or any Extension headers shall be considered - * to be part of the payload, i.e. included in the length count. - */ - gtp_h->length = htobe16(pkbuf->len - OGS_GTPV1U_HEADER_LEN); - - /* Fill Extention Header */ - if (gtp_h->flags & OGS_GTPU_FLAGS_E) { - ext_h = (ogs_gtp2_extension_header_t *) - (pkbuf->data + OGS_GTPV1U_HEADER_LEN); - ogs_assert(ext_h); - - if (ext_hdesc->qos_flow_identifier) { - /* 5G Core */ - ext_h->type = OGS_GTP2_EXTENSION_HEADER_TYPE_PDU_SESSION_CONTAINER; - ext_h->len = 1; - ext_h->pdu_type = ext_hdesc->pdu_type; - ext_h->qos_flow_identifier = ext_hdesc->qos_flow_identifier; - ext_h->next_type = - OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } else { - /* EPC */ - ext_h->type = ext_hdesc->type; - ext_h->len = 1; - ext_h->next_type = - OGS_GTP2_EXTENSION_HEADER_TYPE_NO_MORE_EXTENSION_HEADERS; - } - } + ogs_gtp2_fill_header(gtp_hdesc, ext_hdesc, pkbuf); ogs_trace("SEND GTP-U[%d] to Peer[%s] : TEID[0x%x]", gtp_hdesc->type, OGS_ADDR(&gnode->addr, buf), gtp_hdesc->teid); + rv = ogs_gtp_sendto(gnode, pkbuf); if (rv != OGS_OK) { if (ogs_socket_errno != OGS_EAGAIN) { @@ -342,3 +261,39 @@ void ogs_gtp2_send_echo_response(ogs_gtp_xact_t *xact, rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); } + +void ogs_gtp1_send_error_indication( + ogs_sock_t *sock, uint32_t teid, uint8_t qfi, const ogs_sockaddr_t *to) +{ + ssize_t sent; + ogs_pkbuf_t *pkbuf = NULL; + + ogs_gtp2_header_t gtp_hdesc; + ogs_gtp2_extension_header_t ext_hdesc; + + ogs_assert(sock); + ogs_assert(to); + + pkbuf = ogs_gtp1_build_error_indication(teid, &sock->local_addr); + if (!pkbuf) { + ogs_error("ogs_gtp1_build_error_indication() failed"); + return; + } + + memset(>p_hdesc, 0, sizeof(gtp_hdesc)); + memset(&ext_hdesc, 0, sizeof(ext_hdesc)); + + gtp_hdesc.type = OGS_GTPU_MSGTYPE_ERR_IND; + gtp_hdesc.flags = OGS_GTPU_FLAGS_S|OGS_GTPU_FLAGS_E; + ext_hdesc.type = OGS_GTP2_EXTENSION_HEADER_TYPE_UDP_PORT; + ext_hdesc.qos_flow_identifier = qfi; + + ogs_gtp2_fill_header(>p_hdesc, &ext_hdesc, pkbuf); + + sent = ogs_sendto(sock->fd, pkbuf->data, pkbuf->len, 0, to); + if (sent < 0 || sent != pkbuf->len) { + ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, + "ogs_sendto() failed"); + } + ogs_pkbuf_free(pkbuf); +} diff --git a/lib/gtp/v2/path.h b/lib/gtp/v2/path.h index b9bd26ad1..72e99bfbe 100644 --- a/lib/gtp/v2/path.h +++ b/lib/gtp/v2/path.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -44,6 +44,9 @@ void ogs_gtp2_send_echo_request( void ogs_gtp2_send_echo_response(ogs_gtp_xact_t *xact, uint8_t recovery, uint8_t features); +void ogs_gtp1_send_error_indication( + ogs_sock_t *sock, uint32_t teid, uint8_t qfi, const ogs_sockaddr_t *to); + #ifdef __cplusplus } #endif diff --git a/lib/pfcp/context.c b/lib/pfcp/context.c index b5df54e82..8964b49dd 100644 --- a/lib/pfcp/context.c +++ b/lib/pfcp/context.c @@ -1316,7 +1316,7 @@ void ogs_pfcp_far_f_teid_hash_set(ogs_pfcp_far_t *far) &far->hash.f_teid.key, far->hash.f_teid.len, far); } -ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf) +ogs_pfcp_far_t *ogs_pfcp_far_find_by_gtpu_error_indication(ogs_pkbuf_t *pkbuf) { ogs_pfcp_far_hash_f_teid_t hashkey; int hashkey_len; @@ -1378,6 +1378,61 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf) self.far_f_teid_hash, &hashkey, hashkey_len); } +ogs_pfcp_far_t *ogs_pfcp_far_find_by_pfcp_session_report( + ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_error_indication_report_t *error_indication_report) +{ + ogs_pfcp_far_t *far = NULL; + ogs_pfcp_f_teid_t *remote_f_teid = NULL; + + uint32_t teid; + uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ + uint32_t addr[4]; + + ogs_assert(sess); + ogs_assert(error_indication_report); + + if (error_indication_report->presence == 0) { + ogs_error("No Error Indication Report"); + return NULL; + } + + if (error_indication_report->remote_f_teid.presence == 0) { + ogs_error("No Remote F-TEID"); + return NULL; + } + + remote_f_teid = error_indication_report->remote_f_teid.data; + ogs_assert(remote_f_teid); + + teid = be32toh(remote_f_teid->teid); + if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { + ogs_error("User plane should not set both IPv4 and IPv6"); + return NULL; + } else if (remote_f_teid->ipv4) { + len = OGS_IPV4_LEN; + memcpy(addr, &remote_f_teid->addr, len); + } else if (remote_f_teid->ipv6) { + len = OGS_IPV6_LEN; + memcpy(addr, remote_f_teid->addr6, len); + } else { + ogs_error("No IPv4 and IPv6"); + return NULL; + } + + ogs_list_for_each(&sess->far_list, far) { + if (teid == far->outer_header_creation.teid) + return far; + } + + ogs_error("Cannot find the session context " + "[TEID:0x%x,LEN:%d,ADDR:%08x %08x %08x %08x]", + teid, len, be32toh(addr[0]), be32toh(addr[1]), + be32toh(addr[2]), be32toh(addr[3])); + + return NULL; +} + void ogs_pfcp_far_teid_hash_set(ogs_pfcp_far_t *far) { ogs_assert(far); diff --git a/lib/pfcp/context.h b/lib/pfcp/context.h index d47fdac9c..0dcd98e4b 100644 --- a/lib/pfcp/context.h +++ b/lib/pfcp/context.h @@ -427,7 +427,11 @@ ogs_pfcp_far_t *ogs_pfcp_far_find_or_add( ogs_pfcp_sess_t *sess, ogs_pfcp_far_id_t id); void ogs_pfcp_far_f_teid_hash_set(ogs_pfcp_far_t *far); -ogs_pfcp_far_t *ogs_pfcp_far_find_by_error_indication(ogs_pkbuf_t *pkbuf); +ogs_pfcp_far_t *ogs_pfcp_far_find_by_gtpu_error_indication( + ogs_pkbuf_t *pkbuf); +ogs_pfcp_far_t *ogs_pfcp_far_find_by_pfcp_session_report( + ogs_pfcp_sess_t *sess, + ogs_pfcp_tlv_error_indication_report_t *error_indication_report); void ogs_pfcp_far_teid_hash_set(ogs_pfcp_far_t *far); ogs_pfcp_far_t *ogs_pfcp_far_find_by_teid(uint32_t teid); diff --git a/src/sgwc/context.c b/src/sgwc/context.c index 95e77a8b9..47e84e2d5 100644 --- a/src/sgwc/context.c +++ b/src/sgwc/context.c @@ -610,71 +610,6 @@ sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) return NULL; } -sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report( - sgwc_sess_t *sess, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report) -{ - ogs_pfcp_f_teid_t *remote_f_teid = NULL; - sgwc_bearer_t *bearer = NULL; - sgwc_tunnel_t *tunnel = NULL; - - uint32_t teid; - uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ - uint32_t addr[4]; - - ogs_assert(sess); - ogs_assert(error_indication_report); - - if (error_indication_report->presence == 0) { - ogs_error("No Error Indication Report"); - return NULL; - } - - if (error_indication_report->remote_f_teid.presence == 0) { - ogs_error("No Remote F-TEID"); - return NULL; - } - - remote_f_teid = error_indication_report->remote_f_teid.data; - ogs_assert(remote_f_teid); - - teid = be32toh(remote_f_teid->teid); - if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { - ogs_error("User plane should not set both IPv4 and IPv6"); - return NULL; - } else if (remote_f_teid->ipv4) { - len = OGS_IPV4_LEN; - memcpy(addr, &remote_f_teid->addr, len); - } else if (remote_f_teid->ipv6) { - len = OGS_IPV6_LEN; - memcpy(addr, remote_f_teid->addr6, len); - } else { - ogs_error("No IPv4 and IPv6"); - return NULL; - } - - ogs_list_for_each(&sess->bearer_list, bearer) { - ogs_list_for_each(&bearer->tunnel_list, tunnel) { - if (teid == tunnel->remote_teid) { - if (len == OGS_IPV4_LEN && tunnel->remote_ip.ipv4 && - memcmp(addr, &tunnel->remote_ip.addr, len) == 0) { - return bearer; - } else if (len == OGS_IPV6_LEN && tunnel->remote_ip.ipv6 && - memcmp(addr, tunnel->remote_ip.addr6, len) == 0) { - return bearer; - } - } - } - } - - ogs_error("Cannot find the bearer context " - "[TEID:%d,LEN:%d,ADDR:%08x %08x %08x %08x]", - teid, len, be32toh(addr[0]), be32toh(addr[1]), - be32toh(addr[2]), be32toh(addr[3])); - - return NULL; -} - sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess) { ogs_assert(sess); @@ -722,6 +657,7 @@ sgwc_tunnel_t *sgwc_tunnel_add( break; default: ogs_fatal("Invalid interface type = %d", interface_type); + ogs_assert_if_reached(); } ogs_pool_alloc(&sgwc_tunnel_pool, &tunnel); @@ -902,6 +838,28 @@ sgwc_tunnel_t *sgwc_tunnel_find_by_pdr_id( return NULL; } +sgwc_tunnel_t *sgwc_tunnel_find_by_far_id( + sgwc_sess_t *sess, ogs_pfcp_far_id_t far_id) +{ + sgwc_bearer_t *bearer = NULL; + sgwc_tunnel_t *tunnel = NULL; + + ogs_pfcp_far_t *far = NULL; + + ogs_assert(sess); + + ogs_list_for_each(&sess->bearer_list, bearer) { + ogs_list_for_each(&bearer->tunnel_list, tunnel) { + far = tunnel->far; + ogs_assert(far); + + if (far->id == far_id) return tunnel; + } + } + + return NULL; +} + sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer) { ogs_assert(bearer); diff --git a/src/sgwc/context.h b/src/sgwc/context.h index 59eecea9b..eca5e72d8 100644 --- a/src/sgwc/context.h +++ b/src/sgwc/context.h @@ -168,9 +168,6 @@ sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi( sgwc_sess_t *sess, uint8_t ebi); sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi( sgwc_ue_t *sgwc_ue, uint8_t ebi); -sgwc_bearer_t *sgwc_bearer_find_by_error_indication_report( - sgwc_sess_t *sess, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report); sgwc_bearer_t *sgwc_default_bearer_in_sess(sgwc_sess_t *sess); sgwc_bearer_t *sgwc_bearer_cycle(sgwc_bearer_t *bearer); @@ -183,6 +180,8 @@ sgwc_tunnel_t *sgwc_tunnel_find_by_interface_type( sgwc_bearer_t *bearer, uint8_t interface_type); sgwc_tunnel_t *sgwc_tunnel_find_by_pdr_id( sgwc_sess_t *sess, ogs_pfcp_pdr_id_t pdr_id); +sgwc_tunnel_t *sgwc_tunnel_find_by_far_id( + sgwc_sess_t *sess, ogs_pfcp_far_id_t far_id); sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer); sgwc_tunnel_t *sgwc_ul_tunnel_in_bearer(sgwc_bearer_t *bearer); diff --git a/src/sgwc/gtp-path.c b/src/sgwc/gtp-path.c index 906ab4436..85f0edec5 100644 --- a/src/sgwc/gtp-path.c +++ b/src/sgwc/gtp-path.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * diff --git a/src/sgwc/sxa-handler.c b/src/sgwc/sxa-handler.c index 9bd17f163..14fb667cb 100644 --- a/src/sgwc/sxa-handler.c +++ b/src/sgwc/sxa-handler.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 by Sukchan Lee + * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * @@ -1297,10 +1297,7 @@ void sgwc_sxa_handle_session_deletion_response( ogs_pfcp_xact_commit(pfcp_xact); - if (!gtp_message) { - ogs_error("No GTP Message"); - goto cleanup; - } + if (!gtp_message) goto cleanup; if (gtp_message->h.type == OGS_GTP2_DELETE_SESSION_REQUEST_TYPE) { /* @@ -1380,7 +1377,10 @@ void sgwc_sxa_handle_session_deletion_response( } cleanup: - sgwc_sess_remove(sess); + if (sgwc_sess_cycle(sess)) + sgwc_sess_remove(sess); + else + ogs_error("Session has already been removed"); } void sgwc_sxa_handle_session_report_request( @@ -1390,6 +1390,7 @@ void sgwc_sxa_handle_session_report_request( sgwc_ue_t *sgwc_ue = NULL; sgwc_bearer_t *bearer = NULL; sgwc_tunnel_t *tunnel = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_report_type_t report_type; uint8_t cause_value = 0; @@ -1470,22 +1471,44 @@ void sgwc_sxa_handle_session_report_request( ogs_error("Cannot find the PDR-ID[%d]", pdr_id); } else if (report_type.error_indication_report) { - bearer = sgwc_bearer_find_by_error_indication_report( - sess, &pfcp_req->error_indication_report); - - if (!bearer) return; - - ogs_list_for_each(&sgwc_ue->sess_list, sess) { - - ogs_assert(OGS_OK == - sgwc_pfcp_send_session_modification_request(sess, - /* We only use the `assoc_xact` parameter temporarily here - * to pass the `bearer` context. */ - (ogs_gtp_xact_t *)bearer, - NULL, - OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| - OGS_PFCP_MODIFY_ERROR_INDICATION)); - } + far = ogs_pfcp_far_find_by_pfcp_session_report( + &sess->pfcp, &pfcp_req->error_indication_report); + if (far) { + tunnel = sgwc_tunnel_find_by_far_id(sess, far->id); + ogs_assert(tunnel); + bearer = tunnel->bearer; + ogs_assert(bearer); + if (far->dst_if == OGS_PFCP_INTERFACE_ACCESS) { + ogs_warn("[%s] Error Indication from eNB", sgwc_ue->imsi_bcd); + ogs_list_for_each(&sgwc_ue->sess_list, sess) { + ogs_assert(OGS_OK == + sgwc_pfcp_send_session_modification_request(sess, + /* We only use the `assoc_xact` parameter temporarily here + * to pass the `bearer` context. */ + (ogs_gtp_xact_t *)bearer, + NULL, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| + OGS_PFCP_MODIFY_ERROR_INDICATION)); + } + } else if (far->dst_if == OGS_PFCP_INTERFACE_CORE) { + if (sgwc_default_bearer_in_sess(sess) == bearer) { + ogs_error("[%s] Error Indication(Default Bearer) from SMF", + sgwc_ue->imsi_bcd); + ogs_assert(OGS_OK == + sgwc_pfcp_send_session_deletion_request( + sess, NULL, NULL)); + } else { + ogs_error("[%s] Error Indication(Dedicated Bearer) " + "from SMF", sgwc_ue->imsi_bcd); + ogs_assert(OGS_OK == + sgwc_pfcp_send_bearer_modification_request( + bearer, NULL, NULL, OGS_PFCP_MODIFY_REMOVE)); + } + } else { + ogs_error("Error Indication Ignored for Indirect Tunnel"); + } + } else + ogs_error("Cannot find Session in Error Indication"); } else { ogs_error("Not supported Report Type[%d]", report_type.value); diff --git a/src/sgwu/gtp-path.c b/src/sgwu/gtp-path.c index 2aca69da6..b3868b49a 100644 --- a/src/sgwu/gtp-path.c +++ b/src/sgwu/gtp-path.c @@ -28,11 +28,13 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) { int len; ssize_t size; - char buf[OGS_ADDRSTRLEN]; + char buf1[OGS_ADDRSTRLEN]; + char buf2[OGS_ADDRSTRLEN]; sgwu_sess_t *sess = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_sock_t *sock = NULL; ogs_sockaddr_t from; ogs_gtp2_header_t *gtp_h = NULL; @@ -42,6 +44,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) uint8_t qfi; ogs_assert(fd != INVALID_SOCKET); + sock = data; + ogs_assert(sock); pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_PKT_LEN); ogs_assert(pkbuf); @@ -69,14 +73,14 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) { ogs_pkbuf_t *echo_rsp; - ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf1)); echo_rsp = ogs_gtp2_handle_echo_req(pkbuf); ogs_expect(echo_rsp); if (echo_rsp) { ssize_t sent; /* Echo reply */ - ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf1)); sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from); if (sent < 0 || sent != echo_rsp->len) { @@ -91,7 +95,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); ogs_trace("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", - gtp_h->type, OGS_ADDR(&from, buf), teid); + gtp_h->type, OGS_ADDR(&from, buf1), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -139,7 +143,27 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * Refer to the following 5G standard + * + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, 0, &from); + } goto cleanup; } @@ -164,7 +188,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { ogs_pfcp_far_t *far = NULL; - far = ogs_pfcp_far_find_by_error_indication(pkbuf); + far = ogs_pfcp_far_find_by_gtpu_error_indication(pkbuf); if (far) { ogs_assert(true == ogs_pfcp_up_handle_error_indication(far, &report)); @@ -177,7 +201,6 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) ogs_assert(OGS_OK == sgwu_pfcp_send_session_report_request(sess, &report)); } - } else { ogs_error("[DROP] Cannot find FAR by Error-Indication"); ogs_log_hexdump(OGS_LOG_ERROR, pkbuf->data, pkbuf->len); @@ -193,7 +216,27 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * Refer to the following 5G standard + * + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, 0, &from); + } goto cleanup; } diff --git a/src/smf/context.c b/src/smf/context.c index 6415eb586..867c11480 100644 --- a/src/smf/context.c +++ b/src/smf/context.c @@ -1641,68 +1641,6 @@ void smf_sess_set_paging_n1n2message_location( sess); } -smf_sess_t *smf_sess_find_by_error_indication_report( - smf_ue_t *smf_ue, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report) -{ - smf_sess_t *sess = NULL; - ogs_pfcp_f_teid_t *remote_f_teid = NULL; - - uint32_t teid; - uint16_t len; /* OGS_IPV4_LEN or OGS_IPV6_LEN */ - uint32_t addr[4]; - - ogs_assert(smf_ue); - ogs_assert(error_indication_report); - - if (error_indication_report->presence == 0) { - ogs_error("No Error Indication Report"); - return NULL; - } - - if (error_indication_report->remote_f_teid.presence == 0) { - ogs_error("No Remote F-TEID"); - return NULL; - } - - remote_f_teid = error_indication_report->remote_f_teid.data; - ogs_assert(remote_f_teid); - - teid = be32toh(remote_f_teid->teid); - if (remote_f_teid->ipv4 && remote_f_teid->ipv6) { - ogs_error("User plane should not set both IPv4 and IPv6"); - return NULL; - } else if (remote_f_teid->ipv4) { - len = OGS_IPV4_LEN; - memcpy(addr, &remote_f_teid->addr, len); - } else if (remote_f_teid->ipv6) { - len = OGS_IPV6_LEN; - memcpy(addr, remote_f_teid->addr6, len); - } else { - ogs_error("No IPv4 and IPv6"); - return NULL; - } - - ogs_list_reverse_for_each(&smf_ue->sess_list, sess) { - if (teid == sess->gnb_n3_teid) { - if (len == OGS_IPV4_LEN && sess->gnb_n3_ip.ipv4 && - memcmp(addr, &sess->gnb_n3_ip.addr, len) == 0) { - return sess; - } else if (len == OGS_IPV6_LEN && sess->gnb_n3_ip.ipv6 && - memcmp(addr, sess->gnb_n3_ip.addr6, len) == 0) { - return sess; - } - } - } - - ogs_error("Cannot find the session context " - "[TEID:%d,LEN:%d,ADDR:%08x %08x %08x %08x]", - teid, len, be32toh(addr[0]), be32toh(addr[1]), - be32toh(addr[2]), be32toh(addr[3])); - - return NULL; -} - void smf_sess_remove(smf_sess_t *sess) { int i; diff --git a/src/smf/context.h b/src/smf/context.h index fc20ade17..40eebb76f 100644 --- a/src/smf/context.h +++ b/src/smf/context.h @@ -465,9 +465,6 @@ smf_sess_t *smf_sess_find_by_ipv4(uint32_t addr); smf_sess_t *smf_sess_find_by_ipv6(uint32_t *addr6); smf_sess_t *smf_sess_find_by_paging_n1n2message_location( char *n1n2message_location); -smf_sess_t *smf_sess_find_by_error_indication_report( - smf_ue_t *smf_ue, - ogs_pfcp_tlv_error_indication_report_t *error_indication_report); void smf_sess_create_indirect_data_forwarding(smf_sess_t *sess); bool smf_sess_have_indirect_data_forwarding(smf_sess_t *sess); diff --git a/src/smf/gsm-sm.c b/src/smf/gsm-sm.c index 85e1d2043..63b8aee47 100644 --- a/src/smf/gsm-sm.c +++ b/src/smf/gsm-sm.c @@ -851,6 +851,11 @@ void smf_gsm_state_operational(ogs_fsm_t *s, smf_event_t *e) } break; + case OGS_PFCP_SESSION_DELETION_RESPONSE_TYPE: + ogs_error("Session Released by Error Indication"); + OGS_FSM_TRAN(s, smf_gsm_state_session_will_release); + break; + default: ogs_error("cannot handle PFCP message type[%d]", pfcp_message->h.type); diff --git a/src/smf/n4-handler.c b/src/smf/n4-handler.c index 3c474db43..c92409304 100644 --- a/src/smf/n4-handler.c +++ b/src/smf/n4-handler.c @@ -1148,9 +1148,11 @@ void smf_n4_handle_session_report_request( smf_sess_t *sess, ogs_pfcp_xact_t *pfcp_xact, ogs_pfcp_session_report_request_t *pfcp_req) { + smf_ue_t *smf_ue = NULL; smf_bearer_t *qos_flow = NULL; smf_bearer_t *bearer = NULL; ogs_pfcp_pdr_t *pdr = NULL; + ogs_pfcp_far_t *far = NULL; ogs_pfcp_report_type_t report_type; uint8_t cause_value = 0; @@ -1184,6 +1186,9 @@ void smf_n4_handle_session_report_request( } ogs_assert(sess); + smf_ue = sess->smf_ue; + ogs_assert(smf_ue); + report_type.value = pfcp_req->report_type.u8; if (report_type.downlink_data_report) { @@ -1270,21 +1275,10 @@ void smf_n4_handle_session_report_request( } if (report_type.error_indication_report) { - smf_ue_t *smf_ue = sess->smf_ue; - smf_sess_t *error_indication_session = NULL; - ogs_assert(smf_ue); - - error_indication_session = smf_sess_find_by_error_indication_report( - smf_ue, &pfcp_req->error_indication_report); - - if (error_indication_session) { - ogs_assert(OGS_OK == - smf_5gc_pfcp_send_all_pdr_modification_request( - error_indication_session, NULL, - OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| - OGS_PFCP_MODIFY_ERROR_INDICATION, - 0)); - } + far = ogs_pfcp_far_find_by_pfcp_session_report( + &sess->pfcp, &pfcp_req->error_indication_report); + if (!far) + ogs_error("Cannot find Session in Error Indication"); } if (report_type.usage_report) { @@ -1341,4 +1335,24 @@ void smf_n4_handle_session_report_request( smf_pfcp_send_session_report_response( pfcp_xact, sess, OGS_PFCP_CAUSE_SYSTEM_FAILURE)); } + + /* Error Indication is handled last */ + if (report_type.error_indication_report && far) { + if (sess->epc == true) { + ogs_error("[%s:%s] Error Indication from SGW-C", + smf_ue->imsi_bcd, sess->session.name); + ogs_assert(OGS_OK == + smf_epc_pfcp_send_session_deletion_request( + sess, NULL)); + } else { + ogs_warn("[%s:%s] Error Indication from gNB", + smf_ue->supi, sess->session.name); + ogs_assert(OGS_OK == + smf_5gc_pfcp_send_all_pdr_modification_request( + sess, NULL, + OGS_PFCP_MODIFY_DL_ONLY|OGS_PFCP_MODIFY_DEACTIVATE| + OGS_PFCP_MODIFY_ERROR_INDICATION, + 0)); + } + } } diff --git a/src/upf/gtp-path.c b/src/upf/gtp-path.c index a40b34b43..9edce8ed7 100644 --- a/src/upf/gtp-path.c +++ b/src/upf/gtp-path.c @@ -258,11 +258,13 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) { int len; ssize_t size; - char buf[OGS_ADDRSTRLEN]; + char buf1[OGS_ADDRSTRLEN]; + char buf2[OGS_ADDRSTRLEN]; upf_sess_t *sess = NULL; ogs_pkbuf_t *pkbuf = NULL; + ogs_sock_t *sock = NULL; ogs_sockaddr_t from; ogs_gtp2_header_t *gtp_h = NULL; @@ -272,6 +274,8 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) uint8_t qfi; ogs_assert(fd != INVALID_SOCKET); + sock = data; + ogs_assert(sock); pkbuf = ogs_pkbuf_alloc(packet_pool, OGS_MAX_PKT_LEN); ogs_assert(pkbuf); @@ -300,14 +304,14 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) if (gtp_h->type == OGS_GTPU_MSGTYPE_ECHO_REQ) { ogs_pkbuf_t *echo_rsp; - ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[RECV] Echo Request from [%s]", OGS_ADDR(&from, buf1)); echo_rsp = ogs_gtp2_handle_echo_req(pkbuf); ogs_expect(echo_rsp); if (echo_rsp) { ssize_t sent; /* Echo reply */ - ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf)); + ogs_debug("[SEND] Echo Response to [%s]", OGS_ADDR(&from, buf1)); sent = ogs_sendto(fd, echo_rsp->data, echo_rsp->len, 0, &from); if (sent < 0 || sent != echo_rsp->len) { @@ -322,7 +326,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) teid = be32toh(gtp_h->teid); ogs_trace("[RECV] GPU-U Type [%d] from [%s] : TEID[0x%x]", - gtp_h->type, OGS_ADDR(&from, buf), teid); + gtp_h->type, OGS_ADDR(&from, buf1), teid); qfi = 0; if (gtp_h->flags & OGS_GTPU_FLAGS_E) { @@ -369,7 +373,7 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } else if (gtp_h->type == OGS_GTPU_MSGTYPE_ERR_IND) { ogs_pfcp_far_t *far = NULL; - far = ogs_pfcp_far_find_by_error_indication(pkbuf); + far = ogs_pfcp_far_find_by_gtpu_error_indication(pkbuf); if (far) { ogs_assert(true == ogs_pfcp_up_handle_error_indication(far, &report)); @@ -419,7 +423,25 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) pfcp_object = ogs_pfcp_object_find_by_teid(teid); if (!pfcp_object) { - /* TODO : Send Error Indication */ + /* + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error("[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, qfi, &from); + } goto cleanup; } @@ -458,7 +480,26 @@ static void _gtpv1_u_recv_cb(short when, ogs_socket_t fd, void *data) } if (!pdr) { - /* TODO : Send Error Indication */ + /* + * TS23.527 Restoration procedures + * 4.3 UPF Restoration Procedures + * 4.3.2 Restoration Procedure for PSA UPF Restart + * + * The UPF shall not send GTP-U Error indication message + * for a configurable period after an UPF restart + * when the UPF receives a G-PDU not matching any PDRs. + */ + if (ogs_time_ntp32_now() > + (ogs_pfcp_self()->local_recovery + + ogs_time_sec( + ogs_app()->time.message.pfcp.association_interval))) { + ogs_error( + "[%s] Send Error Indication [TEID:0x%x] to [%s]", + OGS_ADDR(&sock->local_addr, buf1), + teid, + OGS_ADDR(&from, buf2)); + ogs_gtp1_send_error_indication(sock, teid, qfi, &from); + } goto cleanup; } diff --git a/tests/common/gtpu.c b/tests/common/gtpu.c index 1a93390fa..a5c7ce2d3 100644 --- a/tests/common/gtpu.c +++ b/tests/common/gtpu.c @@ -499,7 +499,7 @@ int test_gtpu_send_error_indication( ogs_assert_if_reached(); } - pkbuf = ogs_gtp2_build_error_indication(teid, node->addr); + pkbuf = ogs_gtp1_build_error_indication(teid, node->addr); ogs_assert(pkbuf); memset(>p_hdesc, 0, sizeof(gtp_hdesc)); diff --git a/tests/handover/5gc-n2-test.c b/tests/handover/5gc-n2-test.c index e560c8353..f984b08ad 100644 --- a/tests/handover/5gc-n2-test.c +++ b/tests/handover/5gc-n2-test.c @@ -806,9 +806,6 @@ static void direct_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -926,9 +923,6 @@ static void direct_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1844,9 +1838,6 @@ static void indirect_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -1916,6 +1907,9 @@ static void indirect_complete_func(abts_case *tc, void *data) ABTS_PTR_NOTNULL(tc, recvbuf); ogs_pkbuf_free(recvbuf); + /* Waiting for removing Indirect Data Forwarding */ + ogs_msleep(100); + /* Send HandoverRequired */ sendbuf = testngap_build_handover_required( test_ue, NGAP_HandoverType_intra5gs, @@ -1992,9 +1986,6 @@ static void indirect_complete_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap1, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu2); ABTS_PTR_NOTNULL(tc, recvbuf); @@ -2500,9 +2491,6 @@ static void indirect_cancel_func(abts_case *tc, void *data) rv = testgnb_ngap_send(ngap2, sendbuf); ABTS_INT_EQUAL(tc, OGS_OK, rv); - /* Waiting for N4 */ - ogs_msleep(100); - /* Receive End Mark */ recvbuf = test_gtpu_read(gtpu1); ABTS_PTR_NOTNULL(tc, recvbuf);