mirror of https://github.com/pvnis/srsRAN_4G.git
commit
f25e34aad7
@ -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 <stdint.h>
|
||||||
|
#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
|
@ -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
|
@ -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 <stdint.h>
|
||||||
|
#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
|
@ -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 <stdint.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
@ -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
|
@ -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)
|
@ -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
|
@ -0,0 +1,5 @@
|
|||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/version.h.in
|
||||||
|
${PROJECT_BINARY_DIR}/version.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 <cstddef>
|
||||||
|
#include "srslte/common/log.h"
|
||||||
|
#include "srslte/common/logger_file.h"
|
||||||
|
#include "srslte/common/log_filter.h"
|
||||||
|
#include "srslte/common/buffer_pool.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
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<std::string> 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<uint64_t,hss_ue_ctx_t*> m_imsi_to_ue_ctx;
|
||||||
|
|
||||||
|
enum hss_auth_algo m_auth_algo;
|
||||||
|
|
||||||
|
/*Logs*/
|
||||||
|
srslte::log_filter *m_hss_log;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace srsepc
|
||||||
|
|
||||||
|
#endif // 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 <cstddef>
|
||||||
|
#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
|
@ -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 <boost/thread/mutex.hpp>
|
||||||
|
#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<uint32_t,uint32_t> m_teid_to_mme_s1ap_id;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
@ -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 <strings.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/sctp.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#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<uint32_t, uint32_t> 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<uint16_t, enb_ctx_t*> m_active_enbs;
|
||||||
|
std::map<int32_t, uint16_t> m_sctp_to_enb_id;
|
||||||
|
std::map<uint32_t, ue_ctx_t*> m_active_ues;
|
||||||
|
std::map<uint16_t,std::set<uint32_t> > 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
|
@ -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 <netinet/sctp.h>
|
||||||
|
|
||||||
|
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
|
@ -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
|
@ -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
|
@ -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
|
@ -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 <cstddef>
|
||||||
|
#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<uint32_t,spgw_tunnel_ctx*> m_teid_to_tunnel_ctx; //Map control TEID to tunnel ctx. Usefull to get reply ctrl TEID, UE IP, etc.
|
||||||
|
std::map<in_addr_t,srslte::gtpc_f_teid_ie> 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
|
@ -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 <Interface Name>' "
|
||||||
|
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
|
||||||
|
|
@ -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})
|
@ -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})
|
||||||
|
|
@ -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 <stdlib.h> /* srand, rand */
|
||||||
|
#include <time.h> /* time */
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#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<uint64_t,hss_ue_ctx_t*>::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<std::string> 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<uint64_t,hss_ue_ctx_t*>(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<uint64_t,hss_ue_ctx_t*>::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<std::string>
|
||||||
|
hss::split_string(const std::string &str, char delimiter)
|
||||||
|
{
|
||||||
|
std::vector<std::string> 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
|
@ -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 <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#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<string>(&mme_code)->default_value("0x01"), "MME Code")
|
||||||
|
("mme.name", bpo::value<string>(&mme_name)->default_value("srsmme01"), "MME Name")
|
||||||
|
("mme.mme_group", bpo::value<string>(&mme_group)->default_value("0x01"), "Cell ID")
|
||||||
|
("mme.tac", bpo::value<string>(&tac)->default_value("0x0"), "Tracking Area Code")
|
||||||
|
("mme.mcc", bpo::value<string>(&mcc)->default_value("001"), "Mobile Country Code")
|
||||||
|
("mme.mnc", bpo::value<string>(&mnc)->default_value("01"), "Mobile Network Code")
|
||||||
|
("mme.mme_bind_addr", bpo::value<string>(&mme_bind_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection")
|
||||||
|
("hss.db_file", bpo::value<string>(&hss_db_file)->default_value("ue_db.csv"),".csv file that stores UE's keys")
|
||||||
|
("hss.auth_algo", bpo::value<string>(&hss_auth_algo)->default_value("milenage"),"HSS uthentication algorithm.")
|
||||||
|
("spgw.gtpu_bind_addr", bpo::value<string>(&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<string>(&sgi_if_addr)->default_value("176.16.0.1"),"IP address of TUN interface for the SGi connection")
|
||||||
|
|
||||||
|
("log.s1ap_level", bpo::value<string>(&args->log_args.s1ap_level), "MME S1AP log level")
|
||||||
|
("log.s1ap_hex_limit", bpo::value<int>(&args->log_args.s1ap_hex_limit), "MME S1AP log hex dump limit")
|
||||||
|
("log.gtpc_level", bpo::value<string>(&args->log_args.gtpc_level), "MME GTPC log level")
|
||||||
|
("log.gtpc_hex_limit", bpo::value<int>(&args->log_args.gtpc_hex_limit), "MME GTPC log hex dump limit")
|
||||||
|
("log.spgw_level", bpo::value<string>(&args->log_args.spgw_level), "SPGW log level")
|
||||||
|
("log.spgw_hex_limit", bpo::value<int>(&args->log_args.spgw_hex_limit), "SPGW log hex dump limit")
|
||||||
|
//("log.gtpu_level", bpo::value<string>(&args->log.gtpu_level), "GTPU log level")
|
||||||
|
("log.hss_level", bpo::value<string>(&args->log_args.hss_level), "HSS log level")
|
||||||
|
("log.hss_hex_limit", bpo::value<int>(&args->log_args.hss_hex_limit), "HSS log hex dump limit")
|
||||||
|
//("log.gtpu_hex_limit",bpo::value<int>(&args->log.gtpu_hex_limit), "GTPU log hex dump limit")
|
||||||
|
|
||||||
|
("log.all_level", bpo::value<string>(&args->log_args.all_level)->default_value("info"), "ALL log level")
|
||||||
|
("log.all_hex_limit", bpo::value<int>(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit")
|
||||||
|
|
||||||
|
("log.filename", bpo::value<string>(&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<std::string>();
|
||||||
|
sstr >> args->mme_args.s1ap_args.mme_group;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::stringstream sstr;
|
||||||
|
sstr << std::hex << vm["mme.mme_code"].as<std::string>();
|
||||||
|
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<std::string>();
|
||||||
|
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;
|
||||||
|
}
|
@ -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})
|
||||||
|
|
@ -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 <iostream> //TODO Remove
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/sctp.h>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#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
|
@ -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 <iostream>
|
||||||
|
#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<uint32_t,uint32_t>(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<uint32_t,uint32_t>::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<uint32_t,uint32_t>::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; i<MAX_ERABS_PER_UE; i++)
|
||||||
|
{
|
||||||
|
if(ue_ctx->erabs_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
|
@ -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 <iostream>
|
||||||
|
#include <cmath>
|
||||||
|
#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<uint16_t,enb_ctx_t*>::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<int32_t,uint16_t>::iterator it_assoc = m_sctp_to_enb_id.find(assoc_id);
|
||||||
|
uint16_t enb_id = it_assoc->second;
|
||||||
|
|
||||||
|
std::map<uint16_t,enb_ctx_t*>::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<uint16_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_id_to_ue_ids.find(enb_id);
|
||||||
|
std::set<uint32_t>::iterator ue_id = ues_in_enb->second.begin();
|
||||||
|
while(ue_id != ues_in_enb->second.end() )
|
||||||
|
{
|
||||||
|
std::map<uint32_t, ue_ctx_t*>::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<uint32_t, ue_ctx_t*>::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<int32_t,uint16_t>::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<uint16_t,std::set<uint32_t> >::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<uint16_t,enb_ctx_t*>::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<uint32_t> 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<uint16_t,enb_ctx_t*>(enb_ptr->enb_id,enb_ptr));
|
||||||
|
m_sctp_to_enb_id.insert(std::pair<int32_t,uint16_t>(enb_sri->sinfo_assoc_id, enb_ptr->enb_id));
|
||||||
|
m_enb_id_to_ue_ids.insert(std::pair<uint16_t,std::set<uint32_t> >(enb_ptr->enb_id,ue_set));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ue_ctx_t*
|
||||||
|
s1ap::find_ue_ctx(uint32_t mme_ue_s1ap_id)
|
||||||
|
{
|
||||||
|
std::map<uint32_t, ue_ctx_t*>::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<uint32_t,ue_ctx_t*>(ue_ptr->mme_ue_s1ap_id,ue_ptr));
|
||||||
|
|
||||||
|
std::map<int32_t,uint16_t>::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<uint16_t,std::set<uint32_t> >::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<uint32_t,ue_ctx_t*>::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<uint32_t,uint32_t>(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;i<enb_ctx.nof_supported_ta;i++)
|
||||||
|
{
|
||||||
|
for(int j=0;i<enb_ctx.nof_supported_ta;i++)
|
||||||
|
{
|
||||||
|
m_s1ap_log->info("%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
|
@ -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; i<in_ctxt_resp->E_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;i<MAX_ERABS_PER_UE;i++)
|
||||||
|
{
|
||||||
|
if(ue_ctx->erabs_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;i<MAX_ERABS_PER_UE;i++)
|
||||||
|
{
|
||||||
|
ue_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
|
||||||
|
}
|
||||||
|
//Delete UE context
|
||||||
|
m_s1ap_log->info("Deleted UE Context.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace srsepc
|
@ -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; i<msg->SupportedTAs.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; j<msg->SupportedTAs.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
|
File diff suppressed because it is too large
Load Diff
@ -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})
|
@ -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 <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_tun.h>
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#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<uint32_t,spgw_tunnel_ctx*>::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<uint32_t,srslte::gtpc_f_teid_ie>::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<uint32_t,spgw_tunnel_ctx_t*>(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<uint32_t,spgw_tunnel_ctx_t*>::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<uint32_t,srslte::gtpc_f_teid_ie>(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<uint32_t,spgw_tunnel_ctx_t*>::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<in_addr_t,srslte::gtpc_f_teid_ie>::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
|
@ -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
|
Can't render this file because it contains an unexpected character in line 3 and column 33.
|
Loading…
Reference in New Issue