[GTP-U] Send Error Indication for unknown PDR

This commit is contained in:
Sukchan Lee 2023-04-16 11:50:31 +09:00
parent d2e2a58232
commit aed52a9ad8
21 changed files with 413 additions and 295 deletions

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Copyright (C) 2023 Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Copyright (C) 2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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

View File

@ -1,6 +1,7 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Copyright (C) 2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*
@ -57,7 +57,7 @@ ogs_pkbuf_t *ogs_gtp2_build_echo_response(
return ogs_gtp2_build_msg(&gtp_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;
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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(&gtp_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(&gtp_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);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* This file is part of Open5GS.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
* Copyright (C) 2019-2023 by Sukchan Lee <acetcom@gmail.com>
*
* 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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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));
}
}
}

View File

@ -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;
}

View File

@ -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(&gtp_hdesc, 0, sizeof(gtp_hdesc));

View File

@ -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);