diff --git a/CMakeLists.txt b/CMakeLists.txt index 60484ae5a..ad478f047 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,7 @@ configure_file( ######################################################################## option(ENABLE_SRSUE "Build srsUE application" ON) option(ENABLE_SRSENB "Build srsENB application" ON) +option(ENABLE_SRSEPC "Build srsEPC application" ON) option(DISABLE_SIMD "disable simd instructions" OFF) option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) @@ -161,7 +162,7 @@ else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND) endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND) # Boost -if(ENABLE_SRSUE OR ENABLE_SRSENB) +if(ENABLE_SRSUE OR ENABLE_SRSENB OR ENABLE_SRSEPC) if(BUILD_STATIC) set(Boost_USE_STATIC_LIBS ON) endif(BUILD_STATIC) @@ -183,7 +184,7 @@ if(ENABLE_SRSUE OR ENABLE_SRSENB) "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" ) find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) -endif(ENABLE_SRSUE OR ENABLE_SRSENB) +endif(ENABLE_SRSUE OR ENABLE_SRSENB OR ENABLE_SRSEPC) # srsGUI if(ENABLE_GUI) @@ -374,3 +375,10 @@ if(RF_FOUND) else(RF_FOUND) message(STATUS "srsUE and srsENB builds disabled due to missing RF driver") endif(RF_FOUND) + +if(ENABLE_SRSEPC) + message(STATUS "Building with srsEPC") + add_subdirectory(srsepc) +else(ENABLE_SRSEPC) + message(STATUS "srsEPC build disabled") +endif(ENABLE_SRSEPC) diff --git a/lib/include/srslte/asn1/gtpc.h b/lib/include/srslte/asn1/gtpc.h new file mode 100644 index 000000000..aca57a4bf --- /dev/null +++ b/lib/include/srslte/asn1/gtpc.h @@ -0,0 +1,102 @@ +/* \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 GTPC_V2_H +#define GTPC_V2_H + +#include +#include "srslte/asn1/gtpc_msg.h" + + +namespace srslte{ + +/*GTP-C Version*/ +const uint8_t GTPC_V2 = 2; + +/**************************************************************************** + * GTP-C v2 Header + * Ref: 3GPP TS 29.274 v10.14.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version | P | T | S | S | S | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * m | If T=1, TEID (1st Octet) | + * m+1 | If T=1, TEID (2nd Octet) | + * m+2 | If T=1, TEID (3st Octet) | + * m+3 | If T=1, TEID (4st Octet) | + * n | Sequence | + * n+1 | Sequence | + * n+2 | Sequence | + * n+3 | Spare | + ***************************************************************************/ + + typedef struct gtpc_header + { + uint8_t version; + bool piggyback; + bool teid_present; + uint8_t type; + uint64_t teid; + uint64_t sequence; + } gtpc_header_t; + +/**************************************************************************** + * GTP-C v2 Payload + * Ref: 3GPP TS 29.274 v10.14.0 Section 5 + * + * Union that hold the different structures for the possible message types. + ***************************************************************************/ + +typedef union gtpc_msg_choice +{ + struct gtpc_create_session_request create_session_request; + struct gtpc_create_session_response create_session_response; + struct gtpc_modify_bearer_request modify_bearer_request; + struct gtpc_modify_bearer_response modify_bearer_response; + struct gtpc_delete_session_request delete_session_request; + struct gtpc_delete_session_response delete_session_response; +} gtpc_msg_choice_t; + +/**************************************************************************** + * GTP-C v2 Message + * Ref: 3GPP TS 29.274 v10.14.0 + * + * This is the main structure to represent a GTP-C message. It is composed + * of one GTP-C header and one union of structures, which can hold + * all the possible GTP-C messages + ***************************************************************************/ + +typedef struct gtpc_pdu +{ + struct gtpc_header header; + union gtpc_msg_choice choice; +} gtpc_pdu_t; + + + +}; + +#endif diff --git a/lib/include/srslte/asn1/gtpc_ies.h b/lib/include/srslte/asn1/gtpc_ies.h new file mode 100644 index 000000000..35b5e6498 --- /dev/null +++ b/lib/include/srslte/asn1/gtpc_ies.h @@ -0,0 +1,428 @@ +/* \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 GTPC_IES_H +#define GTPC_IES_H + +#include "srslte/phy/io/netsource.h" + +namespace srslte +{ + +/**************************************************************** + * + * GTP-C IE Types + * Ref: TS 29.274 v10.14.0 Table 8.1-1 + * + ****************************************************************/ +enum gtpc_ie_type +{ +//const uint8_t GTPC_IE_TYPE_RESERVED = 0; + GTPC_IE_TYPE_IMSI = 1, + GTPC_IE_TYPE_CAUSE = 2, + GTPC_IE_TYPE_RECOVERY = 3, +//4 to 50 RESERVED_FOR_S101_INTERFACE + GTPC_IE_TYPE_STN_SR = 51, +//52 to 70 RESERVED_FOR_SV_INTERFACE + GTPC_IE_TYPE_APN = 71, + GTPC_IE_TYPE_AMBR = 72, + GTPC_IE_TYPE_EBI = 73, + GTPC_IE_TYPE_IP_ADDRESS = 74, + GTPC_IE_TYPE_MEI = 75, + GTPC_IE_TYPE_MSISDN = 76, + GTPC_IE_TYPE_INDICATION = 77, + GTPC_IE_TYPE_PCO = 78, + GTPC_IE_TYPE_PDN_ADDRESS_ALLOCATION = 79, + GTPC_IE_TYPE_BEARER_QOS = 80, + GTPC_IE_TYPE_FLOW_QOS = 81, + GTPC_IE_TYPE_RAT_TYPE = 82, + GTPC_IE_TYPE_SERVING_NETWORK = 83, + GTPC_IE_TYPE_BEARER_TFT = 84, + GTPC_IE_TYPE_TAD = 85, + GTPC_IE_TYPE_ULI = 86, + GTPC_IE_TYPE_F_TEID = 87, + GTPC_IE_TYPE_TMSI = 88, + GTPC_IE_TYPE_GLOBAL_CN_ID = 89, + GTPC_IE_TYPE_S103_PDN_DATA_FORWARDING_INFO = 90, + GTPC_IE_TYPE_S1_U_DATA_FORWARDING_INFO = 91, + GTPC_IE_TYPE_DELAY_VALUE = 92, + GTPC_IE_TYPE_BEARER_CONTEXT = 93, + GTPC_IE_TYPE_CHARGING_ID = 94, + GTPC_IE_TYPE_CHARGING_CHARACTERISTICS = 95, + GTPC_IE_TYPE_TRACE_INFORMATION = 96, + GTPC_IE_TYPE_BEARER_FLAGS = 97, +//98 Reserved + GTPC_IE_TYPE_PDN_TYPE = 99, + GTPC_IE_TYPE_PROCEDURE_TRANSACTION_ID = 100, + GTPC_IE_TYPE_DRX_PARAMETER = 101, +//102 Reserved + GTPC_IE_TYPE_MM_CONTEXT_GSM_KEY_AND_TRIPLETS = 103, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_USED_CIPHER_AND_QUINTUPLETS = 104, + GTPC_IE_TYPE_MM_CONTEXT_GSM_KEY_USED_CIPHER_AND_QUINTUPLETS = 105, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_AND_QUINTUPLETS = 106, + GTPC_IE_TYPE_MM_CONTEXT_EPS_SECURITY_CONTEXT_QUADRUPLETS_AND_QUINTUPLETS = 107, + GTPC_IE_TYPE_MM_CONTEXT_UMTS_KEY_QUADRUPLETS_AND_QUINTUPLETS = 108, + GTPC_IE_TYPE_PDN_CONNECTION = 109, + GTPC_IE_TYPE_PDU_NUMBERS = 110, + GTPC_IE_TYPE_P_TMSI = 111, + GTPC_IE_TYPE_P_TMSI_SIGNATURE = 112, + GTPC_IE_TYPE_HOP_COUNTER = 113, + GTPC_IE_TYPE_UE_TIME_ZONE = 114, + GTPC_IE_TYPE_TRACE_REFERENCE = 115, + GTPC_IE_TYPE_COMPLETE_REQUEST_MESSAGE = 116, + GTPC_IE_TYPE_GUTI = 117, + GTPC_IE_TYPE_F_CONTAINER = 118, + GTPC_IE_TYPE_F_CAUSE = 119, + GTPC_IE_TYPE_SELECTED_PLMN_ID = 120, + GTPC_IE_TYPE_TARGET_IDENTIFICATION = 121, +//122 Reserved + GTPC_IE_TYPE_PACKET_FLOW_ID = 123, + GTPC_IE_TYPE_RAB_CONTEXT = 124, + GTPC_IE_TYPE_SOURCE_RNC_PDCP_CONTEXT_INFO = 125, + GTPC_IE_TYPE_UDP_SOURCE_PORT_NUMBER = 126, + GTPC_IE_TYPE_APN_RESTRICTION = 127, + GTPC_IE_TYPE_SELECTION_MODE = 128, + GTPC_IE_TYPE_SOURCE_IDENTIFICATION = 129, +//130 RESERVED + GTPC_IE_TYPE_CHANGE_REPORTING_ACTION = 131, + GTPC_IE_TYPE_FQ_CSID = 132, + GTPC_IE_TYPE_CHANNEL_NEEDED = 133, + GTPC_IE_TYPE_EMLPP_PRIORITY = 134, + GTPC_IE_TYPE_NODE_TYPE = 135, + GTPC_IE_TYPE_FQDN = 136, + GTPC_IE_TYPE_TI = 137, + GTPC_IE_TYPE_MBMS_SESSION_DURATION = 138, + GTPC_IE_TYPE_MBMS_SERVICE_AREA = 139, + GTPC_IE_TYPE_MBMS_SESSION_IDENTIFIER = 140, + GTPC_IE_TYPE_MBMS_FLOW_IDENTIFIER = 141, + GTPC_IE_TYPE_MBMS_IP_MULTICAST_DISTRIBUTION = 142, + GTPC_IE_TYPE_MBMS_DISTRIBUTION_ACKNOWLEDGE = 143, + GTPC_IE_TYPE_RFSP_INDEX = 144, + GTPC_IE_TYPE_UCI = 145, + GTPC_IE_TYPE_CSG_INFORMATION_REPORTING_ACTION = 146, + GTPC_IE_TYPE_CSG_ID = 147, + GTPC_IE_TYPE_CMI = 148, + GTPC_IE_TYPE_SERVICE_INDICATOR = 149, + GTPC_IE_TYPE_DETACH_TYPE = 150, + GTPC_IE_TYPE_LDN = 151, + GTPC_IE_TYPE_NODE_FEATURES = 152, + GTPC_IE_TYPE_MBMS_TIME_TO_DATA_TRANSFER = 153, + GTPC_IE_TYPE_THROTTLING =154, + GTPC_IE_TYPE_ARP = 155, + GTPC_IE_TYPE_EPC_TIMER = 156, + GTPC_IE_TYPE_SIGNALLING_PRIORITY_INDICATION = 157, + GTPC_IE_TYPE_TMGI = 158, + GTPC_IE_TYPE_ADDITIONAL_MM_CONTEXT_FOR_SRVCC = 159, + GTPC_IE_TYPE_ADDITIONAL_FLAGS_FOR_SRVCC = 160, +//161 RESERVED + GTPC_IE_TYPE_MDT_CONFIGURATION = 162, + GTPC_IE_TYPE_APCO = 163, +//164 RESERVED + GTPC_IE_TYPE_CHANGE_TO_REPORT_FLAGS = 165, +//168 TO 254 SPARE. FOR FUTURE USE. + GTPC_IE_TYPE_PRIVATE_EXTENSION = 255 +}; + +/**************************************************************** + * + * GTP-C IMSI IE + * Ref: TS 29.274 v10.14.0 Figure 8.3-1 + * + ****************************************************************/ +/* + * The IMSI should be kept as an uint64_t. + * The responsibility to convert from uint64_t to BCD coded is on + * the pack_imsi_ie function + */ + +/**************************************************************************** + * + * GTP-C Cause IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.4-1 and Table 8.4-1 + * + ***************************************************************************/ +enum gtpc_cause_value +{ + //Reserved + GTPC_CAUSE_VALUE_LOCAL_DETACH = 2, + GTPC_CAUSE_VALUE_COMPLETE_DETACH = 3, + GTPC_CAUSE_VALUE_RAT_CHANGED_FROM_3GPP_TO_NON_3GPP = 4, + GTPC_CAUSE_VALUE_ISR_DEACTIVATION = 5, + GTPC_CAUSE_VALUE_ERROR_INDICATION_RECEIVED_FROM_RNC_ENODEB_S4_SGSN = 6, + GTPC_CAUSE_VALUE_IMSI_DETACH_ONLY = 7, + GTPC_CAUSE_VALUE_REACTIVATION_REQUESTED = 8, + GTPC_CAUSE_VALUE_PDN_RECONNECTION_TO_THIS_APN_DISALLOWED = 9, + GTPC_CAUSE_VALUE_ACCESS_CHANGED_FROM_NON_3GPP_TO_3GPP = 10, + GTPC_CAUSE_VALUE_PDN_CONNECTION_INACTIVITY_TIMER_EXPIRES = 11, + //Spare. This value range shall be used by Cause values in an initial/request message. + GTPC_CAUSE_VALUE_REQUEST_ACCEPTED = 16, + GTPC_CAUSE_VALUE_REQUEST_ACCEPTED_PARTIALLY = 17, + GTPC_CAUSE_VALUE_NEW_PDN_TYPE_DUE_TO_NETWORK_PREFERENCE = 18, + GTPC_CAUSE_VALUE_NEW_PDN_TYPE_DUE_TO_SINGLE_ADDRESS_BEARER_ONLY = 19, + //20-63 Spare. + GTPC_CAUSE_VALUE_CONTEXT_NOT_FOUND = 64, + GTPC_CAUSE_VALUE_INVALID_MESSAGE_FORMAT = 65, + GTPC_CAUSE_VALUE_VERSION_NOT_SUPPORTED_BY_NEXT_PEER = 66, + GTPC_CAUSE_VALUE_INVALID_LENGTH = 67, + GTPC_CAUSE_VALUE_SERVICE_NOT_SUPPORTED = 68, + GTPC_CAUSE_VALUE_MANDATORY_IE_INCORRECT = 69, + GTPC_CAUSE_VALUE_MANDATORY_IE_MISSING = 70, + //71 Shall not be used. + GTPC_CAUSE_VALUE_SYSTEM_FAILURE = 72, + GTPC_CAUSE_VALUE_NO_RESOURCES_AVAILABLE = 73, + GTPC_CAUSE_VALUE_SEMANTIC_ERROR_IN_THE_TFT_OPERATION = 74, + GTPC_CAUSE_VALUE_SYNTACTIC_ERROR_IN_THE_TFT_OPERATION = 75, + GTPC_CAUSE_VALUE_SEMANTIC_ERRORS_IN_PACKET_FILTER = 76, + GTPC_CAUSE_VALUE_SYNTACTIC_ERRORS_IN_PACKET_FILTER = 77, + GTPC_CAUSE_VALUE_MISSING_OR_UNKNOWN_APN = 78, + //79 Shall not be used. + GTPC_CAUSE_VALUE_GRE_KEY_NOT_FOUND = 80, + GTPC_CAUSE_VALUE_RELOCATION_FAILURE = 81, + GTPC_CAUSE_VALUE_DENIED_IN_RAT = 82, + GTPC_CAUSE_VALUE_PREFERRED_PDN_TYPE_NOT_SUPPORTED = 83, + GTPC_CAUSE_VALUE_ALL_DYNAMIC_ADDRESSES_ARE_OCCUPIED = 84, + GTPC_CAUSE_VALUE_UE_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED = 85, + GTPC_CAUSE_VALUE_PROTOCOL_TYPE_NOT_SUPPORTED = 86, + GTPC_CAUSE_VALUE_UE_NOT_RESPONDING = 87, + GTPC_CAUSE_VALUE_UE_REFUSES = 88, + GTPC_CAUSE_VALUE_SERVICE_DENIED = 89, + GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE = 90, + GTPC_CAUSE_VALUE_NO_MEMORY_AVAILABLE = 91, + GTPC_CAUSE_VALUE_USER_AUTHENTICATION_FAILED = 92, + GTPC_CAUSE_VALUE_APN_ACCESS_DENIED_NO_SUBSCRIPTION = 93, + GTPC_CAUSE_VALUE_REQUEST_REJECTED = 94, + GTPC_CAUSE_VALUE_P_TMSI_SIGNATURE_MISMATCH = 95, + GTPC_CAUSE_VALUE_IMSI_IMEI_NOT_KNOWN = 96, + GTPC_CAUSE_VALUE_SEMANTIC_ERROR_IN_THE_TAD_OPERATION = 97, + GTPC_CAUSE_VALUE_SYNTACTIC_ERROR_IN_THE_TAD_OPERATION = 98, + //99 Shall not be used. + GTPC_CAUSE_VALUE_REMOTE_PEER_NOT_RESPONDING = 100, + GTPC_CAUSE_VALUE_COLLISION_WITH_NETWORK_INITIATED_REQUEST = 101, + GTPC_CAUSE_VALUE_UNABLE_TO_PAGE_UE_DUE_TO_SUSPENSION = 102, + GTPC_CAUSE_VALUE_CONDITIONAL_IE_MISSING = 103, + GTPC_CAUSE_VALUE_APN_RESTRICTION_TYPE_INCOMPATIBLE_WITH_CURRENTLY_ACTIVE_PDN_CONNECTION = 104, + GTPC_CAUSE_VALUE_INVALID_OVERALL_LENGTH_OF_THE_TRIGGERED_RESPONSE_MSG_AND_A_PIGGYBACKED_INITIAL_MSG = 105, + GTPC_CAUSE_VALUE_DATA_FORWARDING_NOT_SUPPORTED = 106, + GTPC_CAUSE_VALUE_INVALID_REPLY_FROM_REMOTE_PEER = 107, + GTPC_CAUSE_VALUE_FALLBACK_TO_GTPV1 = 108, + GTPC_CAUSE_VALUE_INVALID_PEER = 109, + GTPC_CAUSE_VALUE_TEMPORARILY_REJECTED_DUE_TO_HANDOVER_PROCEDURE_IN_PROGRESS = 110, + GTPC_CAUSE_VALUE_MODIFICATIONS_NOT_LIMITED_TO_S1_U_BEARERS = 111, + GTPC_CAUSE_VALUE_REQUEST_REJECTED_FOR_A_PMIPV6_REASON = 112, + GTPC_CAUSE_VALUE_APN_CONGESTION = 113, + GTPC_CAUSE_VALUE_BEARER_HANDLING_NOT_SUPPORTED = 114, + GTPC_CAUSE_VALUE_UE_ALREADY_RE_ATTACHED = 115, + GTPC_CAUSE_VALUE_MULTIPLE_PDN_CONNECTIONS_FOR_A_GIVEN_APN_NOT_ALLOWED = 116 + //117-239 Spare. For future use in a triggered/response message. + //240-255 Spare. For future use in an initial/request message. +}; + +struct gtpc_cause_ie +{ + enum gtpc_cause_value cause_value; + bool pce; + bool bce; + bool cs; + enum gtpc_ie_type offending_ie_type; + uint16_t length_of_offending_ie; + uint8_t offending_ie_instance; +}; + +/**************************************************************************** + * + * GTP-C Recovery IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.5-1 + * + ***************************************************************************/ +/* + * The Recovery (Restart Counter) IE should be kept as an uint8_t. + */ + +/**************************************************************************** + * + * GTP-C Access Point Name IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.6-1 + * + ***************************************************************************/ +/* + * APN IE should be kept as an null terminated string. + * This string will be kept in a char[MAX_APN_LENGTH] buffer. + */ +#define MAX_APN_LENGTH 1024 + +/**************************************************************************** + * + * GTP-C Aggregate Maximum bit-rate IE + * Ref: 3GPP TS 29.274 v10.14.0 Table 8.7-1 + * + ***************************************************************************/ +struct gtpc_ambr_ie +{ + uint32_t apn_ambr_uplink; + uint32_t apn_ambr_downlink; +}; + +/**************************************************************************** + * + * GTP-C EPS Bearer ID address IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.8-1 + * + ***************************************************************************/ +/* + * The EPS Bearer ID (EBI) IE should be kept as an uint8_t. + */ + +/**************************************************************************** + * + * GTP-C IP address IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.9-1 + * + ***************************************************************************/ +/* + * IP addresse IEs should the sockaddr_storage struct, which can hold IPv4 + * and IPv6 addresses. + */ + +//TODO +//TODO IEs between 8.10 and 8.13 missing +//TODO + +/**************************************************************************** + * + * GTP-C PDN Type IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.14-1 + * + ***************************************************************************/ +enum gtpc_pdn_type +{ + GTPC_PDN_TYPE_IPV4 = 1, + GTPC_PDN_TYPE_IPV6 = 2, + GTPC_PDN_TYPE_IPV4V6 = 3 +}; + +struct gtpc_pdn_address_allocation_ie +{ + enum gtpc_pdn_type pdn_type; + bool ipv4_present; + bool ipv6_present; + in_addr_t ipv4; + struct in6_addr ipv6; +}; + +//TODO +//TODO IEs between 8.15 and 8.17 missing +//TODO + +/**************************************************************************** + * + * GTP-C RAT Type IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.17-1 + * + ***************************************************************************/ + +enum gtpc_rat_type +{ + UTRAN = 1, + GERAN, + WLAN, + GAN, + HSPA_EVOLUTION, + EUTRAN, + Virtual +}; + +//TODO +//TODO IEs between 8.17 and 8.22 missing +//TODO + +/**************************************************************************** + * + * GTP-C Fully Qualified Tunnel End-point Identifier (F-TEID) IE + * Ref: 3GPP TS 29.274 v10.14.0 Figure 8.22-1 + * + ***************************************************************************/ +enum gtpc_interface_type +{ + S1_U_ENODEB_GTP_U_INTERFACE, + S1_U_SGW_GTP_U_INTERFACE, + S12_RNC_GTP_U_INTERFACE, + S12_SGW_GTP_U_INTERFACE, + S5_S8_SGW_GTP_U_INTERFACE, + S5_S8_PGW_GTP_U_INTERFACE, + S5_S8_SGW_GTP_C_INTERFACE, + S5_S8_PGW_GTP_C_INTERFACE, + S5_S8_SGW_PMIPV6_INTERFACE, //(the 32 bit GRE key is encoded in 32 bit TEID field and since alternate CoA is not used the control plane and user plane addresses are the same for PMIPv6) + S5_S8_PGW_PMIPV6_INTERFACE, //(the 32 bit GRE key is encoded in 32 bit TEID field and the control plane and user plane addresses are the same for PMIPv6) + S11_MME_GTP_C_INTERFACE, + S11_S4_SGW_GTP_C_INTERFACE, + S10_MME_GTP_C_INTERFACE, + S3_MME_GTP_C_INTERFACE, + S3_SGSN_GTP_C_INTERFACE, + S4_SGSN_GTP_U_INTERFACE, + S4_SGW_GTP_U_INTERFACE, + S4_SGSN_GTP_C_INTERFACE, + S16_SGSN_GTP_C_INTERFACE, + ENODEB_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, + ENODEB_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING, + RNC_GTP_U_INTERFACE_FOR_DATA_FORWARDING, + SGSN_GTP_U_INTERFACE_FOR_DATA_FORWARDING, + SGW_GTP_U_INTERFACE_FOR_DL_DATA_FORWARDING, + SM_MBMS_GW_GTP_C_INTERFACE, + SN_MBMS_GW_GTP_C_INTERFACE, + SM_MME_GTP_C_INTERFACE, + SN_SGSN_GTP_C_INTERFACE, + SGW_GTP_U_INTERFACE_FOR_UL_DATA_FORWARDING, + SN_SGSN_GTP_U_INTERFACE, + S2B_EPDG_GTP_C_INTERFACE, + S2B_U_EPDG_GTP_U_INTERFACE, + S2B_PGW_GTP_C_INTERFACE, + S2B_U_PGW_GTP_U_INTERFACE +}; + +struct gtpc_f_teid_ie +{ + bool ipv4_present; + bool ipv6_present; + enum gtpc_interface_type interface_type; + uint32_t teid; + in_addr_t ipv4; + struct in6_addr ipv6; //FIXME +}; + +//TODO +//TODO IEs between 8.22 and 8.28 missing +//TODO + +/**************************************************************************** + * + * GTP-C Bearer Context IE + * Ref: 3GPP TS 29.274 v10.14.0 Table 8.28-1 + * + ***************************************************************************/ +//The usage of this grouped IE is specific to the GTP-C message being sent. +//As such, each GTP-C message will define it's bearer context structures +//locally, according to the rules of TS 29.274 v10.14.0 Section 7. + +} //namespace +#endif //GTPC_IES_H diff --git a/lib/include/srslte/asn1/gtpc_msg.h b/lib/include/srslte/asn1/gtpc_msg.h new file mode 100644 index 000000000..4271b064d --- /dev/null +++ b/lib/include/srslte/asn1/gtpc_msg.h @@ -0,0 +1,414 @@ +/* \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 GTPC_V2_MSG_H +#define GTPC_V2_MSG_H + +#include "srslte/asn1/gtpc_ies.h" + +namespace srslte{ + +/**************************************************************** + * + * GTP-C Message Types + * Ref: TS 29.274 v10.14.0 Table 6.1-1 + * + ****************************************************************/ +const uint8_t GTPC_MSG_TYPE_RESERVED = 0; +const uint8_t GTPC_MSG_TYPE_ECHO_REQUEST = 1; +const uint8_t GTPC_MSG_TYPE_ECHO_RESPONSE = 2; +const uint8_t GTPC_MSG_TYPE_VERSION_SUPPORT = 3; +//4-24 Reserved for S101 +//25-31 Reserved for Sv interface +//SGSN/MME/ePDG to PGW (S4/S11, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_CREATE_SESSION_REQUEST = 32; +const uint8_t GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE = 33; +const uint8_t GTPC_MSG_TYPE_DELETE_SESSION_REQUEST = 36; +const uint8_t GTPC_MSG_TYPE_DELETE_SESSION_RESPONSE = 37; +//SGSN/MME to PGW (S4/S11, S5/S8) +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST = 34; +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_RESPONSE = 35; +const uint8_t GTPC_MSG_TYPE_CHANGE_NOTIFICATION_REQUEST = 38; +const uint8_t GTPC_MSG_TYPE_CHANGE_NOTIFICATION_RESPONSE = 39; +//40 - 63 for future use +const uint8_t GTPC_MSG_TYPE_RESUME_NOTIFICATION = 164; +const uint8_t GTPC_MSG_TYPE_RESUME_ACKNOWLEDGE = 165; +//Messages without explicit response +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_COMMAND = 64; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_MODIFY_BEARER_FAILURE_INDICATION = 65; //(PGW to MME/SGSN/ePDG – S5/S8, S11/S4, S2b) +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_COMMAND = 66; //(MME/SGSN to PGW – S11/S4, S5/S8) +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_FAILURE_INDICATION = 67; //(PGW to MME/SGSN – S5/S8, S11/S4)) +const uint8_t GTPC_MSG_TYPE_BEARER_RESOURCE_COMMAND = 68; //(MME/SGSN to PGW – S11/S4, S5/S8) +const uint8_t GTPC_MSG_TYPE_BEARER_RESOURCE_FAILURE_INDICATION = 69; //(PGW to MME/SGSN – S5/S8, S11/S4) +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_FAILURE_INDICATION = 70; //(SGSN/MME to SGW – S4/S11) +const uint8_t GTPC_MSG_TYPE_TRACE_SESSION_ACTIVATION = 71; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_TRACE_SESSION_DEACTIVATION = 72; //(MME/SGSN/ePDG to PGW – S11/S4, S5/S8, S2b) +const uint8_t GTPC_MSG_TYPE_STOP_PAGING_INDICATION = 73; //(SGW to MME/SGSN – S11/S4) +//74-94 For future use +//P-GW to SGSN/MME/ePDG +const uint8_t GTPC_MSG_TYPE_CREATE_BEARER_REQUEST = 95; +const uint8_t GTPC_MSG_TYPE_CREATE_BEARER_RESPONSE = 96; +const uint8_t GTPC_MSG_TYPE_UPDATE_BEARER_REQUEST = 97; +const uint8_t GTPC_MSG_TYPE_UPDATE_BEARER_RESPONSE = 98; +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_REQUEST = 99; +const uint8_t GTPC_MSG_TYPE_DELETE_BEARER_RESPONSE = 100; +//PGW to MME, MME to PGW, SGW to PGW, SGW to MME, PGW to ePDG, ePDG to PGW (S5/S8, S11, S2b) +const uint8_t GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_REQUEST = 101; +const uint8_t GTPC_MSG_TYPE_DELETE_PDN_CONNECTION_SET_RESPONSE = 102; +//103-127 For future use +//MME to MME, SGSN to MME, MME to SGSN, SGSN to SGSN (S3/S10/S16) +const uint8_t GTPC_MSG_TYPE_IDENTIFICATION_REQUEST = 128; +const uint8_t GTPC_MSG_TYPE_IDENTIFICATION_RESPONSE = 129; +const uint8_t GTPC_MSG_TYPE_CONTEXT_REQUEST = 130; +const uint8_t GTPC_MSG_TYPE_CONTEXT_RESPONSE = 131; +const uint8_t GTPC_MSG_TYPE_CONTEXT_ACKNOWLEDGE = 132; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_REQUEST = 133; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_RESPONSE = 134; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_NOTIFICATION = 135; +const uint8_t GTPC_MSG_TYPE_FORWARD_RELOCATION_COMPLETE_ACKNOWLEDGE = 136; +const uint8_t GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_NOTIFICATION = 137; +const uint8_t GTPC_MSG_TYPE_FORWARD_ACCESS_CONTEXT_ACKNOWLEDGE = 138; +const uint8_t GTPC_MSG_TYPE_RELOCATION_CANCEL_REQUEST = 139; +const uint8_t GTPC_MSG_TYPE_RELOCATION_CANCEL_RESPONSE = 140; +const uint8_t GTPC_MSG_TYPE_CONFIGURATION_TRANSFER_TUNNEL = 141; +//142 - 148 For future use +const uint8_t GTPC_MSG_TYPE_RAN_INFORMATION_RELAY = 152; +//SGSN to MME, MME to SGSN (S3) +const uint8_t GTPC_MSG_TYPE_DETACH_NOTIFICATION = 149; +const uint8_t GTPC_MSG_TYPE_DETACH_ACKNOWLEDGE = 150; +const uint8_t GTPC_MSG_TYPE_CS_PAGING_INDICATION = 151; +const uint8_t GTPC_MSG_TYPE_ALERT_MME_NOTIFICATION = 153; +const uint8_t GTPC_MSG_TYPE_ALERT_MME_ACKNOWLEDGE = 154; +const uint8_t GTPC_MSG_TYPE_UE_ACTIVITY_NOTIFICATION = 155; +const uint8_t GTPC_MSG_TYPE_UE_ACTIVITY_ACKNOWLEDGE = 156; +//157 - 159 For future use +//GSN/MME to SGW, SGSN to MME (S4/S11/S3) SGSN to SGSN (S16), SGW to PGW (S5/S8) +const uint8_t GTPC_MSG_TYPE_SUSPEND_NOTIFICATION = 162; +const uint8_t GTPC_MSG_TYPE_SUSPEND_ACKNOWLEDGE = 163; +//SGSN/MME to SGW (S4/S11) const uint8_t GTPC_IE_TYPE_ +const uint8_t GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_REQUEST = 160; +const uint8_t GTPC_MSG_TYPE_CREATE_FORWARDING_TUNNEL_RESPONSE = 161; +const uint8_t GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST = 166; +const uint8_t GTPC_MSG_TYPE_CREATE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE = 167; +const uint8_t GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_REQUEST = 168; +const uint8_t GTPC_MSG_TYPE_DELETE_INDIRECT_DATA_FORWARDING_TUNNEL_RESPONSE = 169; +const uint8_t GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_REQUEST = 170; +const uint8_t GTPC_MSG_TYPE_RELEASE_ACCESS_BEARERS_RESPONSE = 171; +//172 - 175 For future use +//SGW to SGSN/MME (S4/S11) +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION = 176; +const uint8_t GTPC_MSG_TYPE_DOWNLINK_DATA_NOTIFICATION_ACKNOWLEDGE = 177; +const uint8_t GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION = 179; +const uint8_t GTPC_MSG_TYPE_PGW_RESTART_NOTIFICATION_ACKNOWLEDGE = 180; +//SGW to SGSN (S4) +//178 Reserved. Allocated in earlier version of the specification. +//181 -189 For future use +//SGW to PGW, PGW to SGW (S5/S8) +const uint8_t GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_REQUEST = 200; +const uint8_t GTPC_MSG_TYPE_UPDATE_PDN_CONNECTION_SET_RESPONSE = 201; +//For future use +//MME to SGW (S11) +const uint8_t GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_REQUEST = 211; +const uint8_t GTPC_MSG_TYPE_MODIFY_ACCESS_BEARERS_RESPONSE = 212; +//For future use +//MBMS GW to MME/SGSN (Sm/Sn) +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_START_REQUEST = 231; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_START_RESPONSE = 232; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_REQUEST = 233; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_UPDATE_RESPONSE = 234; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_REQUEST = 235; +const uint8_t GTPC_MSG_TYPE_MBMS_SESSION_STOP_RESPONSE = 236; +//For future use +//Other +//240 - 255 For future use + +/**************************************************************************** + * + * GTP-C v2 Create Session Request + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.1-1 + * + ***************************************************************************/ + +struct gtpc_create_session_request +{ + bool imsi_present; + uint64_t imsi; // C + //bool msidn_present; + //uint64_t msisdn; // C + //bool mei_present; + //uint64_t mei; // C/CO + //bool user_location_info_present; + //struct gtpc_user_location_info_ie uli; // C/CO + //bool serving_network_present; + //struct gtpc_serving_network_ie serving_network; // C/CO + + enum gtpc_rat_type rat_type; // M + //bool indication_flags_present; + //struct indication_flags_ indication_flags; // C + + struct gtpc_f_teid_ie sender_f_teid; // M + bool pgw_addr_present; + struct gtpc_f_teid_ie pgw_addr; // C + + char apn[MAX_APN_LENGTH]; // M + //bool selection_mode_present; + //enum selection_mode_ selection_mode; // C/CO + //bool pdn_type_present; + //enum gtpc_pdn_type pdn_type; // C + //bool pdn_addr_alloc_present; + //struct pdn_addr_alloc_ pdn_addr_alloc; // C/CO + //bool max_apn_restriction_present; + //enum apn_restriction_ max_apn_restriction; // C + //bool apn_ambr_present; + //struct ambr_ apn_ambr; // C + //bool linked_eps_bearer_id_present; + //uint8_t linked_eps_bearer_id; // C + //bool pco_present; + //uint8_t pco; // C + + struct gtpc_bearer_context_created_ie //see TS 29.274 v10.14.0 Table 7.2.1-2 + { + uint8_t ebi; + bool tft_present; + bool s1_u_enodeb_f_teid_present; + struct gtpc_f_teid_ie s1_u_enodeb_f_teid; + bool s4_u_sgsn_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgsn_f_teid; + bool s5_s8_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_sgw_f_teid; + bool s5_s8_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_pgw_f_teid; + bool s12_rnc_f_teid_present; + struct gtpc_f_teid_ie s12_rnc_f_teid; + bool s2b_u_epdg_f_teid_present; + struct gtpc_f_teid_ie s2b_u_epdg_f_teid; + } eps_bearer_context_created; // M + //bool bearer_context_deleted_present; + //struct bearer_context_ bearer_context_deleted; // C + //bool trace_information_present; + //struct trace_infromation_ trace_information; // C + //bool recovery_present + //uint8_t recovery; // C + //bool mme_fq_csid_present; + //struct fq_csid_ mme_fq_csid; // C + //bool sgw_fq_csid_present; + //struct fq_csid_ sgw_fq_csid; // C + //bool epdg_fq_csid_present; + //struct fq_csid_ epdg_fq_csid; // C + //bool ue_time_zone_present; + //struct ue_time_zone_ ue_time_zone; // CO + //bool uci_present; + //struct uci_ uci; // CO + //bool charging_caracteristics_present; + //enum charging_characteristics_ charging_caracteristics; // O + //bool mme_ldn_present; + //uint8_t mme_ldn[LDN_MAX_SIZE]; // O + //bool sgw_ldn_present; + //uint8_t sgw_ldn[LDN_MAX_SIZE]; // O + //bool epgd_ldn_present; + //uint8_t epdg_ldn[LDN_MAX_SIZE]; // O + //bool signaling_priority_indication; + //enum signalling_priority_indication_ spi; // CO + //bool acpo_present; + //uint8_t apco; // CO + //bool ext; // O +}; + +/**************************************************************************** + * + * GTP-C v2 Create Session Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.2-1 + * + ***************************************************************************/ +struct gtpc_create_session_response +{ + struct gtpc_cause_ie cause; //M + //Change Reporting Action //C + //CSG Information Reporting Action //CO + bool sender_f_teid_present; + struct gtpc_f_teid_ie sender_f_teid; //C + //PGW S5/S8/S2b F-TEID //C + bool paa_present; + struct gtpc_pdn_address_allocation_ie paa; //C + //apn_restriction + //apn_ambr + //linked_eps_bearer_id + //pco + struct gtpc_bearer_context_created_ie + { + uint8_t ebi; + gtpc_cause_ie cause; + bool s1_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s1_u_sgw_f_teid; + bool s4_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgw_f_teid; + bool s5_s8_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_pgw_f_teid; + bool s12_sgw_f_teid_present; + struct gtpc_f_teid_ie s12_sgw_f_teid; + bool s2b_u_pgw_f_teid_present; + struct gtpc_f_teid_ie s2b_u_pgw_f_teid; + //bearer_level_qos_present + //bearer_level_qos + //charging_id_present + //charging_id + //bearer_flags_present + //bearer_flags + } eps_bearer_context_created; //M + + /* + struct gtpc_bearer_context_removed_ie + { + uint8_t ebi; + // + } bearer_context_removed; //C + */ + //recovery; //C + //charging_gateway_name; //C + //charging_gateway_address; //C + //PGW-FQ-CSID //C + //SGW-FQ-CSID //C + //SGW LDN //O + //PGW LDN //O + //PGW Back-Off Time //O + //acpo //CO +}; + +/**************************************************************************** + * + * GTP-C v2 Modify Bearer Request + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.7-1, 7.2.7-2 and 7.2.7-3 + * + ***************************************************************************/ + +struct gtpc_modify_bearer_request +{ + //ME Identity (MEI)//C + //User Location Information (ULI)//C + //Serving Network //CO + //RAT Type //C/CO + //Indication Flags + //Sender F-TEID for Control Plane + //APN-AMBR + //Delay Downlink Packet Notification Request + struct gtpc_bearer_context_modified_ie + { + uint8_t ebi; + gtpc_cause_ie cause; + bool s1_u_enb_f_teid_present; + struct gtpc_f_teid_ie s1_u_enb_f_teid; + bool s5_s8_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s5_s8_u_sgw_f_teid; + bool s12_rnc_f_teid_present; + struct gtpc_f_teid_ie s12_rnc_f_teid; + bool s4_u_sgsn_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgsn_f_teid; + } eps_bearer_context_to_modify; + //Bearer Contexts to be removed + //Recovery + //UE Time Zone + //MME-FQ-CSID + //SGW-FQ-CSID + //User CSG Information (UCI) + //MME/S4-SGSN LDN + //SGW LDN +}; + +/**************************************************************************** + * + * GTP-C v2 Modify Bearer Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.8-1 + * + ***************************************************************************/ + +struct gtpc_modify_bearer_response +{ + struct gtpc_cause_ie cause; + //MSISDN + //Linked EPS Bearer ID + //APN-AMBR + //APN Restriction + //Protocol Configuration Options + + struct gtpc_bearer_context_modified_ie + { + uint8_t ebi; + struct gtpc_cause_ie cause; + bool s1_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s1_u_sgw_f_teid; + bool s12_sgw_f_teid_present; + struct gtpc_f_teid_ie s12_sgw_f_teid; + bool s4_u_sgw_f_teid_present; + struct gtpc_f_teid_ie s4_u_sgw_f_teid; + //charging id + //bearer flags + } eps_bearer_context_modified; + //Bearer Contexts marked for removal + //Change Reporting action + //CSG information reporting action + //Charging gateway name + //charging gateway address + //P-GW FQ-CSID + //S-GW FQ-CSID + //Recovery + //S-GW LDN + //P-GW LDN + //indication Flags + //ext +}; + +/**************************************************************************** + * + * GTP-C v2 Delete Session Resquest + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.9.1-1 + * + ***************************************************************************/ + +struct gtpc_delete_session_request +{ + struct gtpc_cause_ie cause; + //Linked EPS Bearer ID + //User Location Information + //Indication Flags + //Protocol Configuration Options + //Originating Node + //Private extension +}; + +/**************************************************************************** + * + * GTP-C v2 Delete Session Response + * Ref: 3GPP TS 29.274 v10.14.0 Table 7.2.10.1-1 + * + ***************************************************************************/ + +struct gtpc_delete_session_response +{ + struct gtpc_cause_ie cause; + //Recovery + //Protocol Configuration Options + //Private extension +}; + +} //namespace +#endif //GTPC_V2_MSG_H diff --git a/lib/include/srslte/asn1/liblte_mme.h b/lib/include/srslte/asn1/liblte_mme.h index 842fd2e17..78e2cae69 100644 --- a/lib/include/srslte/asn1/liblte_mme.h +++ b/lib/include/srslte/asn1/liblte_mme.h @@ -3759,6 +3759,10 @@ typedef struct{ uint8 proc_transaction_id; }LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT; // Functions +LIBLTE_ERROR_ENUM srslte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + uint8 sec_hdr_type, + uint32 count, + LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, @@ -3789,6 +3793,8 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_IN LIBLTE_BYTE_MSG_STRUCT *msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); +LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp); /********************************************************************* Message Name: ESM Status diff --git a/lib/include/srslte/common/bcd_helpers.h b/lib/include/srslte/common/bcd_helpers.h index b696954c2..10bed4347 100644 --- a/lib/include/srslte/common/bcd_helpers.h +++ b/lib/include/srslte/common/bcd_helpers.h @@ -120,6 +120,78 @@ inline std::string plmn_id_to_string(LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_id) { return mcc_str + mnc_str; } +/****************************************************************************** + * Convert PLMN to BCD-coded MCC and MNC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_plmn_to_mccmnc(uint32_t plmn, uint16_t *mcc, uint16_t *mnc) +{ + uint8_t nibbles[6]; + nibbles[0] = (plmn & 0xF00000) >> 20; + nibbles[1] = (plmn & 0x0F0000) >> 16; + nibbles[2] = (plmn & 0x00F000) >> 12; + nibbles[3] = (plmn & 0x000F00) >> 8; + nibbles[4] = (plmn & 0x0000F0) >> 4; + nibbles[5] = (plmn & 0x00000F); + + *mcc = 0xF000; + *mnc = 0xF000; + *mcc |= nibbles[1] << 8; // MCC digit 1 + *mcc |= nibbles[0] << 4; // MCC digit 2 + *mcc |= nibbles[3]; // MCC digit 3 + + if(nibbles[2] == 0xF) { + // 2-digit MNC + *mnc |= 0x0F00; // MNC digit 1 + *mnc |= nibbles[5] << 4; // MNC digit 2 + *mnc |= nibbles[4]; // MNC digit 3 + } else { + // 3-digit MNC + *mnc |= nibbles[5] << 8; // MNC digit 1 + *mnc |= nibbles[4] << 4; // MNC digit 2 + *mnc |= nibbles[2] ; // MNC digit 3 + } +} + +/****************************************************************************** + * Convert BCD-coded MCC and MNC to PLMN. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn) +{ + uint8_t nibbles[6]; + nibbles[1] = (mcc & 0x0F00) >> 8; // MCC digit 1 + nibbles[0] = (mcc & 0x00F0) >> 4; // MCC digit 2 + nibbles[3] = (mcc & 0x000F); // MCC digit 3 + + if((mnc & 0xFF00) == 0xFF00) { + // 2-digit MNC + nibbles[2] = 0x0F; // MNC digit 1 + nibbles[5] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[4] = (mnc & 0x000F); // MNC digit 3 + } else { + // 3-digit MNC + nibbles[5] = (mnc & 0x0F00) >> 8; // MNC digit 1 + nibbles[4] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[2] = (mnc & 0x000F); // MNC digit 3 + } + + *plmn = 0x000000; + *plmn |= nibbles[0] << 20; + *plmn |= nibbles[1] << 16; + *plmn |= nibbles[2] << 12; + *plmn |= nibbles[3] << 8; + *plmn |= nibbles[4] << 4; + *plmn |= nibbles[5]; +} + + } // namespace srslte #endif // BCD_HELPERS diff --git a/lib/include/srslte/upper/gtpu.h b/lib/include/srslte/upper/gtpu.h new file mode 100644 index 000000000..3cd8a1202 --- /dev/null +++ b/lib/include/srslte/upper/gtpu.h @@ -0,0 +1,95 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * srsUE 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. + * + * srsUE 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 SRSLTE_GTPU_H +#define SRSLTE_GTPU_H + +#include +#include "srslte/common/common.h" + +namespace srslte { + +/**************************************************************************** + * GTPU Header + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version |PT | * | E | S |PN | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * 5 | TEID (1st Octet) | + * 6 | TEID (2nd Octet) | + * 7 | TEID (3rd Octet) | + * 8 | TEID (4th Octet) | + ***************************************************************************/ + +#define GTPU_HEADER_LEN 8 + +typedef struct{ + uint8_t flags; // Only support 0x30 - v1, PT1 (GTP), no other flags + uint8_t message_type; // Only support 0xFF - T-PDU type + uint16_t length; + uint32_t teid; +}gtpu_header_t; + + +bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header); +bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu); + +inline void uint8_to_uint32(uint8_t *buf, uint32_t *i) +{ + *i = (uint32_t)buf[0] << 24 | + (uint32_t)buf[1] << 16 | + (uint32_t)buf[2] << 8 | + (uint32_t)buf[3]; +} + +inline void uint32_to_uint8(uint32_t i, uint8_t *buf) +{ + buf[0] = (i >> 24) & 0xFF; + buf[1] = (i >> 16) & 0xFF; + buf[2] = (i >> 8) & 0xFF; + buf[3] = i & 0xFF; +} + +inline void uint8_to_uint16(uint8_t *buf, uint16_t *i) +{ + *i = (uint32_t)buf[0] << 8 | + (uint32_t)buf[1]; +} + +inline void uint16_to_uint8(uint16_t i, uint8_t *buf) +{ + buf[0] = (i >> 8) & 0xFF; + buf[1] = i & 0xFF; +} + + +}//namespace + +#endif diff --git a/lib/src/asn1/CMakeLists.txt b/lib/src/asn1/CMakeLists.txt index d3c52b807..caddb384c 100644 --- a/lib/src/asn1/CMakeLists.txt +++ b/lib/src/asn1/CMakeLists.txt @@ -24,5 +24,6 @@ add_library(srslte_asn1 STATIC liblte_rrc.cc liblte_mme.cc liblte_s1ap.cc + gtpc.cc ) install(TARGETS srslte_asn1 DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/asn1/gtpc.cc b/lib/src/asn1/gtpc.cc new file mode 100644 index 000000000..ac0116840 --- /dev/null +++ b/lib/src/asn1/gtpc.cc @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of srsLTE. + * + * srsUE 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. + * + * srsUE 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 +#include "srslte/asn1/gtpc.h" +#include "srslte/common/common.h" + +namespace srslte{ + + +int +gtpc_pack_create_session_request(struct gtpc_create_session_request *cs_req, srslte::byte_buffer_t) +{ + //FIXME + return 0; +} + +}; diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index ce3c08987..ea7729519 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -7111,7 +7111,6 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_request_msg(LIBLTE_BYTE_MSG_STRUCT return(err); } - /********************************************************************* Message Name: Identity Response @@ -7149,6 +7148,7 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_identity_response_msg(LIBLTE_MME_ID_RESPONSE_M return(err); } + LIBLTE_ERROR_ENUM liblte_mme_unpack_identity_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ID_RESPONSE_MSG_STRUCT *id_resp) { @@ -9898,11 +9898,15 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_request_msg(LI Description: Sent by the network to the UE to request the UE to provide ESM information, i.e. protocol configuration - options or APN or both. + options or APN or both. This function is being added + to support encryption and integrety protection on + ESM information transfer. Document Reference: 24.301 v10.2.0 Section 8.3.13 *********************************************************************/ -LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, +LIBLTE_ERROR_ENUM srslte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT *msg) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; @@ -9911,6 +9915,20 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INF if(esm_info_req != NULL && msg != NULL) { + + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) + { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } // Protocol Discriminator and EPS Bearer ID *msg_ptr = (esm_info_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); msg_ptr++; @@ -9931,6 +9949,50 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INF return(err); } + + +/********************************************************************* + Message Name: ESM Information Request + + Description: Sent by the network to the UE to request the UE to + provide ESM information, i.e. protocol configuration + options or APN or both. + + Document Reference: 24.301 v10.2.0 Section 8.3.13 +*********************************************************************/ +LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_request_msg(LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req, + LIBLTE_BYTE_MSG_STRUCT *msg) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + + if(esm_info_req != NULL && + msg != NULL) + { + + // Protocol Discriminator and EPS Bearer ID + *msg_ptr = (esm_info_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); + msg_ptr++; + + // Procedure Transaction ID + *msg_ptr = esm_info_req->proc_transaction_id; + msg_ptr++; + + // Message Type + *msg_ptr = LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST; + msg_ptr++; + + // Fill in the number of bytes used + msg->N_bytes = msg_ptr - msg->msg; + + err = LIBLTE_SUCCESS; + } + + return(err); +} + + + LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_request_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT *esm_info_req) { @@ -10011,6 +10073,62 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_esm_information_response_msg(LIBLTE_MME_ESM_IN return(err); } +LIBLTE_ERROR_ENUM srslte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8 *msg_ptr = msg->msg; + uint8 sec_hdr_type; + + if(msg != NULL && + esm_info_resp != NULL) + { + + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if(LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) + { + msg_ptr++; + }else{ + msg_ptr += 7; + } + // EPS Bearer ID + esm_info_resp->eps_bearer_id = (*msg_ptr >> 4); + msg_ptr++; + + // Procedure Transaction ID + esm_info_resp->proc_transaction_id = *msg_ptr; + msg_ptr++; + + // Skip Message Type + msg_ptr++; + + // Access Point Name + if(LIBLTE_MME_ACCESS_POINT_NAME_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_access_point_name_ie(&msg_ptr, &esm_info_resp->apn); + esm_info_resp->apn_present = true; + }else{ + esm_info_resp->apn_present = false; + } + + // Protocol Configuration Options + if(LIBLTE_MME_PROTOCOL_CONFIGURATION_OPTIONS_IEI == *msg_ptr) + { + msg_ptr++; + liblte_mme_unpack_protocol_config_options_ie(&msg_ptr, &esm_info_resp->protocol_cnfg_opts); + esm_info_resp->protocol_cnfg_opts_present = true; + }else{ + esm_info_resp->protocol_cnfg_opts_present = false; + } + + err = LIBLTE_SUCCESS; + } + + return(err); +} + LIBLTE_ERROR_ENUM liblte_mme_unpack_esm_information_response_msg(LIBLTE_BYTE_MSG_STRUCT *msg, LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT *esm_info_resp) { diff --git a/lib/src/common/logger_file.cc b/lib/src/common/logger_file.cc index 6dacde733..18dc64974 100644 --- a/lib/src/common/logger_file.cc +++ b/lib/src/common/logger_file.cc @@ -40,7 +40,7 @@ logger_file::logger_file() logger_file::~logger_file() { not_done = false; - log(new std::string("Closing log")); + log(new std::string("Closing log\n")); if(inited) { wait_thread_finish(); flush(); diff --git a/lib/src/upper/gtpu.cc b/lib/src/upper/gtpu.cc new file mode 100644 index 000000000..668af8915 --- /dev/null +++ b/lib/src/upper/gtpu.cc @@ -0,0 +1,96 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2017 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsLTE library. + * + * srsUE 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. + * + * srsUE 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 "srslte/upper/gtpu.h" + + +namespace srslte { + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + ***************************************************************************/ + +bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu) +{ + if(header->flags != 0x30) { + //gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + //gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + if(pdu->get_headroom() < GTPU_HEADER_LEN) { + //gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); + return false; + } + + pdu->msg -= GTPU_HEADER_LEN; + pdu->N_bytes += GTPU_HEADER_LEN; + + uint8_t *ptr = pdu->msg; + + *ptr = header->flags; + ptr++; + *ptr = header->message_type; + ptr++; + uint16_to_uint8(header->length, ptr); + ptr += 2; + uint32_to_uint8(header->teid, ptr); + + return true; +} + +bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header) +{ + uint8_t *ptr = pdu->msg; + + pdu->msg += GTPU_HEADER_LEN; + pdu->N_bytes -= GTPU_HEADER_LEN; + + header->flags = *ptr; + ptr++; + header->message_type = *ptr; + ptr++; + uint8_to_uint16(ptr, &header->length); + ptr += 2; + uint8_to_uint32(ptr, &header->teid); + + if(header->flags != 0x30) { + //gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + //gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + + return true; +} + +} // namespace srslte diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 9ec86fea2..21955eaac 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -22,8 +22,8 @@ phy_cell_id = 1 tac = 0x0007 mcc = 001 mnc = 01 -mme_addr = 127.0.1.100 -gtp_bind_addr = 127.0.1.1 +mme_addr = 127.0.0.1 +gtp_bind_addr = 127.0.0.1 n_prb = 25 ##################################################################### diff --git a/srsepc/CMakeLists.txt b/srsepc/CMakeLists.txt new file mode 100644 index 000000000..0006e5191 --- /dev/null +++ b/srsepc/CMakeLists.txt @@ -0,0 +1,50 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# 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/. +# + +find_package(LibConfig REQUIRED) +find_package(SCTP REQUIRED) + +if(BUILD_STATIC) + set(LIBCONFIGPP_LIBRARIES "${LIBCONFIGPP_STATIC_LIBRARY_PATH}") +endif(BUILD_STATIC) + +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsEPC") +endif() + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/srsepc/hdr +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +#add_subdirectory(test) diff --git a/srsepc/epc.conf.example b/srsepc/epc.conf.example new file mode 100644 index 000000000..bce4085ac --- /dev/null +++ b/srsepc/epc.conf.example @@ -0,0 +1,72 @@ +##################################################################### +# srsEPC configuration file +##################################################################### + +##################################################################### +# MME configuration +# +# mme_code: 8-bit MME code identifies the MME within a group. +# mme_group: 16-bit MME group identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# mme_bindx_addr: IP subnet to listen for eNB S1 connnections +# +##################################################################### +[mme] +mme_code = 0x1a +mme_group = 0x0001 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_bind_addr = 127.0.1.100 + +##################################################################### +# HSS configuration +# +# db_file: Location of .csv file that stores UEs information. +# +##################################################################### +[hss] +auth_algo = xor +db_file = user_db.csv + + +##################################################################### +# SP-GW configuration +# +# gtpu_bind_addr: Location of .csv file that stores UEs information. +# +##################################################################### + +[spgw] +gtpu_bind_addr=127.0.1.100 +sgi_if_addr=172.16.0.1 + +#################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. s1ap_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. s1ap_hex_limit = 32 +# +# Logging layers: s1ap, gtpc, 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_hex_limit = 32 +filename = /tmp/epc.log + +#s1ap_level = debug +#gtpc_level = debug +#spgw_level = debug +#hss_level = debug diff --git a/srsepc/hdr/CMakeLists.txt b/srsepc/hdr/CMakeLists.txt new file mode 100644 index 000000000..093086b96 --- /dev/null +++ b/srsepc/hdr/CMakeLists.txt @@ -0,0 +1,5 @@ +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in + ${PROJECT_BINARY_DIR}/version.h +) + diff --git a/srsepc/hdr/hss/hss.h b/srsepc/hdr/hss/hss.h new file mode 100644 index 000000000..4074aeb9a --- /dev/null +++ b/srsepc/hdr/hss/hss.h @@ -0,0 +1,106 @@ +/** + * + * \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/. + * + */ + +/****************************************************************************** + * File: hss.h + * Description: Top-level HSS class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef HSS_H +#define HSS_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/logger_file.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include +#include + +namespace srsepc{ + +typedef struct{ + std::string auth_algo; + std::string db_file; +}hss_args_t; + +typedef struct{ + std::string name; + uint64_t imsi; + uint8_t key[16]; + uint8_t op[16]; + uint8_t amf[2]; +}hss_ue_ctx_t; + +enum hss_auth_algo { + HSS_ALGO_XOR, + HSS_ALGO_MILENAGE +}; + +class hss +{ +public: + static hss* get_instance(void); + static void cleanup(void); + int init(hss_args_t *hss_args, srslte::log_filter* hss_log); + void stop(void); + + bool set_auth_algo(std::string auth_algo); + bool read_db_file(std::string db_file); + + void get_sqn(uint8_t sqn[6]); + void gen_rand(uint8_t rand_[16]); + bool get_k_amf_op(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *op); + bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + bool gen_auth_info_answer_milenage(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + bool gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); + + std::vector split_string(const std::string &str, char delimiter); + void get_uint_vec_from_hex_str(const std::string &key_str, uint8_t *key, uint len); + +private: + + hss(); + virtual ~hss(); + static hss *m_instance; + + uint64_t m_sqn; //48 bits + srslte::byte_buffer_pool *m_pool; + std::ifstream m_db_file; + + std::map m_imsi_to_ue_ctx; + + enum hss_auth_algo m_auth_algo; + + /*Logs*/ + srslte::log_filter *m_hss_log; + +}; + +} // namespace srsepc + +#endif // MME_H diff --git a/srsepc/hdr/mme/mme.h b/srsepc/hdr/mme/mme.h new file mode 100644 index 000000000..26296c2c6 --- /dev/null +++ b/srsepc/hdr/mme/mme.h @@ -0,0 +1,92 @@ +/** + * + * \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/. + * + */ + +/****************************************************************************** + * File: mme.h + * Description: Top-level MME class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef MME_H +#define MME_H + +#include +#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/threads.h" +#include "s1ap.h" + + +namespace srsepc{ + +/* +typedef struct { + std::string s1ap_level; + std::string all_level; + int s1ap_hex_limit; + std::string filename; +}log_args_t; +*/ + +typedef struct{ + s1ap_args_t s1ap_args; + //diameter_args_t diameter_args; + //gtpc_args_t gtpc_args; +} mme_args_t; + + +class mme: + public thread +{ +public: + static mme* get_instance(void); + static void cleanup(void); + int init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log); + void stop(); + int get_s1_mme(); + void run_thread(); + +private: + + mme(); + virtual ~mme(); + static mme *m_instance; + s1ap *m_s1ap; + mme_gtpc *m_mme_gtpc; + + bool m_running; + srslte::byte_buffer_pool *m_pool; + + /*Logs*/ + srslte::log_filter *m_s1ap_log; + srslte::log_filter *m_mme_gtpc_log; +}; + +} // namespace srsepc + +#endif // MME_H diff --git a/srsepc/hdr/mme/mme_gtpc.h b/srsepc/hdr/mme/mme_gtpc.h new file mode 100644 index 000000000..f2804e22b --- /dev/null +++ b/srsepc/hdr/mme/mme_gtpc.h @@ -0,0 +1,76 @@ +/** + * + * \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 MME_GTPC_H +#define MME_GTPC_H + +#include "srslte/common/log.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/buffer_pool.h" +#include +#include "srslte/asn1/gtpc.h" +#include "mme/s1ap_common.h" +namespace srsepc +{ + +class spgw; +class s1ap; + +class mme_gtpc +{ +public: + + static mme_gtpc* get_instance(void); + static void cleanup(void); + + bool init(srslte::log_filter *mme_gtpc_log); + + uint32_t get_new_ctrl_teid(); + void send_create_session_request(uint64_t imsi, uint32_t mme_s1ap_id); + void handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu); + void send_modify_bearer_request(erab_ctx_t *bearer_ctx); + void handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu); + void send_delete_session_request(ue_ctx_t *ue_ctx); + +private: + + mme_gtpc(); + virtual ~mme_gtpc(); + static mme_gtpc *m_instance; + + srslte::log_filter *m_mme_gtpc_log; + srslte::byte_buffer_pool *m_pool; + + s1ap* m_s1ap; + spgw* m_spgw; + in_addr_t m_mme_gtpc_ip; + + uint32_t m_next_ctrl_teid; + std::map m_teid_to_mme_s1ap_id; + +}; + +} +#endif diff --git a/srsepc/hdr/mme/s1ap.h b/srsepc/hdr/mme/s1ap.h new file mode 100644 index 000000000..fc4dd3d49 --- /dev/null +++ b/srsepc/hdr/mme/s1ap.h @@ -0,0 +1,128 @@ +/** + * + * \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 S1AP_H +#define S1AP_H + +#include "srslte/asn1/gtpc.h" +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/asn1/liblte_mme.h" +#include "srslte/common/common.h" +#include "srslte/common/log.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mme/s1ap_common.h" +#include "mme/s1ap_mngmt_proc.h" +#include "mme/s1ap_nas_transport.h" +#include "mme/s1ap_ctx_mngmt_proc.h" +#include "mme/mme_gtpc.h" +#include "hss/hss.h" + +namespace srsepc{ + +const uint16_t S1MME_PORT = 36412; + +class s1ap +{ +public: + + static s1ap* get_instance(); + static void cleanup(); + + int enb_listen(); + int init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log); + void stop(); + + int get_s1_mme(); + + void delete_enb_ctx(int32_t assoc_id); + void delete_ues_in_enb(uint16_t enb_id); + + bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu, struct sctp_sndrcvinfo *enb_sri); + bool handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri); + bool handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + + void activate_eps_bearer(uint32_t mme_s1ap_id, uint8_t ebi); + + void print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx); + + uint32_t get_plmn(); + uint32_t get_next_mme_ue_s1ap_id(); + enb_ctx_t* find_enb_ctx(uint16_t enb_id); + void add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo* enb_sri); + ue_ctx_t* find_ue_ctx(uint32_t mme_ue_s1ap_id); + void add_new_ue_ctx(const ue_ctx_t &ue_ctx); + bool delete_ue_ctx(ue_ctx_t *ue_ctx); + + uint32_t allocate_m_tmsi(uint32_t mme_ue_s1ap_id); + + s1ap_args_t m_s1ap_args; + srslte::log_filter *m_s1ap_log; + + s1ap_mngmt_proc* m_s1ap_mngmt_proc; + s1ap_nas_transport* m_s1ap_nas_transport; + s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc; + + std::map m_tmsi_to_s1ap_id; + +private: + s1ap(); + virtual ~s1ap(); + + static s1ap *m_instance; + + uint32_t m_plmn; + srslte::byte_buffer_pool *m_pool; + + hss *m_hss; + int m_s1mme; + std::map m_active_enbs; + std::map m_sctp_to_enb_id; + std::map m_active_ues; + std::map > m_enb_id_to_ue_ids; + uint32_t m_next_mme_ue_s1ap_id; + uint32_t m_next_m_tmsi; + + //FIXME the GTP-C should be moved to the MME class, the the packaging of GTP-C messages is done. + mme_gtpc *m_mme_gtpc; +}; + +inline uint32_t +s1ap::get_plmn() +{ + return m_plmn; +} + + +} //namespace srsepc + +#endif //S1AP_H diff --git a/srsepc/hdr/mme/s1ap_common.h b/srsepc/hdr/mme/s1ap_common.h new file mode 100644 index 000000000..f5462ad65 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_common.h @@ -0,0 +1,101 @@ +/* + * \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 S1AP_COMMON_H +#define S1AP_COMMON_H + +#include "srslte/common/security.h" +#include "srslte/asn1/gtpc_ies.h" +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/asn1/liblte_mme.h" +#include + +namespace srsepc{ + +static const uint8_t MAX_TA=255; //Maximum TA supported +static const uint8_t MAX_BPLMN=6; //Maximum broadcasted PLMNs per TAC +static const uint8_t MAX_ERABS_PER_UE = 16; + +enum erab_state +{ + ERAB_DEACTIVATED, + ERAB_CTX_REQUESTED, + ERAB_CTX_SETUP, + ERAB_ACTIVE +}; + +typedef struct{ + uint8_t mme_code; + uint16_t mme_group; + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string mme_bind_addr; + std::string mme_name; +} s1ap_args_t; + +typedef struct{ + bool enb_name_present; + uint32_t enb_id; + uint8_t enb_name[150]; + uint16_t mcc, mnc; + uint32_t plmn; + uint8_t nof_supported_ta; + uint16_t tac[MAX_TA]; + uint8_t nof_supported_bplmns[MAX_TA]; + uint16_t bplmns[MAX_TA][MAX_BPLMN]; + LIBLTE_S1AP_PAGINGDRX_ENUM drx; + struct sctp_sndrcvinfo sri; +} enb_ctx_t; + +typedef struct{ + uint8_t k_asme[32]; + uint8_t xres[16]; //minimum 6, maximum 16 + uint32_t dl_nas_count; + uint32_t ul_nas_count; + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + uint8_t k_nas_enc[32]; + uint8_t k_nas_int[32]; +} eps_security_ctx_t; + +typedef struct{ + enum erab_state state; + uint8_t erab_id; + srslte::gtpc_f_teid_ie enb_fteid; + srslte::gtpc_f_teid_ie sgw_ctrl_fteid; +} erab_ctx_t; + +typedef struct{ + uint64_t imsi; + uint32_t enb_ue_s1ap_id; + uint32_t mme_ue_s1ap_id; + uint16_t enb_id; + struct sctp_sndrcvinfo enb_sri; + eps_security_ctx_t security_ctxt; + erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE]; + LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap; + bool ms_network_cap_present; + LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap; + bool eit; + uint8_t procedure_transaction_id; +} ue_ctx_t; +}//namespace +#endif diff --git a/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h new file mode 100644 index 000000000..2eaca64ab --- /dev/null +++ b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h @@ -0,0 +1,70 @@ +/** + * + * \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 S1AP_CTX_MNGMT_PROC_H +#define S1AP_CTX_MNGMT_PROC_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/common.h" +#include "mme/s1ap_common.h" +#include "srslte/common/log_filter.h" +#include "mme/mme_gtpc.h" +#include "srslte/common/buffer_pool.h" + +namespace srsepc{ + +class s1ap; + +class s1ap_ctx_mngmt_proc +{ +public: + + static s1ap_ctx_mngmt_proc *m_instance; + static s1ap_ctx_mngmt_proc* get_instance(void); + static void cleanup(void); + + void init(void); + + bool send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid); + bool handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp); + bool handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + +private: + s1ap_ctx_mngmt_proc(); + virtual ~s1ap_ctx_mngmt_proc(); + + s1ap* m_s1ap; + s1ap_nas_transport* m_s1ap_nas_transport; + srslte::log_filter *m_s1ap_log; + + s1ap_args_t m_s1ap_args; + + mme_gtpc* m_mme_gtpc; + srslte::byte_buffer_pool *m_pool; +}; + +} //namespace srsepc + +#endif //S1AP_MNGMT_PROC diff --git a/srsepc/hdr/mme/s1ap_mngmt_proc.h b/srsepc/hdr/mme/s1ap_mngmt_proc.h new file mode 100644 index 000000000..7f5771676 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_mngmt_proc.h @@ -0,0 +1,68 @@ +/** + * + * \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 S1AP_MNGMT_PROC_H +#define S1AP_MNGMT_PROC_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/common.h" +#include "mme/s1ap_common.h" +#include "srslte/common/log_filter.h" + +namespace srsepc{ + +class s1ap; + +class s1ap_mngmt_proc +{ +public: + + static s1ap_mngmt_proc *m_instance; + + static s1ap_mngmt_proc* get_instance(void); + static void cleanup(void); + void init(void); + + bool handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + + //Packing/unpacking helper functions + bool unpack_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, enb_ctx_t* enb_ctx); + bool pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_ENUM cause, srslte::byte_buffer_t* msg); + bool pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buffer_t* msg); + +private: + s1ap_mngmt_proc(); + virtual ~s1ap_mngmt_proc(); + + s1ap* m_s1ap; + srslte::log_filter *m_s1ap_log; + + int m_s1mme; + s1ap_args_t m_s1ap_args; +}; + +} //namespace srsepc + +#endif //S1AP_MNGMT_PROC diff --git a/srsepc/hdr/mme/s1ap_nas_transport.h b/srsepc/hdr/mme/s1ap_nas_transport.h new file mode 100644 index 000000000..e37ce4d61 --- /dev/null +++ b/srsepc/hdr/mme/s1ap_nas_transport.h @@ -0,0 +1,106 @@ +/** + * + * \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 S1AP_NAS_TRANSPORT_H +#define S1AP_NAS_TRANSPORT_H + +#include "srslte/asn1/liblte_s1ap.h" +#include "srslte/common/buffer_pool.h" +#include "mme/s1ap_common.h" +#include "srslte/asn1/gtpc.h" +#include "hss/hss.h" +#include "mme/mme_gtpc.h" + +namespace srsepc{ + +class s1ap_nas_transport +{ +public: + + static s1ap_nas_transport* m_instance; + static s1ap_nas_transport* get_instance(void); + static void cleanup(void); + void init(void); + + bool handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + + bool handle_nas_attach_request( uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + bool handle_nas_imsi_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + bool handle_nas_guti_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri); + bool handle_nas_authentication_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool* reply_flag); + bool handle_nas_security_mode_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_nas_attach_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag); + bool handle_esm_information_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + bool handle_identity_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + bool handle_tracking_area_update_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag); + + bool pack_authentication_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t next_mme_ue_s1ap_id, uint8_t *autn,uint8_t *rand); + bool pack_authentication_reject(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id); + bool unpack_authentication_response(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp); + + bool pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx); + bool pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx); + + bool pack_attach_accept(ue_ctx_t *ue_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer); + bool pack_identity_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id); + + bool pack_emm_information(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id); + + void log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req); + void log_unhandled_pdn_con_request_ies(const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req); + void log_unhandled_initial_ue_message_ies(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue); + + +private: + s1ap_nas_transport(); + virtual ~s1ap_nas_transport(); + + srslte::log *m_s1ap_log; + srslte::byte_buffer_pool *m_pool; + + s1ap* m_s1ap; + hss* m_hss; + mme_gtpc* m_mme_gtpc; +}; + +} //namespace srsepc + +#endif //S1AP_NAS_TRANSPORT diff --git a/srsepc/hdr/spgw/spgw.h b/srsepc/hdr/spgw/spgw.h new file mode 100644 index 000000000..095b4d9b0 --- /dev/null +++ b/srsepc/hdr/spgw/spgw.h @@ -0,0 +1,127 @@ +/** + * + * \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/. + * + */ + +/****************************************************************************** + * File: spgw.h + * Description: Top-level SP-GW class. Creates and links all + * interfaces and helpers. + *****************************************************************************/ + +#ifndef SPGW_H +#define SPGW_H + +#include +#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/threads.h" +#include "srslte/asn1/gtpc.h" + +namespace srsepc{ + +class mme_gtpc; + +const uint16_t GTPU_RX_PORT = 2152; + +typedef struct { + std::string gtpu_bind_addr; + std::string sgi_if_addr; +} spgw_args_t; + + +typedef struct spgw_tunnel_ctx { + uint64_t imsi; + in_addr_t ue_ipv4; + uint8_t ebi; + struct srslte::gtpc_f_teid_ie up_ctrl_fteid; + struct srslte::gtpc_f_teid_ie up_user_fteid; + struct srslte::gtpc_f_teid_ie dw_ctrl_fteid; + struct srslte::gtpc_f_teid_ie dw_user_fteid; +} spgw_tunnel_ctx_t; + +class spgw: + public thread +{ +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_sgi_pdu(srslte::byte_buffer_t *msg); + void handle_s1u_pdu(srslte::byte_buffer_t *msg); + +private: + + spgw(); + virtual ~spgw(); + static spgw *m_instance; + + srslte::error_t init_sgi_if(spgw_args_t *args); + srslte::error_t init_s1u(spgw_args_t *args); + 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(); + + bool m_running; + 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; + + pthread_mutex_t m_mutex; + 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; + + /*Logs*/ + srslte::log_filter *m_spgw_log; + +}; + +} // namespace srsepc + +#endif // SGW_H diff --git a/srsepc/if_masq.sh b/srsepc/if_masq.sh new file mode 100755 index 000000000..88d7f370a --- /dev/null +++ b/srsepc/if_masq.sh @@ -0,0 +1,37 @@ +#/bin/bash + +################################################################### +# +# 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/. +# +################################################################### + +#Check for sudo rights +sudo -v || exit + +#Check if outbound interface was specified +if [ ! $# -eq 1 ] + then + echo "Usage :'sudo ./if_masq.sh ' " + exit +fi + +echo "Masquerading Interface "$1 + +echo 1 | sudo tee /proc/sys/net/ipv4/ip_forward 1>/dev/null +sudo iptables -t nat -A POSTROUTING -o $1 -j MASQUERADE + diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt new file mode 100644 index 000000000..e6f4c2538 --- /dev/null +++ b/srsepc/src/CMakeLists.txt @@ -0,0 +1,45 @@ + +add_subdirectory(mme) +add_subdirectory(hss) +add_subdirectory(spgw) + +# Link libstdc++ and libgcc +if(BUILD_STATIC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") +endif(BUILD_STATIC) + + +if (RPATH) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + + +add_executable(srsepc main.cc ) +target_link_libraries(srsepc srsepc_mme + srsepc_hss + srsepc_sgw + srslte_upper + srslte_common + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${SEC_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + +if (RPATH) + set_target_properties(srsepc PROPERTIES INSTALL_RPATH ".") +endif (RPATH) + +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILDEPC_CMD} STREQUAL "") + message(STATUS "Added custom post-build-EPC command: ${BUILDENB_CMD}") + add_custom_command(TARGET srsenb POST_BUILD COMMAND ${BUILDENB_CMD}) +else(NOT ${BUILDEPC_CMD} STREQUAL "") + message(STATUS "No post-build-EPC command defined") +endif (NOT ${BUILDEPC_CMD} STREQUAL "") + +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) diff --git a/srsepc/src/hss/CMakeLists.txt b/srsepc/src/hss/CMakeLists.txt new file mode 100644 index 000000000..276612c64 --- /dev/null +++ b/srsepc/src/hss/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_hss STATIC ${SOURCES}) +install(TARGETS srsepc_hss DESTINATION ${LIBRARY_DIR}) + diff --git a/srsepc/src/hss/hss.cc b/srsepc/src/hss/hss.cc new file mode 100644 index 000000000..5fc383078 --- /dev/null +++ b/srsepc/src/hss/hss.cc @@ -0,0 +1,446 @@ +/** + * + * \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 /* srand, rand */ +#include /* time */ +#include +#include +#include +#include "hss/hss.h" +#include "srslte/common/security.h" + +using namespace srslte; + +namespace srsepc{ + +hss* hss::m_instance = NULL; +boost::mutex hss_instance_mutex; + +hss::hss() +// :m_sqn(0x112233445566) + :m_sqn(0) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + return; +} + +hss::~hss() +{ + return; +} + +hss* +hss::get_instance(void) +{ + boost::mutex::scoped_lock lock(hss_instance_mutex); + if(NULL == m_instance) { + m_instance = new hss(); + } + return(m_instance); +} + +void +hss::cleanup(void) +{ + boost::mutex::scoped_lock lock(hss_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +int +hss::init(hss_args_t *hss_args, srslte::log_filter *hss_log) +{ + srand(time(NULL)); + /*Init loggers*/ + m_hss_log = hss_log; + + /*Set authentication algorithm*/ + if(set_auth_algo(hss_args->auth_algo) == false) + { + return -1; + } + /*Read user information from DB*/ + if(read_db_file(hss_args->db_file) == false) + { + m_hss_log->console("Error reading user database file %s\n", hss_args->db_file.c_str()); + return -1; + } + + m_hss_log->info("HSS Initialized. DB file %s, authentication algorithm %s\n", hss_args->db_file.c_str(),hss_args->auth_algo.c_str()); + m_hss_log->console("HSS Initialized\n"); + return 0; +} + +void +hss::stop(void) +{ + std::map::iterator it = m_imsi_to_ue_ctx.begin(); + while(it!=m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("Deleting UE context in HSS. IMSI: %lu\n", it->second->imsi); + m_hss_log->console("Deleting UE context in HSS. IMSI: %lu\n", it->second->imsi); + delete it->second; + m_imsi_to_ue_ctx.erase(it++); + } + if(m_db_file.is_open()) + { + m_db_file.close(); + } + return; +} + + +bool +hss::set_auth_algo(std::string auth_algo) +{ + if(auth_algo != "xor" && auth_algo != "milenage" ) + { + m_hss_log->error("Unrecognized authentication algorithm. auth_algo = %s\n", auth_algo.c_str()); + return false; + } + if(auth_algo == "xor") + { + m_auth_algo = HSS_ALGO_XOR; + } + else + { + m_auth_algo = HSS_ALGO_MILENAGE; + } + return true; +} + +bool +hss::read_db_file(std::string db_filename) +{ + m_db_file.open(db_filename.c_str(), std::ifstream::in); + if(!m_db_file.is_open()) + { + return false; + } + m_hss_log->info("Opened DB file: %s\n", db_filename.c_str() ); + + std::string line; + while (std::getline(m_db_file, line)) + { + if(line[0] != '#') + { + std::vector split = split_string(line,','); + if(split.size()!=5) + { + m_hss_log->error("Error parsing UE database\n"); + return false; + } + hss_ue_ctx_t *ue_ctx = new hss_ue_ctx_t; + ue_ctx->name = split[0]; + ue_ctx->imsi = atoll(split[1].c_str()); + get_uint_vec_from_hex_str(split[2],ue_ctx->key,16); + get_uint_vec_from_hex_str(split[3],ue_ctx->op,16); + get_uint_vec_from_hex_str(split[4],ue_ctx->amf,2); + + m_hss_log->debug("Added user from DB, IMSI: %015lu\n", ue_ctx->imsi); + m_hss_log->debug_hex(ue_ctx->key, 16, "User Key : "); + m_hss_log->debug_hex(ue_ctx->op, 16, "User OP : "); + m_hss_log->debug_hex(ue_ctx->amf, 2, "AMF : "); + + m_imsi_to_ue_ctx.insert(std::pair(ue_ctx->imsi,ue_ctx)); + } + } + + return true; +} + +bool +hss::gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + bool ret = false; + switch (m_auth_algo) + { + case HSS_ALGO_XOR: + ret = gen_auth_info_answer_xor(imsi, k_asme, autn, rand, xres); + break; + case HSS_ALGO_MILENAGE: + ret = gen_auth_info_answer_milenage(imsi, k_asme, autn, rand, xres); + break; + } + return ret; + +} + +bool +hss::gen_auth_info_answer_milenage(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + uint8_t k[16]; + uint8_t amf[2]; + uint8_t op[16]; + uint8_t sqn[6]; + + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + uint8_t mac[8]; + + uint16_t mcc=61441; //001 + uint16_t mnc=65281; //01 + + if(!get_k_amf_op(imsi,k,amf,op)) + { + return false; + } + gen_rand(rand); + get_sqn(sqn); + + security_milenage_f2345( k, + op, + rand, + xres, + ck, + ik, + ak); + + security_milenage_f1( k, + op, + rand, + sqn, + amf, + mac); + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + m_hss_log->debug_hex(sqn, 6, "User SQN : "); + m_hss_log->debug_hex(autn, 8, "User AUTN: "); + m_hss_log->debug_hex(xres, 8, "User XRES: "); + + return true; +} + +bool +hss::gen_auth_info_answer_xor(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) +{ + uint8_t k[16]; + uint8_t amf[2]; + uint8_t op[16]; + uint8_t sqn[6]; + + uint8_t xdout[16]; + uint8_t cdout[8]; + + uint8_t ck[16]; + uint8_t ik[16]; + uint8_t ak[6]; + uint8_t mac[8]; + + uint16_t mcc=61441; //001 + uint16_t mnc=65281; //01 + + int i = 0; + + if(!get_k_amf_op(imsi,k,amf,op)) + { + return false; + } + gen_rand(rand); + get_sqn(sqn); + + // Use RAND and K to compute RES, CK, IK and AK + for(i=0; i<16; i++) { + xdout[i] = k[i]^rand[i]; + } + + for(i=0; i<16; i++) { + xres[i] = xdout[i]; + ck[i] = xdout[(i+1)%16]; + ik[i] = xdout[(i+2)%16]; + } + for(i=0; i<6; i++) { + ak[i] = xdout[i+3]; + } + + // Generate cdout + for(i=0; i<6; i++) { + cdout[i] = sqn[i]; + } + for(i=0; i<2; i++) { + cdout[6+i] = amf[i]; + } + + // Generate MAC + for(i=0;i<8;i++) { + mac[i] = xdout[i] ^ cdout[i]; + } + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + // Generate K_asme + security_generate_k_asme( ck, + ik, + ak, + sqn, + mcc, + mnc, + k_asme); + + //Generate AUTN (autn = sqn ^ ak |+| amf |+| mac) + for(int i=0;i<6;i++ ) + { + autn[i] = sqn[i]^ak[i]; + } + for(int i=0;i<2;i++) + { + autn[6+i]=amf[i]; + } + for(int i=0;i<8;i++) + { + autn[8+i]=mac[i]; + } + + m_hss_log->debug_hex(sqn, 6, "User SQN : "); + m_hss_log->debug_hex(autn, 8, "User AUTN: "); + m_hss_log->debug_hex(xres, 8, "User XRES: "); + + return true; +} + + +bool +hss::get_k_amf_op(uint64_t imsi, uint8_t *k, uint8_t *amf, uint8_t *op ) +{ + + /* + uint8_t k_tmp[16] ={0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff}; + uint8_t amf_tmp[2]={0x80,0x00}; + uint8_t op_tmp[16]={0x63,0xbf,0xA5,0x0E,0xE6,0x52,0x33,0x65,0xFF,0x14,0xC1,0xF4,0x5F,0x88,0x73,0x7D}; + */ + std::map::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); + if(ue_ctx_it == m_imsi_to_ue_ctx.end()) + { + m_hss_log->info("User not found. IMSI: %015lu\n",imsi); + m_hss_log->console("User not found. IMSI: %015lu\n",imsi); + return false; + } + hss_ue_ctx_t *ue_ctx = ue_ctx_it->second; + m_hss_log->info("Found User %015lu\n",imsi); + memcpy(k,ue_ctx->key,16); + memcpy(amf,ue_ctx->amf,2); + memcpy(op,ue_ctx->op,16); + + return true; +} + +void +hss::get_sqn(uint8_t sqn[6]) +{ + for (int i=0; i<6; i++) + { + sqn[i] = ((uint8_t *)&m_sqn)[i]; + } + m_sqn++; + return; //TODO See TS 33.102, Annex C +} + +void +hss::gen_rand(uint8_t rand_[16]) +{ + for(int i=0;i<16;i++) + { + rand_[i]=rand()%256; //Pulls on byte at a time. It's slow, but does not depend on RAND_MAX. + } + return; +} + +/* Helper functions*/ +std::vector +hss::split_string(const std::string &str, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(str); + + while (std::getline(tokenStream, token, delimiter)) + { + tokens.push_back(token); + } + return tokens; +} + +void +hss::get_uint_vec_from_hex_str(const std::string &key_str, uint8_t *key, uint len) +{ + const char *pos = key_str.c_str(); + + for (uint count = 0; count < len; count++) { + sscanf(pos, "%2hhx", &key[count]); + pos += 2; + } + + return; +} + + /* +uint64_t +string_to_imsi() +{ + uint64_t imsi = 0; + for(int i=0;i<=14;i++){ + imsi += attach_req.eps_mobile_id.imsi[i]*std::pow(10,14-i); + } + return imsi; +} + */ +} //namespace srsepc diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc new file mode 100644 index 000000000..f70473505 --- /dev/null +++ b/srsepc/src/main.cc @@ -0,0 +1,333 @@ +/** + * + * \section COPYRIGHT + * + * \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 +#include +#include +#include +#include +#include +#include "srslte/common/bcd_helpers.h" +#include "mme/mme.h" +#include "hss/hss.h" +#include "spgw/spgw.h" + +using namespace std; +using namespace srsepc; +namespace bpo = boost::program_options; + +bool running = true; + +void +sig_int_handler(int signo){ + running = false; +} + +typedef struct { + std::string s1ap_level; + int s1ap_hex_limit; + std::string gtpc_level; + int gtpc_hex_limit; + std::string spgw_level; + int spgw_hex_limit; + std::string hss_level; + int hss_hex_limit; + + std::string all_level; + int all_hex_limit; + std::string filename; +}log_args_t; + + +typedef struct{ + mme_args_t mme_args; + hss_args_t hss_args; + spgw_args_t spgw_args; + log_args_t log_args; +}all_args_t; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void +parse_args(all_args_t *args, int argc, char* argv[]) { + + string mme_name; + string mme_code; + string mme_group; + string tac; + string mcc; + string mnc; + string mme_bind_addr; + string spgw_bind_addr; + string sgi_if_addr; + string hss_db_file; + string hss_auth_algo; + string log_filename; + + // Command line only options + bpo::options_description general("General options"); + general.add_options() + ("help,h", "Produce help message") + ("version,v", "Print version information and exit") + ; + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + + ("mme.mme_code", bpo::value(&mme_code)->default_value("0x01"), "MME Code") + ("mme.name", bpo::value(&mme_name)->default_value("srsmme01"), "MME Name") + ("mme.mme_group", bpo::value(&mme_group)->default_value("0x01"), "Cell ID") + ("mme.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("mme.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("mme.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") + ("mme.mme_bind_addr", bpo::value(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys") + ("hss.auth_algo", bpo::value(&hss_auth_algo)->default_value("milenage"),"HSS uthentication algorithm.") + ("spgw.gtpu_bind_addr", bpo::value(&spgw_bind_addr)->default_value("127.0.0.1"),"IP address of SP-GW for the S1-U connection") + ("spgw.sgi_if_addr", bpo::value(&sgi_if_addr)->default_value("176.16.0.1"),"IP address of TUN interface for the SGi connection") + + ("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.gtpu_level", bpo::value(&args->log.gtpu_level), "GTPU log level") + ("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.gtpu_hex_limit",bpo::value(&args->log.gtpu_hex_limit), "GTPU 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 + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "MME configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help")) { + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + cout << common << endl << general << endl; + exit(0); + } + + //Parsing Config File + if (!vm.count("config_file")) { + cout << "Error: Configuration file not provided" << endl; + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + exit(0); + } else { + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + } + + + + //Concert hex strings + { + std::stringstream sstr; + sstr << std::hex << vm["mme.mme_group"].as(); + sstr >> args->mme_args.s1ap_args.mme_group; + } + { + std::stringstream sstr; + sstr << std::hex << vm["mme.mme_code"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + args->mme_args.s1ap_args.mme_code = tmp; + } + { + std::stringstream sstr; + sstr << std::hex << vm["mme.tac"].as(); + sstr >> args->mme_args.s1ap_args.tac; + } + // Convert MCC/MNC strings + if(!srslte::string_to_mcc(mcc, &args->mme_args.s1ap_args.mcc)) { + cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if(!srslte::string_to_mnc(mnc, &args->mme_args.s1ap_args.mnc)) { + cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } + + args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; + args->spgw_args.gtpu_bind_addr = spgw_bind_addr; + args->spgw_args.sgi_if_addr = sgi_if_addr; + args->hss_args.db_file = hss_db_file; + args->hss_args.auth_algo = hss_auth_algo; + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + 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.spgw_level")) { + args->log_args.spgw_level = args->log_args.all_level; + } + if(!vm.count("log.hss_level")) { + args->log_args.hss_level = args->log_args.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + 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.spgw_hex_limit")) { + args->log_args.spgw_hex_limit = args->log_args.all_hex_limit; + } + if(!vm.count("log.hss_hex_limit")) { + args->log_args.hss_hex_limit = args->log_args.all_hex_limit; + } + } + return; +} + +srslte::LOG_LEVEL_ENUM +level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +int +main (int argc,char * argv[] ) +{ + cout << endl <<"--- Software Radio Systems EPC ---" << endl << endl; + signal(SIGINT, sig_int_handler); + + all_args_t args; + parse_args(&args, argc, argv); + + srslte::logger_stdout logger_stdout; + srslte::logger_file logger_file; + srslte::logger *logger; + + + /*Init logger*/ + if (!args.log_args.filename.compare("stdout")) { + logger = &logger_stdout; + } else { + logger_file.init(args.log_args.filename); + logger_file.log("\n--- Software Radio Systems EPC log ---\n\n"); + logger = &logger_file; + } + + srslte::log_filter s1ap_log; + s1ap_log.init("S1AP",logger); + s1ap_log.set_level(level(args.log_args.s1ap_level)); + 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); + + 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_log; + spgw_log.init("SPGW",logger); + spgw_log.set_level(level(args.log_args.spgw_level)); + spgw_log.set_hex_limit(args.log_args.spgw_hex_limit); + + mme *mme = mme::get_instance(); + if (mme->init(&args.mme_args, &s1ap_log, &mme_gtpc_log)) { + cout << "Error initializing MME" << endl; + exit(1); + } + + hss *hss = hss::get_instance(); + if (hss->init(&args.hss_args,&hss_log)) { + cout << "Error initializing HSS" << endl; + exit(1); + } + + spgw *spgw = spgw::get_instance(); + if (spgw->init(&args.spgw_args,&spgw_log)) { + cout << "Error initializing SP-GW" << endl; + exit(1); + } + + mme->start(); + spgw->start(); + while(running) { + sleep(1); + } + + mme->stop(); + mme->cleanup(); + spgw->stop(); + spgw->cleanup(); + hss->stop(); + hss->cleanup(); + + cout << std::endl <<"--- exiting ---" << endl; + return 0; +} diff --git a/srsepc/src/mme/CMakeLists.txt b/srsepc/src/mme/CMakeLists.txt new file mode 100644 index 000000000..cd1958566 --- /dev/null +++ b/srsepc/src/mme/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_mme STATIC ${SOURCES}) +install(TARGETS srsepc_mme DESTINATION ${LIBRARY_DIR}) + diff --git a/srsepc/src/mme/mme.cc b/srsepc/src/mme/mme.cc new file mode 100644 index 000000000..ab4778c20 --- /dev/null +++ b/srsepc/src/mme/mme.cc @@ -0,0 +1,168 @@ +/** + * + * \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 //TODO Remove +#include +#include +#include +#include +#include +#include "mme/mme.h" + +namespace srsepc{ + +mme* mme::m_instance = NULL; +boost::mutex mme_instance_mutex; + +mme::mme(): + m_running(false) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + return; +} + +mme::~mme() +{ + return; +} + +mme* +mme::get_instance(void) +{ + boost::mutex::scoped_lock lock(mme_instance_mutex); + if(NULL == m_instance) { + m_instance = new mme(); + } + return(m_instance); +} + +void +mme::cleanup(void) +{ + boost::mutex::scoped_lock lock(mme_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +int +mme::init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log) +{ + + /*Init logger*/ + m_s1ap_log = s1ap_log; + m_mme_gtpc_log = mme_gtpc_log; + /*Init S1AP*/ + m_s1ap = s1ap::get_instance(); + if(m_s1ap->init(args->s1ap_args, s1ap_log)){ + m_s1ap_log->error("Error initializing MME S1APP\n"); + exit(-1); + } + + /*Init GTP-C*/ + m_mme_gtpc = mme_gtpc::get_instance(); + if(!m_mme_gtpc->init(m_mme_gtpc_log)) + { + m_s1ap_log->console("Error initializing GTP-C\n"); + exit(-1); + } + + /*Log successful initialization*/ + m_s1ap_log->info("MME Initialized. MCC: %d, MNC: %d\n",args->s1ap_args.mcc, args->s1ap_args.mnc); + m_s1ap_log->console("MME Initialized. \n"); + return 0; +} + +void +mme::stop() +{ + if(m_running) + { + m_s1ap->stop(); + m_s1ap->cleanup(); + m_running = false; + thread_cancel(); + wait_thread_finish(); + } + return; +} + +void +mme::run_thread() +{ + srslte::byte_buffer_t *pdu = m_pool->allocate(); + uint32_t sz = SRSLTE_MAX_BUFFER_SIZE_BYTES - SRSLTE_BUFFER_HEADER_OFFSET; + + struct sockaddr_in enb_addr; + struct sctp_sndrcvinfo sri; + socklen_t fromlen = sizeof(enb_addr); + bzero(&enb_addr, sizeof(enb_addr)); + int rd_sz; + int msg_flags=0; + + //Mark the thread as running + m_running=true; + + //Get S1-MME socket + int s1mme = m_s1ap->get_s1_mme(); + while(m_running) + { + m_s1ap_log->debug("Waiting for SCTP Msg\n"); + pdu->reset(); + rd_sz = sctp_recvmsg(s1mme, pdu->msg, sz,(struct sockaddr*) &enb_addr, &fromlen, &sri, &msg_flags); + if (rd_sz == -1 && errno != EAGAIN){ + m_s1ap_log->error("Error reading from SCTP socket: %s", strerror(errno)); + } + else if (rd_sz == -1 && errno == EAGAIN){ + m_s1ap_log->debug("Socket timeout reached"); + } + else{ + if(msg_flags & MSG_NOTIFICATION) + { + //Received notification + union sctp_notification *notification = (union sctp_notification*)pdu->msg; + m_s1ap_log->debug("SCTP Notification %d\n", notification->sn_header.sn_type); + if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) + { + m_s1ap_log->info("SCTP Association Shutdown. Association: %d\n",sri.sinfo_assoc_id); + m_s1ap_log->console("SCTP Association Shutdown. Association: %d\n",sri.sinfo_assoc_id); + m_s1ap->delete_enb_ctx(sri.sinfo_assoc_id); + } + } + else + { + //Received data + pdu->N_bytes = rd_sz; + m_s1ap_log->info("Received S1AP msg. Size: %d\n", pdu->N_bytes); + m_s1ap->handle_s1ap_rx_pdu(pdu,&sri); + } + } + } + return; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc new file mode 100644 index 000000000..4b396f7a8 --- /dev/null +++ b/srsepc/src/mme/mme_gtpc.cc @@ -0,0 +1,257 @@ +/** + * + * \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 +#include "srslte/asn1/gtpc.h" +#include "mme/mme_gtpc.h" +#include "mme/s1ap.h" +#include "spgw/spgw.h" + +namespace srsepc{ + +mme_gtpc* mme_gtpc::m_instance = NULL; +boost::mutex mme_gtpc_instance_mutex; + +mme_gtpc::mme_gtpc() +{ +} + +mme_gtpc::~mme_gtpc() +{ +} + +mme_gtpc* +mme_gtpc::get_instance(void) +{ + boost::mutex::scoped_lock lock(mme_gtpc_instance_mutex); + if(NULL == m_instance) { + m_instance = new mme_gtpc(); + } + return(m_instance); +} + +void +mme_gtpc::cleanup(void) +{ + boost::mutex::scoped_lock lock(mme_gtpc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + + +bool +mme_gtpc::init(srslte::log_filter *mme_gtpc_log) +{ + + /*Init log*/ + m_mme_gtpc_log = mme_gtpc_log; + + m_next_ctrl_teid = 1; + + m_s1ap = s1ap::get_instance(); + m_mme_gtpc_ip = inet_addr("127.0.0.1");//FIXME At the moment, the GTP-C messages are not sent over the wire. So this parameter is not used. + m_spgw = spgw::get_instance(); + + m_mme_gtpc_log->info("MME GTP-C Initialized\n"); + m_mme_gtpc_log->console("MME GTP-C Initialized\n"); + return true; +} + +uint32_t +mme_gtpc::get_new_ctrl_teid() +{ + return m_next_ctrl_teid++; //FIXME Use a Id pool? +} +void +mme_gtpc::send_create_session_request(uint64_t imsi, uint32_t mme_ue_s1ap_id) +{ + m_mme_gtpc_log->info("Sending Create Session Request.\n"); + m_mme_gtpc_log->console("Sending Create Session Request.\n"); + struct srslte::gtpc_pdu cs_req_pdu; + struct srslte::gtpc_create_session_request *cs_req = &cs_req_pdu.choice.create_session_request; + + struct srslte::gtpc_pdu cs_resp_pdu; + + + //Initialize GTP-C message to zero + bzero(&cs_req_pdu, sizeof(struct srslte::gtpc_pdu)); + + //Setup GTP-C Header. FIXME: Length, sequence and other fields need to be added. + cs_req_pdu.header.piggyback = false; + cs_req_pdu.header.teid_present = true; + cs_req_pdu.header.teid = 0; //Send create session request to the butler TEID + cs_req_pdu.header.type = srslte::GTPC_MSG_TYPE_CREATE_SESSION_REQUEST; + + //Setup GTP-C Create Session Request IEs + cs_req->imsi = imsi; + // Control TEID allocated + cs_req->sender_f_teid.teid = get_new_ctrl_teid(); + cs_req->sender_f_teid.ipv4 = m_mme_gtpc_ip; + + m_mme_gtpc_log->info("Next control TEID: %lu \n", m_next_ctrl_teid); + m_mme_gtpc_log->info("Allocated control TEID: %lu \n", cs_req->sender_f_teid.teid); + m_mme_gtpc_log->console("Creating Session Response -- IMSI: %015lu \n", imsi); + m_mme_gtpc_log->console("Creating Session Response -- MME control TEID: %lu \n", cs_req->sender_f_teid.teid); + // APN + memcpy(cs_req->apn, "internet", sizeof("internet")); + // RAT Type + //cs_req->rat_type = srslte::GTPC_RAT_TYPE::EUTRAN; + + //Save RX Control TEID + m_teid_to_mme_s1ap_id.insert(std::pair(cs_req->sender_f_teid.teid, mme_ue_s1ap_id)); + + m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu); + +} + +void +mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) +{ + struct srslte::gtpc_create_session_response *cs_resp = & cs_resp_pdu->choice.create_session_response; + m_mme_gtpc_log->info("Received Create Session Response\n"); + m_mme_gtpc_log->console("Received Create Session Response\n"); + if (cs_resp_pdu->header.type != srslte::GTPC_MSG_TYPE_CREATE_SESSION_RESPONSE) + { + m_mme_gtpc_log->warning("Could not create GTPC session. Not a create session response\n"); + //TODO Handle err + return; + } + if (cs_resp->cause.cause_value != srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED){ + m_mme_gtpc_log->warning("Could not create GTPC session. Create Session Request not accepted\n"); + //TODO Handle error + return; + } + + //Get MME_UE_S1AP_ID from the control TEID + std::map::iterator id_it = m_teid_to_mme_s1ap_id.find(cs_resp_pdu->header.teid); + if(id_it == m_teid_to_mme_s1ap_id.end()) + { + //Could not find MME UE S1AP TEID + m_mme_gtpc_log->warning("Could not find MME UE S1AP TEID.\n"); + return; + } + uint32_t mme_s1ap_id = id_it->second; + + //Get S-GW Control F-TEID + srslte::gtpc_f_teid_ie sgw_ctrl_fteid; + sgw_ctrl_fteid.teid = cs_resp_pdu->header.teid; + sgw_ctrl_fteid.ipv4 = 0; //FIXME This is not used for now. In the future it will be obtained from the socket addr_info + + m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctrl_fteid.teid); + in_addr s1u_addr; + s1u_addr.s_addr = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4; + m_mme_gtpc_log->console("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr)); + m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(mme_s1ap_id, cs_resp, sgw_ctrl_fteid); +} + + +void +mme_gtpc::send_modify_bearer_request(erab_ctx_t *erab_ctx) +{ + m_mme_gtpc_log->info("Sending GTP-C Modify bearer request\n"); + srslte::gtpc_pdu mb_req_pdu; + srslte::gtpc_f_teid_ie *enb_fteid = &erab_ctx->enb_fteid; + srslte::gtpc_f_teid_ie *sgw_ctrl_fteid = &erab_ctx->sgw_ctrl_fteid; + + srslte::gtpc_header *header = &mb_req_pdu.header; + header->teid_present = true; + header->teid = sgw_ctrl_fteid->teid; + header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST; + + srslte::gtpc_modify_bearer_request *mb_req = &mb_req_pdu.choice.modify_bearer_request; + mb_req->eps_bearer_context_to_modify.ebi = erab_ctx->erab_id; + mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.ipv4 = enb_fteid->ipv4; + mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid = enb_fteid->teid; + + m_mme_gtpc_log->info("GTP-C Modify bearer request -- S-GW Control TEID %d\n", sgw_ctrl_fteid->teid ); + struct in_addr addr; + 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) ); + + // + 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); + return; +} + +void +mme_gtpc::handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu) +{ + uint32_t mme_ctrl_teid = mb_resp_pdu->header.teid; + std::map::iterator mme_s1ap_id_it = m_teid_to_mme_s1ap_id.find(mme_ctrl_teid); + if(mme_s1ap_id_it == m_teid_to_mme_s1ap_id.end()) + { + m_mme_gtpc_log->error("Could not find MME S1AP Id from control TEID\n"); + return; + } + + uint8_t ebi = mb_resp_pdu->choice.modify_bearer_response.eps_bearer_context_modified.ebi; + m_mme_gtpc_log->debug("Activating EPS bearer with id %d\n", ebi); + m_s1ap->activate_eps_bearer(mme_s1ap_id_it->second,ebi); + + return; +} + +void +mme_gtpc::send_delete_session_request(ue_ctx_t *ue_ctx) +{ + m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request\n"); + srslte::gtpc_pdu del_req_pdu; + srslte::gtpc_f_teid_ie *sgw_ctrl_fteid = NULL; + + //FIXME the UE control TEID sould be stored in the UE ctxt, not in the E-RAB ctxt + //Maybe a mme_s1ap_id to ctrl teid map as well? + + for(int i = 0; ierabs_ctx[i].state != ERAB_DEACTIVATED) + { + sgw_ctrl_fteid = &ue_ctx->erabs_ctx[i].sgw_ctrl_fteid; + break; + } + } + //FIXME: add proper error handling + assert(sgw_ctrl_fteid != NULL); + + srslte::gtpc_header *header = &del_req_pdu.header; + header->teid_present = true; + header->teid = sgw_ctrl_fteid->teid; + header->type = srslte::GTPC_MSG_TYPE_DELETE_SESSION_REQUEST; + + srslte::gtpc_delete_session_request *del_req = &del_req_pdu.choice.delete_session_request; + 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_ctrl_fteid->teid ); + + srslte::gtpc_pdu del_resp_pdu; + m_spgw->handle_delete_session_request(&del_req_pdu, &del_resp_pdu); + + //TODO Handle delete session response + return; +} +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap.cc b/srsepc/src/mme/s1ap.cc new file mode 100644 index 000000000..07563e7ca --- /dev/null +++ b/srsepc/src/mme/s1ap.cc @@ -0,0 +1,475 @@ +/** + * + * \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 +#include +#include "srslte/common/bcd_helpers.h" +#include "mme/s1ap.h" +#include "srslte/asn1/gtpc.h" +#include "srslte/common/liblte_security.h" + +namespace srsepc{ + +s1ap* s1ap::m_instance = NULL; +boost::mutex s1ap_instance_mutex; + +s1ap::s1ap(): + m_s1mme(-1), + m_next_mme_ue_s1ap_id(1) +{ +} + +s1ap::~s1ap() +{ +} + +s1ap* +s1ap::get_instance(void) +{ + boost::mutex::scoped_lock lock(s1ap_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap(); + } + return(m_instance); +} + +void +s1ap::cleanup(void) +{ + boost::mutex::scoped_lock lock(s1ap_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +int +s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log) +{ + m_pool = srslte::byte_buffer_pool::get_instance(); + + m_s1ap_args = s1ap_args; + srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn); + m_next_m_tmsi = rand(); + //Init log + m_s1ap_log = s1ap_log; + + //Init message handlers + m_s1ap_mngmt_proc = s1ap_mngmt_proc::get_instance(); //Managment procedures + m_s1ap_mngmt_proc->init(); + m_s1ap_nas_transport = s1ap_nas_transport::get_instance(); //NAS Transport procedures + m_s1ap_nas_transport->init(); + m_s1ap_ctx_mngmt_proc = s1ap_ctx_mngmt_proc::get_instance(); //Context Management Procedures + m_s1ap_ctx_mngmt_proc->init(); + + + //Get pointer to the HSS + m_hss = hss::get_instance(); + + //Get pointer to GTP-C class + m_mme_gtpc = mme_gtpc::get_instance(); + + //Initialize S1-MME + m_s1mme = enb_listen(); + + m_s1ap_log->info("S1AP Initialized\n"); + return 0; +} + +void +s1ap::stop() +{ + if (m_s1mme != -1){ + close(m_s1mme); + } + std::map::iterator it = m_active_enbs.begin(); + while(it!=m_active_enbs.end()) + { + m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", it->second->enb_id); + m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", it->second->enb_id); + delete_ues_in_enb(it->second->enb_id); + delete it->second; + m_active_enbs.erase(it++); + } + + //Cleanup message handlers + s1ap_mngmt_proc::cleanup(); + s1ap_nas_transport::cleanup(); + s1ap_ctx_mngmt_proc::cleanup(); + return; +} + +void +s1ap::delete_enb_ctx(int32_t assoc_id) +{ + std::map::iterator it_assoc = m_sctp_to_enb_id.find(assoc_id); + uint16_t enb_id = it_assoc->second; + + std::map::iterator it_ctx = m_active_enbs.find(enb_id); + if(it_ctx == m_active_enbs.end() || it_assoc == m_sctp_to_enb_id.end()) + { + m_s1ap_log->error("Could not find eNB to delete. Association: %d\n",assoc_id); + return; + } + + m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_id); + m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_id); + + //Delete connected UEs ctx + delete_ues_in_enb(enb_id); + + //Delete eNB + delete it_ctx->second; + m_active_enbs.erase(it_ctx); + m_sctp_to_enb_id.erase(it_assoc); + return; +} + +void +s1ap::delete_ues_in_enb(uint16_t enb_id) +{ + //delete UEs ctx + std::map >::iterator ues_in_enb = m_enb_id_to_ue_ids.find(enb_id); + std::set::iterator ue_id = ues_in_enb->second.begin(); + while(ue_id != ues_in_enb->second.end() ) + { + std::map::iterator ue_ctx = m_active_ues.find(*ue_id); + m_s1ap_log->info("Deleting UE context. UE IMSI: %lu\n", ue_ctx->second->imsi); + m_s1ap_log->console("Deleting UE context. UE IMSI: %lu\n", ue_ctx->second->imsi); + delete ue_ctx->second; //delete UE context + m_active_ues.erase(ue_ctx); //remove from general MME map + ues_in_enb->second.erase(ue_id++); //erase from the eNB's UE set + } + +} + +int +s1ap::get_s1_mme() +{ + return m_s1mme; +} + +int +s1ap::enb_listen() +{ + /*This function sets up the SCTP socket for eNBs to connect to*/ + int sock_fd, err; + struct sockaddr_in s1mme_addr; + struct sctp_event_subscribe evnts; + + m_s1ap_log->info("S1-MME Initializing\n"); + sock_fd = socket (AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); + if (sock_fd == -1){ + m_s1ap_log->console("Could not create SCTP socket\n"); + return -1; + } + + //Sets the data_io_event to be able to use sendrecv_info + //Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown + bzero (&evnts, sizeof (evnts)) ; + evnts.sctp_data_io_event = 1; + evnts.sctp_shutdown_event=1; + if(setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof (evnts))){ + m_s1ap_log->console("Subscribing to sctp_data_io_events failed\n"); + return -1; + } + + //S1-MME bind + bzero(&s1mme_addr, sizeof(s1mme_addr)); + s1mme_addr.sin_family = AF_INET; + inet_pton(AF_INET, m_s1ap_args.mme_bind_addr.c_str(), &(s1mme_addr.sin_addr) ); + s1mme_addr.sin_port = htons(S1MME_PORT); + err = bind(sock_fd, (struct sockaddr*) &s1mme_addr, sizeof (s1mme_addr)); + if (err != 0){ + m_s1ap_log->error("Error binding SCTP socket\n"); + m_s1ap_log->console("Error binding SCTP socket\n"); + return -1; + } + + //Listen for connections + err = listen(sock_fd,SOMAXCONN); + if (err != 0){ + m_s1ap_log->error("Error in SCTP socket listen\n"); + m_s1ap_log->console("Error in SCTP socket listen\n"); + return -1; + } + + return sock_fd; +} + + +bool +s1ap::handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu, struct sctp_sndrcvinfo *enb_sri) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT rx_pdu; + + if(liblte_s1ap_unpack_s1ap_pdu((LIBLTE_BYTE_MSG_STRUCT*)pdu, &rx_pdu) != LIBLTE_SUCCESS) { + m_s1ap_log->error("Failed to unpack received PDU\n"); + return false; + } + + switch(rx_pdu.choice_type) { + case LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE: + m_s1ap_log->info("Received initiating PDU\n"); + return handle_initiating_message(&rx_pdu.choice.initiatingMessage, enb_sri); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME: + m_s1ap_log->info("Received Succeseful Outcome PDU\n"); + return handle_successful_outcome(&rx_pdu.choice.successfulOutcome); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME: + m_s1ap_log->info("Received Unsucceseful Outcome PDU\n"); + return true;//TODO handle_unsuccessfuloutcome(&rx_pdu.choice.unsuccessfulOutcome); + break; + default: + m_s1ap_log->error("Unhandled PDU type %d\n", rx_pdu.choice_type); + return false; + } + + return true; + +} + +bool +s1ap::handle_initiating_message(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri) +{ + bool reply_flag = false; + srslte::byte_buffer_t * reply_buffer = m_pool->allocate(); + + switch(msg->choice_type) { + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST: + m_s1ap_log->info("Received S1 Setup Request.\n"); + m_s1ap_mngmt_proc->handle_s1_setup_request(&msg->choice.S1SetupRequest, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE: + m_s1ap_log->info("Received Initial UE Message.\n"); + m_s1ap_nas_transport->handle_initial_ue_message(&msg->choice.InitialUEMessage, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT: + m_s1ap_log->info("Received Uplink NAS Transport Message.\n"); + m_s1ap_nas_transport->handle_uplink_nas_transport(&msg->choice.UplinkNASTransport, enb_sri, reply_buffer, &reply_flag); + break; + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST: + m_s1ap_log->info("Received UE Context Release Request Message.\n"); + m_s1ap_ctx_mngmt_proc->handle_ue_context_release_request(&msg->choice.UEContextReleaseRequest, enb_sri, reply_buffer, &reply_flag); + break; + default: + m_s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); + } + //Send Reply to eNB + if(reply_flag == true) + { + ssize_t n_sent = sctp_send(m_s1mme,reply_buffer->msg, reply_buffer->N_bytes, enb_sri, 0); + if(n_sent == -1) + { + m_s1ap_log->console("Failed to send S1 Setup Setup Reply\n"); + m_pool->deallocate(reply_buffer); + return false; + } + } + m_pool->deallocate(reply_buffer); + return true; +} + +bool +s1ap::handle_successful_outcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE: + m_s1ap_log->info("Received Initial Context Setup Response.\n"); + return m_s1ap_ctx_mngmt_proc->handle_initial_context_setup_response(&msg->choice.InitialContextSetupResponse); + default: + m_s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + + +bool +s1ap::delete_ue_ctx(ue_ctx_t *ue_ctx) +{ + uint32_t mme_ue_s1ap_id = ue_ctx->mme_ue_s1ap_id; + std::map::iterator ue_ctx_it = m_active_ues.find(mme_ue_s1ap_id); + if(ue_ctx_it == m_active_ues.end() ) + { + m_s1ap_log->info("UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + return false; + } + + //Delete UE within eNB UE set + std::map::iterator it = m_sctp_to_enb_id.find(ue_ctx->enb_sri.sinfo_assoc_id); + if(it == m_sctp_to_enb_id.end() ) + { + m_s1ap_log->error("Could not find eNB for this request.\n"); + return false; + } + uint16_t enb_id = it->second; + std::map >::iterator ue_set = m_enb_id_to_ue_ids.find(enb_id); + if(ue_set == m_enb_id_to_ue_ids.end()) + { + m_s1ap_log->error("Could not find the eNB's UEs.\n"); + return false; + } + ue_set->second.erase(mme_ue_s1ap_id); + + //Delete UE context + delete ue_ctx; + m_active_ues.erase(ue_ctx_it); + m_s1ap_log->info("Deleted UE Context.\n"); + + return true; +} + +enb_ctx_t* +s1ap::find_enb_ctx(uint16_t enb_id) +{ + std::map::iterator it = m_active_enbs.find(enb_id); + if(it == m_active_enbs.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +void +s1ap::add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo *enb_sri) +{ + m_s1ap_log->info("Adding new eNB context. eNB ID %d\n", enb_ctx.enb_id); + std::set ue_set; + enb_ctx_t *enb_ptr = new enb_ctx_t; + memcpy(enb_ptr,&enb_ctx,sizeof(enb_ctx_t)); + m_active_enbs.insert(std::pair(enb_ptr->enb_id,enb_ptr)); + m_sctp_to_enb_id.insert(std::pair(enb_sri->sinfo_assoc_id, enb_ptr->enb_id)); + m_enb_id_to_ue_ids.insert(std::pair >(enb_ptr->enb_id,ue_set)); + + return; +} + +ue_ctx_t* +s1ap::find_ue_ctx(uint32_t mme_ue_s1ap_id) +{ + std::map::iterator it = m_active_ues.find(mme_ue_s1ap_id); + if(it == m_active_ues.end()) + { + return NULL; + } + else + { + return it->second; + } +} + +void +s1ap::add_new_ue_ctx(const ue_ctx_t &ue_ctx) +{ + ue_ctx_t *ue_ptr = new ue_ctx_t; + memcpy(ue_ptr,&ue_ctx,sizeof(ue_ctx)); + m_active_ues.insert(std::pair(ue_ptr->mme_ue_s1ap_id,ue_ptr)); + + std::map::iterator it_enb = m_sctp_to_enb_id.find(ue_ptr->enb_sri.sinfo_assoc_id); + uint16_t enb_id = it_enb->second; + std::map >::iterator it_ue_id = m_enb_id_to_ue_ids.find(enb_id); + if(it_ue_id==m_enb_id_to_ue_ids.end()) + { + m_s1ap_log->error("Could not find eNB's UEs\n"); + return; + } + it_ue_id->second.insert(ue_ptr->mme_ue_s1ap_id); + return; +} + +uint32_t +s1ap::get_next_mme_ue_s1ap_id() +{ + return m_next_mme_ue_s1ap_id++; +} + +void +s1ap::activate_eps_bearer(uint32_t mme_s1ap_id, uint8_t ebi) +{ + std::map::iterator ue_ctx_it = m_active_ues.find(mme_s1ap_id); + if(ue_ctx_it == m_active_ues.end()) + { + m_s1ap_log->error("Could not find UE context\n"); + return; + } + ue_ctx_t * ue_ctx = ue_ctx_it->second; + if (ue_ctx->erabs_ctx[ebi].state != ERAB_CTX_SETUP) + { + m_s1ap_log->error("EPS Bearer could not be activated. MME S1AP Id %d, EPS Bearer id %d, state %d\n",mme_s1ap_id,ebi,ue_ctx->erabs_ctx[ebi].state); + m_s1ap_log->console("EPS Bearer could not be activated. MME S1AP Id %d, EPS Bearer id %d\n",mme_s1ap_id,ebi,ue_ctx->erabs_ctx[ebi].state); + return; + } + + ue_ctx->erabs_ctx[ebi].state = ERAB_ACTIVE; + m_s1ap_log->info("Activated EPS Bearer\n"); + return; +} + +uint32_t +s1ap::allocate_m_tmsi(uint32_t mme_ue_s1ap_id) +{ + //uint32_t m_tmsi = m_next_m_tmsi++; + //m_tmsi_to_s1ap_id.insert(std::pair(m_tmsi,mme_ue_s1ap_id)); + uint32_t m_tmsi = 0x0123; + return m_tmsi; +} + +void +s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx) +{ + std::string mnc_str, mcc_str; + + if(enb_ctx.enb_name_present) + { + m_s1ap_log->console("%s - eNB Name: %s, eNB id: 0x%x\n",prefix.c_str(), enb_ctx.enb_name, enb_ctx.enb_id); + m_s1ap_log->info("%s - eNB Name: %s, eNB id: 0x%x\n", prefix.c_str(), enb_ctx.enb_name, enb_ctx.enb_id); + } + else + { + m_s1ap_log->console("%s - eNB Id 0x%x\n",prefix.c_str(), enb_ctx.enb_id); + m_s1ap_log->info("%s - eNB Id 0x%x\n", prefix.c_str(), enb_ctx.enb_id); + } + srslte::mcc_to_string(enb_ctx.mcc, &mcc_str); + srslte::mnc_to_string(enb_ctx.mnc, &mnc_str); + m_s1ap_log->info("%s - MCC:%s, MNC:%s, PLMN: %d\n", prefix.c_str(), mcc_str.c_str(), mnc_str.c_str(), enb_ctx.plmn); + m_s1ap_log->console("%s - MCC:%s, MNC:%s, PLMN: %d\n", prefix.c_str(), mcc_str.c_str(), mnc_str.c_str(), enb_ctx.plmn); + for(int i=0;iinfo("%s - TAC %d, B-PLMN %d\n",prefix.c_str(), enb_ctx.tac[i],enb_ctx.bplmns[i][j]); + m_s1ap_log->console("%s - TAC %d, B-PLMN %d\n",prefix.c_str(), enb_ctx.tac[i],enb_ctx.bplmns[i][j]); + } + } + m_s1ap_log->console("%s - Paging DRX %d\n",prefix.c_str(),enb_ctx.drx); + return; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc new file mode 100644 index 000000000..7dffe3c35 --- /dev/null +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -0,0 +1,302 @@ +/** + * + * \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 "srslte/upper/s1ap_common.h" +#include "srslte/common/bcd_helpers.h" +#include "mme/s1ap.h" +#include "mme/s1ap_ctx_mngmt_proc.h" +#include "srslte/common/liblte_security.h" + + +namespace srsepc{ + +s1ap_ctx_mngmt_proc* s1ap_ctx_mngmt_proc::m_instance = NULL; +boost::mutex s1ap_ctx_mngmt_proc_instance_mutex; + + +s1ap_ctx_mngmt_proc::s1ap_ctx_mngmt_proc() +{ +} + +s1ap_ctx_mngmt_proc::~s1ap_ctx_mngmt_proc() +{ +} + +s1ap_ctx_mngmt_proc* +s1ap_ctx_mngmt_proc::get_instance(void) +{ + boost::mutex::scoped_lock lock(s1ap_ctx_mngmt_proc_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_ctx_mngmt_proc(); + } + return(m_instance); +} + +void +s1ap_ctx_mngmt_proc::cleanup(void) +{ + boost::mutex::scoped_lock lock(s1ap_ctx_mngmt_proc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +void +s1ap_ctx_mngmt_proc::init(void) +{ + m_s1ap = s1ap::get_instance(); + m_mme_gtpc = mme_gtpc::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_s1ap_args = m_s1ap->m_s1ap_args; + m_pool = srslte::byte_buffer_pool::get_instance(); + m_s1ap_nas_transport = s1ap_nas_transport::get_instance(); +} + +bool +s1ap_ctx_mngmt_proc::send_initial_context_setup_request(uint32_t mme_ue_s1ap_id, struct srslte::gtpc_create_session_response *cs_resp, struct srslte::gtpc_f_teid_ie sgw_ctrl_fteid) +{ + int s1mme = m_s1ap->get_s1_mme(); + + //Prepare reply PDU + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST; + + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *in_ctxt_req = &init->choice.InitialContextSetupRequest; + + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt = &in_ctxt_req->E_RABToBeSetupListCtxtSUReq.buffer[0]; //FIXME support more than one erab + srslte::byte_buffer_t *reply_buffer = m_pool->allocate(); + + m_s1ap_log->info("Preparing to send Initial Context Setup request\n"); + + //Find UE Context + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->error("Could not find UE to send Setup Context Request. MME S1AP Id: %d", mme_ue_s1ap_id); + return false; + } + + //Add MME and eNB S1AP Ids + in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->mme_ue_s1ap_id; + in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->enb_ue_s1ap_id; + + //Set UE-AMBR + in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000;//2^32-1 + in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL.BitRate=1000000000;//FIXME Get UE-AMBR from HSS + + //Setup eRAB context + in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1; + erab_ctxt->e_RAB_ID.E_RAB_ID = cs_resp->eps_bearer_context_created.ebi; + //Setup E-RAB QoS parameters + erab_ctxt->e_RABlevelQoSParameters.qCI.QCI = 9; + erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest + erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION; + erab_ctxt->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE; + + erab_ctxt->e_RABlevelQoSParameters.gbrQosInformation_present=false; + + //Set E-RAB S-GW F-TEID + if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){ + m_s1ap_log->error("Did not receive S1-U TEID in create session response\n"); + return false; + } + erab_ctxt->transportLayerAddress.n_bits = 32; //IPv4 + uint32_t sgw_s1u_ip = htonl(cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4); + //uint32_t sgw_s1u_ip = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4; + uint8_t *tmp_ptr = erab_ctxt->transportLayerAddress.buffer; + liblte_value_2_bits(sgw_s1u_ip, &tmp_ptr, 32);//FIXME consider ipv6 + + uint32_t tmp_teid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.teid; + memcpy(erab_ctxt->gTP_TEID.buffer, &tmp_teid, sizeof(uint32_t)); + + //Set UE security capabilities and k_enb + bzero(in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer,sizeof(uint8_t)*16); + bzero(in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer,sizeof(uint8_t)*16); + for(int i = 0; i<3; i++) + { + if(ue_ctx->ue_network_cap.eea[i+1] == true) + { + in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 1; //EEA supported + } + else + { + in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 0; //EEA not supported + } + if(ue_ctx->ue_network_cap.eia[i+1] == true) + { + in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 1; //EEA supported + } + else + { + in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 0; //EEA not supported + } + // in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[0] = 1; //EIA1 + } + uint8_t key_enb[32]; + liblte_security_generate_k_enb(ue_ctx->security_ctxt.k_asme, ue_ctx->security_ctxt.ul_nas_count, key_enb); + liblte_unpack(key_enb, 32, in_ctxt_req->SecurityKey.buffer); + m_s1ap_log->info("Generating KeNB with UL NAS COUNT: %d\n",ue_ctx->security_ctxt.ul_nas_count); + //Set Attach accepted and activat default bearer NAS messages + if(cs_resp->paa_present != true) + { + m_s1ap_log->error("PAA not present\n"); + return false; + } + if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) + { + m_s1ap_log->error("IPv6 not supported yet\n"); + return false; + } + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + m_s1ap_nas_transport->pack_attach_accept(ue_ctx, erab_ctxt, &cs_resp->paa, nas_buffer); + + + LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n"); + return false; + } + //Send Reply to eNB + ssize_t n_sent = sctp_send(s1mme,reply_buffer->msg, reply_buffer->N_bytes, &ue_ctx->enb_sri, 0); + if(n_sent == -1) + { + m_s1ap_log->error("Failed to send Initial Context Setup Request\n"); + return false; + } + + //Change E-RAB state to Context Setup Requested and save S-GW control F-TEID + ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED; + ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.teid = sgw_ctrl_fteid.teid; + ue_ctx->erabs_ctx[erab_ctxt->e_RAB_ID.E_RAB_ID].sgw_ctrl_fteid.ipv4 = sgw_ctrl_fteid.ipv4; + + struct in_addr addr; + addr.s_addr = htonl(sgw_s1u_ip); + m_s1ap_log->info("Sent Intial Context Setup Request. E-RAB id %d \n",erab_ctxt->e_RAB_ID.E_RAB_ID); + m_s1ap_log->info("Initial Context -- S1-U TEID 0x%x. IP %s \n", tmp_teid,inet_ntoa(addr)); + m_s1ap_log->console("Sent Intial Context Setup Request, E-RAB id %d\n",erab_ctxt->e_RAB_ID.E_RAB_ID); + m_s1ap_log->console("Initial Context -- S1-U TEID 0x%x. IP %s \n", tmp_teid,inet_ntoa(addr)); + + m_pool->deallocate(reply_buffer); + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp) +{ + + uint32_t mme_ue_s1ap_id = in_ctxt_resp->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id); + if (ue_ctx == NULL) + { + m_s1ap_log->error("Could not find UE's context in active UE's map\n"); + return false; + } + + //Setup E-RABs + for(uint32_t i=0; iE_RABSetupListCtxtSURes.len;i++) + { + uint8_t erab_id = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].e_RAB_ID.E_RAB_ID; + erab_ctx_t *erab_ctx = &ue_ctx->erabs_ctx[erab_id]; + if (erab_ctx->state != ERAB_CTX_REQUESTED) + { + m_s1ap_log->error("E-RAB requested was not active %d\n",erab_id); + return false; + } + //Mark E-RAB with context setup + erab_ctx->state = ERAB_CTX_SETUP; + + //Set the GTP information + uint8_t *bit_ptr = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer; + erab_ctx->enb_fteid.ipv4 = htonl(liblte_bits_2_value(&bit_ptr,32)); + memcpy(&erab_ctx->enb_fteid.teid, in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].gTP_TEID.buffer, 4); + erab_ctx->enb_fteid.teid = ntohl(erab_ctx->enb_fteid.teid); + + char enb_addr_str[INET_ADDRSTRLEN+1]; + const char *err = inet_ntop(AF_INET, &erab_ctx->enb_fteid.ipv4,enb_addr_str,sizeof(enb_addr_str)); + if(err == NULL) + { + m_s1ap_log->error("Error converting IP to string\n"); + } + + m_s1ap_log->info("E-RAB Context Setup. E-RAB id %d\n",erab_ctx->erab_id); + m_s1ap_log->info("E-RAB Context -- eNB TEID 0x%x, eNB Address %s\n", erab_ctx->enb_fteid.teid, enb_addr_str); + m_s1ap_log->console("E-RAB Context Setup. E-RAB id %d\n",erab_ctx->erab_id); + m_s1ap_log->console("E-RAB Context -- eNB TEID 0x%x; eNB GTP-U Address %s\n", erab_ctx->enb_fteid.teid, enb_addr_str); + + } + return true; +} + +bool +s1ap_ctx_mngmt_proc::handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *ue_rel, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + uint32_t mme_ue_s1ap_id = ue_rel->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + m_s1ap_log->info("Received UE Context Release Request. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + m_s1ap_log->console("Received UE Context Release Request. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); + + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + m_s1ap_log->info("UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); + return false; + } + + //Delete any context at the SPGW + bool active = false; + for(int i=0;ierabs_ctx[i].state != ERAB_DEACTIVATED) + { + active = true; + //ue_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; + break; + } + } + if(active == true) + { + //There are active E-RABs, send delete session request + m_mme_gtpc->send_delete_session_request(ue_ctx); + } + //m_s1ap->delete_ue_ctx(ue_ctx); + for(int i=0;ierabs_ctx[i].state = ERAB_DEACTIVATED; + } + //Delete UE context + m_s1ap_log->info("Deleted UE Context.\n"); + return true; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc new file mode 100644 index 000000000..8fa469ec3 --- /dev/null +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -0,0 +1,266 @@ +/** + * + * \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 "srslte/upper/s1ap_common.h" +#include "srslte/common/bcd_helpers.h" +#include "mme/s1ap.h" +#include "mme/s1ap_mngmt_proc.h" + +namespace srsepc{ + +s1ap_mngmt_proc* s1ap_mngmt_proc::m_instance = NULL; +boost::mutex s1ap_mngmt_proc_instance_mutex; + + +s1ap_mngmt_proc::s1ap_mngmt_proc() +{ +} + +s1ap_mngmt_proc::~s1ap_mngmt_proc() +{ +} + +s1ap_mngmt_proc* +s1ap_mngmt_proc::get_instance(void) +{ + boost::mutex::scoped_lock lock(s1ap_mngmt_proc_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_mngmt_proc(); + } + return(m_instance); +} + +void +s1ap_mngmt_proc::cleanup(void) +{ + boost::mutex::scoped_lock lock(s1ap_mngmt_proc_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +void +s1ap_mngmt_proc::init(void) +{ + m_s1ap = s1ap::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_s1mme = m_s1ap->get_s1_mme(); + m_s1ap_args = m_s1ap->m_s1ap_args; +} + +bool +s1ap_mngmt_proc::handle_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + enb_ctx_t enb_ctx; + LIBLTE_S1AP_S1AP_PDU_STRUCT reply_pdu; + + if(!unpack_s1_setup_request(msg, &enb_ctx)) + { + m_s1ap_log->error("Malformed S1 Setup Request\n"); + return false; + } + + //Log S1 Setup Request Info + m_s1ap_log->console("Received S1 Setup Request.\n"); + m_s1ap->print_enb_ctx_info(std::string("S1 Setup Request"),enb_ctx); + + //Check matching PLMNs + if(enb_ctx.plmn!=m_s1ap->get_plmn()){ + + m_s1ap_log->console("Sending S1 Setup Failure - Unkown PLMN\n"); + m_s1ap_log->warning("Sending S1 Setup Failure - Unkown PLMN\n"); + pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_UNKNOWN_PLMN,reply_buffer); + } + else{ + enb_ctx_t *enb_ptr = m_s1ap->find_enb_ctx(enb_ctx.enb_id); + if(enb_ptr != NULL) + { + //eNB already registered + //TODO replace enb_ctx + } + else + { + //new eNB + m_s1ap->add_new_enb_ctx(enb_ctx,enb_sri); + } + + pack_s1_setup_response(m_s1ap_args, reply_buffer); + m_s1ap_log->console("Sending S1 Setup Response\n"); + m_s1ap_log->info("Sending S1 Setup Response\n"); + } + + *reply_flag = true; + return true; +} + + +/* + * Packing/Unpacking helper functions. + */ +bool +s1ap_mngmt_proc::unpack_s1_setup_request(LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *msg, enb_ctx_t* enb_ctx) +{ + + uint8_t enb_id_bits[32]; + uint32_t plmn = 0; + uint16_t tac, bplmn; + + uint32_t tmp32=0; + //eNB Name + enb_ctx->enb_name_present=msg->eNBname_present; + if(msg->eNBname_present) + { + bzero(enb_ctx->enb_name,sizeof(enb_ctx->enb_name)); + memcpy(enb_ctx->enb_name,&msg->eNBname.buffer,msg->eNBname.n_octets); + } + //eNB Id + bzero(enb_id_bits,sizeof(enb_id_bits)); + memcpy(&enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], msg->Global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer, LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + liblte_pack(enb_id_bits, 32, (uint8_t*) &tmp32); + enb_ctx->enb_id=ntohl(tmp32); + + //PLMN Id + ((uint8_t*)&plmn)[1] = msg->Global_ENB_ID.pLMNidentity.buffer[0]; + ((uint8_t*)&plmn)[2] = msg->Global_ENB_ID.pLMNidentity.buffer[1]; + ((uint8_t*)&plmn)[3] = msg->Global_ENB_ID.pLMNidentity.buffer[2]; + + enb_ctx->plmn = ntohl(plmn); + srslte::s1ap_plmn_to_mccmnc(enb_ctx->plmn, &enb_ctx->mcc, &enb_ctx->mnc); + + //SupportedTAs + enb_ctx->nof_supported_ta=msg->SupportedTAs.len; + for(uint16_t i=0; iSupportedTAs.len; i++) + { + //TAC + ((uint8_t*)&enb_ctx->tac[i])[0] = msg->SupportedTAs.buffer[i].tAC.buffer[0]; + ((uint8_t*)&enb_ctx->tac[i])[1] = msg->SupportedTAs.buffer[i].tAC.buffer[1]; + enb_ctx->tac[i]=ntohs(enb_ctx->tac[i]); + enb_ctx->nof_supported_bplmns[i]=msg->SupportedTAs.buffer[i].broadcastPLMNs.len; + for (uint16_t j=0; jSupportedTAs.buffer[i].broadcastPLMNs.len; j++) + { + //BPLMNs + ((uint8_t*)&enb_ctx->bplmns[i][j])[1] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[0]; + ((uint8_t*)&enb_ctx->bplmns[i][j])[2] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[1]; + ((uint8_t*)&enb_ctx->bplmns[i][j])[3] = msg->SupportedTAs.buffer[i].broadcastPLMNs.buffer[j].buffer[2]; + + enb_ctx->bplmns[i][j] = ntohl(enb_ctx->bplmns[i][j]); + } + } + + //Default Paging DRX + enb_ctx->drx = msg->DefaultPagingDRX.e; + + return true; +} + +bool +s1ap_mngmt_proc::pack_s1_setup_failure(LIBLTE_S1AP_CAUSEMISC_ENUM cause, srslte::byte_buffer_t *msg) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME; + + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *unsucc = &pdu.choice.unsuccessfulOutcome; + unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + unsucc->criticality = LIBLTE_S1AP_CRITICALITY_REJECT; + unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE; + + LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT* s1_fail=(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT*)&unsucc->choice; + + s1_fail->TimeToWait_present=false; + s1_fail->CriticalityDiagnostics_present=false; + s1_fail->Cause.ext=false; + s1_fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_MISC; + s1_fail->Cause.choice.misc.ext=false; + s1_fail->Cause.choice.misc.e=cause; + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)msg); + return true; + } + + +bool +s1ap_mngmt_proc::pack_s1_setup_response(s1ap_args_t s1ap_args, srslte::byte_buffer_t *msg) +{ + + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + succ->criticality = LIBLTE_S1AP_CRITICALITY_IGNORE; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE; + + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT* s1_resp=(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT*)&succ->choice; + + s1_resp->ext=false; + + //MME Name + s1_resp->MMEname_present=true; + s1_resp->MMEname.ext=false; + s1_resp->MMEname.n_octets=s1ap_args.mme_name.length(); + memcpy(s1_resp->MMEname.buffer,s1ap_args.mme_name.c_str(),s1ap_args.mme_name.length()); + + //Served GUMEIs + s1_resp->ServedGUMMEIs.len=1;//TODO Only one served GUMMEI supported + LIBLTE_S1AP_SERVEDGUMMEISITEM_STRUCT *serv_gummei = &s1_resp->ServedGUMMEIs.buffer[0]; + + serv_gummei->ext=false; + //serv_gummei->iE_Extensions=false; + + uint32_t plmn=0; + srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &plmn); + plmn=htonl(plmn); + serv_gummei->servedPLMNs.len = 1; //Only one PLMN supported + serv_gummei->servedPLMNs.buffer[0].buffer[0]=((uint8_t*)&plmn)[1]; + serv_gummei->servedPLMNs.buffer[0].buffer[1]=((uint8_t*)&plmn)[2]; + serv_gummei->servedPLMNs.buffer[0].buffer[2]=((uint8_t*)&plmn)[3]; + + serv_gummei->servedGroupIDs.len=1; //LIBLTE_S1AP_SERVEDGROUPIDS_STRUCT + uint16_t tmp=htons(s1ap_args.mme_group); + serv_gummei->servedGroupIDs.buffer[0].buffer[0]=((uint8_t*)&tmp)[0]; + serv_gummei->servedGroupIDs.buffer[0].buffer[1]=((uint8_t*)&tmp)[1]; + + serv_gummei->servedMMECs.len=1; //Only one MMEC served + serv_gummei->servedMMECs.buffer[0].buffer[0]=s1ap_args.mme_code; + + //Relative MME Capacity + s1_resp->RelativeMMECapacity.RelativeMMECapacity=255; + + //Relay Unsupported + s1_resp->MMERelaySupportIndicator_present=false; + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)msg); + + return true; +} + +} //namespace srsepc diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc new file mode 100644 index 000000000..8eb0f21ea --- /dev/null +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -0,0 +1,1366 @@ +/** + * + * \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 "mme/s1ap.h" +#include "mme/s1ap_nas_transport.h" +#include "srslte/common/security.h" + +namespace srsepc{ + +s1ap_nas_transport* s1ap_nas_transport::m_instance = NULL; +boost::mutex s1ap_nas_transport_instance_mutex; + +s1ap_nas_transport::s1ap_nas_transport() +{ + return; +} + +s1ap_nas_transport::~s1ap_nas_transport() +{ + return; +} + +s1ap_nas_transport* +s1ap_nas_transport::get_instance(void) +{ + boost::mutex::scoped_lock lock(s1ap_nas_transport_instance_mutex); + if(NULL == m_instance) { + m_instance = new s1ap_nas_transport(); + } + return(m_instance); +} + +void +s1ap_nas_transport::cleanup(void) +{ + boost::mutex::scoped_lock lock(s1ap_nas_transport_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +void +s1ap_nas_transport::init(void) +{ + m_s1ap = s1ap::get_instance(); + m_s1ap_log = m_s1ap->m_s1ap_log; + m_pool = srslte::byte_buffer_pool::get_instance(); + + m_hss = hss::get_instance(); + m_mme_gtpc = mme_gtpc::get_instance(); +} + + +bool +s1ap_nas_transport::handle_initial_ue_message(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req; + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT pdn_con_req; + LIBLTE_MME_SERVICE_REQUEST_MSG_STRUCT service_req; + + m_s1ap_log->console("Received Initial UE Message.\n"); + m_s1ap_log->info("Received Initial UE Message.\n"); + + //Get info from initial UE message + uint32_t enb_ue_s1ap_id = init_ue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + + //Log unhandled Initial UE message IEs + log_unhandled_initial_ue_message_ies(init_ue); + + /*Check whether NAS Attach Request or Service Request*/ + uint8_t pd, msg_type; + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + memcpy(nas_msg->msg, &init_ue->NAS_PDU.buffer, init_ue->NAS_PDU.n_octets); + nas_msg->N_bytes = init_ue->NAS_PDU.n_octets; + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + if(msg_type == LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST) + { + //Get NAS Attach Request and PDN connectivity request messages + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_attach_request_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &attach_req); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS attach request. Error: %s\n", liblte_error_text[err]); + m_pool->deallocate(nas_msg); + return false; + } + /*Get PDN Connectivity Request*/ + err = liblte_mme_unpack_pdn_connectivity_request_msg(&attach_req.esm_msg, &pdn_con_req); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS PDN Connectivity Request. Error: %s\n", liblte_error_text[err]); + m_pool->deallocate(nas_msg); + return false; + } + handle_nas_attach_request(enb_ue_s1ap_id, attach_req,pdn_con_req,reply_buffer,reply_flag, enb_sri); + } + else if(msg_type == LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST) + { + m_s1ap_log->info("Received Service Request \n"); + m_s1ap_log->console("Received Service Request \n"); + liblte_mme_unpack_service_request_msg((LIBLTE_BYTE_MSG_STRUCT*) nas_msg, &service_req); + return false; + } + m_pool->deallocate(nas_msg); + + return true; +} + +bool +s1ap_nas_transport::handle_uplink_nas_transport(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, struct sctp_sndrcvinfo *enb_sri, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + + bool ue_valid = true; + uint32_t enb_ue_s1ap_id = ul_xport->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + uint32_t mme_ue_s1ap_id = ul_xport->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + + ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + //TODO UE not registered, send error message. + m_s1ap_log->warning("Received uplink NAS, but could not find UE context. MME-UE S1AP id: %lu\n",mme_ue_s1ap_id); + return false; + } + + m_s1ap_log->debug("Received uplink NAS and found UE. MME-UE S1AP id: %lu\n",mme_ue_s1ap_id); + + //Get NAS message type + uint8_t pd, msg_type; + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + + memcpy(nas_msg->msg, &ul_xport->NAS_PDU.buffer, ul_xport->NAS_PDU.n_octets); + nas_msg->N_bytes = ul_xport->NAS_PDU.n_octets; + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + + switch (msg_type) { + case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE: + m_s1ap_log->info("Uplink NAS: Received Authentication Response\n"); + m_s1ap_log->console("Uplink NAS: Received Authentication Response\n"); + handle_nas_authentication_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE: + m_s1ap_log->info("Uplink NAS: Received Security Mode Complete\n"); + m_s1ap_log->console("Uplink NAS: Received Security Mode Complete\n"); + handle_nas_security_mode_complete(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + case LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE: + m_s1ap_log->info("Uplink NAS: Received Attach Complete\n"); + m_s1ap_log->console("Uplink NAS: Received Attach Complete\n"); + handle_nas_attach_complete(nas_msg, ue_ctx, reply_buffer, reply_flag); + ue_ctx->security_ctxt.ul_nas_count++; + break; + case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE: + m_s1ap_log->info("Uplink NAS: Received ESM Information Response\n"); + m_s1ap_log->console("Uplink NAS: Received ESM Information Response\n"); + handle_esm_information_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + ue_ctx->security_ctxt.ul_nas_count++; + break; + case LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE: + m_s1ap_log->info("Uplink NAS: Received Identity Response\n"); + m_s1ap_log->console("Uplink NAS: Received Identity Response\n"); + handle_identity_response(nas_msg, ue_ctx, reply_buffer, reply_flag); + //ue_ctx->security_ctxt.ul_nas_count++; + break; + case LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST: + m_s1ap_log->info("UL NAS: Tracking Area Update Request\n"); + handle_tracking_area_update_request(nas_msg, ue_ctx, reply_buffer, reply_flag); + break; + default: + m_s1ap_log->warning("Unhandled NAS message 0x%x\n", msg_type ); + m_s1ap_log->console("Unhandled NAS message 0x%x\n", msg_type ); + return false; //FIXME (nas_msg deallocate needs to be called) + } + + if(*reply_flag == true) + { + m_s1ap_log->info("DL NAS: Sent Downlink NAS message\n"); + m_s1ap_log->console("DL NAS: Sent Downlink NAs Message\n"); + } + m_pool->deallocate(nas_msg); + + return true; +} + +bool +s1ap_nas_transport::handle_nas_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + //Get attach type from attach request + if(attach_req.eps_mobile_id.type_of_id == LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI) + { + m_s1ap_log->console("Attach Request -- IMSI-style attach request\n"); + m_s1ap_log->info("Attach Request -- IMSI-style attach request\n"); + handle_nas_imsi_attach_request(enb_ue_s1ap_id, attach_req, pdn_con_req, reply_buffer, reply_flag, enb_sri); + } + else if(attach_req.eps_mobile_id.type_of_id == LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI) + { + m_s1ap_log->console("Attach Request -- GUTI-style attach request\n"); + m_s1ap_log->info("Attach Request -- GUTI-style attach request\n"); + handle_nas_guti_attach_request(enb_ue_s1ap_id, attach_req, pdn_con_req, reply_buffer, reply_flag, enb_sri); + } + else + { + m_s1ap_log->error("Unhandle Mobile Id type in attach request\n"); + return false; + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_imsi_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + uint8_t k_asme[32]; + uint8_t autn[16]; + uint8_t rand[6]; + uint8_t xres[8]; + + ue_ctx_t ue_ctx; + ue_ctx.imsi = 0; + ue_ctx.enb_ue_s1ap_id = enb_ue_s1ap_id; + ue_ctx.mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + + //Save UE network capabilities + memcpy(&ue_ctx.ue_network_cap, &attach_req.ue_network_cap, sizeof(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT)); + ue_ctx.ms_network_cap_present = attach_req.ms_network_cap_present; + if(attach_req.ms_network_cap_present) + { + memcpy(&ue_ctx.ms_network_cap, &attach_req.ms_network_cap, sizeof(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT)); + } + uint8_t eps_bearer_id = pdn_con_req.eps_bearer_id; //TODO: Unused + ue_ctx.procedure_transaction_id = pdn_con_req.proc_transaction_id; + + //Save whether ESM information transfer is necessary + ue_ctx.eit = pdn_con_req.esm_info_transfer_flag_present; + //m_s1ap_log->console("EPS Bearer id: %d\n", eps_bearer_id); + //Initialize NAS count + ue_ctx.security_ctxt.ul_nas_count = 0; + ue_ctx.security_ctxt.dl_nas_count = 0; + //Add eNB info to UE ctxt + memcpy(&ue_ctx.enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ue_ctx.erabs_ctx[i].state = ERAB_DEACTIVATED; + ue_ctx.erabs_ctx[i].erab_id = i; + } + + //IMSI style attach + ue_ctx.imsi = 0; + for(int i=0;i<=14;i++){ + ue_ctx.imsi += attach_req.eps_mobile_id.imsi[i]*std::pow(10,14-i); + } + + m_s1ap_log->console("Attach request -- IMSI: %015lu\n", ue_ctx.imsi); + m_s1ap_log->info("Attach request -- IMSI: %015lu\n", ue_ctx.imsi); + m_s1ap_log->console("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ue_ctx.enb_ue_s1ap_id, ue_ctx.mme_ue_s1ap_id); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EEA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eea[0], + attach_req.ue_network_cap.eea[1], + attach_req.ue_network_cap.eea[2], + attach_req.ue_network_cap.eea[3], + attach_req.ue_network_cap.eea[4], + attach_req.ue_network_cap.eea[5], + attach_req.ue_network_cap.eea[6], + attach_req.ue_network_cap.eea[7]); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EIA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eia[0], + attach_req.ue_network_cap.eia[1], + attach_req.ue_network_cap.eia[2], + attach_req.ue_network_cap.eia[3], + attach_req.ue_network_cap.eia[4], + attach_req.ue_network_cap.eia[5], + attach_req.ue_network_cap.eia[6], + attach_req.ue_network_cap.eia[7]); + m_s1ap_log->console("Attach Request -- MS Network Capabilities Present: %s\n", attach_req.ms_network_cap_present ? "true" : "false"); + m_s1ap_log->console("PDN Connectivity Request -- EPS Bearer Identity requested: %d\n", pdn_con_req.eps_bearer_id); + m_s1ap_log->console("PDN Connectivity Request -- Procedure Transaction Id: %d\n", pdn_con_req.proc_transaction_id); + m_s1ap_log->console("PDN Connectivity Request -- ESM Information Transfer requested: %s\n", pdn_con_req.esm_info_transfer_flag_present ? "true" : "false"); + + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(ue_ctx.imsi, ue_ctx.security_ctxt.k_asme, autn, rand, ue_ctx.security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n",ue_ctx.imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n",ue_ctx.imsi); + return false; + } + + m_s1ap->add_new_ue_ctx(ue_ctx); + //Pack NAS Authentication Request in Downlink NAS Transport msg + pack_authentication_request(reply_buffer, ue_ctx.enb_ue_s1ap_id, ue_ctx.mme_ue_s1ap_id, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sending Athentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sending Athentication Request\n"); + return true; +} + +bool +s1ap_nas_transport::handle_nas_guti_attach_request(uint32_t enb_ue_s1ap_id, + const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req, + const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req, + srslte::byte_buffer_t *reply_buffer, + bool* reply_flag, + struct sctp_sndrcvinfo *enb_sri) +{ + //GUTI style attach + uint32_t m_tmsi = attach_req.eps_mobile_id.guti.m_tmsi; + std::map::iterator it = m_s1ap->m_tmsi_to_s1ap_id.find(m_tmsi); + if(it == m_s1ap->m_tmsi_to_s1ap_id.end()) + { + //Could not find IMSI from M-TMSI, send Id request + ue_ctx_t ue_ctx; + ue_ctx.imsi = 0; + ue_ctx.enb_ue_s1ap_id = enb_ue_s1ap_id; + ue_ctx.mme_ue_s1ap_id = m_s1ap->get_next_mme_ue_s1ap_id(); + + //Save UE network capabilities + memcpy(&ue_ctx.ue_network_cap, &attach_req.ue_network_cap, sizeof(LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT)); + ue_ctx.ms_network_cap_present = attach_req.ms_network_cap_present; + if(attach_req.ms_network_cap_present) + { + memcpy(&ue_ctx.ms_network_cap, &attach_req.ms_network_cap, sizeof(LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT)); + } + uint8_t eps_bearer_id = pdn_con_req.eps_bearer_id; //TODO: Unused + ue_ctx.procedure_transaction_id = pdn_con_req.proc_transaction_id; + + //Save whether ESM information transfer is necessary + ue_ctx.eit = pdn_con_req.esm_info_transfer_flag_present; + //m_s1ap_log->console("EPS Bearer id: %d\n", eps_bearer_id); + //Initialize NAS count + ue_ctx.security_ctxt.ul_nas_count = 0; + ue_ctx.security_ctxt.dl_nas_count = 0; + //Add eNB info to UE ctxt + memcpy(&ue_ctx.enb_sri, enb_sri, sizeof(struct sctp_sndrcvinfo)); + //Initialize E-RABs + for(uint i = 0 ; i< MAX_ERABS_PER_UE; i++) + { + ue_ctx.erabs_ctx[i].state = ERAB_DEACTIVATED; + ue_ctx.erabs_ctx[i].erab_id = i; + } + m_s1ap_log->console("Attach request -- IMSI: %015lu\n", ue_ctx.imsi); + m_s1ap_log->info("Attach request -- IMSI: %015lu\n", ue_ctx.imsi); + m_s1ap_log->console("Attach request -- eNB-UE S1AP Id: %d, MME-UE S1AP Id: %d\n", ue_ctx.enb_ue_s1ap_id, ue_ctx.mme_ue_s1ap_id); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EEA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eea[0], + attach_req.ue_network_cap.eea[1], + attach_req.ue_network_cap.eea[2], + attach_req.ue_network_cap.eea[3], + attach_req.ue_network_cap.eea[4], + attach_req.ue_network_cap.eea[5], + attach_req.ue_network_cap.eea[6], + attach_req.ue_network_cap.eea[7]); + m_s1ap_log->console("Attach Request -- UE Network Capabilities EIA: %d%d%d%d%d%d%d%d\n", + attach_req.ue_network_cap.eia[0], + attach_req.ue_network_cap.eia[1], + attach_req.ue_network_cap.eia[2], + attach_req.ue_network_cap.eia[3], + attach_req.ue_network_cap.eia[4], + attach_req.ue_network_cap.eia[5], + attach_req.ue_network_cap.eia[6], + attach_req.ue_network_cap.eia[7]); + m_s1ap_log->console("Attach Request -- MS Network Capabilities Present: %s\n", attach_req.ms_network_cap_present ? "true" : "false"); + m_s1ap_log->console("PDN Connectivity Request -- EPS Bearer Identity requested: %d\n", pdn_con_req.eps_bearer_id); + m_s1ap_log->console("PDN Connectivity Request -- Procedure Transaction Id: %d\n", pdn_con_req.proc_transaction_id); + m_s1ap_log->console("PDN Connectivity Request -- ESM Information Transfer requested: %s\n", pdn_con_req.esm_info_transfer_flag_present ? "true" : "false"); + + m_s1ap_log->console("Could not find M-TMSI=0x%x. Sending ID request\n",m_tmsi); + m_s1ap_log->info("Could not find M-TMSI=0x%d. Sending Id Request\n", m_tmsi); + m_s1ap->add_new_ue_ctx(ue_ctx); + pack_identity_request(reply_buffer, ue_ctx.enb_ue_s1ap_id, ue_ctx.mme_ue_s1ap_id); + *reply_flag = true; + return true; + } + else{ + m_s1ap_log->console("Attach Request -- Found M-TMSI: %d\n",m_tmsi); + ue_ctx_t *ue_ctx_ptr = m_s1ap->find_ue_ctx(it->second); + if(ue_ctx_ptr!=NULL) + { + m_s1ap_log->console("Found UE context. IMSI: %015lu\n",ue_ctx_ptr->imsi); + m_mme_gtpc->send_create_session_request(ue_ctx_ptr->imsi, ue_ctx_ptr->mme_ue_s1ap_id); + *reply_flag = false; //No reply needed + return true; + } + else + { + m_s1ap_log->error("Found M-TMSI but could not find UE context\n"); + return false; + } + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_authentication_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool* reply_flag) +{ + + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp; + bool ue_valid=true; + + m_s1ap_log->console("Authentication Response -- IMSI %015lu\n", ue_ctx->imsi); + m_s1ap_log->console("Authentication Response -- RES 0x%x%x%x%x%x%x%x%x\n", + auth_resp.res[0], + auth_resp.res[1], + auth_resp.res[2], + auth_resp.res[3], + auth_resp.res[4], + auth_resp.res[5], + auth_resp.res[6], + auth_resp.res[7] + ); + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &auth_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + for(int i=0; i<8;i++) + { + if(auth_resp.res[i] != ue_ctx->security_ctxt.xres[i]) + { + ue_valid = false; + } + } + if(!ue_valid) + { + std::cout<security_ctxt.xres[i]; + } + std::cout<console("UE Authentication Rejected.\n"); + m_s1ap_log->warning("UE Authentication Rejected.\n"); + //Send back Athentication Reject + pack_authentication_reject(reply_buffer, ue_ctx->enb_ue_s1ap_id, ue_ctx->mme_ue_s1ap_id); + *reply_flag = true; + m_s1ap_log->console("Downlink NAS: Sending Authentication Reject.\n"); + return false; + } + else + { + m_s1ap_log->console("UE Authentication Accepted.\n"); + m_s1ap_log->info("UE Authentication Accepted.\n"); + //Send Security Mode Command + pack_security_mode_command(reply_buffer, ue_ctx); + *reply_flag = true; + m_s1ap_log->console("Downlink NAS: Sending NAS Security Mode Command.\n"); + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_security_mode_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag) +{ + LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sm_comp; + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_security_mode_complete_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &sm_comp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + //TODO Check integrity + + //TODO Handle imeisv + if(sm_comp.imeisv_present) + { + m_s1ap_log->warning("IMEI-SV present but not handled"); + } + + m_s1ap_log->info("Security Mode Command Complete -- IMSI: %lu\n", ue_ctx->imsi); + m_s1ap_log->console("Security Mode Command Complete -- IMSI: %lu\n", ue_ctx->imsi); + if(ue_ctx->eit == true) + { + pack_esm_information_request(reply_buffer, ue_ctx); + m_s1ap_log->console("Sending ESM information request\n"); + *reply_flag = true; + } + else + { + //FIXME The packging of GTP-C messages is not ready. + //This means that GTP-U tunnels are created with function calls, as opposed to GTP-C. + m_mme_gtpc->send_create_session_request(ue_ctx->imsi, ue_ctx->mme_ue_s1ap_id); + *reply_flag = false; //No reply needed + } + return true; +} + +bool +s1ap_nas_transport::handle_nas_attach_complete(srslte::byte_buffer_t *nas_msg, ue_ctx_t *ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + + LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT attach_comp; + uint8_t pd, msg_type; + srslte::byte_buffer_t *esm_msg = m_pool->allocate(); + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT act_bearer; + + m_s1ap_log->info_hex(nas_msg->msg, nas_msg->N_bytes, "NAS Attach complte"); + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_attach_complete_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &attach_comp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + err = liblte_mme_unpack_activate_default_eps_bearer_context_accept_msg( (LIBLTE_BYTE_MSG_STRUCT *) &attach_comp.esm_msg, &act_bearer); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking Activate EPS Bearer Context Accept Msg. Error: %s\n", liblte_error_text[err]); + return false; + } + + m_s1ap_log->console("Unpacked Attached Complete Message\n"); + m_s1ap_log->console("Unpacked Activavate Default EPS Bearer message. EPS Bearer id %d\n",act_bearer.eps_bearer_id); + //ue_ctx->erabs_ctx[act_bearer->eps_bearer_id].enb_fteid; + if(act_bearer.eps_bearer_id < 5 || act_bearer.eps_bearer_id > 16) + { + m_s1ap_log->error("EPS Bearer ID out of range\n"); + return false; + } + m_mme_gtpc->send_modify_bearer_request(&ue_ctx->erabs_ctx[act_bearer.eps_bearer_id]); + return true; +} + +bool +s1ap_nas_transport::handle_esm_information_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + LIBLTE_MME_ESM_INFORMATION_RESPONSE_MSG_STRUCT esm_info_resp; + + //Get NAS authentication response + LIBLTE_ERROR_ENUM err = srslte_mme_unpack_esm_information_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &esm_info_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + m_s1ap_log->info("ESM Info: EPS bearer id %d\n",esm_info_resp.eps_bearer_id); + if(esm_info_resp.apn_present) + { + m_s1ap_log->info("ESM Info: APN %s\n",esm_info_resp.eps_bearer_id); + m_s1ap_log->console("ESM Info: APN %s\n",esm_info_resp.eps_bearer_id); + } + + //FIXME The packging of GTP-C messages is not ready. + //This means that GTP-U tunnels are created with function calls, as opposed to GTP-C. + m_mme_gtpc->send_create_session_request(ue_ctx->imsi, ue_ctx->mme_ue_s1ap_id); + return true; +} + +bool +s1ap_nas_transport::handle_identity_response(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + uint8_t autn[16]; + uint8_t rand[6]; + uint8_t xres[8]; + + LIBLTE_MME_ID_RESPONSE_MSG_STRUCT id_resp; + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_identity_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &id_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + uint64_t imsi = 0; + for(int i=0;i<=14;i++){ + imsi += id_resp.mobile_id.imsi[i]*std::pow(10,14-i); + } + m_s1ap_log->info("Id Response -- IMSI: %015lu\n", imsi); + m_s1ap_log->console("Id Response -- IMSI: %015lu\n", imsi); + ue_ctx->imsi = imsi; + + //Get Authentication Vectors from HSS + if(!m_hss->gen_auth_info_answer(imsi, ue_ctx->security_ctxt.k_asme, autn, rand, ue_ctx->security_ctxt.xres)) + { + m_s1ap_log->console("User not found. IMSI %015lu\n",imsi); + m_s1ap_log->info("User not found. IMSI %015lu\n",imsi); + return false; + } + + //Pack NAS Authentication Request in Downlink NAS Transport msg + pack_authentication_request(reply_msg, ue_ctx->enb_ue_s1ap_id, ue_ctx->mme_ue_s1ap_id, autn, rand); + + //Send reply to eNB + *reply_flag = true; + m_s1ap_log->info("Downlink NAS: Sent Athentication Request\n"); + m_s1ap_log->console("Downlink NAS: Sent Athentication Request\n"); + //TODO Start T3460 Timer! + + return true; +} + + +bool +s1ap_nas_transport::handle_tracking_area_update_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag) +{ + + /* + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_tracking_area_update_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &tau_req); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + */ + m_s1ap_log->console("Warning: Tracking Area Update Request messages not handled yet.\n"); + m_s1ap_log->warning("Warning: Tracking Area Update Request messages not handled yet.\n"); + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->mme_ue_s1ap_id; + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + m_s1ap_log->console("Tracking area accept to MME-UE S1AP Id %d\n", ue_ctx->mme_ue_s1ap_id); + + LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT tau_acc; + /*typedef struct{ + LIBLTE_MME_GPRS_TIMER_STRUCT t3412; + LIBLTE_MME_EPS_MOBILE_ID_STRUCT guti; + LIBLTE_MME_TRACKING_AREA_IDENTITY_LIST_STRUCT tai_list; + LIBLTE_MME_EPS_BEARER_CONTEXT_STATUS_STRUCT eps_bearer_context_status; + LIBLTE_MME_LOCATION_AREA_ID_STRUCT lai; + LIBLTE_MME_MOBILE_ID_STRUCT ms_id; + LIBLTE_MME_GPRS_TIMER_STRUCT t3402; + LIBLTE_MME_GPRS_TIMER_STRUCT t3423; + LIBLTE_MME_PLMN_LIST_STRUCT equivalent_plmns; + LIBLTE_MME_EMERGENCY_NUMBER_LIST_STRUCT emerg_num_list; + LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_STRUCT eps_network_feature_support; + LIBLTE_MME_GPRS_TIMER_3_STRUCT t3412_ext; + LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_ENUM additional_update_result; + uint8 eps_update_result; + uint8 emm_cause; + bool t3412_present; + bool guti_present; + bool tai_list_present; + bool eps_bearer_context_status_present; + bool lai_present; + bool ms_id_present; + bool emm_cause_present; + bool t3402_present; + bool t3423_present; + bool equivalent_plmns_present; + bool emerg_num_list_present; + bool eps_network_feature_support_present; + bool additional_update_result_present; + bool t3412_ext_present; +}LIBLTE_MME_TRACKING_AREA_UPDATE_ACCEPT_MSG_STRUCT; +*/ + //Send reply to eNB + //*reply_flag = true; + + return true; +} + + +/*Packing/Unpacking helper functions*/ +bool +s1ap_nas_transport::pack_authentication_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t next_mme_ue_s1ap_id, uint8_t *autn, uint8_t *rand) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = next_mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + //Pack NAS PDU + LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req; + memcpy(auth_req.autn , autn, 16); + memcpy(auth_req.rand, rand, 16); + auth_req.nas_ksi.tsc_flag=LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + auth_req.nas_ksi.nas_ksi=0; + + LIBLTE_ERROR_ENUM err = liblte_mme_pack_authentication_request_msg(&auth_req, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Athentication Request\n"); + m_s1ap_log->console("Error packing Athentication Request\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Athentication Request\n"); + m_s1ap_log->console("Error packing Athentication Request\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + + return true; +} + +bool +s1ap_nas_transport::pack_authentication_reject(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_AUTHENTICATION_REJECT_MSG_STRUCT auth_rej; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_authentication_reject_msg(&auth_rej, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Athentication Reject\n"); + m_s1ap_log->console("Error packing Athentication Reject\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Athentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Athentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::unpack_authentication_response(LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ul_xport, + LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT *auth_resp ) +{ + + /*Get NAS Authentiation Response Message*/ + uint8_t pd, msg_type; + srslte::byte_buffer_t *nas_msg = m_pool->allocate(); + + memcpy(nas_msg->msg, &ul_xport->NAS_PDU.buffer, ul_xport->NAS_PDU.n_octets); + nas_msg->N_bytes = ul_xport->NAS_PDU.n_octets; + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, &pd, &msg_type); + + if(msg_type!=LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE){ + m_s1ap_log->error("Error unpacking NAS authentication response\n"); + return false; + } + + LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT *) nas_msg, auth_resp); + if(err != LIBLTE_SUCCESS){ + m_s1ap_log->error("Error unpacking NAS authentication response. Error: %s\n", liblte_error_text[err]); + return false; + } + + m_pool->deallocate(nas_msg); + return true; +} + +bool +s1ap_nas_transport::pack_security_mode_command(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->mme_ue_s1ap_id; + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + //Pack NAS PDU + LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sm_cmd; + + sm_cmd.selected_nas_sec_algs.type_of_eea = LIBLTE_MME_TYPE_OF_CIPHERING_ALGORITHM_EEA0; + sm_cmd.selected_nas_sec_algs.type_of_eia = LIBLTE_MME_TYPE_OF_INTEGRITY_ALGORITHM_128_EIA1; + + sm_cmd.nas_ksi.tsc_flag=LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; + sm_cmd.nas_ksi.nas_ksi=0; + + //Replay UE security cap + memcpy(sm_cmd.ue_security_cap.eea,ue_ctx->ue_network_cap.eea,8*sizeof(bool)); + memcpy(sm_cmd.ue_security_cap.eia,ue_ctx->ue_network_cap.eia,8*sizeof(bool)); + sm_cmd.ue_security_cap.uea_present = ue_ctx->ue_network_cap.uea_present; + memcpy(sm_cmd.ue_security_cap.uea,ue_ctx->ue_network_cap.uea,8*sizeof(bool)); + sm_cmd.ue_security_cap.uia_present = ue_ctx->ue_network_cap.uia_present; + memcpy(sm_cmd.ue_security_cap.uia,ue_ctx->ue_network_cap.uia,8*sizeof(bool)); + sm_cmd.ue_security_cap.gea_present = ue_ctx->ms_network_cap_present; + memcpy(sm_cmd.ue_security_cap.gea,ue_ctx->ms_network_cap.gea,8*sizeof(bool)); + + sm_cmd.imeisv_req_present=false; + sm_cmd.nonce_ue_present=false; + sm_cmd.nonce_mme_present=false; + + uint8_t sec_hdr_type=3; + + ue_ctx->security_ctxt.dl_nas_count = 0; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_security_mode_command_msg(&sm_cmd,sec_hdr_type, ue_ctx->security_ctxt.dl_nas_count,(LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->console("Error packing Athentication Request\n"); + return false; + } + + //Generate MAC for integrity protection + //FIXME Write wrapper to support EIA1, EIA2, etc. + //TODO which is the RB ID? Standard says a constant, but which? + uint8_t mac[4]; + + srslte::security_generate_k_nas( ue_ctx->security_ctxt.k_asme, + srslte::CIPHERING_ALGORITHM_ID_EEA0, + srslte::INTEGRITY_ALGORITHM_ID_128_EIA1, + ue_ctx->security_ctxt.k_nas_enc, + ue_ctx->security_ctxt.k_nas_int + ); + + srslte::security_128_eia1 (&ue_ctx->security_ctxt.k_nas_int[16], + ue_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->console("Error packing Athentication Request\n"); + return false; + } + m_s1ap_log->debug_hex(reply_msg->msg, reply_msg->N_bytes, "Security Mode Command: "); + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_ctx_t *ue_ctx) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctx->mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctx->enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_ESM_INFORMATION_REQUEST_MSG_STRUCT esm_info_req; + esm_info_req.eps_bearer_id=0; + esm_info_req.proc_transaction_id = ue_ctx->procedure_transaction_id; + uint8_t sec_hdr_type=2; + + ue_ctx->security_ctxt.dl_nas_count++; + + LIBLTE_ERROR_ENUM err = srslte_mme_pack_esm_information_request_msg(&esm_info_req, sec_hdr_type,ue_ctx->security_ctxt.dl_nas_count,(LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing ESM information request\n"); + m_s1ap_log->console("Error packing ESM information request\n"); + return false; + } + + uint8_t mac[4]; + srslte::security_128_eia1 (&ue_ctx->security_ctxt.k_nas_int[16], + ue_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Athentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Athentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_attach_accept(ue_ctx_t *ue_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer) { + LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept; + LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req; + bzero(&act_def_eps_bearer_context_req,sizeof(LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT)); + + m_s1ap_log->info("Packing Attach Accept\n"); + + //Get decimal MCC and MNC + uint32_t mcc = 0; + mcc += 0x000F & m_s1ap->m_s1ap_args.mcc; + mcc += 10*( (0x00F0 & m_s1ap->m_s1ap_args.mcc) >> 4); + mcc += 100*( (0x0F00 & m_s1ap->m_s1ap_args.mcc) >> 8); + + uint32_t mnc = 0; + if( 0xFF00 == (m_s1ap->m_s1ap_args.mnc & 0xFF00 )) + { + //Two digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + } + else + { + //Three digit MNC + mnc += 0x000F & m_s1ap->m_s1ap_args.mnc; + mnc += 10*((0x00F0 & m_s1ap->m_s1ap_args.mnc) >> 4); + mnc += 100*((0x0F00 & m_s1ap->m_s1ap_args.mnc) >> 8); + } + + //Attach accept + attach_accept.eps_attach_result = LIBLTE_MME_EPS_ATTACH_RESULT_EPS_ONLY; + //Mandatory + //FIXME: Set t3412 from config + attach_accept.t3412.unit = LIBLTE_MME_GPRS_TIMER_UNIT_1_MINUTE; // GPRS 1 minute unit + attach_accept.t3412.value = 30; // 30 minute periodic timer + //FIXME: Set tai_list from config + attach_accept.tai_list.N_tais = 1; + attach_accept.tai_list.tai[0].mcc = mcc; + attach_accept.tai_list.tai[0].mnc = mnc; + attach_accept.tai_list.tai[0].tac = m_s1ap->m_s1ap_args.tac; + + //Allocate a GUTI ot the UE + attach_accept.guti_present=true; + attach_accept.guti.type_of_id = 6; //110 -> GUTI + attach_accept.guti.guti.mcc = mcc; + attach_accept.guti.guti.mnc = mnc; + attach_accept.guti.guti.mme_group_id = m_s1ap->m_s1ap_args.mme_group; + attach_accept.guti.guti.mme_code = m_s1ap->m_s1ap_args.mme_code; + attach_accept.guti.guti.m_tmsi = m_s1ap->allocate_m_tmsi(ue_ctx->mme_ue_s1ap_id); + m_s1ap_log->debug("Allocated GUTI: MCC %d, MNC %d, MME Group Id %d, MME Code 0x%x, M-TMSI 0x%x\n", + attach_accept.guti.guti.mcc, + attach_accept.guti.guti.mnc, + attach_accept.guti.guti.mme_group_id, + attach_accept.guti.guti.mme_code, + attach_accept.guti.guti.m_tmsi); + + //Make sure all unused options are set to false + attach_accept.lai_present=false; + attach_accept.ms_id_present=false; + attach_accept.emm_cause_present=false; + attach_accept.t3402_present=false; + attach_accept.t3423_present=false; + attach_accept.equivalent_plmns_present=false; + attach_accept.emerg_num_list_present=false; + attach_accept.eps_network_feature_support_present=false; + attach_accept.additional_update_result_present=false; + attach_accept.t3412_ext_present=false; + + //Set activate default eps bearer (esm_ms) + //Set pdn_addr + act_def_eps_bearer_context_req.pdn_addr.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4; + memcpy(act_def_eps_bearer_context_req.pdn_addr.addr, &paa->ipv4, 4); + //Set eps bearer id + act_def_eps_bearer_context_req.eps_bearer_id = erab_ctxt->e_RAB_ID.E_RAB_ID; + act_def_eps_bearer_context_req.transaction_id_present = false; + //set eps_qos + act_def_eps_bearer_context_req.eps_qos.qci = erab_ctxt->e_RABlevelQoSParameters.qCI.QCI; + act_def_eps_bearer_context_req.eps_qos.mbr_ul = 254; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_dl = 254; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_ul_ext = 250; //FIXME + act_def_eps_bearer_context_req.eps_qos.mbr_dl_ext = 250; //FIXME check + //set apn + //act_def_eps_bearer_context_req.apn + std::string apn("test123"); + act_def_eps_bearer_context_req.apn.apn = apn; //FIXME + act_def_eps_bearer_context_req.proc_transaction_id = ue_ctx->procedure_transaction_id; //FIXME + + //Set DNS server + act_def_eps_bearer_context_req.protocol_cnfg_opts_present = true; + act_def_eps_bearer_context_req.protocol_cnfg_opts.N_opts = 1; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].id = 0x0d; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].len = 4; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents[0] = 8; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents[1] = 8; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents[2] = 8; + act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents[3] = 8; + + uint8_t sec_hdr_type =2; + ue_ctx->security_ctxt.dl_nas_count++; + liblte_mme_pack_activate_default_eps_bearer_context_request_msg(&act_def_eps_bearer_context_req, &attach_accept.esm_msg); + liblte_mme_pack_attach_accept_msg(&attach_accept, sec_hdr_type, ue_ctx->security_ctxt.dl_nas_count, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + //Integrity protect NAS message + uint8_t mac[4]; + srslte::security_128_eia1 (&ue_ctx->security_ctxt.k_nas_int[16], + ue_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + m_s1ap_log->info("Packed Attach Complete\n"); + + //Add nas message to context setup request + erab_ctxt->nAS_PDU_present = true; + memcpy(erab_ctxt->nAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + erab_ctxt->nAS_PDU.n_octets = nas_buffer->N_bytes; + + return true; +} + +bool +s1ap_nas_transport::pack_identity_request(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_ID_REQUEST_MSG_STRUCT id_req; + id_req.id_type = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_identity_request_msg(&id_req, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Identity Request\n"); + m_s1ap_log->console("Error packing Identity REquest\n"); + return false; + } + + //Copy NAS PDU to Downlink NAS Trasport message buffer + memcpy(dw_nas->NAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes); + dw_nas->NAS_PDU.n_octets = nas_buffer->N_bytes; + + //Pack Downlink NAS Transport Message + err = liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT *) reply_msg); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Dw NAS Transport: Athentication Reject\n"); + m_s1ap_log->console("Error packing Downlink NAS Transport: Athentication Reject\n"); + return false; + } + + m_pool->deallocate(nas_buffer); + return true; +} + +bool +s1ap_nas_transport::pack_emm_information(srslte::byte_buffer_t *reply_msg, uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id) +{ + srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); + + //Setup initiating message + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + bzero(&tx_pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_DOWNLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT; + + //Setup Dw NAS structure + LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *dw_nas = &init->choice.DownlinkNASTransport; + dw_nas->ext=false; + dw_nas->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_s1ap_id;//FIXME Change name + dw_nas->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_s1ap_id; + dw_nas->HandoverRestrictionList_present=false; + dw_nas->SubscriberProfileIDforRFP_present=false; + + LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT emm_info; + emm_info.full_net_name_present = true; + emm_info.full_net_name.name = std::string("srsLTE"); + emm_info.full_net_name.add_ci = LIBLTE_MME_ADD_CI_DONT_ADD; + emm_info.short_net_name_present = true; + emm_info.short_net_name.name = std::string("srsLTE"); + emm_info.short_net_name.add_ci = LIBLTE_MME_ADD_CI_DONT_ADD; + + emm_info.local_time_zone_present = false; + emm_info.utc_and_local_time_zone_present = false; + emm_info.net_dst_present = false; + + //Integrity check + ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx(mme_ue_s1ap_id); + if(ue_ctx == NULL) + { + return false; + } + uint8_t sec_hdr_type =2; + ue_ctx->security_ctxt.dl_nas_count++; + LIBLTE_ERROR_ENUM err = liblte_mme_pack_emm_information_msg(&emm_info, sec_hdr_type, ue_ctx->security_ctxt.dl_nas_count, (LIBLTE_BYTE_MSG_STRUCT *) nas_buffer); + if(err != LIBLTE_SUCCESS) + { + m_s1ap_log->error("Error packing Identity Request\n"); + m_s1ap_log->console("Error packing Identity REquest\n"); + return false; + } + + uint8_t mac[4]; + srslte::security_128_eia1 (&ue_ctx->security_ctxt.k_nas_int[16], + ue_ctx->security_ctxt.dl_nas_count, + 0, + SECURITY_DIRECTION_DOWNLINK, + &nas_buffer->msg[5], + nas_buffer->N_bytes - 5, + mac + ); + + memcpy(&nas_buffer->msg[1],mac,4); + + + m_s1ap_log->info("Packed \n"); + return true; +} + +/*Helper functions*/ +void +s1ap_nas_transport::log_unhandled_attach_request_ies(const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT *attach_req) +{ + if(attach_req->old_p_tmsi_signature_present) + { + m_s1ap_log->warning("NAS attach request: Old P-TMSI signature present, but not handled.\n"); + } + if(attach_req->additional_guti_present) + { + m_s1ap_log->warning("NAS attach request: Aditional GUTI present, but not handled.\n"); + } + if(attach_req->last_visited_registered_tai_present) + { + m_s1ap_log->warning("NAS attach request: Last visited registered TAI present, but not handled.\n"); + } + if(attach_req->drx_param_present) + { + m_s1ap_log->warning("NAS attach request: DRX Param present, but not handled.\n"); + } + if(attach_req->ms_network_cap_present) + { + m_s1ap_log->warning("NAS attach request: MS network cap present, but not handled.\n"); + } + if(attach_req->old_lai_present) + { + m_s1ap_log->warning("NAS attach request: Old LAI present, but not handled.\n"); + } + if(attach_req->tmsi_status_present) + { + m_s1ap_log->warning("NAS attach request: TSMI status present, but not handled.\n"); + } + if(attach_req->ms_cm2_present) + { + m_s1ap_log->warning("NAS attach request: MS CM2 present, but not handled.\n"); + } + if(attach_req->ms_cm3_present) + { + m_s1ap_log->warning("NAS attach request: MS CM3 present, but not handled.\n"); + } + if(attach_req->supported_codecs_present) + { + m_s1ap_log->warning("NAS attach request: Supported CODECs present, but not handled.\n"); + } + if(attach_req->additional_update_type_present) + { + m_s1ap_log->warning("NAS attach request: Additional Update Type present, but not handled.\n"); + } + if(attach_req->voice_domain_pref_and_ue_usage_setting_present) + { + m_s1ap_log->warning("NAS attach request: Voice domain preference and UE usage setting present, but not handled.\n"); + } + if(attach_req->device_properties_present) + { + m_s1ap_log->warning("NAS attach request: Device properties present, but not handled.\n"); + } + if(attach_req->old_guti_type_present) + { + m_s1ap_log->warning("NAS attach request: Old GUTI type present, but not handled.\n"); + } + return; +} + +void +s1ap_nas_transport::log_unhandled_pdn_con_request_ies(const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT *pdn_con_req) +{ + //Handle the optional flags + if(pdn_con_req->esm_info_transfer_flag_present) + { + m_s1ap_log->warning("PDN Connectivity request: ESM info transfer flag properties present, but not handled.\n"); + } + if(pdn_con_req->apn_present) + { + m_s1ap_log->warning("PDN Connectivity request: APN present, but not handled.\n"); + } + if(pdn_con_req->protocol_cnfg_opts_present) + { + m_s1ap_log->warning("PDN Connectivity request: Protocol Cnfg options present, but not handled.\n"); + } + if(pdn_con_req->device_properties_present) + { + m_s1ap_log->warning("PDN Connectivity request: Device properties present, but not handled.\n"); + } +} + + +void +s1ap_nas_transport::log_unhandled_initial_ue_message_ies(LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *init_ue) +{ + if(init_ue->S_TMSI_present){ + m_s1ap_log->warning("S-TMSI present, but not handled.\n"); + } + if(init_ue->CSG_Id_present){ + m_s1ap_log->warning("S-TMSI present, but not handled.\n"); + } + if(init_ue->GUMMEI_ID_present){ + m_s1ap_log->warning("GUMMEI ID present, but not handled.\n"); + } + if(init_ue->CellAccessMode_present){ + m_s1ap_log->warning("Cell Access Mode present, but not handled.\n"); + } + if(init_ue->GW_TransportLayerAddress_present){ + m_s1ap_log->warning("GW Transport Layer present, but not handled.\n"); + } + if(init_ue->GW_TransportLayerAddress_present){ + m_s1ap_log->warning("GW Transport Layer present, but not handled.\n"); + } + if(init_ue->RelayNode_Indicator_present){ + m_s1ap_log->warning("Relay Node Indicator present, but not handled.\n"); + } + if(init_ue->GUMMEIType_present){ + m_s1ap_log->warning("GUMMEI Type present, but not handled.\n"); + } + if(init_ue->Tunnel_Information_for_BBF_present){ + m_s1ap_log->warning("Tunnel Information for BBF present, but not handled.\n"); + } + if(init_ue->SIPTO_L_GW_TransportLayerAddress_present){ + m_s1ap_log->warning("SIPTO GW Transport Layer Address present, but not handled.\n"); + } + if(init_ue->LHN_ID_present){ + m_s1ap_log->warning("LHN Id present, but not handled.\n"); + } + return; +} + + +} //namespace srsepc diff --git a/srsepc/src/spgw/CMakeLists.txt b/srsepc/src/spgw/CMakeLists.txt new file mode 100644 index 000000000..ba50e480c --- /dev/null +++ b/srsepc/src/spgw/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# 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/. +# + +file(GLOB SOURCES "*.cc") +add_library(srsepc_sgw STATIC ${SOURCES}) +install(TARGETS srsepc_sgw DESTINATION ${LIBRARY_DIR}) diff --git a/srsepc/src/spgw/spgw.cc b/srsepc/src/spgw/spgw.cc new file mode 100644 index 000000000..2458a065b --- /dev/null +++ b/srsepc/src/spgw/spgw.cc @@ -0,0 +1,572 @@ +/** + * + * \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "spgw/spgw.h" +#include "mme/mme_gtpc.h" +#include "srslte/upper/gtpu.h" + +namespace srsepc{ + +spgw* spgw::m_instance = NULL; +boost::mutex spgw_instance_mutex; + +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) +{ + return; +} + +spgw::~spgw() +{ + return; +} + +spgw* +spgw::get_instance(void) +{ + boost::mutex::scoped_lock lock(spgw_instance_mutex); + if(NULL == m_instance) { + m_instance = new spgw(); + } + return(m_instance); +} + +void +spgw::cleanup(void) +{ + boost::mutex::scoped_lock lock(spgw_instance_mutex); + if(NULL != m_instance) { + delete m_instance; + m_instance = NULL; + } +} + +int +spgw::init(spgw_args_t* args, srslte::log_filter *spgw_log) +{ + srslte::error_t err; + m_pool = srslte::byte_buffer_pool::get_instance(); + + //Init 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"); + 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) + { + m_spgw_log->console("Could not initialize the S1-U interface.\n"); + return -1; + } + + //Init mutex + pthread_mutex_init(&m_mutex,NULL); + m_spgw_log->info("SP-GW Initialized.\n"); + m_spgw_log->console("SP-GW Initialized.\n"); + return 0; +} + +void +spgw::stop() +{ + if(m_running) + { + 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); + } + } + std::map::iterator it = m_teid_to_tunnel_ctx.begin(); //Map control TEID to tunnel ctx. Usefull to get reply ctrl TEID, UE IP, etc. + while(it!=m_teid_to_tunnel_ctx.end()) + { + m_spgw_log->info("Deleting SP-GW Tunnel. IMSI: %lu\n", it->second->imsi); + m_spgw_log->console("Deleting SP-GW Tunnel. IMSI: %lu\n", it->second->imsi); + delete it->second; + m_teid_to_tunnel_ctx.erase(it++); + } + return; +} + +srslte::error_t +spgw::init_sgi_if(spgw_args_t *args) +{ + char dev[IFNAMSIZ] = "srs_spgw_sgi"; + struct ifreq ifr; + + 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); + } + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); + 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 + m_running=true; + srslte::byte_buffer_t *msg; + msg = m_pool->allocate(); + + struct sockaddr src_addr; + socklen_t addrlen; + struct iphdr *ip_pkt; + int sgi = m_sgi_if; + + fd_set set; + //struct timeval to; + int max_fd = std::max(m_s1u,sgi); + while (m_running) + { + msg->reset(); + FD_ZERO(&set); + FD_SET(m_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) + { + //m_spgw_log->info("Data is available now.\n"); + if (FD_ISSET(m_s1u, &set)) + { + msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen ); + //m_spgw_log->console("Received PDU from S1-U. Bytes %d\n", msg->N_bytes); + //m_spgw_log->debug("Received PDU from S1-U. Bytes %d\n", msg->N_bytes); + handle_s1u_pdu(msg); + } + if (FD_ISSET(m_sgi_if, &set)) + { + msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); + //m_spgw_log->console("Received PDU from SGi. Bytes %d\n", msg->N_bytes); + //m_spgw_log->debug("Received PDU from SGi. Bytes %d\n", msg->N_bytes); + handle_sgi_pdu(msg); + } + } + else + { + m_spgw_log->debug("No data from select.\n"); + } + } + m_pool->deallocate(msg); + 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; + + version = msg->msg[0]>>4; + ((uint8_t*)&dest_ip)[0] = msg->msg[16]; + ((uint8_t*)&dest_ip)[1] = msg->msg[17]; + ((uint8_t*)&dest_ip)[2] = msg->msg[18]; + ((uint8_t*)&dest_ip)[3] = msg->msg[19]; + + dest_addr.s_addr = dest_ip; + + //m_spgw_log->console("IP version: %d\n", version); + //m_spgw_log->console("Received packet to IP: %s\n", inet_ntoa(dest_addr)); + + pthread_mutex_lock(&m_mutex); + gtp_fteid_it = m_ip_to_teid.find(dest_ip); + 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) + { + //m_spgw_log->console("IP Packet is not for any UE\n"); + 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; + //m_spgw_log->console("UE F-TEID found, TEID 0x%x, eNB IP %s\n", enb_fteid.teid, inet_ntoa(enb_addr.sin_addr)); + + //Setup GTP-U header + srslte::gtpu_header_t header; + header.flags = 0x30; + header.message_type = 0xFF; + header.length = msg->N_bytes; + header.teid = enb_fteid.teid; + + //Write header into packet + if(!srslte::gtpu_write_header(&header, msg)) + { + 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; + } + //m_spgw_log->console("Sent packet to %s:%d. Bytes=%d/%d\n",inet_ntoa(enb_addr.sin_addr), GTPU_RX_PORT,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->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",n); + } + else + { + //m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes); + } + return; +} +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 +} + +void +spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *cs_req, struct srslte::gtpc_pdu *cs_resp_pdu) +{ + srslte::gtpc_header *header = &cs_resp_pdu->header; + srslte::gtpc_create_session_response *cs_resp = &cs_resp_pdu->choice.create_session_response; + + + m_spgw_log->info("Received Create Session Request\n"); + //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; + + //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_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)); + + //Create session response message + //Setup GTP-C header + header->piggyback = false; + header->teid_present = true; + header->teid = cs_req->sender_f_teid.teid; //Send create session requesponse to the CS Request 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.teid = spgw_uplink_ctrl_teid; + cs_resp->sender_f_teid.ipv4 = 0;//FIXME This is not relevant, as the GTP-C is not transmitted over sockets yet. + //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.teid = spgw_uplink_user_teid; + cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4 = m_s1u_addr.sin_addr.s_addr; + //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 = ue_ip; + 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 + pthread_mutex_lock(&m_mutex); + 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; + //printf("%d %d\n",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; +} + +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; +} + +} //namespace srsepc diff --git a/srsepc/user_db.csv b/srsepc/user_db.csv new file mode 100644 index 000000000..89d0dedf1 --- /dev/null +++ b/srsepc/user_db.csv @@ -0,0 +1,13 @@ +# +# .csv to store UE's information in HSS +# Kept in the following format: "Name,IMSI,Key,OP,AMF" +# +# Name: Human readable name to help distinguish UE's. Largely ignored by the HSS +# IMSI: UE's IMSI value +# Key: UE's key, where other keys are derived from. Stored in hexadecimal +# OP: Operator's code, sotred in hexadecimal +# AMF: Authentication management feild, stored in hexadecimal +# +# Note: Lines starting by '#' are ignored +ue1,001010123456789,00112233445566778899aabbccddeeff,63BFA50EE6523365FF14C1F45F88737D,9001 +ue2,001010123456780,00112233445566778899aabbccddeeaa,63BFA50EE6523365FF14C1F45F88737D,2000 diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 3c4fb5d1b..e72b90bc3 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -91,9 +91,9 @@ file_max_size = -1 # imei: 15 digit International Mobile Station Equipment Identity ##################################################################### [usim] -algo = milenage +algo = xor op = 63BFA50EE6523365FF14C1F45F88737D -amf = 8000 +amf = 9001 k = 00112233445566778899aabbccddeeff imsi = 001010123456789 imei = 353490069873319