[GTP] Incorrect destination TEID=0 (#3043)

If eg. PCRF or AAA diameter link is not yet ready (eg. PCRF crashed),
and a client sends a CreateSessionRequest announcing its ow F-TEID,
then open5gs-smfd answers with Create Session Response Cause=
"Remote peer not responding", but it is not setting the received F-TEID
in the header of the response, instead it sends with TEI=0.

As a result, the peer cannot match the CreateSessionResponse,
and needs to rely on its own timeout timer to figure out
that specific request failed.

To address this issue, I modified the GTP Response message to check
the Sender F-TEID and send it accordingly, setting the destination TEID
to the value of the Sender F-TEID.

I've made this modification only for SMF, but MME and SGW-C have not done so;
if you need to, you can work from the examples in SMF.

Similarly, the same situation can happen with PFCP. If anyone needs to do this
in the future, I think you can work on it this way.
This commit is contained in:
Sukchan Lee 2024-04-06 16:34:03 +09:00
parent 990bfe96a8
commit 8484a5af60
6 changed files with 127 additions and 12 deletions

View File

@ -233,7 +233,7 @@ MME-frDi = 127.0.0.2 :3868 for S6a
SGWC-gtpc = 127.0.0.3 :2123 for S11
SGWC-pfcp = 127.0.0.3 :8805 for Sxa
SMF-gtpc = 127.0.0.4 :2123 for S5c, N11
SMF-gtpc = 127.0.0.4 :2123 for S5c
SMF-gtpu = 127.0.0.4 :2152 for N4u (Sxu)
SMF-pfcp = 127.0.0.4 :8805 for N4 (Sxb)
SMF-frDi = 127.0.0.4 :3868 for Gx auth

View File

@ -180,3 +180,68 @@ uint16_t ogs_in_cksum(uint16_t *addr, int len)
return answer;
}
void ogs_gtp2_sender_f_teid(
ogs_gtp2_sender_f_teid_t *sender_f_teid, ogs_gtp2_message_t *message)
{
ogs_gtp2_tlv_f_teid_t *tlv_f_teid = NULL;
ogs_gtp2_f_teid_t *f_teid = NULL;
ogs_assert(sender_f_teid);
ogs_assert(message);
memset(sender_f_teid, 0, sizeof(*sender_f_teid));
switch (message->h.type) {
case OGS_GTP2_CREATE_SESSION_REQUEST_TYPE:
tlv_f_teid = &message->create_session_request.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE:
tlv_f_teid = &message->create_session_response.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_MODIFY_BEARER_REQUEST_TYPE:
tlv_f_teid = &message->modify_bearer_request.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_DELETE_SESSION_REQUEST_TYPE:
tlv_f_teid = &message->delete_session_request.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_MODIFY_BEARER_COMMAND_TYPE:
tlv_f_teid = &message->modify_bearer_command.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_DELETE_BEARER_COMMAND_TYPE:
tlv_f_teid = &message->delete_bearer_command.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_BEARER_RESOURCE_COMMAND_TYPE:
tlv_f_teid = &message->bearer_resource_command.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST_TYPE:
tlv_f_teid = &message->create_indirect_data_forwarding_tunnel_request.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE_TYPE:
tlv_f_teid = &message->create_indirect_data_forwarding_tunnel_response.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_DOWNLINK_DATA_NOTIFICATION_TYPE:
tlv_f_teid = &message->downlink_data_notification.
sender_f_teid_for_control_plane;
break;
case OGS_GTP2_MODIFY_ACCESS_BEARERS_REQUEST_TYPE:
tlv_f_teid = &message->modify_access_bearers_request.
sender_f_teid_for_control_plane;
default:
break;
}
if (tlv_f_teid && tlv_f_teid->presence && (f_teid = tlv_f_teid->data)) {
sender_f_teid->teid_presence = true;
sender_f_teid->teid = be32toh(f_teid->teid);
}
}

View File

@ -32,6 +32,14 @@ int ogs_gtpu_parse_header(
ogs_gtp2_header_desc_t *header_desc, ogs_pkbuf_t *pkbuf);
uint16_t ogs_in_cksum(uint16_t *addr, int len);
typedef struct ogs_gtp2_sender_f_teid_s {
bool teid_presence;
uint32_t teid;
} ogs_gtp2_sender_f_teid_t;
void ogs_gtp2_sender_f_teid(
ogs_gtp2_sender_f_teid_t *sender_f_teid, ogs_gtp2_message_t *message);
#ifdef __cplusplus
}
#endif

