/* * Copyright (C) 2019 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; 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 { e = sgwc_event_new(SGWC_EVT_S11_MESSAGE); 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); ogs_assert(gnode); gnode->sock = data; } 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); ogs_assert(sock); node->poll = ogs_pollset_add(ogs_app()->pollset, OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); } ogs_list_for_each(&ogs_gtp_self()->gtpc_list6, node) { sock = ogs_gtp_server(node); ogs_assert(sock); node->poll = ogs_pollset_add(ogs_app()->pollset, OGS_POLLIN, sock->fd, _gtpv2_c_recv_cb, sock); } OGS_SETUP_GTPC_SERVER; 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_GTP_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); } } void 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_pkbuf_t *pkbuf = NULL; ogs_gtp_header_t h; 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_gtp_header_t)); h.type = OGS_GTP_DOWNLINK_DATA_NOTIFICATION_TYPE; h.teid = sgwc_ue->mme_s11_teid; 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, bearer_timeout, bearer); ogs_expect_or_return(gtp_xact); rv = ogs_gtp_xact_commit(gtp_xact); ogs_expect(rv == OGS_OK); }