From 5f7b6ba79e8dd51a05c4349dfb4adef070a9204e Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 21 Mar 2019 17:03:46 +0000 Subject: [PATCH] Split GTP-C and GTP-U functionality into seperate classes in the SPGW. Added GTP-U specific logging in the EPC. Added handle_s11_pdu function to both the MME GTP-C entity and the SPGW GTP-C entity. --- lib/include/srslte/asn1/gtpc_msg.h | 3 + .../srslte/interfaces/epc_interfaces.h | 14 + lib/src/asn1/gtpc.cc | 163 ++++++ srsepc/epc.conf.example | 9 +- srsepc/hdr/mme/mme_gtpc.h | 6 + srsepc/hdr/spgw/gtpc.h | 103 ++++ srsepc/hdr/spgw/gtpu.h | 105 ++++ srsepc/hdr/spgw/spgw.h | 58 +- srsepc/src/main.cc | 86 ++- srsepc/src/mme/mme_gtpc.cc | 37 +- srsepc/src/mme/s1ap_ctx_mngmt_proc.cc | 4 +- srsepc/src/spgw/gtpc.cc | 316 +++++++++++ srsepc/src/spgw/gtpu.cc | 329 ++++++++++++ srsepc/src/spgw/spgw.cc | 501 +----------------- 14 files changed, 1180 insertions(+), 554 deletions(-) create mode 100644 srsepc/hdr/spgw/gtpc.h create mode 100644 srsepc/hdr/spgw/gtpu.h create mode 100644 srsepc/src/spgw/gtpc.cc create mode 100644 srsepc/src/spgw/gtpu.cc diff --git a/lib/include/srslte/asn1/gtpc_msg.h b/lib/include/srslte/asn1/gtpc_msg.h index 9eebb5afa..57bcd3bf6 100644 --- a/lib/include/srslte/asn1/gtpc_msg.h +++ b/lib/include/srslte/asn1/gtpc_msg.h @@ -142,6 +142,9 @@ const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE = 236; // Other // 240 - 255 For future use +// Helper function to pretty print GTP-C message type +const char* gtpc_msg_type_to_str(uint8_t type); + /**************************************************************************** * * GTP-C v2 Create Session Request diff --git a/lib/include/srslte/interfaces/epc_interfaces.h b/lib/include/srslte/interfaces/epc_interfaces.h index 157d676fd..0d44f417a 100644 --- a/lib/include/srslte/interfaces/epc_interfaces.h +++ b/lib/include/srslte/interfaces/epc_interfaces.h @@ -20,7 +20,9 @@ */ #ifndef SRSLTE_EPC_INTERFACES_H #define SRSLTE_EPC_INTERFACES_H + #include "srslte/asn1/gtpc_ies.h" +#include "srslte/common/common.h" #include namespace srsepc { @@ -72,5 +74,17 @@ public: virtual bool resync_sqn(uint64_t imsi, uint8_t* auts) = 0; }; +/******************* + * SPGW Interfaces * + *******************/ +class gtpu_interface_gtpc //GTP-C -> GTP-U +{ +public: + virtual in_addr_t get_s1u_addr() = 0; + + virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid, uint32_t up_ctrl_teid) = 0; + virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4) = 0; +}; + } // namespace srsepc #endif // SRSLTE_EPC_INTERFACES_H diff --git a/lib/src/asn1/gtpc.cc b/lib/src/asn1/gtpc.cc index ccfdafb90..85a5a3557 100644 --- a/lib/src/asn1/gtpc.cc +++ b/lib/src/asn1/gtpc.cc @@ -28,6 +28,169 @@ namespace srslte { +const char* gtpc_msg_type_to_str(uint8_t type) +{ + switch (type) { + case GTPC_MSG_TYPE_RESERVED: + return "GTPC_MSG_TYPE_RESERVED"; + case GTPC_MSG_TYPE_ECHO_REQUEST: + return "GTPC_MSG_TYPE_ECHO_REQUEST"; + case GTPC_MSG_TYPE_ECHO_RESPONSE: + return "GTPC_MSG_TYPE_ECHO_RESPONSE"; + case GTPC_MSG_TYPE_VERSION_SUPPORT: + return "GTPC_MSG_TYPE_VERSION_SUPPORT"; + case GTPC_MSG_TYPE_CREATE_SESSION_REQUEST: + return "GTPC_MSG_TYPE_CREATE_SESSION_REQUEST"; + case GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE: + return "GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE"; + case GTPC_MSG_TYPE_DELETE_SESSION_REQUEST: + return "GTPC_MSG_TYPE_DELETE_SESSION_REQUEST"; + case GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE: + return "GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE"; + case GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST: + return "GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST"; + case GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE: + return "GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE"; + case GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST: + return "GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST"; + case GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE: + return "GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE"; + case GTPC_MSG_TYPE_RESUME_NOTIFICATION: + return "GTPC_MSG_TYPE_RESUME_NOTIFICATION"; + case GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND: + return "GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND"; + case GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION: + return "GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION"; + case GTPC_MSG_TYPE_DELETE_BEARER_COMMAND: + return "GTPC_MSG_TYPE_DELETE_BEARER_COMMAND"; + case GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION: + return "GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION"; + case GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND: + return "GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND"; + case GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION: + return "GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION"; + case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION: + return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION"; + case GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION: + return "GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION"; + case GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION: + return "GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION"; + case GTPC_MSG_TYPE_STOP_PAGING_INDICATION: + return "GTPC_MSG_TYPE_STOP_PAGING_INDICATION"; + case GTPC_MSG_TYPE_CREATE_BEARER_REQUEST: + return "GTPC_MSG_TYPE_CREATE_BEARER_REQUEST"; + case GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE: + return "GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE"; + case GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST: + return "GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST"; + case GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE: + return "GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE"; + case GTPC_MSG_TYPE_DELETE_BEARER_REQUEST: + return "GTPC_MSG_TYPE_DELETE_BEARER_REQUEST"; + case GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE: + return "GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE"; + case GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST: + return "GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST"; + case GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE: + return "GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE"; + case GTPC_MSG_TYPE_IDENTIFICATION_REQUEST: + return "GTPC_MSG_TYPE_IDENTIFICATION_REQUEST"; + case GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE: + return "GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE"; + case GTPC_MSG_TYPE_CONTEXT_REQUEST: + return "GTPC_MSG_TYPE_CONTEXT_REQUEST"; + case GTPC_MSG_TYPE_CONTEXT_RESPONSE: + return "GTPC_MSG_TYPE_CONTEXT_RESPONSE"; + case GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST: + return "GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST"; + case GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE: + return "GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE"; + case GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION: + return "GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION"; + case GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION: + return "GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION"; + case GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST: + return "GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST"; + case GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE: + return "GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE"; + case GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL: + return "GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL"; + case GTPC_MSG_TYPE_RAN_INFORMATION_RELAY: + return "GTPC_MSG_TYPE_RAN_INFORMATION_RELAY"; + case GTPC_MSG_TYPE_DETACH_NOTIFICATION: + return "GTPC_MSG_TYPE_DETACH_NOTIFICATION"; + case GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_CS_PAGING_INDICATION: + return "GTPC_MSG_TYPE_CS_PAGING_INDICATION"; + case GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION: + return "GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION"; + case GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION: + return "GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION"; + case GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_SUSPEND_NOTIFICATION: + return "GTPC_MSG_TYPE_SUSPEND_NOTIFICATION"; + case GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST: + return "GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST"; + case GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE: + return "GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE"; + case GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST: + return "GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST"; + case GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE: + return "GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE"; + case GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST: + return "GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST"; + case GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE: + return "GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE"; + case GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST: + return "GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST"; + case GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE: + return "GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE"; + case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION: + return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION"; + case GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION: + return "GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION"; + case GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE: + return "GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE"; + case GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST: + return "GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST"; + case GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE: + return "GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE"; + case GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST: + return "GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST"; + case GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE: + return "GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE"; + case GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST: + return "GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST"; + case GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE: + return "GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE"; + case GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST: + return "GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST"; + case GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE: + return "GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE"; + case GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST: + return "GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST"; + case GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE: + return "GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE"; + } + return "GTPC_MSG_TYPE_INVALID"; +} + int gtpc_pack_create_session_request(struct gtpc_create_session_request* cs_req, srslte::byte_buffer_t) { // TODO diff --git a/srsepc/epc.conf.example b/srsepc/epc.conf.example index 55004d5c2..2d6e7ce7c 100644 --- a/srsepc/epc.conf.example +++ b/srsepc/epc.conf.example @@ -85,18 +85,21 @@ filename = /tmp/epc.pcap # configured. # Format: e.g. s1ap_hex_limit = 32 # -# Logging layers: s1ap, gtpc, spgw, hss, all +# Logging layers: nas, s1ap, mme_gtpc, spgw_gtpc, gtpu, spgw, hss, all # Logging levels: debug, info, warning, error, none # # filename: File path to use for log output. Can be set to stdout # to print logs to standard output ##################################################################### [log] -all_level = debug +all_level = info all_hex_limit = 32 filename = /tmp/epc.log +#nas_level = debug #s1ap_level = debug -#gtpc_level = debug +#mme_gtpc_level = debug +#spgw_gtpc_level = debug +#gtpu_level = debug #spgw_level = debug #hss_level = debug diff --git a/srsepc/hdr/mme/mme_gtpc.h b/srsepc/hdr/mme/mme_gtpc.h index a854124f8..5864ca9c6 100644 --- a/srsepc/hdr/mme/mme_gtpc.h +++ b/srsepc/hdr/mme/mme_gtpc.h @@ -48,6 +48,7 @@ public: static void cleanup(void); bool init(srslte::log_filter* mme_gtpc_log); + void handle_s11_pdu(srslte::gtpc_pdu* msg); uint32_t get_new_ctrl_teid(); virtual bool send_create_session_request(uint64_t imsi); @@ -74,5 +75,10 @@ private: std::map m_imsi_to_gtpc_ctx; }; +inline uint32_t mme_gtpc::get_new_ctrl_teid() +{ + return m_next_ctrl_teid++; +} + } // namespace srsepc #endif // SRSEPC_MME_GTPC_H diff --git a/srsepc/hdr/spgw/gtpc.h b/srsepc/hdr/spgw/gtpc.h new file mode 100644 index 000000000..c3ff5d686 --- /dev/null +++ b/srsepc/hdr/spgw/gtpc.h @@ -0,0 +1,103 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSEPC_GTPC_H +#define SRSEPC_GTPC_H + +#include "srsepc/hdr/spgw/spgw.h" +#include "srslte/asn1/gtpc.h" +#include "srslte/interfaces/epc_interfaces.h" + +namespace srsepc { + +class spgw::gtpc +{ +public: + gtpc(); + virtual ~gtpc(); + int init(spgw_args_t* args, spgw* spgw, gtpu_interface_gtpc* gtpu, srslte::log_filter* gtpc_log); + void stop(); + + srslte::error_t init_ue_ip(spgw_args_t* args); + + uint64_t get_new_ctrl_teid(); + uint64_t get_new_user_teid(); + in_addr_t get_new_ue_ipv4(); + + void handle_s11_pdu(srslte::gtpc_pdu* msg, srslte::gtpc_pdu* reply_msg); + void handle_create_session_request(const srslte::gtpc_create_session_request& cs_req, srslte::gtpc_pdu* gtpc_pdu); + void handle_modify_bearer_request(const srslte::gtpc_header& mb_req_hdr, + const srslte::gtpc_modify_bearer_request& mb_req, + srslte::gtpc_pdu* mb_resp_pdu); + void handle_delete_session_request(const srslte::gtpc_header& header, + const srslte::gtpc_delete_session_request& del_req, + srslte::gtpc_pdu* del_resp_pdu); + void handle_release_access_bearers_request(const srslte::gtpc_header& header, + const srslte::gtpc_release_access_bearers_request& rel_req, + srslte::gtpc_pdu* rel_resp_pdu); + + spgw_tunnel_ctx_t* create_gtpc_ctx(const srslte::gtpc_create_session_request& cs_req); + bool delete_gtpc_ctx(uint32_t ctrl_teid); + + spgw* m_spgw; + gtpu_interface_gtpc* m_gtpu; + + uint32_t m_h_next_ue_ip; + uint64_t m_next_ctrl_teid; + uint64_t m_next_user_teid; + + std::map m_imsi_to_ctr_teid; // IMSI to control TEID map. Important to check if UE + // is previously connected + std::map m_teid_to_tunnel_ctx; // Map control TEID to tunnel ctx. Usefull to get + // reply ctrl TEID, UE IP, etc. + + srslte::log_filter* m_gtpc_log; + srslte::byte_buffer_pool* m_pool; +}; + +inline uint64_t spgw::gtpc::get_new_ctrl_teid() +{ + return m_next_ctrl_teid++; +} + +inline uint64_t spgw::gtpc::get_new_user_teid() +{ + return m_next_user_teid++; +} + +inline in_addr_t spgw::gtpc::get_new_ue_ipv4() +{ + m_h_next_ue_ip++; + return ntohl(m_h_next_ue_ip); +} + +inline srslte::error_t spgw::gtpc::init_ue_ip(spgw_args_t* args) +{ + m_h_next_ue_ip = ntohl(inet_addr(args->sgi_if_addr.c_str())); + return srslte::ERROR_NONE; +} + +} // namespace srsepc +#endif // SRSEPC_SPGW_H diff --git a/srsepc/hdr/spgw/gtpu.h b/srsepc/hdr/spgw/gtpu.h new file mode 100644 index 000000000..654677c6a --- /dev/null +++ b/srsepc/hdr/spgw/gtpu.h @@ -0,0 +1,105 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSEPC_GTPU_H +#define SRSEPC_GTPU_H + +#include "srsepc/hdr/spgw/spgw.h" +#include "srslte/asn1/gtpc.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/interfaces/epc_interfaces.h" +#include + +namespace srsepc { + +class spgw::gtpu : public gtpu_interface_gtpc { + public: + gtpu(); + virtual ~gtpu(); + int init(spgw_args_t *args, spgw *spgw, srslte::log_filter *gtpu_log); + void stop(); + + srslte::error_t init_sgi(spgw_args_t *args); + srslte::error_t init_s1u(spgw_args_t *args); + int get_sgi(); + int get_s1u(); + + void handle_sgi_pdu(srslte::byte_buffer_t *msg); + void handle_s1u_pdu(srslte::byte_buffer_t *msg); + void send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t *msg); + + virtual in_addr_t get_s1u_addr(); + + virtual bool modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtp_fteid_t dw_user_fteid, uint32_t up_ctr_fteid); + virtual bool delete_gtpu_tunnel(in_addr_t ue_ipv4); + + std::string gtpu_ntoa(uint32_t addr); + + spgw *m_spgw; + + bool m_sgi_up; + int m_sgi; + + bool m_s1u_up; + int m_s1u; + sockaddr_in m_s1u_addr; + + std::map m_ip_to_usr_teid; // Map IP to User-plane TEID for downlink traffic + + srslte::log_filter *m_gtpu_log; + +private: + srslte::byte_buffer_pool *m_pool; +}; + +inline int spgw::gtpu::get_sgi(){ + return m_sgi; +} + +inline int spgw::gtpu::get_s1u(){ + return m_s1u; +} + +inline in_addr_t spgw::gtpu::get_s1u_addr() +{ + return m_s1u_addr.sin_addr.s_addr; +} + +// Helper function to return a string from IPv4 address for easy printing +inline std::string spgw::gtpu::gtpu_ntoa(uint32_t addr){ + char tmp_str[INET_ADDRSTRLEN+1]; + bzero(tmp_str, sizeof(tmp_str)); + struct in_addr tmp_addr; + tmp_addr.s_addr = addr; + const char* tmp_ptr = inet_ntop(AF_INET, &tmp_addr, tmp_str, INET_ADDRSTRLEN); + if(tmp_ptr == NULL){ + return std::string("Invalid IPv4 address"); + } + return std::string(tmp_str); +} + +}// namespace srsepc +#endif // SRSEPC_GTPU_H diff --git a/srsepc/hdr/spgw/spgw.h b/srsepc/hdr/spgw/spgw.h index 7cf2782cd..f7e17d1ca 100644 --- a/srsepc/hdr/spgw/spgw.h +++ b/srsepc/hdr/spgw/spgw.h @@ -33,13 +33,13 @@ #ifndef SRSEPC_SPGW_H #define SRSEPC_SPGW_H -#include +#include "srslte/asn1/gtpc.h" +#include "srslte/common/buffer_pool.h" #include "srslte/common/log.h" -#include "srslte/common/logger_file.h" #include "srslte/common/log_filter.h" -#include "srslte/common/buffer_pool.h" +#include "srslte/common/logger_file.h" #include "srslte/common/threads.h" -#include "srslte/asn1/gtpc.h" +#include namespace srsepc { @@ -54,9 +54,9 @@ typedef struct { } spgw_args_t; typedef struct spgw_tunnel_ctx { - uint64_t imsi; - in_addr_t ue_ipv4; - uint8_t ebi; + uint64_t imsi; + in_addr_t ue_ipv4; + uint8_t ebi; srslte::gtp_fteid_t up_ctrl_fteid; srslte::gtp_fteid_t up_user_fteid; srslte::gtp_fteid_t dw_ctrl_fteid; @@ -65,22 +65,19 @@ typedef struct spgw_tunnel_ctx { class spgw : public thread { + class gtpc; + class gtpu; + public: static spgw* get_instance(void); static void cleanup(void); - int init(spgw_args_t* args, srslte::log_filter* spgw_log); - void stop(); - void run_thread(); - - void handle_create_session_request(struct srslte::gtpc_create_session_request* cs_req, - struct srslte::gtpc_pdu* cs_resp_pdu); - void handle_modify_bearer_request(struct srslte::gtpc_pdu* mb_req_pdu, struct srslte::gtpc_pdu* mb_resp_pdu); - void handle_delete_session_request(struct srslte::gtpc_pdu* del_req_pdu, struct srslte::gtpc_pdu* del_resp_pdu); - void handle_release_access_bearers_request(struct srslte::gtpc_pdu* rel_req_pdu, - struct srslte::gtpc_pdu* rel_resp_pdu); + int init(spgw_args_t* args, srslte::log_filter* gtpu_log, srslte::log_filter* gtpc_log, srslte::log_filter* spgw_log); + void stop(); + void run_thread(); void handle_sgi_pdu(srslte::byte_buffer_t* msg); void handle_s1u_pdu(srslte::byte_buffer_t* msg); + void handle_s11_pdu(srslte::gtpc_pdu* pdu, srslte::gtpc_pdu* reply_pdu); private: spgw(); @@ -102,31 +99,14 @@ private: srslte::byte_buffer_pool* m_pool; mme_gtpc* m_mme_gtpc; - bool m_sgi_up; - int m_sgi_if; - int m_sgi_sock; - - bool m_s1u_up; - int m_s1u; - - uint64_t m_next_ctrl_teid; - uint64_t m_next_user_teid; - - sockaddr_in m_s1u_addr; - + // data-plane/control-plane mutex pthread_mutex_t m_mutex; - std::map m_imsi_to_ctr_teid; // IMSI to control TEID map. - // Important to check if UE is previously connected - - std::map m_teid_to_tunnel_ctx; // Map control TEID to tunnel ctx. - // Usefull to get reply ctrl TEID, UE IP, etc. - - std::map m_ip_to_teid; // Map IP to User-plane TEID for downlink traffic - - uint32_t m_h_next_ue_ip; + // GTP-C and GTP-U handlers + gtpc* m_gtpc; + gtpu* m_gtpu; - /*Logs*/ + // Logs srslte::log_filter* m_spgw_log; }; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index 2cdafac09..fc8ad850c 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -52,8 +52,12 @@ typedef struct { int nas_hex_limit; std::string s1ap_level; int s1ap_hex_limit; - std::string gtpc_level; - int gtpc_hex_limit; + std::string mme_gtpc_level; + int mme_gtpc_hex_limit; + std::string spgw_gtpc_level; + int spgw_gtpc_hex_limit; + std::string gtpu_level; + int gtpu_hex_limit; std::string spgw_level; int spgw_hex_limit; std::string hss_level; @@ -122,24 +126,28 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("spgw.sgi_if_addr", bpo::value(&sgi_if_addr)->default_value("176.16.0.1"), "IP address of TUN interface for the SGi connection") ("spgw.sgi_if_name", bpo::value(&sgi_if_name)->default_value("srs_spgw_sgi"), "Name of TUN interface for the SGi connection") - ("pcap.enable", bpo::value(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP") - ("pcap.filename", bpo::value(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename") - - ("log.nas_level", bpo::value(&args->log_args.nas_level), "MME NAS log level") - ("log.nas_hex_limit", bpo::value(&args->log_args.nas_hex_limit), "MME NAS log hex dump limit") - ("log.s1ap_level", bpo::value(&args->log_args.s1ap_level), "MME S1AP log level") - ("log.s1ap_hex_limit", bpo::value(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit") - ("log.gtpc_level", bpo::value(&args->log_args.gtpc_level), "MME GTPC log level") - ("log.gtpc_hex_limit", bpo::value(&args->log_args.gtpc_hex_limit), "MME GTPC log hex dump limit") - ("log.spgw_level", bpo::value(&args->log_args.spgw_level), "SPGW log level") - ("log.spgw_hex_limit", bpo::value(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit") - ("log.hss_level", bpo::value(&args->log_args.hss_level), "HSS log level") - ("log.hss_hex_limit", bpo::value(&args->log_args.hss_hex_limit), "HSS log hex dump limit") - - ("log.all_level", bpo::value(&args->log_args.all_level)->default_value("info"), "ALL log level") - ("log.all_hex_limit", bpo::value(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit") - - ("log.filename", bpo::value(&args->log_args.filename)->default_value("/tmp/epc.log"),"Log filename") + ("pcap.enable", bpo::value(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP") + ("pcap.filename", bpo::value(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename") + + ("log.nas_level", bpo::value(&args->log_args.nas_level), "MME NAS log level") + ("log.nas_hex_limit", bpo::value(&args->log_args.nas_hex_limit), "MME NAS log hex dump limit") + ("log.s1ap_level", bpo::value(&args->log_args.s1ap_level), "MME S1AP log level") + ("log.s1ap_hex_limit", bpo::value(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit") + ("log.mme_gtpc_level", bpo::value(&args->log_args.mme_gtpc_level), "MME GTPC log level") + ("log.mme_gtpc_hex_limit", bpo::value(&args->log_args.mme_gtpc_hex_limit), "MME GTPC log hex dump limit") + ("log.spgw_gtpc_level", bpo::value(&args->log_args.spgw_gtpc_level), "SPGW GTPC log level") + ("log.spgw_gtpc_hex_limit", bpo::value(&args->log_args.spgw_gtpc_hex_limit), "SPGW GTPC log hex dump limit") + ("log.gtpu_level", bpo::value(&args->log_args.gtpu_level), "GTP-U log level") + ("log.gtpu_hex_limit", bpo::value(&args->log_args.gtpu_hex_limit), "GTP-U log hex dump limit") + ("log.spgw_level", bpo::value(&args->log_args.spgw_level), "SPGW log level") + ("log.spgw_hex_limit", bpo::value(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit") + ("log.hss_level", bpo::value(&args->log_args.hss_level), "HSS log level") + ("log.hss_hex_limit", bpo::value(&args->log_args.hss_hex_limit), "HSS log hex dump limit") + + ("log.all_level", bpo::value(&args->log_args.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log_args.filename)->default_value("/tmp/epc.log"),"Log filename") ; // Positional options - config file location @@ -268,8 +276,14 @@ void parse_args(all_args_t* args, int argc, char* argv[]) if (!vm.count("log.s1ap_level")) { args->log_args.s1ap_level = args->log_args.all_level; } - if (!vm.count("log.gtpc_level")) { - args->log_args.gtpc_level = args->log_args.all_level; + if (!vm.count("log.mme_gtpc_level")) { + args->log_args.mme_gtpc_level = args->log_args.all_level; + } + if (!vm.count("log.spgw_gtpc_level")) { + args->log_args.spgw_gtpc_level = args->log_args.all_level; + } + if (!vm.count("log.gtpu_level")) { + args->log_args.gtpu_level = args->log_args.all_level; } if (!vm.count("log.spgw_level")) { args->log_args.spgw_level = args->log_args.all_level; @@ -284,8 +298,14 @@ void parse_args(all_args_t* args, int argc, char* argv[]) if (!vm.count("log.s1ap_hex_limit")) { args->log_args.s1ap_hex_limit = args->log_args.all_hex_limit; } - if (!vm.count("log.gtpc_hex_limit")) { - args->log_args.gtpc_hex_limit = args->log_args.all_hex_limit; + if (!vm.count("log.mme_gtpc_hex_limit")) { + args->log_args.mme_gtpc_hex_limit = args->log_args.all_hex_limit; + } + if (!vm.count("log.spgw_gtpc_hex_limit")) { + args->log_args.spgw_gtpc_hex_limit = args->log_args.all_hex_limit; + } + if (!vm.count("log.gtpu_hex_limit")) { + args->log_args.gtpu_hex_limit = args->log_args.all_hex_limit; } if (!vm.count("log.spgw_hex_limit")) { args->log_args.spgw_hex_limit = args->log_args.all_hex_limit; @@ -386,15 +406,25 @@ int main(int argc, char* argv[]) s1ap_log.set_hex_limit(args.log_args.s1ap_hex_limit); srslte::log_filter mme_gtpc_log; - mme_gtpc_log.init("GTPC", logger); - mme_gtpc_log.set_level(level(args.log_args.gtpc_level)); - mme_gtpc_log.set_hex_limit(args.log_args.gtpc_hex_limit); + mme_gtpc_log.init("MME GTPC", logger); + mme_gtpc_log.set_level(level(args.log_args.mme_gtpc_level)); + mme_gtpc_log.set_hex_limit(args.log_args.mme_gtpc_hex_limit); srslte::log_filter hss_log; hss_log.init("HSS ", logger); hss_log.set_level(level(args.log_args.hss_level)); hss_log.set_hex_limit(args.log_args.hss_hex_limit); + srslte::log_filter spgw_gtpc_log; + spgw_gtpc_log.init("SPGW GTPC", logger); + spgw_gtpc_log.set_level(level(args.log_args.spgw_gtpc_level)); + spgw_gtpc_log.set_hex_limit(args.log_args.spgw_gtpc_hex_limit); + + srslte::log_filter gtpu_log; + gtpu_log.init("GTPU", logger); + gtpu_log.set_level(level(args.log_args.mme_gtpc_level)); + gtpu_log.set_hex_limit(args.log_args.mme_gtpc_hex_limit); + srslte::log_filter spgw_log; spgw_log.init("SPGW", logger); spgw_log.set_level(level(args.log_args.spgw_level)); @@ -413,7 +443,7 @@ int main(int argc, char* argv[]) } spgw* spgw = spgw::get_instance(); - if (spgw->init(&args.spgw_args, &spgw_log)) { + if (spgw->init(&args.spgw_args, >pu_log, &spgw_gtpc_log, &spgw_log)) { cout << "Error initializing SP-GW" << endl; exit(1); } diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index 8d38925cb..13296493f 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -81,9 +81,20 @@ bool mme_gtpc::init(srslte::log_filter* mme_gtpc_log) return true; } -uint32_t mme_gtpc::get_new_ctrl_teid() +void mme_gtpc::handle_s11_pdu(srslte::gtpc_pdu *pdu) { - return m_next_ctrl_teid++; // FIXME Use a Id pool? + m_mme_gtpc_log->debug("MME Received GTP-C PDU. Message type %s\n",srslte::gtpc_msg_type_to_str(pdu->header.type)); + switch(pdu->header.type){ + case srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE: + handle_create_session_response(pdu); + break; + case srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE: + handle_modify_bearer_response(pdu); + break; + default: + m_mme_gtpc_log->error("Unhandled GTP-C Message type\n"); + } + return; } bool mme_gtpc::send_create_session_request(uint64_t imsi) @@ -150,7 +161,11 @@ bool mme_gtpc::send_create_session_request(uint64_t imsi) bzero(>pc_ctx, sizeof(gtpc_ctx_t)); gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid; m_imsi_to_gtpc_ctx.insert(std::pair(imsi, gtpc_ctx)); - m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu); + + // Send msg to SPGW + srslte::gtpc_pdu mb_resp_pdu; + m_spgw->handle_s11_pdu(&cs_req_pdu, &cs_resp_pdu); + handle_s11_pdu(&cs_resp_pdu); return true; } @@ -267,9 +282,10 @@ bool mme_gtpc::send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify addr.s_addr = enb_fteid->ipv4; m_mme_gtpc_log->info("GTP-C Modify bearer request -- S1-U TEID 0x%x, IP %s\n", enb_fteid->teid, inet_ntoa(addr)); + // Send msg to SPGW srslte::gtpc_pdu mb_resp_pdu; - m_spgw->handle_modify_bearer_request(&mb_req_pdu, &mb_resp_pdu); - handle_modify_bearer_response(&mb_resp_pdu); + m_spgw->handle_s11_pdu(&mb_req_pdu, &mb_resp_pdu); + handle_s11_pdu(&mb_resp_pdu); return true; } @@ -314,11 +330,10 @@ bool mme_gtpc::send_delete_session_request(uint64_t imsi) del_req->cause.cause_value = srslte::GTPC_CAUSE_VALUE_ISR_DEACTIVATION; m_mme_gtpc_log->info("GTP-C Delete Session Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid); + // Send msg to SPGW srslte::gtpc_pdu del_resp_pdu; - m_spgw->handle_delete_session_request(&del_req_pdu, &del_resp_pdu); - - // TODO Handle delete session response - + m_spgw->handle_s11_pdu(&del_req_pdu, &del_resp_pdu); + // Delete GTP-C context std::map::iterator it_imsi = m_mme_ctr_teid_to_imsi.find(mme_ctr_fteid.teid); if (it_imsi == m_mme_ctr_teid_to_imsi.end()) { @@ -353,10 +368,12 @@ void mme_gtpc::send_release_access_bearers_request(uint64_t imsi) srslte::gtpc_release_access_bearers_request* rel_req = &rel_req_pdu.choice.release_access_bearers_request; m_mme_gtpc_log->info("GTP-C Release Access Berarers Request -- S-GW Control TEID %d\n", sgw_ctr_fteid.teid); + // Send msg to SPGW srslte::gtpc_pdu rel_resp_pdu; - m_spgw->handle_release_access_bearers_request(&rel_req_pdu, &rel_resp_pdu); + m_spgw->handle_s11_pdu(&rel_req_pdu, &rel_resp_pdu); // The GTP-C connection will not be torn down, just the user plane bearers. return; } + } // namespace srsepc diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index 702ce01a3..9b98a596d 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -352,8 +352,8 @@ bool s1ap_ctx_mngmt_proc::handle_ue_context_release_complete( // Delete user plane context at the SPGW (but keep GTP-C connection). if (ecm_ctx->state == ECM_STATE_CONNECTED) { // There are active E-RABs, send release access mearers request - m_s1ap_log->console("There are active E-RABs, send release access bearers request"); - m_s1ap_log->info("There are active E-RABs, send release access bearers request"); + m_s1ap_log->console("There are active E-RABs, send release access bearers request\n"); + m_s1ap_log->info("There are active E-RABs, send release access bearers request\n"); m_mme_gtpc->send_release_access_bearers_request(emm_ctx->imsi); // The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED // It will release the UEs downstream S1-U and keep the upstream S1-U connection active. diff --git a/srsepc/src/spgw/gtpc.cc b/srsepc/src/spgw/gtpc.cc new file mode 100644 index 000000000..b455674c0 --- /dev/null +++ b/srsepc/src/spgw/gtpc.cc @@ -0,0 +1,316 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsepc/hdr/spgw/gtpc.h" +#include +#include +#include // for printing uint64_t +#include +#include +#include +#include +#include +#include +#include + +namespace srsepc { + +/********************************************** + * + * GTP-C class that handles the GTP-C protocol + * comminication with the MME + * + **********************************************/ +spgw::gtpc::gtpc() : m_h_next_ue_ip(0), m_next_ctrl_teid(1), m_next_user_teid(1) +{ + return; +} + +spgw::gtpc::~gtpc() +{ + return; +} + +int spgw::gtpc::init(spgw_args_t* args, spgw* spgw, gtpu_interface_gtpc* gtpu, srslte::log_filter* gtpc_log) +{ + srslte::error_t err; + m_pool = srslte::byte_buffer_pool::get_instance(); + + // Init log + m_gtpc_log = gtpc_log; + + // Init interfaces + m_spgw = spgw; + m_gtpu = gtpu; + + // Init IP pool + err = init_ue_ip(args); + if (err != srslte::ERROR_NONE) { + m_gtpc_log->console("Could not initialize the IP pool.\n"); + return -1; + } + + m_gtpc_log->info("SPGW S11 Initialized.\n"); + m_gtpc_log->console("SPGW S11 Initialized.\n"); + return 0; +} + +void spgw::gtpc::stop() +{ + std::map::iterator it = m_teid_to_tunnel_ctx.begin(); + while (it != m_teid_to_tunnel_ctx.end()) { + m_gtpc_log->info("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); + m_gtpc_log->console("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); + delete it->second; + m_teid_to_tunnel_ctx.erase(it++); + } + return; +} + +void spgw::gtpc::handle_s11_pdu(srslte::gtpc_pdu* pdu, srslte::gtpc_pdu* reply_pdu) +{ + m_gtpc_log->console("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type)); + m_gtpc_log->debug("Received GTP-C PDU. Message type: %s\n", srslte::gtpc_msg_type_to_str(pdu->header.type)); + switch (pdu->header.type) { + case srslte::GTPC_MSG_TYPE_CREATE_SESSION_REQUEST: + handle_create_session_request(pdu->choice.create_session_request, reply_pdu); + break; + case srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST: + handle_modify_bearer_request(pdu->header, pdu->choice.modify_bearer_request, reply_pdu); + break; + case srslte::GTPC_MSG_TYPE_DELETE_SESSION_REQUEST: + handle_delete_session_request(pdu->header, pdu->choice.delete_session_request, reply_pdu); + break; + case srslte::GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST: + handle_release_access_bearers_request(pdu->header, pdu->choice.release_access_bearers_request, reply_pdu); + break; + default: + m_gtpc_log->error("Unhandled GTP-C message type\n"); + } + return; +} + +void spgw::gtpc::handle_create_session_request(const struct srslte::gtpc_create_session_request& cs_req, srslte::gtpc_pdu *cs_resp_pdu) +{ + m_gtpc_log->info("SPGW Received Create Session Request\n"); + spgw_tunnel_ctx_t* tunnel_ctx; + int default_bearer_id = 5; + // Check if IMSI has active GTP-C and/or GTP-U + bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req.imsi); + if (gtpc_present) { + m_gtpc_log->console("SPGW: GTP-C context for IMSI %015lu already exists.\n", cs_req.imsi); + delete_gtpc_ctx(m_imsi_to_ctr_teid[cs_req.imsi]); + m_gtpc_log->console("SPGW: Deleted previous context.\n"); + } + + m_gtpc_log->info("Creating new GTP-C context\n"); + tunnel_ctx = create_gtpc_ctx(cs_req); + + // Create session response message + srslte::gtpc_header* header = &cs_resp_pdu->header; + srslte::gtpc_create_session_response* cs_resp = &cs_resp_pdu->choice.create_session_response; + + // Setup GTP-C header + header->piggyback = false; + header->teid_present = true; + header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // Send create session response to the UE's MME Ctrl TEID + header->type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE; + + // Initialize to zero + bzero(cs_resp, sizeof(struct srslte::gtpc_create_session_response)); + // Setup Cause + cs_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + // Setup sender F-TEID (ctrl) + cs_resp->sender_f_teid.ipv4_present = true; + cs_resp->sender_f_teid = tunnel_ctx->up_ctrl_fteid; + + // Bearer context created + cs_resp->eps_bearer_context_created.ebi = default_bearer_id; + cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present = true; + cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid; + + // Fill in the PAA + cs_resp->paa_present = true; + cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4; + cs_resp->paa.ipv4_present = true; + cs_resp->paa.ipv4 = tunnel_ctx->ue_ipv4; + m_gtpc_log->info("Sending Create Session Response\n"); + + // Send Create session response to MME + return; +} + +void spgw::gtpc::handle_modify_bearer_request(const struct srslte::gtpc_header& mb_req_hdr, + const struct srslte::gtpc_modify_bearer_request& mb_req, + srslte::gtpc_pdu* mb_resp_pdu) +{ + m_gtpc_log->info("Received Modified Bearer Request\n"); + + // Get control tunnel info from mb_req PDU + uint32_t ctrl_teid = mb_req_hdr.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if (tunnel_it == m_teid_to_tunnel_ctx.end()) { + m_gtpc_log->warning("Could not find TEID %d to modify\n", ctrl_teid); + return; + } + spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; + + // Store user DW link TEID + tunnel_ctx->dw_user_fteid.teid = mb_req.eps_bearer_context_to_modify.s1_u_enb_f_teid.teid; + tunnel_ctx->dw_user_fteid.ipv4 = mb_req.eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4; + // Set up actual tunnel + m_gtpc_log->info("Setting Up GTP-U tunnel. Tunnel info: \n"); + struct in_addr addr; + addr.s_addr = tunnel_ctx->ue_ipv4; + m_gtpc_log->info("IMSI: %lu, UE IP: %s \n", tunnel_ctx->imsi, inet_ntoa(addr)); + m_gtpc_log->info("S-GW Rx Ctrl TEID 0x%x, MME Rx Ctrl TEID 0x%x\n", tunnel_ctx->up_ctrl_fteid.teid, + tunnel_ctx->dw_ctrl_fteid.teid); + m_gtpc_log->info("S-GW Rx Ctrl IP (NA), MME Rx Ctrl IP (NA)\n"); + + struct in_addr addr2; + addr2.s_addr = tunnel_ctx->up_user_fteid.ipv4; + m_gtpc_log->info("S-GW Rx User TEID 0x%x, S-GW Rx User IP %s\n", tunnel_ctx->up_user_fteid.teid, inet_ntoa(addr2)); + + struct in_addr addr3; + addr3.s_addr = tunnel_ctx->dw_user_fteid.ipv4; + m_gtpc_log->info("eNB Rx User TEID 0x%x, eNB Rx User IP %s\n", tunnel_ctx->dw_user_fteid.teid, inet_ntoa(addr3)); + + // Setup IP to F-TEID map + m_gtpu->modify_gtpu_tunnel(tunnel_ctx->ue_ipv4, tunnel_ctx->dw_user_fteid, tunnel_ctx->up_ctrl_fteid.teid); + + // Setting up Modify bearer response PDU + // Header + srslte::gtpc_header* header = &mb_resp_pdu->header; + header->piggyback = false; + header->teid_present = true; + header->teid = tunnel_ctx->dw_ctrl_fteid.teid; + header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE; + + // PDU + srslte::gtpc_modify_bearer_response* mb_resp = &mb_resp_pdu->choice.modify_bearer_response; + mb_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi; + mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; + + //Send Modify Bearer Response PDU + return; +} + +void spgw::gtpc::handle_delete_session_request(const srslte::gtpc_header& header, + const srslte::gtpc_delete_session_request& del_req_pdu, + srslte::gtpc_pdu* del_resp_pdu) +{ + uint32_t ctrl_teid = header.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if (tunnel_it == m_teid_to_tunnel_ctx.end()) { + m_gtpc_log->warning("Could not find TEID 0x%x to delete session\n", ctrl_teid); + return; + } + spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; + in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; + m_gtpu->delete_gtpu_tunnel(ue_ipv4); + delete_gtpc_ctx(ctrl_teid); + return; +} + +void spgw::gtpc::handle_release_access_bearers_request(const srslte::gtpc_header& header, + const srslte::gtpc_release_access_bearers_request& rel_req, + srslte::gtpc_pdu* rel_resp_pdu) +{ + // Find tunel ctxt + uint32_t ctrl_teid = header.teid; + std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); + if (tunnel_it == m_teid_to_tunnel_ctx.end()) { + m_gtpc_log->warning("Could not find TEID 0x%x to release bearers\n", ctrl_teid); + return; + } + spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; + in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; + + // Delete data tunnel & do NOT delete control tunnel + m_gtpu->delete_gtpu_tunnel(ue_ipv4); + return; +} + +/* + * Context management functions + */ +spgw_tunnel_ctx_t* spgw::gtpc::create_gtpc_ctx(const struct srslte::gtpc_create_session_request& cs_req) +{ + // Setup uplink control TEID + uint64_t spgw_uplink_ctrl_teid = get_new_ctrl_teid(); + // Setup uplink user TEID + uint64_t spgw_uplink_user_teid = get_new_user_teid(); + // Allocate UE IP + in_addr_t ue_ip = get_new_ue_ipv4(); + + uint8_t default_bearer_id = 5; + + m_gtpc_log->console("SPGW: Allocated Ctrl TEID %" PRIu64 "\n", spgw_uplink_ctrl_teid); + m_gtpc_log->console("SPGW: Allocated User TEID %" PRIu64 "\n", spgw_uplink_user_teid); + struct in_addr ue_ip_; + ue_ip_.s_addr = ue_ip; + m_gtpc_log->console("SPGW: Allocate UE IP %s\n", inet_ntoa(ue_ip_)); + + // Save the UE IP to User TEID map + spgw_tunnel_ctx_t* tunnel_ctx = new spgw_tunnel_ctx_t; + + tunnel_ctx->imsi = cs_req.imsi; + tunnel_ctx->ebi = default_bearer_id; + + tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid; + tunnel_ctx->ue_ipv4 = ue_ip; + tunnel_ctx->up_user_fteid.teid = spgw_uplink_user_teid; + tunnel_ctx->up_user_fteid.ipv4 = m_gtpu->get_s1u_addr(); + tunnel_ctx->dw_ctrl_fteid.teid = cs_req.sender_f_teid.teid; + tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req.sender_f_teid.ipv4; + bzero(&tunnel_ctx->dw_user_fteid, sizeof(srslte::gtp_fteid_t)); + + m_teid_to_tunnel_ctx.insert(std::pair(spgw_uplink_ctrl_teid, tunnel_ctx)); + m_imsi_to_ctr_teid.insert(std::pair(cs_req.imsi, spgw_uplink_ctrl_teid)); + return tunnel_ctx; +} + +bool spgw::gtpc::delete_gtpc_ctx(uint32_t ctrl_teid) +{ + spgw_tunnel_ctx_t* tunnel_ctx; + if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) { + m_gtpc_log->error("Could not find GTP context to delete.\n"); + return false; + } + tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid]; + + // Remove Ctrl TEID from IMSI to control TEID map + m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi); + + // Remove GTP context from control TEID mapping + m_teid_to_tunnel_ctx.erase(ctrl_teid); + delete tunnel_ctx; + return true; +} + +} // namespace srsepc diff --git a/srsepc/src/spgw/gtpu.cc b/srsepc/src/spgw/gtpu.cc new file mode 100644 index 000000000..473655273 --- /dev/null +++ b/srsepc/src/spgw/gtpu.cc @@ -0,0 +1,329 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsLTE 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. + * + * srsLTE 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 Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsepc/hdr/spgw/gtpu.h" +#include "srsepc/hdr/mme/mme_gtpc.h" +#include "srslte/upper/gtpu.h" +#include +#include +#include // for printing uint64_t +#include +#include +#include +#include +#include +#include + +namespace srsepc { + +/************************************** + * + * GTP-U class that handles the packet + * forwarding to and from eNBs + * + **************************************/ + +spgw::gtpu::gtpu() : m_sgi_up(false), m_s1u_up(false) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + return; +} + +spgw::gtpu::~gtpu() +{ + return; +} + +int spgw::gtpu::init(spgw_args_t* args, spgw* spgw, srslte::log_filter* gtpu_log) +{ + srslte::error_t err; + + // Init log + m_gtpu_log = gtpu_log; + + // Store interfaces + m_spgw = spgw; + + // Init SGi interface + err = init_sgi(args); + if (err != srslte::ERROR_NONE) { + m_gtpu_log->console("Could not initialize the SGi interface.\n"); + return -1; + } + + // Init S1-U + err = init_s1u(args); + if (err != srslte::ERROR_NONE) { + m_gtpu_log->console("Could not initialize the S1-U interface.\n"); + return -1; + } + + m_gtpu_log->info("SPGW GTP-U Initialized.\n"); + m_gtpu_log->console("SPGW GTP-U Initialized.\n"); + return 0; +} + +void spgw::gtpu::stop() +{ + // Clean up SGi interface + if (m_sgi_up) { + close(m_sgi); + } + // Clean up S1-U socket + if (m_s1u_up) { + close(m_s1u); + } +} + +srslte::error_t spgw::gtpu::init_sgi(spgw_args_t *args) +{ + struct ifreq ifr; + int sgi_sock; + + if (m_sgi_up) { + return (srslte::ERROR_ALREADY_STARTED); + } + + // Construct the TUN device + m_sgi = open("/dev/net/tun", O_RDWR); + m_gtpu_log->info("TUN file descriptor = %d\n", m_sgi); + if (m_sgi < 0) { + m_gtpu_log->error("Failed to open TUN device: %s\n", strerror(errno)); + return (srslte::ERROR_CANT_START); + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_if_name.c_str(), + std::min(args->sgi_if_name.length(), (size_t)(IFNAMSIZ - 1))); + ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; + + if (ioctl(m_sgi, TUNSETIFF, &ifr) < 0) { + m_gtpu_log->error("Failed to set TUN device name: %s\n", strerror(errno)); + close(m_sgi); + return (srslte::ERROR_CANT_START); + } + + // Bring up the interface + sgi_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (ioctl(sgi_sock, SIOCGIFFLAGS, &ifr) < 0) { + m_gtpu_log->error("Failed to bring up socket: %s\n", strerror(errno)); + close(m_sgi); + return (srslte::ERROR_CANT_START); + } + + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if (ioctl(sgi_sock, SIOCSIFFLAGS, &ifr) < 0) { + m_gtpu_log->error("Failed to set socket flags: %s\n", strerror(errno)); + close(sgi_sock); + close(m_sgi); + return (srslte::ERROR_CANT_START); + } + + // Set IP of the interface + struct sockaddr_in *addr = (struct sockaddr_in *)&ifr.ifr_addr; + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str()); + addr->sin_port = 0; + + if (ioctl(sgi_sock, SIOCSIFADDR, &ifr) < 0) { + m_gtpu_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_if_addr.c_str(), + strerror(errno)); + close(m_sgi); + close(sgi_sock); + return srslte::ERROR_CANT_START; + } + + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if (ioctl(sgi_sock, SIOCSIFNETMASK, &ifr) < 0) { + m_gtpu_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno)); + close(m_sgi); + close(sgi_sock); + return srslte::ERROR_CANT_START; + } + + close(sgi_sock); + m_sgi_up = true; + m_gtpu_log->info("Initialized SGi interface\n"); + return (srslte::ERROR_NONE); +} + +srslte::error_t spgw::gtpu::init_s1u(spgw_args_t* args) +{ + // Open S1-U socket + m_s1u = socket(AF_INET, SOCK_DGRAM, 0); + if (m_s1u == -1) { + m_gtpu_log->error("Failed to open socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_s1u_up = true; + + // Bind the socket + m_s1u_addr.sin_family = AF_INET; + m_s1u_addr.sin_addr.s_addr = inet_addr(args->gtpu_bind_addr.c_str()); + m_s1u_addr.sin_port = htons(GTPU_RX_PORT); + + if (bind(m_s1u, (struct sockaddr*)&m_s1u_addr, sizeof(struct sockaddr_in))) { + m_gtpu_log->error("Failed to bind socket: %s\n", strerror(errno)); + return srslte::ERROR_CANT_START; + } + m_gtpu_log->info("S1-U socket = %d\n", m_s1u); + m_gtpu_log->info("S1-U IP = %s, Port = %d \n", inet_ntoa(m_s1u_addr.sin_addr), ntohs(m_s1u_addr.sin_port)); + + m_gtpu_log->info("Initialized S1-U interface\n"); + return srslte::ERROR_NONE; +} + +void spgw::gtpu::handle_sgi_pdu(srslte::byte_buffer_t *msg) +{ + uint8_t version = 0; + uint32_t dest_ip; + bool usr_found = false; + bool ctr_found = false; + + struct in_addr dest_addr; + std::map::iterator gtpu_fteid_it; + std::map::iterator gtpc_teid_it; + srslte::gtpc_f_teid_ie enb_fteid; + uint32_t spgw_teid; + struct iphdr *iph = (struct iphdr *)msg->msg; + m_gtpu_log->debug("Received SGi PDU. Bytes %d\n", msg->N_bytes); + + if (iph->version != 4) { + m_gtpu_log->warning("IPv6 not supported yet.\n"); + return; + } + if (ntohs(iph->tot_len) < 20) { + m_gtpu_log->warning("Invalid IP header length. IP lenght %d.\n", ntohs(iph->tot_len)); + return; + } + + //Logging PDU info + m_gtpu_log->debug("SGi PDU -- IP version %d, Total length %d\n", iph->version, ntohs(iph->tot_len)); + m_gtpu_log->debug("SGi PDU -- IP src addr %s\n", gtpu_ntoa(iph->saddr).c_str()); + m_gtpu_log->debug("SGi PDU -- IP dst addr %s\n", gtpu_ntoa(iph->daddr).c_str()); + + // Find user and control tunnel + pthread_mutex_lock(&m_spgw->m_mutex); + gtpu_fteid_it = m_ip_to_usr_teid.find(iph->daddr); + if (gtpu_fteid_it != m_ip_to_usr_teid.end()) { + usr_found = true; + enb_fteid = gtpu_fteid_it->second; + } + pthread_mutex_unlock(&m_spgw->m_mutex); + + if (usr_found == false) { + m_gtpu_log->debug("Packet for unknown UE. Discarding packet.\n"); + return; + } else { + send_s1u_pdu(enb_fteid, msg); + } + return; +} + +void spgw::gtpu::handle_s1u_pdu(srslte::byte_buffer_t* msg) +{ + srslte::gtpu_header_t header; + srslte::gtpu_read_header(msg, &header, m_gtpu_log); + + m_gtpu_log->debug("Received PDU from S1-U. Bytes=%d\n", msg->N_bytes); + m_gtpu_log->debug("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes); + int n = write(m_sgi, msg->msg, msg->N_bytes); + if (n < 0) { + m_gtpu_log->error("Could not write to TUN interface.\n"); + } else { + m_gtpu_log->debug("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes); + } + return; +} + +void spgw::gtpu::send_s1u_pdu(srslte::gtp_fteid_t enb_fteid, srslte::byte_buffer_t* msg) +{ + // Set eNB destination address + struct sockaddr_in enb_addr; + enb_addr.sin_family = AF_INET; + enb_addr.sin_port = htons(GTPU_RX_PORT); + enb_addr.sin_addr.s_addr = enb_fteid.ipv4; + + // Setup GTP-U header + srslte::gtpu_header_t header; + header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL; + header.message_type = GTPU_MSG_DATA_PDU; + header.length = msg->N_bytes; + header.teid = enb_fteid.teid; + + m_gtpu_log->debug("User plane tunnel found SGi PDU. Forwarding packet to S1-U.\n"); + m_gtpu_log->debug("eNB F-TEID -- eNB IP %s, eNB TEID 0x%x.\n", inet_ntoa(enb_addr.sin_addr), enb_fteid.teid); + + // Write header into packet + if (!srslte::gtpu_write_header(&header, msg, m_gtpu_log)) { + m_gtpu_log->error("Error writing GTP-U header on PDU\n"); + return; + } + + // Send packet to destination + int n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr)); + if (n < 0) { + m_gtpu_log->error("Error sending packet to eNB\n"); + } else if ((unsigned int)n != msg->N_bytes) { + m_gtpu_log->error("Mis-match between packet bytes and sent bytes: Sent: %d/%d\n", n, msg->N_bytes); + } + return; +} + +/* + * Tunnel managment + */ +bool spgw::gtpu::modify_gtpu_tunnel(in_addr_t ue_ipv4, srslte::gtpc_f_teid_ie dw_user_fteid, + uint32_t up_ctrl_teid) +{ + m_gtpu_log->info("Modifying GTP-U Tunnel.\n"); + m_gtpu_log->info("UE IP %s\n", gtpu_ntoa(ue_ipv4).c_str()); + m_gtpu_log->info("Downlink eNB addr %s, U-TEID 0x%x\n", gtpu_ntoa(dw_user_fteid.ipv4).c_str(), dw_user_fteid.teid); + m_gtpu_log->info("Uplink C-TEID: 0x%x\n", up_ctrl_teid); + pthread_mutex_lock(&m_spgw->m_mutex); + m_ip_to_usr_teid[ue_ipv4] = dw_user_fteid; + pthread_mutex_unlock(&m_spgw->m_mutex); + return true; +} + +bool spgw::gtpu::delete_gtpu_tunnel(in_addr_t ue_ipv4) +{ + // Remove GTP-U connections, if any. + pthread_mutex_lock(&m_spgw->m_mutex); + if (m_ip_to_usr_teid.count(ue_ipv4)) { + m_ip_to_usr_teid.erase(ue_ipv4); + pthread_mutex_unlock(&m_spgw->m_mutex); + } else { + m_gtpu_log->error("Could not find GTP-U Tunnel to delete.\n"); + pthread_mutex_unlock(&m_spgw->m_mutex); + return false; + } + return true; +} + +} // namespace srsepc diff --git a/srsepc/src/spgw/spgw.cc b/srsepc/src/spgw/spgw.cc index 3e5fd15a1..9dcc369ea 100644 --- a/srsepc/src/spgw/spgw.cc +++ b/srsepc/src/spgw/spgw.cc @@ -26,31 +26,27 @@ #include "srsepc/hdr/spgw/spgw.h" #include "srsepc/hdr/mme/mme_gtpc.h" +#include "srsepc/hdr/spgw/gtpc.h" +#include "srsepc/hdr/spgw/gtpu.h" #include "srslte/upper/gtpu.h" -#include -#include #include // for printing uint64_t -#include -#include -#include -#include -#include -#include namespace srsepc { spgw* spgw::m_instance = NULL; pthread_mutex_t spgw_instance_mutex = PTHREAD_MUTEX_INITIALIZER; -const uint16_t SPGW_BUFFER_SIZE = 2500; - -spgw::spgw() : m_running(false), m_sgi_up(false), m_s1u_up(false), m_next_ctrl_teid(1), m_next_user_teid(1) +spgw::spgw() : m_running(false) { + m_gtpc = new spgw::gtpc; + m_gtpu = new spgw::gtpu; return; } spgw::~spgw() { + delete m_gtpc; + delete m_gtpu; return; } @@ -74,7 +70,10 @@ void spgw::cleanup() pthread_mutex_unlock(&spgw_instance_mutex); } -int spgw::init(spgw_args_t* args, srslte::log_filter* spgw_log) +int spgw::init(spgw_args_t* args, + srslte::log_filter* gtpu_log, + srslte::log_filter* gtpc_log, + srslte::log_filter* spgw_log) { srslte::error_t err; m_pool = srslte::byte_buffer_pool::get_instance(); @@ -83,22 +82,14 @@ int spgw::init(spgw_args_t* args, srslte::log_filter* spgw_log) m_spgw_log = spgw_log; m_mme_gtpc = mme_gtpc::get_instance(); - // Init SGi interface - err = init_sgi_if(args); - if (err != srslte::ERROR_NONE) { - m_spgw_log->console("Could not initialize the SGi interface.\n"); + // Init GTP-U + if (m_gtpu->init(args, this, gtpu_log) != 0) { + m_spgw_log->console("Could not initialize the SPGW's GTP-U.\n"); return -1; } - // Init S1-U - err = init_s1u(args); - if (err != srslte::ERROR_NONE) { - m_spgw_log->console("Could not initialize the S1-U interface.\n"); - return -1; - } - // Initialize UE ip pool - err = init_ue_ip(args); - if (err != srslte::ERROR_NONE) { + // Init GTP-C + if (m_gtpc->init(args, this, m_gtpu, gtpc_log) != 0) { m_spgw_log->console("Could not initialize the S1-U interface.\n"); return -1; } @@ -116,130 +107,13 @@ void spgw::stop() m_running = false; thread_cancel(); wait_thread_finish(); - - // Clean up SGi interface - if (m_sgi_up) { - close(m_sgi_if); - close(m_sgi_sock); - } - // Clean up S1-U socket - if (m_s1u_up) { - close(m_s1u); - } } - //Delete GTP-C tunnel - std::map::iterator it = m_teid_to_tunnel_ctx.begin(); - while (it != m_teid_to_tunnel_ctx.end()) { - m_spgw_log->info("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); - m_spgw_log->console("Deleting SP-GW GTP-C Tunnel. IMSI: %lu\n", it->second->imsi); - delete it->second; - m_teid_to_tunnel_ctx.erase(it++); - } + m_gtpu->stop(); + m_gtpc->stop(); return; } -srslte::error_t spgw::init_sgi_if(spgw_args_t* args) -{ - if (m_sgi_up) { - return (srslte::ERROR_ALREADY_STARTED); - } - - // Construct the TUN device - m_sgi_if = open("/dev/net/tun", O_RDWR); - m_spgw_log->info("TUN file descriptor = %d\n", m_sgi_if); - if (m_sgi_if < 0) { - m_spgw_log->error("Failed to open TUN device: %s\n", strerror(errno)); - return (srslte::ERROR_CANT_START); - } - - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_if_name.c_str(), - std::min(args->sgi_if_name.length(), (size_t)(IFNAMSIZ - 1))); - ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = '\0'; - - if (ioctl(m_sgi_if, TUNSETIFF, &ifr) < 0) { - m_spgw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); - close(m_sgi_if); - return (srslte::ERROR_CANT_START); - } - - // Bring up the interface - m_sgi_sock = socket(AF_INET, SOCK_DGRAM, 0); - - if (ioctl(m_sgi_sock, SIOCGIFFLAGS, &ifr) < 0) { - m_spgw_log->error("Failed to bring up socket: %s\n", strerror(errno)); - close(m_sgi_if); - return (srslte::ERROR_CANT_START); - } - - ifr.ifr_flags |= IFF_UP | IFF_RUNNING; - if (ioctl(m_sgi_sock, SIOCSIFFLAGS, &ifr) < 0) { - m_spgw_log->error("Failed to set socket flags: %s\n", strerror(errno)); - close(m_sgi_if); - return (srslte::ERROR_CANT_START); - } - - // Set IP of the interface - struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str()); - addr->sin_port = 0; - - if (ioctl(m_sgi_sock, SIOCSIFADDR, &ifr) < 0) { - m_spgw_log->error("Failed to set TUN interface IP. Address: %s, Error: %s\n", args->sgi_if_addr.c_str(), - strerror(errno)); - close(m_sgi_if); - close(m_sgi_sock); - return srslte::ERROR_CANT_START; - } - - ifr.ifr_netmask.sa_family = AF_INET; - ((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); - if (ioctl(m_sgi_sock, SIOCSIFNETMASK, &ifr) < 0) { - m_spgw_log->error("Failed to set TUN interface Netmask. Error: %s\n", strerror(errno)); - close(m_sgi_if); - close(m_sgi_sock); - return srslte::ERROR_CANT_START; - } - - m_sgi_up = true; - return (srslte::ERROR_NONE); -} - -srslte::error_t spgw::init_s1u(spgw_args_t* args) -{ - // Open S1-U socket - m_s1u = socket(AF_INET, SOCK_DGRAM, 0); - if (m_s1u == -1) { - m_spgw_log->error("Failed to open socket: %s\n", strerror(errno)); - return srslte::ERROR_CANT_START; - } - m_s1u_up = true; - - // Bind the socket - m_s1u_addr.sin_family = AF_INET; - m_s1u_addr.sin_addr.s_addr = inet_addr(args->gtpu_bind_addr.c_str()); - m_s1u_addr.sin_port = htons(GTPU_RX_PORT); - - if (bind(m_s1u, (struct sockaddr*)&m_s1u_addr, sizeof(struct sockaddr_in))) { - m_spgw_log->error("Failed to bind socket: %s\n", strerror(errno)); - return srslte::ERROR_CANT_START; - } - m_spgw_log->info("S1-U socket = %d\n", m_s1u); - m_spgw_log->info("S1-U IP = %s, Port = %d \n", inet_ntoa(m_s1u_addr.sin_addr), ntohs(m_s1u_addr.sin_port)); - - return srslte::ERROR_NONE; -} - -srslte::error_t spgw::init_ue_ip(spgw_args_t* args) -{ - m_h_next_ue_ip = ntohl(inet_addr(args->sgi_if_addr.c_str())); - return srslte::ERROR_NONE; -} - void spgw::run_thread() { // Mark the thread as running @@ -250,29 +124,28 @@ void spgw::run_thread() struct sockaddr src_addr; socklen_t addrlen; struct iphdr* ip_pkt; - int sgi = m_sgi_if; + int sgi = m_gtpu->get_sgi(); + int s1u = m_gtpu->get_s1u(); fd_set set; - // struct timeval to; - int max_fd = std::max(m_s1u, sgi); + int max_fd = std::max(s1u, sgi); while (m_running) { msg->reset(); FD_ZERO(&set); - FD_SET(m_s1u, &set); + FD_SET(s1u, &set); FD_SET(sgi, &set); - // m_spgw_log->info("Waiting for S1-U or SGi packets.\n"); int n = select(max_fd + 1, &set, NULL, NULL, NULL); if (n == -1) { m_spgw_log->error("Error from select\n"); } else if (n) { - if (FD_ISSET(m_s1u, &set)) { - msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen); - handle_s1u_pdu(msg); + if (FD_ISSET(s1u, &set)) { + msg->N_bytes = recvfrom(s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen); + m_gtpu->handle_s1u_pdu(msg); } - if (FD_ISSET(m_sgi_if, &set)) { + if (FD_ISSET(sgi, &set)) { msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); - handle_sgi_pdu(msg); + m_gtpu->handle_sgi_pdu(msg); } } else { m_spgw_log->debug("No data from select.\n"); @@ -282,323 +155,7 @@ void spgw::run_thread() return; } -void spgw::handle_sgi_pdu(srslte::byte_buffer_t* msg) -{ - uint8_t version = 0; - uint32_t dest_ip; - struct in_addr dest_addr; - std::map::iterator gtp_fteid_it; - bool ip_found = false; - srslte::gtpc_f_teid_ie enb_fteid; - - struct iphdr* iph = (struct iphdr*)msg->msg; - if (iph->version != 4) { - m_spgw_log->warning("IPv6 not supported yet.\n"); - return; - } - if (iph->tot_len < 20) { - m_spgw_log->warning("Invalid IP header length.\n"); - return; - } - - pthread_mutex_lock(&m_mutex); - gtp_fteid_it = m_ip_to_teid.find(iph->daddr); - if (gtp_fteid_it != m_ip_to_teid.end()) { - ip_found = true; - enb_fteid = gtp_fteid_it->second; - } - pthread_mutex_unlock(&m_mutex); - - if (ip_found == false) { - return; - } - - struct sockaddr_in enb_addr; - enb_addr.sin_family = AF_INET; - enb_addr.sin_port = htons(GTPU_RX_PORT); - enb_addr.sin_addr.s_addr = enb_fteid.ipv4; - - // Setup GTP-U header - srslte::gtpu_header_t header; - header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL; - header.message_type = GTPU_MSG_DATA_PDU; - header.length = msg->N_bytes; - header.teid = enb_fteid.teid; - - // Write header into packet - if (!srslte::gtpu_write_header(&header, msg, m_spgw_log)) { - m_spgw_log->console("Error writing GTP-U header on PDU\n"); - } - - // Send packet to destination - int n = sendto(m_s1u, msg->msg, msg->N_bytes, 0, (struct sockaddr*)&enb_addr, sizeof(enb_addr)); - if (n < 0) { - m_spgw_log->error("Error sending packet to eNB\n"); - return; - } else if ((unsigned int)n != msg->N_bytes) { - m_spgw_log->error("Mis-match between packet bytes and sent bytes: Sent: %d, Packet: %d \n", n, msg->N_bytes); - } - - return; -} - -void spgw::handle_s1u_pdu(srslte::byte_buffer_t* msg) -{ - // m_spgw_log->console("Received PDU from S1-U. Bytes=%d\n",msg->N_bytes); - srslte::gtpu_header_t header; - srslte::gtpu_read_header(msg, &header, m_spgw_log); - - // m_spgw_log->console("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes); - int n = write(m_sgi_if, msg->msg, msg->N_bytes); - if (n < 0) { - m_spgw_log->error("Could not write to TUN interface.\n"); - } else { - // m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes); - } - return; +void spgw::handle_s11_pdu(srslte::gtpc_pdu *pdu, srslte::gtpc_pdu *reply_pdu){ + m_gtpc->handle_s11_pdu(pdu, reply_pdu); } - - -/* - * GTP-C Handler Functions - */ -void spgw::handle_create_session_request(struct srslte::gtpc_create_session_request* cs_req, - struct srslte::gtpc_pdu* cs_resp_pdu) -{ - m_spgw_log->info("Received Create Session Request\n"); - spgw_tunnel_ctx_t* tunnel_ctx; - int default_bearer_id = 5; - // Check if IMSI has active GTP-C and/or GTP-U - bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req->imsi); - if (gtpc_present) { - m_spgw_log->console("SPGW: GTP-C context for IMSI %015" PRIu64 " already exists.\n", cs_req->imsi); - delete_gtp_ctx(m_imsi_to_ctr_teid[cs_req->imsi]); - m_spgw_log->console("SPGW: Deleted previous context.\n"); - } - - m_spgw_log->info("Creating new GTP-C context\n"); - tunnel_ctx = create_gtp_ctx(cs_req); - - // Create session response message - srslte::gtpc_header* header = &cs_resp_pdu->header; - srslte::gtpc_create_session_response* cs_resp = &cs_resp_pdu->choice.create_session_response; - - // Setup GTP-C header - header->piggyback = false; - header->teid_present = true; - header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // Send create session requesponse to the UE's MME Ctrl TEID - header->type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE; - - // Initialize to zero - bzero(cs_resp, sizeof(struct srslte::gtpc_create_session_response)); - // Setup Cause - cs_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; - // Setup sender F-TEID (ctrl) - cs_resp->sender_f_teid.ipv4_present = true; - cs_resp->sender_f_teid = tunnel_ctx->up_ctrl_fteid; - - // Bearer context created - cs_resp->eps_bearer_context_created.ebi = default_bearer_id; - cs_resp->eps_bearer_context_created.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; - cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present = true; - cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid = tunnel_ctx->up_user_fteid; - - // Fill in the PAA - cs_resp->paa_present = true; - cs_resp->paa.pdn_type = srslte::GTPC_PDN_TYPE_IPV4; - cs_resp->paa.ipv4_present = true; - cs_resp->paa.ipv4 = tunnel_ctx->ue_ipv4; - m_spgw_log->info("Sending Create Session Response\n"); - m_mme_gtpc->handle_create_session_response(cs_resp_pdu); - return; -} - -void spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu* mb_req_pdu, struct srslte::gtpc_pdu* mb_resp_pdu) -{ - m_spgw_log->info("Received Modified Bearer Request\n"); - - // Get control tunnel info from mb_req PDU - uint32_t ctrl_teid = mb_req_pdu->header.teid; - std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); - if (tunnel_it == m_teid_to_tunnel_ctx.end()) { - m_spgw_log->warning("Could not find TEID %d to modify\n", ctrl_teid); - return; - } - spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; - - // Store user DW link TEID - srslte::gtpc_modify_bearer_request* mb_req = &mb_req_pdu->choice.modify_bearer_request; - tunnel_ctx->dw_user_fteid.teid = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid; - tunnel_ctx->dw_user_fteid.ipv4 = mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4; - // Set up actual tunnel - m_spgw_log->info("Setting Up GTP-U tunnel. Tunnel info: \n"); - struct in_addr addr; - addr.s_addr = tunnel_ctx->ue_ipv4; - m_spgw_log->info("IMSI: %lu, UE IP, %s \n", tunnel_ctx->imsi, inet_ntoa(addr)); - m_spgw_log->info("S-GW Rx Ctrl TEID 0x%x, MME Rx Ctrl TEID 0x%x\n", tunnel_ctx->up_ctrl_fteid.teid, - tunnel_ctx->dw_ctrl_fteid.teid); - m_spgw_log->info("S-GW Rx Ctrl IP (NA), MME Rx Ctrl IP (NA)\n"); - - struct in_addr addr2; - addr2.s_addr = tunnel_ctx->up_user_fteid.ipv4; - m_spgw_log->info("S-GW Rx User TEID 0x%x, S-GW Rx User IP %s\n", tunnel_ctx->up_user_fteid.teid, inet_ntoa(addr2)); - - struct in_addr addr3; - addr3.s_addr = tunnel_ctx->dw_user_fteid.ipv4; - m_spgw_log->info("eNB Rx User TEID 0x%x, eNB Rx User IP %s\n", tunnel_ctx->dw_user_fteid.teid, inet_ntoa(addr3)); - - // Setup IP to F-TEID map - // bool ret = false; - pthread_mutex_lock(&m_mutex); - m_ip_to_teid[tunnel_ctx->ue_ipv4] = tunnel_ctx->dw_user_fteid; - // ret = m_ip_to_teid.insert(std::pair(tunnel_ctx->ue_ipv4, - // tunnel_ctx->dw_user_fteid)); - pthread_mutex_unlock(&m_mutex); - - // Setting up Modify bearer response PDU - // Header - srslte::gtpc_header* header = &mb_resp_pdu->header; - header->piggyback = false; - header->teid_present = true; - header->teid = tunnel_ctx->dw_ctrl_fteid.teid; // - header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE; - - // PDU - srslte::gtpc_modify_bearer_response* mb_resp = &mb_resp_pdu->choice.modify_bearer_response; - mb_resp->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; - mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi; - mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED; - - return; -} - -void spgw::handle_delete_session_request(struct srslte::gtpc_pdu* del_req_pdu, struct srslte::gtpc_pdu* del_resp_pdu) -{ - // Find tunel ctxt - uint32_t ctrl_teid = del_req_pdu->header.teid; - std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); - if (tunnel_it == m_teid_to_tunnel_ctx.end()) { - m_spgw_log->warning("Could not find TEID %d to delete\n", ctrl_teid); - return; - } - spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; - in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; - - // Delete data tunnel - pthread_mutex_lock(&m_mutex); - std::map::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4); - if (data_it != m_ip_to_teid.end()) { - m_ip_to_teid.erase(data_it); - } - pthread_mutex_unlock(&m_mutex); - m_teid_to_tunnel_ctx.erase(tunnel_it); - - delete tunnel_ctx; - return; -} - -void spgw::handle_release_access_bearers_request(struct srslte::gtpc_pdu* rel_req_pdu, - struct srslte::gtpc_pdu* rel_resp_pdu) -{ - // Find tunel ctxt - uint32_t ctrl_teid = rel_req_pdu->header.teid; - std::map::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_teid); - if (tunnel_it == m_teid_to_tunnel_ctx.end()) { - m_spgw_log->warning("Could not find TEID %d to release bearers from\n", ctrl_teid); - return; - } - spgw_tunnel_ctx_t* tunnel_ctx = tunnel_it->second; - in_addr_t ue_ipv4 = tunnel_ctx->ue_ipv4; - - // Delete data tunnel - pthread_mutex_lock(&m_mutex); - std::map::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4); - if (data_it != m_ip_to_teid.end()) { - m_ip_to_teid.erase(data_it); - } - pthread_mutex_unlock(&m_mutex); - - // Do NOT delete control tunnel - return; -} - -/* - * Helper Functions - */ -uint64_t spgw::get_new_ctrl_teid() -{ - return m_next_ctrl_teid++; -} - -uint64_t spgw::get_new_user_teid() -{ - return m_next_user_teid++; -} - -in_addr_t spgw::get_new_ue_ipv4() -{ - m_h_next_ue_ip++; - return ntohl(m_h_next_ue_ip); // FIXME Tmp hack -} - -spgw_tunnel_ctx_t* spgw::create_gtp_ctx(struct srslte::gtpc_create_session_request* cs_req) -{ - // Setup uplink control TEID - uint64_t spgw_uplink_ctrl_teid = get_new_ctrl_teid(); - // Setup uplink user TEID - uint64_t spgw_uplink_user_teid = get_new_user_teid(); - // Allocate UE IP - in_addr_t ue_ip = get_new_ue_ipv4(); - // in_addr_t ue_ip = inet_addr("172.16.0.2"); - uint8_t default_bearer_id = 5; - - m_spgw_log->console("SPGW: Allocated Ctrl TEID %" PRIu64 "\n", spgw_uplink_ctrl_teid); - m_spgw_log->console("SPGW: Allocated User TEID %" PRIu64 "\n", spgw_uplink_user_teid); - struct in_addr ue_ip_; - ue_ip_.s_addr = ue_ip; - m_spgw_log->console("SPGW: Allocate UE IP %s\n", inet_ntoa(ue_ip_)); - - // Save the UE IP to User TEID map - spgw_tunnel_ctx_t* tunnel_ctx = new spgw_tunnel_ctx_t; - bzero(tunnel_ctx, sizeof(spgw_tunnel_ctx_t)); - - tunnel_ctx->imsi = cs_req->imsi; - tunnel_ctx->ebi = default_bearer_id; - tunnel_ctx->up_user_fteid.teid = spgw_uplink_user_teid; - tunnel_ctx->up_user_fteid.ipv4 = m_s1u_addr.sin_addr.s_addr; - tunnel_ctx->dw_ctrl_fteid.teid = cs_req->sender_f_teid.teid; - tunnel_ctx->dw_ctrl_fteid.ipv4 = cs_req->sender_f_teid.ipv4; - - tunnel_ctx->up_ctrl_fteid.teid = spgw_uplink_ctrl_teid; - tunnel_ctx->ue_ipv4 = ue_ip; - m_teid_to_tunnel_ctx.insert(std::pair(spgw_uplink_ctrl_teid, tunnel_ctx)); - m_imsi_to_ctr_teid.insert(std::pair(cs_req->imsi, spgw_uplink_ctrl_teid)); - return tunnel_ctx; -} - -bool spgw::delete_gtp_ctx(uint32_t ctrl_teid) -{ - spgw_tunnel_ctx_t* tunnel_ctx; - if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) { - m_spgw_log->error("Could not find GTP context to delete.\n"); - return false; - } - tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid]; - - // Remove GTP-U connections, if any. - if (m_ip_to_teid.count(tunnel_ctx->ue_ipv4)) { - pthread_mutex_lock(&m_mutex); - m_ip_to_teid.erase(tunnel_ctx->ue_ipv4); - pthread_mutex_unlock(&m_mutex); - } - - // Remove Ctrl TEID from IMSI to control TEID map - m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi); - - // Remove GTP context from control TEID mapping - m_teid_to_tunnel_ctx.erase(ctrl_teid); - delete tunnel_ctx; - return true; -} - } // namespace srsepc