/* * Copyright (C) 2019-2023 by Sukchan Lee * * This file is part of Open5GS. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "gtp-path.h" static void _gtpv2_c_recv_cb(short when, ogs_socket_t fd, void *data) { sgwc_event_t *e = NULL; int rv; ssize_t size; ogs_pkbuf_t *pkbuf = NULL; ogs_sockaddr_t from; ogs_gtp_node_t *gnode = NULL; char frombuf[OGS_ADDRSTRLEN]; ogs_assert(fd != INVALID_SOCKET); pkbuf = ogs_pkbuf_alloc(NULL, OGS_MAX_SDU_LEN); ogs_assert(pkbuf); ogs_pkbuf_put(pkbuf, OGS_MAX_SDU_LEN); size = ogs_recvfrom(fd, pkbuf->data, pkbuf->len, 0, &from); if (size <= 0) { ogs_log_message(OGS_LOG_ERROR, ogs_socket_errno, "ogs_recvfrom() failed"); ogs_pkbuf_free(pkbuf); return; } ogs_pkbuf_trim(pkbuf, size); /* * 5.5.2 in spec 29.274 * * If a peer's TEID is not available, the TEID field still shall be * present in the header and its value shall be set to "0" in the * following messages: * * - Create Session Request message on S2a/S2b/S5/S8 * * - Create Session Request message on S4/S11, if for a given UE, * the SGSN/MME has not yet obtained the Control TEID of the SGW. * * - If a node receives a message and the TEID-C in the GTPv2 header of * the received message is not known, it shall respond with * "Context not found" Cause in the corresponding response message * to the sender, the TEID used in the GTPv2-C header in the response * message shall be then set to zero. * * - If a node receives a request message containing protocol error, * e.g. Mandatory IE missing, which requires the receiver to reject * the message as specified in clause 7.7, it shall reject * the request message. For the response message, the node should * look up the remote peer's TEID and accordingly set the GTPv2-C * header TEID and the message cause code. As an implementation * option, the node may not look up the remote peer's TEID and * set the GTPv2-C header TEID to zero in the response message. * However in this case, the cause code shall not be set to * "Context not found". */ gnode = ogs_gtp_node_find_by_addr(&sgwc_self()->pgw_s5c_list, &from); if (gnode) { e = sgwc_event_new(SGWC_EVT_S5C_MESSAGE); ogs_assert(e); e->gnode = gnode; } else { gnode = ogs_gtp_node_find_by_addr(&sgwc_self()->mme_s11_list, &from); if (!gnode) { gnode = ogs_gtp_node_add_by_addr(&sgwc_self()->mme_s11_list, &from); if (!gnode) { ogs_error("Failed to create new gnode(%s:%u), mempool full, ignoring msg!", OGS_ADDR(&from, frombuf), OGS_PORT(&from)); ogs_pkbuf_free(pkbuf); return; } gnode->sock = data; } e = sgwc_event_new(SGWC_EVT_S11_MESSAGE); ogs_assert(e); e->gnode = gnode; } e->pkbuf = pkbuf; rv = ogs_queue_push(ogs_app()->queue, e); if (rv != OGS_OK) { ogs_error("ogs_queue_push() failed:%d", (int)rv); ogs_pkbuf_free(e->pkbuf); sgwc_event_free(e); } } int sgwc_gtp_open(void) { ogs_socknode_t *node = NULL; ogs_sock_t *sock = NULL; ogs_list_for_each(&ogs_gtp_self()->gtpc_list, node) { sock = ogs_gtp_server(node); if (!sock) return OGS_ERROR; node->poll = ogs_pollset_add(ogs_app()->pollset, OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); ogs_assert(node->poll); } ogs_list_for_each(&ogs_gtp_self()->gtpc_list6, node) { sock = ogs_gtp_server(node); if (!sock) return OGS_ERROR; node->poll = ogs_pollset_add(ogs_app()->pollset, OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); ogs_assert(node->poll); } OGS_SETUP_GTPC_SERVER; ogs_assert(ogs_gtp_self()->gtpc_sock || ogs_gtp_self()->gtpc_sock6); ogs_assert(ogs_gtp_self()->gtpc_addr || ogs_gtp_self()->gtpc_addr6); return OGS_OK; } void sgwc_gtp_close(void) { ogs_socknode_remove_all(&ogs_gtp_self()->gtpc_list); ogs_socknode_remove_all(&ogs_gtp_self()->gtpc_list6); } static void bearer_timeout(ogs_gtp_xact_t *xact, void *data) { sgwc_bearer_t *bearer = data; sgwc_sess_t *sess = NULL; sgwc_ue_t *sgwc_ue = NULL; uint8_t type = 0; ogs_assert(xact); ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); type = xact->seq[0].type; switch (type) { case OGS_GTP2_DOWNLINK_DATA_NOTIFICATION_TYPE: ogs_warn("[%s] No Downlink Data Notification ACK", sgwc_ue->imsi_bcd); break; default: ogs_error("GTP Timeout : IMSI[%s] Message-Type[%d]", sgwc_ue->imsi_bcd, type); } } int sgwc_gtp_send_create_session_response( sgwc_sess_t *sess, ogs_gtp_xact_t *xact) { int rv; sgwc_ue_t *sgwc_ue = NULL; ogs_gtp2_header_t h; ogs_pkbuf_t *pkbuf = NULL; ogs_assert(sess); sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); ogs_assert(xact); memset(&h, 0, sizeof(ogs_gtp2_header_t)); h.type = OGS_GTP2_CREATE_SESSION_RESPONSE_TYPE; h.teid = sgwc_ue->mme_s11_teid; pkbuf = sgwc_s11_build_create_session_response(h.type, sess); if (!pkbuf) { ogs_error("sgwc_s11_build_create_session_response() failed"); return OGS_ERROR; } rv = ogs_gtp_xact_update_tx(xact, &h, pkbuf); if (rv != OGS_OK) { ogs_error("ogs_gtp_xact_update_tx() failed"); return OGS_ERROR; } rv = ogs_gtp_xact_commit(xact); ogs_expect(rv == OGS_OK); return rv; } int sgwc_gtp_send_downlink_data_notification( uint8_t cause_value, sgwc_bearer_t *bearer) { int rv; sgwc_ue_t *sgwc_ue = NULL; sgwc_sess_t *sess = NULL; ogs_gtp_xact_t *gtp_xact = NULL; ogs_gtp2_header_t h; ogs_pkbuf_t *pkbuf = NULL; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); sgwc_ue = bearer->sgwc_ue; ogs_assert(sgwc_ue); ogs_assert(sgwc_ue->gnode); ogs_debug("Downlink Data Notification"); ogs_debug(" MME_S11_TEID[%d] SGW_S11_TEID[%d]", sgwc_ue->mme_s11_teid, sgwc_ue->sgw_s11_teid); memset(&h, 0, sizeof(ogs_gtp2_header_t)); h.type = OGS_GTP2_DOWNLINK_DATA_NOTIFICATION_TYPE; h.teid = sgwc_ue->mme_s11_teid; pkbuf = sgwc_s11_build_downlink_data_notification(cause_value, bearer); if (!pkbuf) { ogs_error("sgwc_s11_build_downlink_data_notification() failed"); return OGS_ERROR; } gtp_xact = ogs_gtp_xact_local_create( sgwc_ue->gnode, &h, pkbuf, bearer_timeout, bearer); if (!gtp_xact) { ogs_error("ogs_gtp_xact_local_create() failed"); return OGS_ERROR; } gtp_xact->local_teid = sgwc_ue->sgw_s11_teid; rv = ogs_gtp_xact_commit(gtp_xact); ogs_expect(rv == OGS_OK); return rv; }