/* * 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 #include "context.h" static sgwc_context_t self; int __sgwc_log_domain; static OGS_POOL(sgwc_ue_pool, sgwc_ue_t); static OGS_POOL(sgwc_sess_pool, sgwc_sess_t); static OGS_POOL(sgwc_bearer_pool, sgwc_bearer_t); static OGS_POOL(sgwc_tunnel_pool, sgwc_tunnel_t); static int context_initialized = 0; static int num_of_sgwc_sess = 0; static void stats_add_sgwc_session(void); static void stats_remove_sgwc_session(void); void sgwc_context_init(void) { ogs_assert(context_initialized == 0); memset(&self, 0, sizeof(sgwc_context_t)); ogs_log_install_domain(&__sgwc_log_domain, "sgwc", ogs_core()->log.level); ogs_pool_init(&sgwc_ue_pool, ogs_app()->max.ue); ogs_pool_init(&sgwc_sess_pool, ogs_app()->pool.sess); ogs_pool_init(&sgwc_bearer_pool, ogs_app()->pool.bearer); ogs_pool_init(&sgwc_tunnel_pool, ogs_app()->pool.tunnel); self.imsi_ue_hash = ogs_hash_make(); ogs_assert(self.imsi_ue_hash); ogs_list_init(&self.sgw_ue_list); context_initialized = 1; } void sgwc_context_final(void) { ogs_assert(context_initialized == 1); sgwc_ue_remove_all(); ogs_assert(self.imsi_ue_hash); ogs_hash_destroy(self.imsi_ue_hash); ogs_pool_final(&sgwc_tunnel_pool); ogs_pool_final(&sgwc_bearer_pool); ogs_pool_final(&sgwc_sess_pool); ogs_pool_final(&sgwc_ue_pool); ogs_gtp_node_remove_all(&self.mme_s11_list); ogs_gtp_node_remove_all(&self.pgw_s5c_list); context_initialized = 0; } sgwc_context_t *sgwc_self(void) { return &self; } static int sgwc_context_prepare(void) { return OGS_OK; } static int sgwc_context_validation(void) { if (ogs_list_empty(&ogs_gtp_self()->gtpc_list) && ogs_list_empty(&ogs_gtp_self()->gtpc_list6)) { ogs_error("No sgwc.gtpc in '%s'", ogs_app()->file); return OGS_ERROR; } return OGS_OK; } int sgwc_context_parse_config(void) { int rv; yaml_document_t *document = NULL; ogs_yaml_iter_t root_iter; document = ogs_app()->document; ogs_assert(document); rv = sgwc_context_prepare(); if (rv != OGS_OK) return rv; ogs_yaml_iter_init(&root_iter, document); while (ogs_yaml_iter_next(&root_iter)) { const char *root_key = ogs_yaml_iter_key(&root_iter); ogs_assert(root_key); if (!strcmp(root_key, "sgwc")) { ogs_yaml_iter_t sgwc_iter; ogs_yaml_iter_recurse(&root_iter, &sgwc_iter); while (ogs_yaml_iter_next(&sgwc_iter)) { const char *sgwc_key = ogs_yaml_iter_key(&sgwc_iter); ogs_assert(sgwc_key); if (!strcmp(sgwc_key, "gtpc")) { /* handle config in gtp library */ } else if (!strcmp(sgwc_key, "pfcp")) { /* handle config in pfcp library */ } else ogs_warn("unknown key `%s`", sgwc_key); } } } rv = sgwc_context_validation(); if (rv != OGS_OK) return rv; return OGS_OK; } sgwc_ue_t *sgwc_ue_add_by_message(ogs_gtp2_message_t *message) { sgwc_ue_t *sgwc_ue = NULL; ogs_gtp2_create_session_request_t *req = &message->create_session_request; ogs_assert(message); req = &message->create_session_request; if (req->imsi.presence == 0) { ogs_error("No IMSI"); return NULL; } ogs_trace("sgwc_ue_add_by_message() - IMSI "); ogs_log_hexdump(OGS_LOG_TRACE, req->imsi.data, req->imsi.len); /* * 7.2.1 in 3GPP TS 29.274 Release 15 * * If the new Create Session Request received by the SGW collides with * an existing active PDN connection context (the existing PDN connection * context is identified with the tuple [IMSI, EPS Bearer ID], where IMSI * shall be replaced by TAC and SNR part of ME Identity for emergency * attached UE without UICC or authenticated IMSI), this Create Session * Request shall be treated as a request for a new session. Before creating * the new session, the SGW should delete: * * - the existing PDN connection context locally, if the Create Session * Request is received with the TEID set to zero in the header, or * if it is received with a TEID not set to zero in the header and * it collides with the default bearer of an existing PDN connection * context; * - the existing dedicated bearer context locally, if the Create Session * Request collides with an existing dedicated bearer context and * the message is received with a TEID not set to zero in the header. */ sgwc_ue = sgwc_ue_find_by_imsi(req->imsi.data, req->imsi.len); if (sgwc_ue) sgwc_ue_remove(sgwc_ue); sgwc_ue = sgwc_ue_add(req->imsi.data, req->imsi.len); ogs_assert(sgwc_ue); return sgwc_ue; } sgwc_ue_t *sgwc_ue_add(uint8_t *imsi, int imsi_len) { sgwc_ue_t *sgwc_ue = NULL; ogs_assert(imsi); ogs_assert(imsi_len); ogs_pool_alloc(&sgwc_ue_pool, &sgwc_ue); ogs_assert(sgwc_ue); memset(sgwc_ue, 0, sizeof *sgwc_ue); sgwc_ue->sgw_s11_teid = ogs_pool_index(&sgwc_ue_pool, sgwc_ue); ogs_assert(sgwc_ue->sgw_s11_teid > 0 && sgwc_ue->sgw_s11_teid <= ogs_app()->max.ue); /* Set IMSI */ sgwc_ue->imsi_len = imsi_len; memcpy(sgwc_ue->imsi, imsi, sgwc_ue->imsi_len); ogs_buffer_to_bcd(sgwc_ue->imsi, sgwc_ue->imsi_len, sgwc_ue->imsi_bcd); ogs_list_init(&sgwc_ue->sess_list); ogs_hash_set(self.imsi_ue_hash, sgwc_ue->imsi, sgwc_ue->imsi_len, sgwc_ue); ogs_list_add(&self.sgw_ue_list, sgwc_ue); ogs_info("[Added] Number of SGWC-UEs is now %d", ogs_list_count(&self.sgw_ue_list)); return sgwc_ue; } int sgwc_ue_remove(sgwc_ue_t *sgwc_ue) { ogs_assert(sgwc_ue); ogs_list_remove(&self.sgw_ue_list, sgwc_ue); ogs_hash_set(self.imsi_ue_hash, sgwc_ue->imsi, sgwc_ue->imsi_len, NULL); sgwc_sess_remove_all(sgwc_ue); ogs_pool_free(&sgwc_ue_pool, sgwc_ue); ogs_info("[Removed] Number of SGWC-UEs is now %d", ogs_list_count(&self.sgw_ue_list)); return OGS_OK; } void sgwc_ue_remove_all(void) { sgwc_ue_t *sgwc_ue = NULL, *next = NULL;; ogs_list_for_each_safe(&self.sgw_ue_list, next, sgwc_ue) sgwc_ue_remove(sgwc_ue); } sgwc_ue_t *sgwc_ue_find_by_imsi_bcd(char *imsi_bcd) { uint8_t imsi[OGS_MAX_IMSI_LEN]; int imsi_len = 0; ogs_assert(imsi_bcd); ogs_bcd_to_buffer(imsi_bcd, imsi, &imsi_len); return sgwc_ue_find_by_imsi(imsi, imsi_len); } sgwc_ue_t *sgwc_ue_find_by_imsi(uint8_t *imsi, int imsi_len) { ogs_assert(imsi && imsi_len); return (sgwc_ue_t *)ogs_hash_get(self.imsi_ue_hash, imsi, imsi_len); } sgwc_ue_t *sgwc_ue_find_by_teid(uint32_t teid) { return ogs_pool_find(&sgwc_ue_pool, teid); } sgwc_sess_t *sgwc_sess_add(sgwc_ue_t *sgwc_ue, char *apn) { sgwc_sess_t *sess = NULL; ogs_assert(sgwc_ue); ogs_pool_alloc(&sgwc_sess_pool, &sess); if (!sess) { ogs_error("Maximum number of session[%lld] reached", (long long)ogs_app()->pool.sess); return NULL; } memset(sess, 0, sizeof *sess); ogs_pfcp_pool_init(&sess->pfcp); sess->index = ogs_pool_index(&sgwc_sess_pool, sess); ogs_assert(sess->index > 0 && sess->index <= ogs_app()->pool.sess); /* Set TEID & SEID */ sess->sgw_s5c_teid = sess->index; sess->sgwc_sxa_seid = sess->index; /* Create BAR in PFCP Session */ ogs_pfcp_bar_new(&sess->pfcp); /* Set APN */ sess->session.name = ogs_strdup(apn); ogs_assert(sess->session.name); sess->sgwc_ue = sgwc_ue; ogs_list_add(&sgwc_ue->sess_list, sess); stats_add_sgwc_session(); return sess; } static bool compare_ue_info(ogs_pfcp_node_t *node, sgwc_sess_t *sess) { sgwc_ue_t *sgwc_ue = NULL; int i; ogs_assert(node); ogs_assert(sess); sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); ogs_assert(sess->session.name); for (i = 0; i < node->num_of_dnn; i++) if (ogs_strcasecmp(node->dnn[i], sess->session.name) == 0) return true; for (i = 0; i < node->num_of_e_cell_id; i++) if (sgwc_ue->uli_presence == true && node->e_cell_id[i] == sgwc_ue->e_cgi.cell_id) return true; for (i = 0; i < node->num_of_tac; i++) if (sgwc_ue->uli_presence == true && node->tac[i] == sgwc_ue->e_tai.tac) return true; return false; } static ogs_pfcp_node_t *selected_sgwu_node( ogs_pfcp_node_t *current, sgwc_sess_t *sess) { ogs_pfcp_node_t *next, *node; ogs_assert(current); ogs_assert(sess); /* continue search from current position */ next = ogs_list_next(current); for (node = next; node; node = ogs_list_next(node)) { if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated) && compare_ue_info(node, sess) == true) return node; } /* cyclic search from top to current position */ for (node = ogs_list_first(&ogs_pfcp_self()->pfcp_peer_list); node != next; node = ogs_list_next(node)) { if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated) && compare_ue_info(node, sess) == true) return node; } if (ogs_app()->parameter.no_pfcp_rr_select == 0) { /* continue search from current position */ next = ogs_list_next(current); for (node = next; node; node = ogs_list_next(node)) { if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated)) return node; } /* cyclic search from top to current position */ for (node = ogs_list_first(&ogs_pfcp_self()->pfcp_peer_list); node != next; node = ogs_list_next(node)) { if (OGS_FSM_CHECK(&node->sm, sgwc_pfcp_state_associated)) return node; } } ogs_error("No SGWUs are PFCP associated that are suited to RR"); return ogs_list_first(&ogs_pfcp_self()->pfcp_peer_list); } void sgwc_sess_select_sgwu(sgwc_sess_t *sess) { char buf[OGS_ADDRSTRLEN]; ogs_assert(sess); /* * When used for the first time, if last node is set, * the search is performed from the first SGW-U in a round-robin manner. */ if (ogs_pfcp_self()->pfcp_node == NULL) ogs_pfcp_self()->pfcp_node = ogs_list_last(&ogs_pfcp_self()->pfcp_peer_list); /* setup GTP session with selected SGW-U */ ogs_pfcp_self()->pfcp_node = selected_sgwu_node(ogs_pfcp_self()->pfcp_node, sess); ogs_assert(ogs_pfcp_self()->pfcp_node); OGS_SETUP_PFCP_NODE(sess, ogs_pfcp_self()->pfcp_node); ogs_debug("UE using SGW-U on IP[%s]", OGS_ADDR(&ogs_pfcp_self()->pfcp_node->addr, buf)); } int sgwc_sess_remove(sgwc_sess_t *sess) { sgwc_ue_t *sgwc_ue = NULL; ogs_assert(sess); sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); ogs_list_remove(&sgwc_ue->sess_list, sess); sgwc_bearer_remove_all(sess); ogs_assert(sess->pfcp.bar); ogs_pfcp_bar_delete(sess->pfcp.bar); ogs_pfcp_pool_final(&sess->pfcp); ogs_assert(sess->session.name); ogs_free(sess->session.name); ogs_pool_free(&sgwc_sess_pool, sess); stats_remove_sgwc_session(); return OGS_OK; } void sgwc_sess_remove_all(sgwc_ue_t *sgwc_ue) { sgwc_sess_t *sess = NULL, *next_sess = NULL; ogs_assert(sgwc_ue); ogs_list_for_each_safe(&sgwc_ue->sess_list, next_sess, sess) sgwc_sess_remove(sess); } sgwc_sess_t *sgwc_sess_find(uint32_t index) { return ogs_pool_find(&sgwc_sess_pool, index); } sgwc_sess_t* sgwc_sess_find_by_teid(uint32_t teid) { return ogs_pool_find(&sgwc_sess_pool, teid); } sgwc_sess_t *sgwc_sess_find_by_seid(uint64_t seid) { return sgwc_sess_find(seid); } sgwc_sess_t* sgwc_sess_find_by_apn(sgwc_ue_t *sgwc_ue, char *apn) { sgwc_sess_t *sess = NULL; ogs_assert(sgwc_ue); ogs_assert(apn); ogs_list_for_each(&sgwc_ue->sess_list, sess) { if (!ogs_strcasecmp(sess->session.name, apn)) return sess; } return NULL; } sgwc_sess_t *sgwc_sess_find_by_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) { sgwc_bearer_t *bearer = NULL; ogs_assert(sgwc_ue); bearer = sgwc_bearer_find_by_ue_ebi(sgwc_ue, ebi); if (bearer) return bearer->sess; return NULL; } sgwc_sess_t *sgwc_sess_cycle(sgwc_sess_t *sess) { return ogs_pool_cycle(&sgwc_sess_pool, sess); } sgwc_bearer_t *sgwc_bearer_add(sgwc_sess_t *sess) { sgwc_bearer_t *bearer = NULL; sgwc_tunnel_t *tunnel = NULL; sgwc_ue_t *sgwc_ue = NULL; ogs_assert(sess); sgwc_ue = sess->sgwc_ue; ogs_assert(sgwc_ue); ogs_pool_alloc(&sgwc_bearer_pool, &bearer); ogs_assert(bearer); memset(bearer, 0, sizeof *bearer); bearer->sgwc_ue = sgwc_ue; bearer->sess = sess; /* Downlink */ tunnel = sgwc_tunnel_add(bearer, OGS_GTP2_F_TEID_S5_S8_SGW_GTP_U); ogs_assert(tunnel); /* Uplink */ tunnel = sgwc_tunnel_add(bearer, OGS_GTP2_F_TEID_S1_U_SGW_GTP_U); ogs_assert(tunnel); ogs_list_add(&sess->bearer_list, bearer); return bearer; } int sgwc_bearer_remove(sgwc_bearer_t *bearer) { ogs_assert(bearer); ogs_assert(bearer->sess); ogs_list_remove(&bearer->sess->bearer_list, bearer); sgwc_tunnel_remove_all(bearer); ogs_pool_free(&sgwc_bearer_pool, bearer); return OGS_OK; } void sgwc_bearer_remove_all(sgwc_sess_t *sess) { sgwc_bearer_t *bearer = NULL, *next_bearer = NULL; ogs_assert(sess); ogs_list_for_each_safe(&sess->bearer_list, next_bearer, bearer) sgwc_bearer_remove(bearer); } sgwc_bearer_t *sgwc_bearer_find_by_sess_ebi(sgwc_sess_t *sess, uint8_t ebi) { sgwc_bearer_t *bearer = NULL; ogs_assert(sess); ogs_list_for_each(&sess->bearer_list, bearer) if (ebi == bearer->ebi) return bearer; return NULL; } sgwc_bearer_t *sgwc_bearer_find_by_ue_ebi(sgwc_ue_t *sgwc_ue, uint8_t ebi) { sgwc_sess_t *sess = NULL; sgwc_bearer_t *bearer = NULL; ogs_assert(sgwc_ue); ogs_list_for_each(&sgwc_ue->sess_list, sess) { ogs_list_for_each(&sess->bearer_list, bearer) { if (ebi == bearer->ebi) return bearer; } } 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); return ogs_list_first(&sess->bearer_list); } sgwc_bearer_t *sgwc_bearer_cycle(sgwc_bearer_t *bearer) { return ogs_pool_cycle(&sgwc_bearer_pool, bearer); } sgwc_tunnel_t *sgwc_tunnel_add( sgwc_bearer_t *bearer, uint8_t interface_type) { sgwc_sess_t *sess = NULL; sgwc_tunnel_t *tunnel = NULL; ogs_pfcp_pdr_t *pdr = NULL; ogs_pfcp_far_t *far = NULL; uint8_t src_if, dst_if; ogs_assert(bearer); sess = bearer->sess; ogs_assert(sess); switch (interface_type) { /* Downlink */ case OGS_GTP2_F_TEID_S5_S8_SGW_GTP_U: src_if = OGS_PFCP_INTERFACE_CORE; dst_if = OGS_PFCP_INTERFACE_ACCESS; break; /* Uplink */ case OGS_GTP2_F_TEID_S1_U_SGW_GTP_U: src_if = OGS_PFCP_INTERFACE_ACCESS; dst_if = OGS_PFCP_INTERFACE_CORE; break; /* Indirect */ case OGS_GTP2_F_TEID_SGW_GTP_U_FOR_DL_DATA_FORWARDING: case OGS_GTP2_F_TEID_SGW_GTP_U_FOR_UL_DATA_FORWARDING: src_if = OGS_PFCP_INTERFACE_ACCESS; dst_if = OGS_PFCP_INTERFACE_ACCESS; break; default: ogs_fatal("Invalid interface type = %d", interface_type); } ogs_pool_alloc(&sgwc_tunnel_pool, &tunnel); ogs_assert(tunnel); memset(tunnel, 0, sizeof *tunnel); tunnel->interface_type = interface_type; tunnel->index = ogs_pool_index(&sgwc_tunnel_pool, tunnel); ogs_assert(tunnel->index > 0 && tunnel->index <= ogs_app()->pool.tunnel); pdr = ogs_pfcp_pdr_add(&sess->pfcp); ogs_assert(pdr); pdr->src_if = src_if; if (sess->session.name) { pdr->apn = ogs_strdup(sess->session.name); ogs_assert(pdr->apn); } far = ogs_pfcp_far_add(&sess->pfcp); ogs_assert(far); far->dst_if = dst_if; ogs_pfcp_pdr_associate_far(pdr, far); far->apply_action = OGS_PFCP_APPLY_ACTION_BUFF| OGS_PFCP_APPLY_ACTION_NOCP; ogs_assert(sess->pfcp.bar); ogs_assert(sess->pfcp_node); if (sess->pfcp_node->up_function_features.ftup) { pdr->f_teid.ch = 1; pdr->f_teid_len = 1; } else { char buf[OGS_ADDRSTRLEN]; ogs_gtpu_resource_t *resource = NULL; ogs_sockaddr_t *addr = sess->pfcp_node->sa_list; ogs_assert(addr); ogs_error("F-TEID allocation/release not supported with peer [%s]:%d", OGS_ADDR(addr, buf), OGS_PORT(addr)); resource = ogs_pfcp_find_gtpu_resource( &sess->pfcp_node->gtpu_resource_list, sess->session.name, OGS_PFCP_INTERFACE_ACCESS); if (resource) { ogs_user_plane_ip_resource_info_to_sockaddr(&resource->info, &tunnel->local_addr, &tunnel->local_addr6); if (resource->info.teidri) tunnel->local_teid = OGS_PFCP_GTPU_INDEX_TO_TEID( tunnel->index, resource->info.teidri, resource->info.teid_range); else tunnel->local_teid = tunnel->index; } else { if (sess->pfcp_node->addr.ogs_sa_family == AF_INET) ogs_assert(OGS_OK == ogs_copyaddrinfo( &tunnel->local_addr, &sess->pfcp_node->addr)); else if (sess->pfcp_node->addr.ogs_sa_family == AF_INET6) ogs_assert(OGS_OK == ogs_copyaddrinfo( &tunnel->local_addr6, &sess->pfcp_node->addr)); else ogs_assert_if_reached(); tunnel->local_teid = tunnel->index; } ogs_assert(OGS_OK == ogs_pfcp_sockaddr_to_f_teid( tunnel->local_addr, tunnel->local_addr6, &pdr->f_teid, &pdr->f_teid_len)); pdr->f_teid.teid = tunnel->local_teid; } tunnel->pdr = pdr; tunnel->far = far; tunnel->bearer = bearer; ogs_list_add(&bearer->tunnel_list, tunnel); return tunnel; } int sgwc_tunnel_remove(sgwc_tunnel_t *tunnel) { ogs_assert(tunnel); ogs_assert(tunnel->bearer); ogs_list_remove(&tunnel->bearer->tunnel_list, tunnel); ogs_pfcp_pdr_remove(tunnel->pdr); ogs_pfcp_far_remove(tunnel->far); if (tunnel->local_addr) ogs_freeaddrinfo(tunnel->local_addr); if (tunnel->local_addr6) ogs_freeaddrinfo(tunnel->local_addr6); ogs_pool_free(&sgwc_tunnel_pool, tunnel); return OGS_OK; } void sgwc_tunnel_remove_all(sgwc_bearer_t *bearer) { sgwc_tunnel_t *tunnel = NULL, *next_tunnel = NULL; ogs_assert(bearer); ogs_list_for_each_safe(&bearer->tunnel_list, next_tunnel, tunnel) sgwc_tunnel_remove(tunnel); } sgwc_tunnel_t *sgwc_tunnel_find_by_teid(sgwc_ue_t *sgwc_ue, uint32_t teid) { sgwc_sess_t *sess = NULL; sgwc_bearer_t *bearer = NULL; sgwc_tunnel_t *tunnel = NULL; ogs_assert(sgwc_ue); ogs_list_for_each(&sgwc_ue->sess_list, sess) { ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->tunnel_list, tunnel) { if (tunnel->local_teid == teid) return tunnel; } } } return NULL; } sgwc_tunnel_t *sgwc_tunnel_find_by_interface_type( sgwc_bearer_t *bearer, uint8_t interface_type) { sgwc_tunnel_t *tunnel = NULL; ogs_assert(bearer); ogs_list_for_each(&bearer->tunnel_list, tunnel) if (tunnel->interface_type == interface_type) return tunnel; return NULL; } sgwc_tunnel_t *sgwc_tunnel_find_by_pdr_id( sgwc_sess_t *sess, ogs_pfcp_pdr_id_t pdr_id) { sgwc_bearer_t *bearer = NULL; sgwc_tunnel_t *tunnel = NULL; ogs_pfcp_pdr_t *pdr = NULL; ogs_assert(sess); ogs_list_for_each(&sess->bearer_list, bearer) { ogs_list_for_each(&bearer->tunnel_list, tunnel) { pdr = tunnel->pdr; ogs_assert(pdr); if (pdr->id == pdr_id) return tunnel; } } return NULL; } sgwc_tunnel_t *sgwc_dl_tunnel_in_bearer(sgwc_bearer_t *bearer) { ogs_assert(bearer); return sgwc_tunnel_find_by_interface_type(bearer, OGS_GTP2_F_TEID_S5_S8_SGW_GTP_U); } sgwc_tunnel_t *sgwc_ul_tunnel_in_bearer(sgwc_bearer_t *bearer) { ogs_assert(bearer); return sgwc_tunnel_find_by_interface_type(bearer, OGS_GTP2_F_TEID_S1_U_SGW_GTP_U); } static void stats_add_sgwc_session(void) { num_of_sgwc_sess = num_of_sgwc_sess + 1; ogs_info("[Added] Number of SGWC-Sessions is now %d", num_of_sgwc_sess); } static void stats_remove_sgwc_session(void) { num_of_sgwc_sess = num_of_sgwc_sess - 1; ogs_info("[Removed] Number of SGWC-Sessions is now %d", num_of_sgwc_sess); }