View File

@ -281,7 +281,8 @@ uint8_t smf_s5c_handle_create_session_request(
/* Control Plane(DL) : SGW-S5C */
sgw_s5c_teid = req->sender_f_teid_for_control_plane.data;
ogs_assert(sgw_s5c_teid);
sess->sgw_s5c_teid = be32toh(sgw_s5c_teid->teid);
/* sess->sgw_s5c_teid has already been updated in SMF-SM */
ogs_assert(sess->sgw_s5c_teid == be32toh(sgw_s5c_teid->teid));
rv = ogs_gtp2_f_teid_to_ip(sgw_s5c_teid, &sess->sgw_s5c_ip);
ogs_assert(rv == OGS_OK);
@ -479,7 +480,8 @@ uint8_t smf_s5c_handle_delete_session_request(
void smf_s5c_handle_modify_bearer_request(
smf_sess_t *sess, ogs_gtp_xact_t *gtp_xact,
ogs_pkbuf_t *gtpbuf, ogs_gtp2_modify_bearer_request_t *req)
ogs_pkbuf_t *gtpbuf, ogs_gtp2_modify_bearer_request_t *req,
ogs_gtp2_sender_f_teid_t *sender_f_teid)
{
int rv, i;
uint8_t cause_value = 0;
@ -492,6 +494,7 @@ void smf_s5c_handle_modify_bearer_request(
ogs_assert(gtp_xact);
ogs_assert(req);
ogs_assert(sender_f_teid);
/************************
* Check Session Context
@ -504,7 +507,10 @@ void smf_s5c_handle_modify_bearer_request(
}
if (cause_value != OGS_GTP2_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp2_send_error_message(gtp_xact, sess ? sess->sgw_s5c_teid : 0,
ogs_gtp2_send_error_message(gtp_xact,
sess ? sess->sgw_s5c_teid :
sender_f_teid->teid_presence == true ?
sender_f_teid->teid : 0,
OGS_GTP2_MODIFY_BEARER_RESPONSE_TYPE, cause_value);
return;
}
@ -1132,7 +1138,8 @@ static int reconfigure_packet_filter(smf_pf_t *pf, ogs_gtp2_tft_t *tft, int i)
void smf_s5c_handle_bearer_resource_command(
smf_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp2_bearer_resource_command_t *cmd)
ogs_gtp2_bearer_resource_command_t *cmd,
ogs_gtp2_sender_f_teid_t *sender_f_teid)
{
int rv;
uint8_t cause_value = 0;
@ -1153,6 +1160,7 @@ void smf_s5c_handle_bearer_resource_command(
ogs_assert(xact);
ogs_assert(cmd);
ogs_assert(sender_f_teid);
ogs_debug("Bearer Resource Command");
@ -1186,7 +1194,10 @@ void smf_s5c_handle_bearer_resource_command(
}
if (cause_value != OGS_GTP2_CAUSE_REQUEST_ACCEPTED) {
ogs_gtp2_send_error_message(xact, sess ? sess->sgw_s5c_teid : 0,
ogs_gtp2_send_error_message(xact,
sess ? sess->sgw_s5c_teid :
sender_f_teid->teid_presence == true ?
sender_f_teid->teid : 0,
OGS_GTP2_BEARER_RESOURCE_FAILURE_INDICATION_TYPE, cause_value);
return;
}

View File

@ -39,7 +39,8 @@ uint8_t smf_s5c_handle_delete_session_request(
ogs_gtp2_delete_session_request_t *req);
void smf_s5c_handle_modify_bearer_request(
smf_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_pkbuf_t *gtpbuf, ogs_gtp2_modify_bearer_request_t *req);
ogs_pkbuf_t *gtpbuf, ogs_gtp2_modify_bearer_request_t *req,
ogs_gtp2_sender_f_teid_t *sender_f_teid);
void smf_s5c_handle_create_bearer_response(
smf_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp2_create_bearer_response_t *rsp);
@ -51,7 +52,8 @@ bool smf_s5c_handle_delete_bearer_response(
ogs_gtp2_delete_bearer_response_t *rsp);
void smf_s5c_handle_bearer_resource_command(
smf_sess_t *sess, ogs_gtp_xact_t *xact,
ogs_gtp2_bearer_resource_command_t *cmd);
ogs_gtp2_bearer_resource_command_t *cmd,
ogs_gtp2_sender_f_teid_t *sender_f_teid);
#ifdef __cplusplus
}

View File

@ -58,6 +58,7 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
ogs_gtp_xact_t *gtp_xact = NULL;
ogs_gtp2_message_t gtp2_message;
ogs_gtp2_sender_f_teid_t gtp2_sender_f_teid;
ogs_gtp1_message_t gtp1_message;
ogs_diam_gx_message_t *gx_message = NULL;
@ -108,6 +109,8 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
}
e->gtp2_message = &gtp2_message;
ogs_gtp2_sender_f_teid(&gtp2_sender_f_teid, &gtp2_message);
rv = ogs_gtp_xact_receive(smf_gnode->gnode, &gtp2_message.h, &gtp_xact);
if (rv != OGS_OK) {
ogs_pkbuf_free(recvbuf);
@ -144,11 +147,21 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
}
if (!sess) {
ogs_error("No Session");
ogs_gtp2_send_error_message(gtp_xact, 0,
ogs_gtp2_send_error_message(gtp_xact,
gtp2_sender_f_teid.teid_presence == true ?
gtp2_sender_f_teid.teid : 0,
OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE,
OGS_GTP2_CAUSE_CONTEXT_NOT_FOUND);
break;
}
if (gtp2_sender_f_teid.teid_presence == true)
sess->sgw_s5c_teid = gtp2_sender_f_teid.teid;
ogs_debug(" SGW_S5C_TEID[0x%x], Sender F-TEID(%d)[0x%x]",
sess->sgw_s5c_teid,
gtp2_sender_f_teid.teid_presence, gtp2_sender_f_teid.teid);
e->sess = sess;
ogs_fsm_dispatch(&sess->sm, e);
break;
@ -158,18 +171,33 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
smf_metrics_inst_gtp_node_inc(smf_gnode->metrics, SMF_METR_GTP_NODE_CTR_S5C_RX_DELETESESSIONREQ);
if (!sess) {
ogs_error("No Session");
ogs_gtp2_send_error_message(gtp_xact, 0,
ogs_gtp2_send_error_message(gtp_xact,
gtp2_sender_f_teid.teid_presence == true ?
gtp2_sender_f_teid.teid : 0,
OGS_GTP2_DELETE_SESSION_RESPONSE_TYPE,
OGS_GTP2_CAUSE_CONTEXT_NOT_FOUND);
break;
}
if (gtp2_sender_f_teid.teid_presence == true) {
if (sess->sgw_s5c_teid != gtp2_sender_f_teid.teid) {
ogs_error("Invalid Sender F-TEID [0x%x != 0x%x]",
sess->sgw_s5c_teid, gtp2_sender_f_teid.teid);
ogs_gtp2_send_error_message(gtp_xact,
gtp2_sender_f_teid.teid_presence == true ?
gtp2_sender_f_teid.teid : 0,
OGS_GTP2_DELETE_SESSION_RESPONSE_TYPE,
OGS_GTP2_CAUSE_INVALID_MESSAGE_FORMAT);
break;
}
}
e->sess = sess;
ogs_fsm_dispatch(&sess->sm, e);
break;
case OGS_GTP2_MODIFY_BEARER_REQUEST_TYPE:
if (!gtp2_message.h.teid_presence) ogs_error("No TEID");
smf_s5c_handle_modify_bearer_request(
sess, gtp_xact, recvbuf, &gtp2_message.modify_bearer_request);
sess, gtp_xact, recvbuf,
&gtp2_message.modify_bearer_request, &gtp2_sender_f_teid);
break;
case OGS_GTP2_CREATE_BEARER_RESPONSE_TYPE:
if (!gtp2_message.h.teid_presence) ogs_error("No TEID");
@ -194,7 +222,8 @@ void smf_state_operational(ogs_fsm_t *s, smf_event_t *e)
case OGS_GTP2_BEARER_RESOURCE_COMMAND_TYPE:
if (!gtp2_message.h.teid_presence) ogs_error("No TEID");
smf_s5c_handle_bearer_resource_command(
sess, gtp_xact, &gtp2_message.bearer_resource_command);
sess, gtp_xact,
&gtp2_message.bearer_resource_command, &gtp2_sender_f_teid);
break;
default:
ogs_warn("Not implemented(type:%d)", gtp2_message.h.type);