master
yagoda 6 years ago
commit 438bbdf6c1

@ -1,6 +1,16 @@
Change Log for Releases Change Log for Releases
============================== ==============================
## 18.09
* Improved Turbo Decoder performance
* Configurable SGi interface name and M1U params
* Support for GPTU echo mechanism
* Added UE detach capability
* Refactor RLC/PDCP classes
* Various fixes for ARM-based devices
* Added support for bladeRF 2.0 micro
* Many bug-fixes and improved stability and performance in all parts
## 18.06.1 ## 18.06.1
* Fixed RLC reestablish * Fixed RLC reestablish
* Fixed aperiodic QCI retx * Fixed aperiodic QCI retx

@ -15,8 +15,14 @@ if(NOT BLADERF_FOUND)
) )
if(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) if(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES)
set(BLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found") CHECK_LIBRARY_EXISTS(bladeRF bladerf_get_board_name BLADERF_LIBRARIES BLADERF_VERSION_OK)
message(STATUS "Found libbladeRF: ${BLADERF_INCLUDE_DIRS}, ${BLADERF_LIBRARIES}") if (BLADERF_VERSION_OK)
set(BLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found")
message(STATUS "Found libbladeRF: ${BLADERF_INCLUDE_DIRS}, ${BLADERF_LIBRARIES}")
else (BLADERF_VERSION_OK)
set(BLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found")
message(STATUS "libbladeRF found but not compatible. Upgrade your driver or use SoapySDR.")
endif (BLADERF_VERSION_OK)
else(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) else(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES)
set(BLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found") set(BLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found")
message(STATUS "libbladeRF not found.") message(STATUS "libbladeRF not found.")

@ -19,6 +19,6 @@
# #
SET(SRSLTE_VERSION_MAJOR 18) SET(SRSLTE_VERSION_MAJOR 18)
SET(SRSLTE_VERSION_MINOR 6) SET(SRSLTE_VERSION_MINOR 9)
SET(SRSLTE_VERSION_PATCH 1) SET(SRSLTE_VERSION_PATCH 0)
SET(SRSLTE_VERSION_STRING "${SRSLTE_VERSION_MAJOR}.${SRSLTE_VERSION_MINOR}.${SRSLTE_VERSION_PATCH}") SET(SRSLTE_VERSION_STRING "${SRSLTE_VERSION_MAJOR}.${SRSLTE_VERSION_MINOR}.${SRSLTE_VERSION_PATCH}")

6
debian/changelog vendored

@ -1,3 +1,9 @@
srslte (18.09-0ubuntu1) bionic; urgency=medium
* Update to srsLTE 18.09
-- Andre Puschmann <andre@softwareradiosystems.com> Wed, 15 Oct 2018 10:05:00 +0200
srslte (18.06.1-0ubuntu1) bionic; urgency=medium srslte (18.06.1-0ubuntu1) bionic; urgency=medium
* Update to srsLTE 18.06.1 * Update to srsLTE 18.06.1

@ -57,6 +57,9 @@
#define LIBLTE_MAX_MSG_SIZE_BYTES 12756 #define LIBLTE_MAX_MSG_SIZE_BYTES 12756
#define LIBLTE_MSG_HEADER_OFFSET 1020 #define LIBLTE_MSG_HEADER_OFFSET 1020
//Macro to make it easier to convert defines into strings
#define LIBLTE_CASE_STR(code) case code: return #code
/******************************************************************************* /*******************************************************************************
TYPEDEFS TYPEDEFS
*******************************************************************************/ *******************************************************************************/
@ -102,7 +105,7 @@ typedef struct{
uint32 N_bits; uint32 N_bits;
uint8 header[LIBLTE_MSG_HEADER_OFFSET]; uint8 header[LIBLTE_MSG_HEADER_OFFSET];
uint8 msg[LIBLTE_MAX_MSG_SIZE_BITS]; uint8 msg[LIBLTE_MAX_MSG_SIZE_BITS];
}LIBLTE_BIT_MSG_STRUCT; }LIBLTE_BIT_MSG_STRUCT __attribute__ ((aligned (8)));
typedef struct{ typedef struct{
uint32 N_bytes; uint32 N_bytes;

@ -2482,14 +2482,17 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_transaction_identifier_ie(uint8
Document Reference: 24.301 v10.2.0 Section 9.1 Document Reference: 24.301 v10.2.0 Section 9.1
*********************************************************************/ *********************************************************************/
// Defines // Defines
//Protocol Descriptor
#define LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT 0x2 #define LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT 0x2
#define LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT 0x7 #define LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT 0x7
//Header Type
#define LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS 0x0 #define LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS 0x0
#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY 0x1 #define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY 0x1
#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED 0x2 #define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED 0x2
#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT 0x3 #define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT 0x3
#define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT 0x4 #define LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT 0x4
#define LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST 0xC #define LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST 0xC
//Message Type
#define LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST 0x41 #define LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST 0x41
#define LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT 0x42 #define LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT 0x42
#define LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE 0x43 #define LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE 0x43
@ -2543,6 +2546,8 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_transaction_identifier_ie(uint8
#define LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE 0xDA #define LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE 0xDA
#define LIBLTE_MME_MSG_TYPE_NOTIFICATION 0xDB #define LIBLTE_MME_MSG_TYPE_NOTIFICATION 0xDB
#define LIBLTE_MME_MSG_TYPE_ESM_STATUS 0xE8 #define LIBLTE_MME_MSG_TYPE_ESM_STATUS 0xE8
const char* liblte_nas_msg_type_to_string(int code);
// Enums // Enums
// Structs // Structs
// Functions // Functions

@ -108,8 +108,7 @@ public:
} }
myobj wait_pop() { // blocking pop myobj wait_pop() { // blocking pop
myobj value; myobj value = myobj();
bzero(&value, sizeof(myobj));
pop_(&value, true); pop_(&value, true);
return value; return value;
} }
@ -154,8 +153,8 @@ private:
} }
if (value) { if (value) {
*value = q.front(); *value = q.front();
q.pop();
} }
q.pop();
ret = true; ret = true;
if (mutexed_callback) { if (mutexed_callback) {
mutexed_callback->popping(*value); mutexed_callback->popping(*value);

@ -204,9 +204,17 @@ public:
b->reset(); b->reset();
if (!pool->deallocate(b)) { if (!pool->deallocate(b)) {
if (log) { if (log) {
#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED
log->error("Deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name); log->error("Deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name);
#else
log->error("Deallocating PDU: Addr=0x%lx\n", (uint64_t) b);
#endif
} else { } else {
#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED
printf("Error deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name); printf("Error deallocating PDU: Addr=0x%lx, name=%s not found in pool\n", (uint64_t) b, b->debug_name);
#else
printf("Error deallocating PDU: Addr=0x%lx\n", (uint64_t) b);
#endif
} }
} }
b = NULL; b = NULL;

@ -65,7 +65,7 @@
#define SRSLTE_MAX_BUFFER_SIZE_BYTES (SRSLTE_MAX_BUFFER_SIZE_BITS/8) #define SRSLTE_MAX_BUFFER_SIZE_BYTES (SRSLTE_MAX_BUFFER_SIZE_BITS/8)
#define SRSLTE_BUFFER_HEADER_OFFSET 1020 #define SRSLTE_BUFFER_HEADER_OFFSET 1020
#define SRSLTE_BUFFER_POOL_LOG_ENABLED //#define SRSLTE_BUFFER_POOL_LOG_ENABLED
#ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED #ifdef SRSLTE_BUFFER_POOL_LOG_ENABLED
#define pool_allocate (pool->allocate(__PRETTY_FUNCTION__)) #define pool_allocate (pool->allocate(__PRETTY_FUNCTION__))
@ -73,6 +73,7 @@
#define SRSLTE_BUFFER_POOL_LOG_NAME_LEN 128 #define SRSLTE_BUFFER_POOL_LOG_NAME_LEN 128
#else #else
#define pool_allocate (pool->allocate()) #define pool_allocate (pool->allocate())
#define pool_allocate_blocking (pool->allocate(NULL, true))
#endif #endif
#define ZERO_OBJECT(x) memset(&(x), 0x0, sizeof((x))) #define ZERO_OBJECT(x) memset(&(x), 0x0, sizeof((x)))
@ -215,13 +216,16 @@ struct bit_buffer_t{
#endif #endif
} }
bit_buffer_t(const bit_buffer_t& buf){ bit_buffer_t(const bit_buffer_t& buf){
msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET];
N_bits = buf.N_bits; N_bits = buf.N_bits;
memcpy(msg, buf.msg, N_bits); memcpy(msg, buf.msg, N_bits);
} }
bit_buffer_t & operator= (const bit_buffer_t & buf){ bit_buffer_t & operator= (const bit_buffer_t & buf){
// avoid self assignment // avoid self assignment
if (&buf == this) if (&buf == this) {
return *this; return *this;
}
msg = &buffer[SRSLTE_BUFFER_HEADER_OFFSET];
N_bits = buf.N_bits; N_bits = buf.N_bits;
memcpy(msg, buf.msg, N_bits); memcpy(msg, buf.msg, N_bits);
return *this; return *this;

@ -38,9 +38,10 @@ namespace srslte {
class srslte_nas_config_t class srslte_nas_config_t
{ {
public: public:
srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string user_ = "", std::string pass_ = "", bool force_imsi_attach_ = false) srslte_nas_config_t(uint32_t lcid_ = 0, std::string apn_ = "", std::string apn_protocol_ = "", std::string user_ = "", std::string pass_ = "", bool force_imsi_attach_ = false)
:lcid(lcid_), :lcid(lcid_),
apn(apn_), apn(apn_),
apn_protocol(apn_protocol_),
user(user_), user(user_),
pass(pass_), pass(pass_),
force_imsi_attach(force_imsi_attach_) force_imsi_attach(force_imsi_attach_)
@ -48,6 +49,7 @@ public:
uint32_t lcid; uint32_t lcid;
std::string apn; std::string apn;
std::string apn_protocol;
std::string user; std::string user;
std::string pass; std::string pass;
bool force_imsi_attach; bool force_imsi_attach;

@ -44,6 +44,7 @@ public:
return true; return true;
} }
void stop() { void stop() {
stop_thread();
// stop all listeners // stop all listeners
for (uint32_t i=0;i<listeners.size();i++) { for (uint32_t i=0;i<listeners.size();i++) {
listeners[i]->stop(); listeners[i]->stop();

@ -280,6 +280,7 @@ public:
virtual bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) = 0; virtual bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio) = 0;
virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) = 0; virtual void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) = 0;
virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) = 0; virtual void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) = 0;
virtual bool is_mme_connected() = 0;
// virtual void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) = 0; // virtual void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) = 0;
}; };

@ -1,13 +1,68 @@
/*
* \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 SRSLTE_EPC_INTERFACES_H #ifndef SRSLTE_EPC_INTERFACES_H
#define SRSLTE_EPC_INTERFACES_H #define SRSLTE_EPC_INTERFACES_H
#include "srslte/asn1/gtpc_ies.h"
#include <netinet/sctp.h>
#include "srslte/srslte.h" namespace srsepc {
#include "srslte/common/common.h" class nas;
namespace srsepc { //NAS -> GTP-C
class gtpc_interface_nas
{
public:
virtual bool send_create_session_request(uint64_t imsi) = 0;
virtual bool send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify, struct srslte::gtpc_f_teid_ie *enb_fteid) = 0;
virtual bool send_delete_session_request(uint64_t imsi) = 0;
};
//GTP-C -> S1AP
class s1ap_interface_gtpc
{
public:
virtual bool send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup) = 0;
};
//NAS -> S1AP
class s1ap_interface_nas
{
public:
virtual uint32_t allocate_m_tmsi(uint64_t imsi) = 0;
virtual uint32_t get_next_mme_ue_s1ap_id() = 0;
virtual bool add_nas_ctx_to_imsi_map(nas *nas_ctx) = 0;
virtual bool add_nas_ctx_to_mme_ue_s1ap_id_map(nas *nas_ctx) = 0;
virtual bool add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id) = 0;
virtual bool release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id) = 0;
virtual bool delete_ue_ctx(uint64_t imsi) = 0;
virtual uint64_t find_imsi_from_m_tmsi(uint32_t m_tmsi) = 0;
virtual nas* find_nas_ctx_from_imsi(uint64_t imsi) = 0;
virtual bool send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup) = 0;
virtual bool send_ue_context_release_command(uint32_t mme_ue_s1ap_id) = 0;
virtual bool send_downlink_nas_transport(uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id, srslte::byte_buffer_t *nas_msg, struct sctp_sndrcvinfo enb_sri) = 0;
};
class hss_interface_s1ap //NAS -> HSS
class hss_interface_nas
{ {
public: public:
virtual bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) = 0; virtual bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres) = 0;

@ -36,6 +36,7 @@
#include <string> #include <string>
#include "srslte/asn1/liblte_rrc.h" #include "srslte/asn1/liblte_rrc.h"
#include "srslte/asn1/liblte_mme.h"
#include "srslte/common/interfaces_common.h" #include "srslte/common/interfaces_common.h"
#include "srslte/common/common.h" #include "srslte/common/common.h"
#include "srslte/common/security.h" #include "srslte/common/security.h"
@ -104,7 +105,7 @@ public:
class gw_interface_nas class gw_interface_nas
{ {
public: public:
virtual srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str) = 0; virtual srslte::error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_id, char *err_str) = 0;
}; };
// GW interface for RRC // GW interface for RRC
@ -139,6 +140,8 @@ public:
virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0; virtual void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) = 0;
virtual uint32_t get_ul_count() = 0; virtual uint32_t get_ul_count() = 0;
virtual bool get_k_asme(uint8_t *k_asme_, uint32_t n) = 0; virtual bool get_k_asme(uint8_t *k_asme_, uint32_t n) = 0;
virtual uint32_t get_ipv4_addr() = 0;
virtual bool get_ipv6_addr(uint8_t *ipv6_addr) = 0;
}; };
// NAS interface for UE // NAS interface for UE
@ -280,6 +283,7 @@ public:
virtual void add_bearer_mrb(uint32_t lcid) = 0; virtual void add_bearer_mrb(uint32_t lcid) = 0;
virtual void del_bearer(uint32_t lcid) = 0; virtual void del_bearer(uint32_t lcid) = 0;
virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0;
virtual bool has_bearer(uint32_t lcid) = 0;
}; };
// RLC interface for PDCP // RLC interface for PDCP

@ -48,14 +48,6 @@ typedef struct {
float tx_rx_gain_offset; float tx_rx_gain_offset;
} srslte_rf_t; } srslte_rf_t;
typedef struct {
float dc_gain;
float dc_phase;
float iq_i;
float iq_q;
} srslte_rf_cal_t;
typedef struct { typedef struct {
double min_tx_gain; double min_tx_gain;
double max_tx_gain; double max_tx_gain;
@ -96,10 +88,6 @@ SRSLTE_API int srslte_rf_start_gain_thread(srslte_rf_t *rf,
SRSLTE_API int srslte_rf_close(srslte_rf_t *h); SRSLTE_API int srslte_rf_close(srslte_rf_t *h);
SRSLTE_API void srslte_rf_set_tx_cal(srslte_rf_t *h, srslte_rf_cal_t *cal);
SRSLTE_API void srslte_rf_set_rx_cal(srslte_rf_t *h, srslte_rf_cal_t *cal);
SRSLTE_API int srslte_rf_start_rx_stream(srslte_rf_t *h, bool now); SRSLTE_API int srslte_rf_start_rx_stream(srslte_rf_t *h, bool now);
SRSLTE_API int srslte_rf_stop_rx_stream(srslte_rf_t *h); SRSLTE_API int srslte_rf_stop_rx_stream(srslte_rf_t *h);

@ -33,17 +33,6 @@
#ifndef SRSLTE_RADIO_H #ifndef SRSLTE_RADIO_H
#define SRSLTE_RADIO_H #define SRSLTE_RADIO_H
typedef struct {
float tx_corr_dc_gain;
float tx_corr_dc_phase;
float tx_corr_iq_i;
float tx_corr_iq_q;
float rx_corr_dc_gain;
float rx_corr_dc_phase;
float rx_corr_iq_i;
float rx_corr_iq_q;
} rf_cal_t;
namespace srslte { namespace srslte {
/* Interface to the RF frontend. /* Interface to the RF frontend.
@ -85,8 +74,6 @@ class radio {
void set_tx_adv(int nsamples); void set_tx_adv(int nsamples);
void set_tx_adv_neg(bool tx_adv_is_neg); void set_tx_adv_neg(bool tx_adv_is_neg);
void set_manual_calibration(rf_cal_t *calibration);
bool is_continuous_tx(); bool is_continuous_tx();
void set_continuous_tx(bool enable); void set_continuous_tx(bool enable);

@ -40,57 +40,83 @@ namespace srslte {
* | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 |
* *
* 1 | Version |PT | * | E | S |PN | * 1 | Version |PT | * | E | S |PN |
* 2 | Message Type | * 2 | Message Type |
* 3 | Length (1st Octet) | * 3 | Length (1st Octet) |
* 4 | Length (2nd Octet) | * 4 | Length (2nd Octet) |
* 5 | TEID (1st Octet) | * 5 | TEID (1st Octet) |
* 6 | TEID (2nd Octet) | * 6 | TEID (2nd Octet) |
* 7 | TEID (3rd Octet) | * 7 | TEID (3rd Octet) |
* 8 | TEID (4th Octet) | * 8 | TEID (4th Octet) |
* 9 | Seq Number (1st Octet) |
* 10 | Seq Number (2st Octet) |
* 11 | N-PDU |
* 12 | Next Extension Header Type |
***************************************************************************/ ***************************************************************************/
#define GTPU_HEADER_LEN 8 #define GTPU_BASE_HEADER_LEN 8
#define GTPU_EXTENDED_HEADER_LEN 12
#define GTPU_FLAGS_VERSION_MASK 0xE0
#define GTPU_FLAGS_VERSION_V1 0x20
#define GTPU_FLAGS_GTP_PRIME_PROTOCOL 0x00
#define GTPU_FLAGS_GTP_PROTOCOL 0x10
#define GTPU_FLAGS_EXTENDED_HDR 0x04
#define GTPU_FLAGS_SEQUENCE 0x02
#define GTPU_FLAGS_PACKET_NUM 0x01
#define GTPU_MSG_ECHO_REQUEST 1
#define GTPU_MSG_ECHO_RESPONSE 2
#define GTPU_MSG_ERROR_INDICATION 26
#define GTPU_MSG_SUPPORTED_EXTENSION_HEADERS_NOTIFICATION 31
#define GTPU_MSG_END_MARKER 254
#define GTPU_MSG_DATA_PDU 255
typedef struct{ typedef struct{
uint8_t flags; // Only support 0x30 - v1, PT1 (GTP), no other flags uint8_t flags;
uint8_t message_type; // Only support 0xFF - T-PDU type uint8_t message_type;
uint16_t length; uint16_t length;
uint32_t teid; uint32_t teid;
uint16_t seq_number;
uint8_t n_pdu;
uint8_t next_ext_hdr_type;
}gtpu_header_t; }gtpu_header_t;
bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header, srslte::log *gtpu_log); bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header, srslte::log *gtpu_log);
bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log); bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log);
inline void uint8_to_uint32(uint8_t *buf, uint32_t *i) inline bool gtpu_supported_flags_check(gtpu_header_t *header, srslte::log *gtpu_log)
{
*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; //flags
buf[1] = (i >> 16) & 0xFF; if( (header->flags & GTPU_FLAGS_VERSION_MASK) != GTPU_FLAGS_VERSION_V1 ) {
buf[2] = (i >> 8) & 0xFF; gtpu_log->error("gtpu_header - Unhandled GTP-U Version. Flags: 0x%x\n", header->flags);
buf[3] = i & 0xFF; return false;
}
if( !(header->flags & GTPU_FLAGS_GTP_PROTOCOL) ) {
gtpu_log->error("gtpu_header - Unhandled Protocol Type. Flags: 0x%x\n\n", header->flags);
return false;
}
if( header->flags & GTPU_FLAGS_EXTENDED_HDR ) {
gtpu_log->error("gtpu_header - Unhandled Header Extensions. Flags: 0x%x\n\n", header->flags);
return false;
}
if( header->flags & GTPU_FLAGS_PACKET_NUM ) {
gtpu_log->error("gtpu_header - Unhandled Packet Number. Flags: 0x%x\n\n", header->flags);
return false;
}
return true;
} }
inline void uint8_to_uint16(uint8_t *buf, uint16_t *i) inline bool gtpu_supported_msg_type_check(gtpu_header_t *header, srslte::log *gtpu_log)
{ {
*i = (uint32_t)buf[0] << 8 | //msg_tpye
(uint32_t)buf[1]; if( header->message_type != GTPU_MSG_DATA_PDU && header->message_type != GTPU_MSG_ECHO_REQUEST && header->message_type != GTPU_MSG_ECHO_RESPONSE) {
gtpu_log->error("gtpu_header - Unhandled message type: 0x%x\n", header->message_type);
return false;
}
return true;
} }
inline void uint16_to_uint8(uint16_t i, uint8_t *buf)
{
buf[0] = (i >> 8) & 0xFF;
buf[1] = i & 0xFF;
}
}//namespace }//namespace
#endif #endif

@ -90,6 +90,7 @@ public:
void del_bearer(uint32_t lcid); void del_bearer(uint32_t lcid);
void del_bearer_mrb(uint32_t lcid); void del_bearer_mrb(uint32_t lcid);
void change_lcid(uint32_t old_lcid, uint32_t new_lcid); void change_lcid(uint32_t old_lcid, uint32_t new_lcid);
bool has_bearer(uint32_t lcid);
private: private:
void reset_metrics(); void reset_metrics();

@ -225,7 +225,8 @@ private:
void timer_expired(uint32_t timeout_id); void timer_expired(uint32_t timeout_id);
// Functions needed by Tx subclass to query rx state // Functions needed by Tx subclass to query rx state
int get_status(rlc_status_pdu_t* status); int get_status_pdu_length();
int get_status_pdu(rlc_status_pdu_t* status, const uint32_t nof_bytes);
bool get_do_status(); bool get_do_status();
void reset_status(); // called when status PDU has been sent void reset_status(); // called when status PDU has been sent
@ -320,7 +321,8 @@ uint32_t rlc_am_packed_length(rlc_amd_retx_t retx);
bool rlc_am_is_control_pdu(byte_buffer_t *pdu); bool rlc_am_is_control_pdu(byte_buffer_t *pdu);
bool rlc_am_is_control_pdu(uint8_t *payload); bool rlc_am_is_control_pdu(uint8_t *payload);
bool rlc_am_is_pdu_segment(uint8_t *payload); bool rlc_am_is_pdu_segment(uint8_t *payload);
std::string rlc_am_to_string(rlc_status_pdu_t *status); std::string rlc_am_status_pdu_to_string(rlc_status_pdu_t *status);
std::string rlc_amd_pdu_header_to_string(const rlc_amd_pdu_header_t &header);
bool rlc_am_start_aligned(const uint8_t fi); bool rlc_am_start_aligned(const uint8_t fi);
bool rlc_am_end_aligned(const uint8_t fi); bool rlc_am_end_aligned(const uint8_t fi);
bool rlc_am_is_unaligned(const uint8_t fi); bool rlc_am_is_unaligned(const uint8_t fi);

@ -37,7 +37,7 @@ namespace srslte {
***************************************************************************/ ***************************************************************************/
#define RLC_AM_WINDOW_SIZE 512 #define RLC_AM_WINDOW_SIZE 512
#define RLC_MAX_SDU_SIZE ((1<<11)-1) // Length of LI field is 11bits
typedef enum{ typedef enum{

@ -146,6 +146,7 @@ private:
bool configure(srslte_rlc_config_t cfg, std::string rb_name); bool configure(srslte_rlc_config_t cfg, std::string rb_name);
void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes); void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes);
void reassemble_rx_sdus(); void reassemble_rx_sdus();
bool pdu_belongs_to_rx_sdu();
bool inside_reordering_window(uint16_t sn); bool inside_reordering_window(uint16_t sn);
uint32_t get_num_rx_bytes(); uint32_t get_num_rx_bytes();
void reset_metrics(); void reset_metrics();
@ -199,7 +200,6 @@ private:
// helper functions // helper functions
void debug_state(); void debug_state();
bool reordering_timeout_running();
const char* get_rb_name(); const char* get_rb_name();
}; };
@ -213,6 +213,7 @@ private:
uint32_t lcid; uint32_t lcid;
srslte_rlc_um_config_t cfg; srslte_rlc_um_config_t cfg;
std::string rb_name; std::string rb_name;
byte_buffer_pool *pool;
std::string get_rb_name(srsue::rrc_interface_rlc *rrc, uint32_t lcid, bool is_mrb); std::string get_rb_name(srsue::rrc_interface_rlc *rrc, uint32_t lcid, bool is_mrb);
}; };

@ -11206,3 +11206,67 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_pdn_disconnect_request_msg(LIBLTE_BYTE_MSG_S
return(err); return(err);
} }
/*******************************************************************************
HELPER FUNCTIONS
*******************************************************************************/
const char* liblte_nas_msg_type_to_string(int code)
{
switch(code)
{
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ATTACH_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ATTACH_COMPLETE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ATTACH_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DETACH_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DETACH_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_COMPLETE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_EXTENDED_SERVICE_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_SERVICE_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMMAND);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_GUTI_REALLOCATION_COMPLETE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_AUTHENTICATION_FAILURE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMPLETE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_SECURITY_MODE_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_EMM_STATUS);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_EMM_INFORMATION);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DOWNLINK_NAS_TRANSPORT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_UPLINK_NAS_TRANSPORT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_CS_SERVICE_NOTIFICATION);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DOWNLINK_GENERIC_NAS_TRANSPORT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_UPLINK_GENERIC_NAS_TRANSPORT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ACTIVATE_DEDICATED_EPS_BEARER_CONTEXT_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_MODIFY_EPS_BEARER_CONTEXT_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_DEACTIVATE_EPS_BEARER_CONTEXT_ACCEPT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_PDN_DISCONNECT_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_ALLOCATION_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_BEARER_RESOURCE_MODIFICATION_REJECT);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_RESPONSE);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_NOTIFICATION);
LIBLTE_CASE_STR(LIBLTE_MME_MSG_TYPE_ESM_STATUS);
default: return "NAS Message Type Unknown";
}
}

@ -524,7 +524,6 @@ int srslte_pusch_encode(srslte_pusch_t *q, srslte_pusch_cfg_t *cfg, srslte_softb
cfg->sf_idx, srslte_mod_string(cfg->grant.mcs.mod), rnti, cfg->sf_idx, srslte_mod_string(cfg->grant.mcs.mod), rnti,
cfg->grant.mcs.tbs, cfg->nbits.nof_re, cfg->nbits.nof_symb, cfg->nbits.nof_bits, cfg->rv); cfg->grant.mcs.tbs, cfg->nbits.nof_re, cfg->nbits.nof_symb, cfg->nbits.nof_bits, cfg->rv);
bzero(q->q, cfg->nbits.nof_bits);
if (srslte_ulsch_uci_encode(&q->ul_sch, cfg, softbuffer, data, uci_data, q->g, q->q)) { if (srslte_ulsch_uci_encode(&q->ul_sch, cfg, softbuffer, data, uci_data, q->g, q->q)) {
fprintf(stderr, "Error encoding TB\n"); fprintf(stderr, "Error encoding TB\n");
return SRSLTE_ERROR; return SRSLTE_ERROR;
@ -534,6 +533,10 @@ int srslte_pusch_encode(srslte_pusch_t *q, srslte_pusch_cfg_t *cfg, srslte_softb
srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits); srslte_sequence_t *seq = get_user_sequence(q, rnti, cfg->sf_idx, cfg->nbits.nof_bits);
// Run scrambling // Run scrambling
if (!seq) {
fprintf(stderr, "Error getting scrambling sequence\n");
return SRSLTE_ERROR;
}
srslte_scrambling_bytes(seq, (uint8_t*) q->q, cfg->nbits.nof_bits); srslte_scrambling_bytes(seq, (uint8_t*) q->q, cfg->nbits.nof_bits);
// Correct UCI placeholder/repetition bits // Correct UCI placeholder/repetition bits

@ -402,23 +402,6 @@ double rf_blade_set_tx_freq(void *h, double freq)
return freq; return freq;
} }
void rf_blade_set_tx_cal(void *h, srslte_rf_cal_t *cal) {
rf_blade_handler_t *handler = (rf_blade_handler_t*) h;
bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_FPGA_PHASE, cal->dc_phase);
bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_FPGA_GAIN, cal->dc_gain);
bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_LMS_DCOFF_I, cal->iq_i);
bladerf_set_correction(handler->dev, BLADERF_MODULE_TX, BLADERF_CORR_LMS_DCOFF_Q, cal->iq_q);
}
void rf_blade_set_rx_cal(void *h, srslte_rf_cal_t *cal) {
rf_blade_handler_t *handler = (rf_blade_handler_t*) h;
bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_PHASE, cal->dc_phase);
bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_FPGA_GAIN, cal->dc_gain);
bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_I, cal->iq_i);
bladerf_set_correction(handler->dev, BLADERF_MODULE_RX, BLADERF_CORR_LMS_DCOFF_Q, cal->iq_q);
}
static void timestamp_to_secs(uint32_t rate, uint64_t timestamp, time_t *secs, double *frac_secs) { static void timestamp_to_secs(uint32_t rate, uint64_t timestamp, time_t *secs, double *frac_secs) {
double totalsecs = (double) timestamp/rate; double totalsecs = (double) timestamp/rate;
time_t secs_i = (time_t) totalsecs; time_t secs_i = (time_t) totalsecs;

@ -40,10 +40,6 @@ SRSLTE_API char* rf_blade_devname(void *h);
SRSLTE_API int rf_blade_close(void *h); SRSLTE_API int rf_blade_close(void *h);
SRSLTE_API void rf_blade_set_tx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API void rf_blade_set_rx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API int rf_blade_start_rx_stream(void *h, bool now); SRSLTE_API int rf_blade_start_rx_stream(void *h, bool now);
SRSLTE_API int rf_blade_start_rx_stream_nsamples(void *h, SRSLTE_API int rf_blade_start_rx_stream_nsamples(void *h,

@ -62,10 +62,6 @@ typedef struct {
int (*srslte_rf_send_timed_multi)(void *h, void *data[4], int nsamples, int (*srslte_rf_send_timed_multi)(void *h, void *data[4], int nsamples,
time_t secs, double frac_secs, bool has_time_spec, time_t secs, double frac_secs, bool has_time_spec,
bool blocking, bool is_start_of_burst, bool is_end_of_burst); bool blocking, bool is_start_of_burst, bool is_end_of_burst);
void (*srslte_rf_set_tx_cal)(void *h, srslte_rf_cal_t *cal);
void (*srslte_rf_set_rx_cal)(void *h, srslte_rf_cal_t *cal);
} rf_dev_t; } rf_dev_t;
/* Define implementation for UHD */ /* Define implementation for UHD */
@ -102,9 +98,7 @@ static rf_dev_t dev_uhd = {
rf_uhd_recv_with_time, rf_uhd_recv_with_time,
rf_uhd_recv_with_time_multi, rf_uhd_recv_with_time_multi,
rf_uhd_send_timed, rf_uhd_send_timed,
.srslte_rf_send_timed_multi = rf_uhd_send_timed_multi, .srslte_rf_send_timed_multi = rf_uhd_send_timed_multi
rf_uhd_set_tx_cal,
rf_uhd_set_rx_cal
}; };
#endif #endif
@ -142,9 +136,7 @@ static rf_dev_t dev_blade = {
rf_blade_recv_with_time, rf_blade_recv_with_time,
rf_blade_recv_with_time_multi, rf_blade_recv_with_time_multi,
rf_blade_send_timed, rf_blade_send_timed,
.srslte_rf_send_timed_multi = rf_blade_send_timed_multi, .srslte_rf_send_timed_multi = rf_blade_send_timed_multi
rf_blade_set_tx_cal,
rf_blade_set_rx_cal
}; };
#endif #endif
@ -181,9 +173,7 @@ static rf_dev_t dev_soapy = {
rf_soapy_recv_with_time, rf_soapy_recv_with_time,
rf_soapy_recv_with_time_multi, rf_soapy_recv_with_time_multi,
rf_soapy_send_timed, rf_soapy_send_timed,
.srslte_rf_send_timed_multi = rf_soapy_send_timed_multi, .srslte_rf_send_timed_multi = rf_soapy_send_timed_multi
rf_soapy_set_tx_cal,
rf_soapy_set_rx_cal
}; };
#endif #endif

@ -129,15 +129,6 @@ int srslte_rf_open_devname(srslte_rf_t *rf, char *devname, char *args, uint32_t
return -1; return -1;
} }
void srslte_rf_set_tx_cal(srslte_rf_t *rf, srslte_rf_cal_t *cal) {
return ((rf_dev_t*) rf->dev)->srslte_rf_set_tx_cal(rf->handler, cal);
}
void srslte_rf_set_rx_cal(srslte_rf_t *rf, srslte_rf_cal_t *cal) {
return ((rf_dev_t*) rf->dev)->srslte_rf_set_rx_cal(rf->handler, cal);
}
const char* srslte_rf_name(srslte_rf_t *rf) { const char* srslte_rf_name(srslte_rf_t *rf) {
return ((rf_dev_t*) rf->dev)->srslte_rf_devname(rf->handler); return ((rf_dev_t*) rf->dev)->srslte_rf_devname(rf->handler);
} }

@ -205,25 +205,18 @@ bool rf_soapy_rx_wait_lo_locked(void *h)
return true; return true;
} }
void rf_soapy_calibrate_tx(void *h)
void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal)
{ {
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;
double actual_bw = SoapySDRDevice_getBandwidth(handler->device, SOAPY_SDR_TX, 0); double actual_bw = SoapySDRDevice_getBandwidth(handler->device, SOAPY_SDR_TX, 0);
char str_buf[25]; char str_buf[25];
snprintf(str_buf, sizeof(str_buf), "%f", actual_bw); snprintf(str_buf, sizeof(str_buf), "%f", actual_bw);
str_buf[24] = 0;
if (SoapySDRDevice_writeSetting(handler->device, "CALIBRATE_TX", str_buf)) { if (SoapySDRDevice_writeSetting(handler->device, "CALIBRATE_TX", str_buf)) {
printf("Error calibrating Rx\n"); printf("Error calibrating Rx\n");
} }
} }
void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal)
{
// not supported
}
int rf_soapy_start_rx_stream(void *h, bool now) int rf_soapy_start_rx_stream(void *h, bool now)
{ {
rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h; rf_soapy_handler_t *handler = (rf_soapy_handler_t*) h;

@ -43,14 +43,12 @@ SRSLTE_API char* rf_soapy_devname(void *h);
SRSLTE_API int rf_soapy_close(void *h); SRSLTE_API int rf_soapy_close(void *h);
SRSLTE_API void rf_soapy_set_tx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API void rf_soapy_set_rx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API int rf_soapy_start_rx_stream(void *h, bool now); SRSLTE_API int rf_soapy_start_rx_stream(void *h, bool now);
SRSLTE_API int rf_soapy_stop_rx_stream(void *h); SRSLTE_API int rf_soapy_stop_rx_stream(void *h);
SRSLTE_API void rf_soapy_calibrate_tx(void *h);
SRSLTE_API void rf_soapy_flush_buffer(void *h); SRSLTE_API void rf_soapy_flush_buffer(void *h);
SRSLTE_API bool rf_soapy_has_rssi(void *h); SRSLTE_API bool rf_soapy_has_rssi(void *h);

@ -230,17 +230,6 @@ bool rf_uhd_rx_wait_lo_locked(void *h)
return val; return val;
} }
void rf_uhd_set_tx_cal(void *h, srslte_rf_cal_t *cal)
{
}
void rf_uhd_set_rx_cal(void *h, srslte_rf_cal_t *cal)
{
}
int rf_uhd_start_rx_stream(void *h, bool now) int rf_uhd_start_rx_stream(void *h, bool now)
{ {
rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h; rf_uhd_handler_t *handler = (rf_uhd_handler_t*) h;

@ -47,10 +47,6 @@ SRSLTE_API char* rf_uhd_devname(void *h);
SRSLTE_API int rf_uhd_close(void *h); SRSLTE_API int rf_uhd_close(void *h);
SRSLTE_API void rf_uhd_set_tx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API void rf_uhd_set_rx_cal(void *h, srslte_rf_cal_t *cal);
SRSLTE_API int rf_uhd_start_rx_stream(void *h, SRSLTE_API int rf_uhd_start_rx_stream(void *h,
bool now); bool now);

@ -450,9 +450,9 @@ int srslte_ue_dl_cfg_grant(srslte_ue_dl_t *q, srslte_ra_dl_grant_t *grant, uint3
} }
} else { } else {
if (grant->pinfo == 2) { if (grant->pinfo == 2) {
ERROR("Not implemented codebook index (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); /* Not implemented */
} else if (grant->pinfo > 2) { } else if (grant->pinfo > 2) {
ERROR("Reserved codebook index (nof_tb=%d, pinfo=%d)", nof_tb, grant->pinfo); /* Reserved */
} }
pmi = grant->pinfo % 2; pmi = grant->pinfo % 2;
} }

@ -97,16 +97,6 @@ void radio::reset()
usleep(100000); usleep(100000);
} }
void radio::set_manual_calibration(rf_cal_t* calibration)
{
srslte_rf_cal_t tx_cal;
tx_cal.dc_gain = calibration->tx_corr_dc_gain;
tx_cal.dc_phase = calibration->tx_corr_dc_phase;
tx_cal.iq_i = calibration->tx_corr_iq_i;
tx_cal.iq_q = calibration->tx_corr_iq_q;
srslte_rf_set_tx_cal(&rf_device, &tx_cal);
}
void radio::set_tx_rx_gain_offset(float offset) { void radio::set_tx_rx_gain_offset(float offset) {
srslte_rf_set_tx_rx_gain_offset(&rf_device, offset); srslte_rf_set_tx_rx_gain_offset(&rf_device, offset);
} }

@ -26,7 +26,7 @@
#include "srslte/upper/gtpu.h" #include "srslte/upper/gtpu.h"
#include "srslte/common/int_helpers.h"
namespace srslte { namespace srslte {
@ -37,24 +37,37 @@ namespace srslte {
bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log) bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte::log *gtpu_log)
{ {
if(header->flags != 0x30) { //flags
gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); if(!gtpu_supported_flags_check(header,gtpu_log)){
return false; gtpu_log->error("gtpu_write_header - Unhandled GTP-U Flags. Flags: 0x%x\n", header->flags);
}
if(header->message_type != 0xFF) {
gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type);
return false; return false;
} }
if(pdu->get_headroom() < GTPU_HEADER_LEN) {
gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); //msg type
if(!gtpu_supported_msg_type_check(header,gtpu_log)){
gtpu_log->error("gtpu_write_header - Unhandled GTP-U Message Type. Message Type: 0x%x\n", header->message_type);
return false; return false;
} }
pdu->msg -= GTPU_HEADER_LEN; //If E, S or PN are set, the header is longer
pdu->N_bytes += GTPU_HEADER_LEN; if (header->flags & (GTPU_FLAGS_EXTENDED_HDR | GTPU_FLAGS_SEQUENCE | GTPU_FLAGS_PACKET_NUM)) {
if(pdu->get_headroom() < GTPU_EXTENDED_HEADER_LEN) {
gtpu_log->error("gtpu_write_header - No room in PDU for header\n");
return false;
}
pdu->msg -= GTPU_EXTENDED_HEADER_LEN;
pdu->N_bytes += GTPU_EXTENDED_HEADER_LEN;
} else {
if(pdu->get_headroom() < GTPU_BASE_HEADER_LEN) {
gtpu_log->error("gtpu_write_header - No room in PDU for header\n");
return false;
}
pdu->msg -= GTPU_BASE_HEADER_LEN;
pdu->N_bytes += GTPU_BASE_HEADER_LEN;
}
//write mandatory fields
uint8_t *ptr = pdu->msg; uint8_t *ptr = pdu->msg;
*ptr = header->flags; *ptr = header->flags;
ptr++; ptr++;
*ptr = header->message_type; *ptr = header->message_type;
@ -62,7 +75,30 @@ bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu, srslte
uint16_to_uint8(header->length, ptr); uint16_to_uint8(header->length, ptr);
ptr += 2; ptr += 2;
uint32_to_uint8(header->teid, ptr); uint32_to_uint8(header->teid, ptr);
//write optional fields, if E, S or PN are set.
if (header->flags & (GTPU_FLAGS_EXTENDED_HDR | GTPU_FLAGS_SEQUENCE | GTPU_FLAGS_PACKET_NUM)) {
//S
if (header->flags & GTPU_FLAGS_SEQUENCE ) {
uint16_to_uint8(header->seq_number, ptr);
} else {
uint16_to_uint8(0, ptr);
}
ptr+=2;
//PN
if (header->flags & GTPU_FLAGS_PACKET_NUM ) {
*ptr = header->n_pdu;
} else {
header->n_pdu = 0;
}
ptr++;
//E
if (header->flags & GTPU_FLAGS_EXTENDED_HDR ) {
*ptr = header->next_ext_hdr_type;
} else {
*ptr = 0;
}
ptr++;
}
return true; return true;
} }
@ -70,26 +106,44 @@ bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header, srslte:
{ {
uint8_t *ptr = pdu->msg; uint8_t *ptr = pdu->msg;
pdu->msg += GTPU_HEADER_LEN; header->flags = *ptr;
pdu->N_bytes -= GTPU_HEADER_LEN;
header->flags = *ptr;
ptr++; ptr++;
header->message_type = *ptr; header->message_type = *ptr;
ptr++; ptr++;
uint8_to_uint16(ptr, &header->length); uint8_to_uint16(ptr, &header->length);
ptr += 2; ptr += 2;
uint8_to_uint32(ptr, &header->teid); uint8_to_uint32(ptr, &header->teid);
if(header->flags != 0x30) { //flags
gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); if(!gtpu_supported_flags_check(header,gtpu_log)){
gtpu_log->error("gtpu_read_header - Unhandled GTP-U Flags. Flags: 0x%x\n", header->flags);
return false; return false;
} }
if(header->message_type != 0xFF) {
gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); //message_type
if(!gtpu_supported_msg_type_check(header,gtpu_log)){
gtpu_log->error("gtpu_read_header - Unhandled GTP-U Message Type. Flags: 0x%x\n", header->message_type);
return false; return false;
} }
//If E, S or PN are set, header is longer
if (header->flags & (GTPU_FLAGS_EXTENDED_HDR | GTPU_FLAGS_SEQUENCE | GTPU_FLAGS_PACKET_NUM)) {
pdu->msg += GTPU_EXTENDED_HEADER_LEN;
pdu->N_bytes -= GTPU_EXTENDED_HEADER_LEN;
uint8_to_uint16(ptr, &header->seq_number);
ptr+=2;
header->n_pdu = *ptr;
ptr++;
header->next_ext_hdr_type = *ptr;
ptr++;
} else {
pdu->msg += GTPU_BASE_HEADER_LEN;
pdu->N_bytes -= GTPU_BASE_HEADER_LEN;
}
return true; return true;
} }

@ -204,7 +204,7 @@ void pdcp_entity::write_pdu(byte_buffer_t *pdu)
} else { } else {
// Handle SRB messages // Handle SRB messages
if (cfg.is_control) { if (cfg.is_control) {
uint32_t sn; uint32_t sn = 0;
if (do_encryption) { if (do_encryption) {
cipher_decrypt(&(pdu->msg[sn_len_bytes]), cipher_decrypt(&(pdu->msg[sn_len_bytes]),
rx_count, rx_count,
@ -218,7 +218,7 @@ void pdcp_entity::write_pdu(byte_buffer_t *pdu)
rx_count, rx_count,
pdu->N_bytes - 4, pdu->N_bytes - 4,
&(pdu->msg[pdu->N_bytes - 4]))) { &(pdu->msg[pdu->N_bytes - 4]))) {
log->error_hex(pdu->msg, pdu->N_bytes, "RX %s PDU SN: %d (Dropping PDU)", rrc->get_rb_name(lcid).c_str(), sn); log->error_hex(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid).c_str());
goto exit; goto exit;
} }
} }

@ -210,6 +210,13 @@ void rlc::empty_queue()
void rlc::write_sdu(uint32_t lcid, byte_buffer_t *sdu, bool blocking) void rlc::write_sdu(uint32_t lcid, byte_buffer_t *sdu, bool blocking)
{ {
// FIXME: rework build PDU logic to allow large SDUs (without concatenation)
if (sdu->N_bytes > RLC_MAX_SDU_SIZE) {
rlc_log->warning("Dropping too long SDU of size %d B (Max. size %d B).\n", sdu->N_bytes, RLC_MAX_SDU_SIZE);
pool->deallocate(sdu);
return;
}
pthread_rwlock_rdlock(&rwlock); pthread_rwlock_rdlock(&rwlock);
if (valid_lcid(lcid)) { if (valid_lcid(lcid)) {
rlc_array.at(lcid)->write_sdu(sdu, blocking); rlc_array.at(lcid)->write_sdu(sdu, blocking);
@ -564,6 +571,15 @@ exit:
} }
bool rlc::has_bearer(uint32_t lcid)
{
pthread_rwlock_rdlock(&rwlock);
bool ret = valid_lcid(lcid);
pthread_rwlock_unlock(&rwlock);
return ret;
}
/******************************************************************************* /*******************************************************************************
Helpers (Lock must be hold when calling those) Helpers (Lock must be hold when calling those)
*******************************************************************************/ *******************************************************************************/

@ -322,7 +322,7 @@ uint32_t rlc_am::rlc_am_tx::get_buffer_state()
// Bytes needed for status report // Bytes needed for status report
if (do_status() && not status_prohibited) { if (do_status() && not status_prohibited) {
n_bytes = parent->rx.get_status(&tx_status); n_bytes = parent->rx.get_status_pdu_length();
log->debug("%s Buffer state - status report: %d bytes\n", RB_NAME, n_bytes); log->debug("%s Buffer state - status report: %d bytes\n", RB_NAME, n_bytes);
goto unlock_and_return; goto unlock_and_return;
} }
@ -378,7 +378,7 @@ uint32_t rlc_am::rlc_am_tx::get_total_buffer_state()
// Bytes needed for status report // Bytes needed for status report
if(do_status() && not status_prohibited) { if(do_status() && not status_prohibited) {
n_bytes += parent->rx.get_status(&tx_status); n_bytes += parent->rx.get_status_pdu_length();
log->debug("%s Buffer state - total status report: %d bytes\n", RB_NAME, n_bytes); log->debug("%s Buffer state - total status report: %d bytes\n", RB_NAME, n_bytes);
} }
@ -582,10 +582,11 @@ bool rlc_am::rlc_am_tx::poll_required()
int rlc_am::rlc_am_tx::build_status_pdu(uint8_t *payload, uint32_t nof_bytes) int rlc_am::rlc_am_tx::build_status_pdu(uint8_t *payload, uint32_t nof_bytes)
{ {
int pdu_len = parent->rx.get_status(&tx_status); int pdu_len = parent->rx.get_status_pdu(&tx_status, nof_bytes);
log->debug("%s\n", rlc_am_status_pdu_to_string(&tx_status).c_str());
if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) { if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) {
log->info("%s Tx status PDU - %s\n", log->info("%s Tx status PDU - %s\n",
RB_NAME, rlc_am_to_string(&tx_status).c_str()); RB_NAME, rlc_am_status_pdu_to_string(&tx_status).c_str());
parent->rx.reset_status(); parent->rx.reset_status();
@ -760,6 +761,10 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
upper += old_header.li[i]; upper += old_header.li[i];
head_len = rlc_am_packed_length(&new_header); head_len = rlc_am_packed_length(&new_header);
// Accomodate some extra space for for LIs if old header contained segments too
head_len += old_header.N_li;
pdu_space = nof_bytes-head_len; pdu_space = nof_bytes-head_len;
if(pdu_space < (retx.so_end-retx.so_start)) { if(pdu_space < (retx.so_end-retx.so_start)) {
retx.so_end = retx.so_start + pdu_space; retx.so_end = retx.so_start + pdu_space;
@ -779,19 +784,17 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
if (upper == retx.so_end) { if (upper == retx.so_end) {
new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU
} }
new_header.li[new_header.N_li++] = li; new_header.li[new_header.N_li] = li;
// only increment N_li if more SDU (segments) are being added
if (retx.so_end > upper) {
new_header.N_li++;
}
} }
lower += old_header.li[i]; lower += old_header.li[i];
} }
// Make sure LI is not deleted in case the SDU boundary is crossed
// FIXME: fix if N_li > 1
if (new_header.N_li == 1 && retx.so_start + new_header.li[0] < retx.so_end && retx.so_end <= retx.so_start + pdu_space) {
// This segment crosses a SDU boundary
new_header.N_li++;
}
// Update retx_queue // Update retx_queue
if(tx_window[retx.sn].buf->N_bytes == retx.so_end) { if(tx_window[retx.sn].buf->N_bytes == retx.so_end) {
retx_queue.pop_front(); retx_queue.pop_front();
@ -804,9 +807,6 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
} else { } else {
retx_queue.front().is_segment = true; retx_queue.front().is_segment = true;
retx_queue.front().so_start = retx.so_end; retx_queue.front().so_start = retx.so_end;
if (new_header.N_li > 0) {
new_header.N_li--;
}
} }
// Write header and pdu // Write header and pdu
@ -816,9 +816,6 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
uint32_t len = retx.so_end - retx.so_start; uint32_t len = retx.so_end - retx.so_start;
memcpy(ptr, data, len); memcpy(ptr, data, len);
log->info("%s Retx PDU segment scheduled for tx. SN: %d, SO: %d\n",
RB_NAME, retx.sn, retx.so_start);
debug_state(); debug_state();
int pdu_len = (ptr-payload) + len; int pdu_len = (ptr-payload) + len;
if (pdu_len > static_cast<int>(nof_bytes)) { if (pdu_len > static_cast<int>(nof_bytes)) {
@ -827,6 +824,10 @@ int rlc_am::rlc_am_tx::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_a
log->debug("%s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d\n", log->debug("%s Retx PDU segment length error. Header len: %ld, Payload len: %d, N_li: %d\n",
RB_NAME, (ptr-payload), len, new_header.N_li); RB_NAME, (ptr-payload), len, new_header.N_li);
} }
log->info_hex(payload, pdu_len, "%s Retx PDU segment of SN=%d (%d B), SO: %d, N_li: %d\n",
RB_NAME, retx.sn, pdu_len, retx.so_start, new_header.N_li);
return pdu_len; return pdu_len;
} }
@ -908,7 +909,7 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
tx_sdu = NULL; tx_sdu = NULL;
} }
if (pdu_space > to_move) { if (pdu_space > to_move) {
pdu_space -= SRSLTE_MIN(to_move, pdu->get_tailroom());; pdu_space -= SRSLTE_MIN(to_move, pdu->get_tailroom());
} else { } else {
pdu_space = 0; pdu_space = 0;
} }
@ -919,9 +920,10 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
} }
// Pull SDUs from queue // Pull SDUs from queue
while (pdu_space > head_len + 1 && tx_sdu_queue.size() > 0) { while (pdu_space > head_len + 1 && tx_sdu_queue.size() > 0 && header.N_li < RLC_AM_WINDOW_SIZE) {
if (last_li > 0) { if (last_li > 0) {
header.li[header.N_li++] = last_li; header.li[header.N_li] = last_li;
header.N_li++;
} }
head_len = rlc_am_packed_length(&header); head_len = rlc_am_packed_length(&header);
if (head_len >= pdu_space) { if (head_len >= pdu_space) {
@ -993,10 +995,11 @@ int rlc_am::rlc_am_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
uint8_t *ptr = payload; uint8_t *ptr = payload;
rlc_am_write_data_pdu_header(&header, &ptr); rlc_am_write_data_pdu_header(&header, &ptr);
memcpy(ptr, pdu->msg, pdu->N_bytes); memcpy(ptr, pdu->msg, pdu->N_bytes);
log->info_hex(payload, pdu->N_bytes, "%s PDU scheduled for tx. SN: %d (%d B)\n", RB_NAME, header.sn, pdu->N_bytes); int total_len = (ptr-payload) + pdu->N_bytes;
log->info_hex(payload, total_len, "%s Tx PDU SN=%d (%d B)\n", RB_NAME, header.sn, total_len);
log->debug("%s\n", rlc_amd_pdu_header_to_string(header).c_str());
debug_state(); debug_state();
return (ptr-payload) + pdu->N_bytes; return total_len;
} }
void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes) void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
@ -1008,10 +1011,10 @@ void rlc_am::rlc_am_tx::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes)
rlc_status_pdu_t status; rlc_status_pdu_t status;
rlc_am_read_status_pdu(payload, nof_bytes, &status); rlc_am_read_status_pdu(payload, nof_bytes, &status);
log->info("%s Rx Status PDU: %s\n", RB_NAME, rlc_am_to_string(&status).c_str()); log->info("%s Rx Status PDU: %s\n", RB_NAME, rlc_am_status_pdu_to_string(&status).c_str());
if (poll_retx_timer != NULL) { if (poll_retx_timer != NULL) {
poll_retx_timer->reset(); poll_retx_timer->stop();
} }
// flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again
@ -1305,11 +1308,11 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
{ {
std::map<uint32_t, rlc_amd_rx_pdu_t>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu_t>::iterator it;
log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d (%d B), %s", log->info_hex(payload, nof_bytes, "%s Rx data PDU SN=%d (%d B)",
RB_NAME, RB_NAME,
header.sn, header.sn,
nof_bytes, nof_bytes);
rlc_fi_field_text[header.fi]); log->debug("%s\n", rlc_amd_pdu_header_to_string(header).c_str());
if(!inside_rx_window(header.sn)) { if(!inside_rx_window(header.sn)) {
if(header.p) { if(header.p) {
@ -1376,9 +1379,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
poll_received = true; poll_received = true;
// 36.322 v10 Section 5.2.3 // 36.322 v10 Section 5.2.3
if(RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || if (RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr)) {
RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr))
{
do_status = true; do_status = true;
} }
// else delay for reordering timer // else delay for reordering timer
@ -1391,7 +1392,7 @@ void rlc_am::rlc_am_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rl
if (reordering_timer != NULL) { if (reordering_timer != NULL) {
if (reordering_timer->is_running()) { if (reordering_timer->is_running()) {
if(vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) { if(vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) {
reordering_timer->reset(); reordering_timer->stop();
} }
} }
@ -1412,8 +1413,9 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
{ {
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
log->info_hex(payload, nof_bytes, "%s Rx data PDU segment. SN: %d, SO: %d", log->info_hex(payload, nof_bytes, "%s Rx data PDU segment of SN=%d (%d B), SO=%d, N_li=%d",
RB_NAME, header.sn, header.so); RB_NAME, header.sn, nof_bytes, header.so, header.N_li);
log->debug("%s\n", rlc_amd_pdu_header_to_string(header).c_str());
// Check inside rx window // Check inside rx window
if(!inside_rx_window(header.sn)) { if(!inside_rx_window(header.sn)) {
@ -1444,9 +1446,9 @@ void rlc_am::rlc_am_rx::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_b
// Check if we already have a segment from the same PDU // Check if we already have a segment from the same PDU
it = rx_segments.find(header.sn); it = rx_segments.find(header.sn);
if(rx_segments.end() != it) { if (rx_segments.end() != it) {
if(header.p) { if (header.p) {
log->info("%s Status packet requested through polling bit\n", RB_NAME); log->info("%s Status packet requested through polling bit\n", RB_NAME);
do_status = true; do_status = true;
} }
@ -1518,6 +1520,9 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
for(uint32_t i=0; i<rx_window[vr_r].header.N_li; i++) for(uint32_t i=0; i<rx_window[vr_r].header.N_li; i++)
{ {
len = rx_window[vr_r].header.li[i]; len = rx_window[vr_r].header.li[i];
log->debug_hex(rx_window[vr_r].buf->msg, len, "Handling segment %d/%d of length %d B of SN=%d\n", i+1, rx_window[vr_r].header.N_li, len, vr_r);
// sanity check to avoid zero-size SDUs // sanity check to avoid zero-size SDUs
if (len == 0) { if (len == 0) {
break; break;
@ -1557,11 +1562,12 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
// Handle last segment // Handle last segment
len = rx_window[vr_r].buf->N_bytes; len = rx_window[vr_r].buf->N_bytes;
log->debug_hex(rx_window[vr_r].buf->msg, len, "Handling last segment of length %d B of SN=%d\n", len, vr_r);
if (rx_sdu->get_tailroom() >= len) { if (rx_sdu->get_tailroom() >= len) {
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len); memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len);
rx_sdu->N_bytes += rx_window[vr_r].buf->N_bytes; rx_sdu->N_bytes += rx_window[vr_r].buf->N_bytes;
} else { } else {
log->error("Cannot fit RLC PDU in SDU buffer, dropping both.\n"); log->error("Cannot fit RLC PDU in SDU buffer, dropping both. Erasing SN=%d.\n", vr_r);
pool->deallocate(rx_sdu); pool->deallocate(rx_sdu);
pool->deallocate(rx_window[vr_r].buf); pool->deallocate(rx_window[vr_r].buf);
rx_window.erase(vr_r); rx_window.erase(vr_r);
@ -1585,6 +1591,19 @@ void rlc_am::rlc_am_rx::reassemble_rx_sdus()
exit: exit:
// Move the rx_window // Move the rx_window
log->debug("Erasing SN=%d.\n", vr_r);
// also erase any segments of this SN
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
it = rx_segments.find(vr_r);
if(rx_segments.end() != it) {
log->debug("Erasing segments of SN=%d\n", vr_r);
std::list<rlc_amd_rx_pdu_t>::iterator segit;
for(segit = it->second.segments.begin(); segit != it->second.segments.end(); ++segit) {
log->debug(" Erasing segment of SN=%d SO=%d Len=%d N_li=%d\n", segit->header.sn, segit->header.so, segit->buf->N_bytes, segit->header.N_li);
pool->deallocate(segit->buf);
}
it->second.segments.clear();
}
pool->deallocate(rx_window[vr_r].buf); pool->deallocate(rx_window[vr_r].buf);
rx_window.erase(vr_r); rx_window.erase(vr_r);
vr_r = (vr_r + 1)%MOD; vr_r = (vr_r + 1)%MOD;
@ -1670,8 +1689,8 @@ void rlc_am::rlc_am_rx::timer_expired(uint32_t timeout_id)
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
} }
// Called from Tx object (packs status PDU and returns length of it) // Called from Tx object to pack status PDU that doesn't exceed a given size
int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status) int rlc_am::rlc_am_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size)
{ {
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
status->N_nack = 0; status->N_nack = 0;
@ -1679,7 +1698,8 @@ int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status)
// We don't use segment NACKs - just NACK the full PDU // We don't use segment NACKs - just NACK the full PDU
uint32_t i = vr_r; uint32_t i = vr_r;
while (RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms) && status->N_nack < RLC_AM_WINDOW_SIZE) { while (RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms) && status->N_nack < RLC_AM_WINDOW_SIZE && rlc_am_packed_length(status) <= max_pdu_size-2) {
status->ack_sn = i;
if(rx_window.find(i) == rx_window.end()) { if(rx_window.find(i) == rx_window.end()) {
status->nacks[status->N_nack].nack_sn = i; status->nacks[status->N_nack].nack_sn = i;
status->N_nack++; status->N_nack++;
@ -1690,6 +1710,22 @@ int rlc_am::rlc_am_rx::get_status(rlc_status_pdu_t* status)
return rlc_am_packed_length(status); return rlc_am_packed_length(status);
} }
// Called from Tx object to obtain length of the full status PDU
int rlc_am::rlc_am_rx::get_status_pdu_length()
{
pthread_mutex_lock(&mutex);
rlc_status_pdu_t status;
uint32_t i = vr_r;
while (RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms) && status.N_nack < RLC_AM_WINDOW_SIZE) {
if(rx_window.find(i) == rx_window.end()) {
status.N_nack++;
}
i = (i + 1)%MOD;
}
pthread_mutex_unlock(&mutex);
return rlc_am_packed_length(&status);
}
void rlc_am::rlc_am_rx::print_rx_segments() void rlc_am::rlc_am_rx::print_rx_segments()
{ {
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it; std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
@ -1758,33 +1794,46 @@ bool rlc_am::rlc_am_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rl
header.fi |= (pdu->segments.front().header.fi & RLC_FI_FIELD_NOT_START_ALIGNED); header.fi |= (pdu->segments.front().header.fi & RLC_FI_FIELD_NOT_START_ALIGNED);
header.fi |= (pdu->segments.back().header.fi & RLC_FI_FIELD_NOT_END_ALIGNED); header.fi |= (pdu->segments.back().header.fi & RLC_FI_FIELD_NOT_END_ALIGNED);
log->debug("Starting header reconstruction of %zd segments\n", pdu->segments.size());
// Reconstruct li fields // Reconstruct li fields
uint16_t count = 0; uint16_t count = 0;
uint16_t carryover = 0; uint16_t carryover = 0;
for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) {
if(it->header.N_li > 0) { log->debug(" Handling %d PDU segments\n", it->header.N_li);
header.li[header.N_li++] = it->header.li[0] + carryover; for(uint32_t i=0; i<it->header.N_li; i++) {
count += it->header.li[0]; header.li[header.N_li] = it->header.li[i];
for(uint32_t i=1; i<it->header.N_li; i++) { if (i == 0) {
header.li[header.N_li++] = it->header.li[i]; header.li[header.N_li] += carryover;
count += it->header.li[i];
} }
log->debug(" - adding segment %d/%d (%d B, SO=%d, carryover=%d, count=%d)\n", i+1, it->header.N_li, header.li[header.N_li], header.so, carryover, count);
header.N_li++;
count += it->header.li[i];
carryover = 0;
} }
// accumulate segment sizes until end aligned PDU is received if (count <= it->buf->N_bytes) {
if (rlc_am_not_start_aligned(it->header.fi)) {
carryover += it->buf->N_bytes - count; carryover += it->buf->N_bytes - count;
log->debug("Incremented carryover (it->buf->N_bytes=%d, count=%d). New carryover=%d\n", it->buf->N_bytes, count, carryover);
} else { } else {
carryover = it->buf->N_bytes - count; // Next segment would be too long, recalculate carryover
header.N_li--;
carryover = it->buf->N_bytes - (count - header.li[header.N_li]);
log->debug("Recalculated carryover=%d (it->buf->N_bytes=%d, count=%d, header.li[header.N_li]=%d)\n", carryover, it->buf->N_bytes, count, header.li[header.N_li]);
} }
tmpit = it; tmpit = it;
if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) { if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) {
header.li[header.N_li++] = carryover; log->debug("Header is end-aligned, overwrite header.li[%d]=%d\n", header.N_li, carryover);
header.li[header.N_li] = carryover;
header.N_li++;
carryover = 0; carryover = 0;
} }
count = 0; count = 0;
} }
log->debug("Finished header reconstruction of %zd segments\n", pdu->segments.size());
// Copy data // Copy data
byte_buffer_t *full_pdu = pool_allocate_blocking; byte_buffer_t *full_pdu = pool_allocate_blocking;
if (full_pdu == NULL) { if (full_pdu == NULL) {
@ -2091,7 +2140,7 @@ bool rlc_am_is_pdu_segment(uint8_t *payload)
return ((*(payload) >> 6) & 0x01) == 1; return ((*(payload) >> 6) & 0x01) == 1;
} }
std::string rlc_am_to_string(rlc_status_pdu_t *status) std::string rlc_am_status_pdu_to_string(rlc_status_pdu_t *status)
{ {
std::stringstream ss; std::stringstream ss;
ss << "ACK_SN = " << status->ack_sn; ss << "ACK_SN = " << status->ack_sn;
@ -2112,6 +2161,28 @@ std::string rlc_am_to_string(rlc_status_pdu_t *status)
return ss.str(); return ss.str();
} }
std::string rlc_amd_pdu_header_to_string(const rlc_amd_pdu_header_t &header)
{
std::stringstream ss;
ss << "[" << rlc_dc_field_text[header.dc];
ss << ", RF=" << (header.rf ? "1":"0");
ss << ", P=" << (header.p ? "1":"0");
ss << ", FI=" << (header.fi ? "1":"0");
ss << ", SN=" << header.sn;
ss << ", LSF=" << (header.lsf ? "1":"0");
ss << ", SO=" << header.so;
ss << ", N_li=" << header.N_li;
if (header.N_li > 0) {
ss << " (";
for (uint32_t i = 0; i < header.N_li; i++) {
ss << header.li[i] << ", ";
}
ss << ")";
}
ss << "]";
return ss.str();
}
bool rlc_am_start_aligned(const uint8_t fi) bool rlc_am_start_aligned(const uint8_t fi)
{ {
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED);

@ -153,7 +153,7 @@ int rlc_tm::read_pdu(uint8_t *payload, uint32_t nof_bytes)
{ {
uint32_t pdu_size = ul_queue.size_tail_bytes(); uint32_t pdu_size = ul_queue.size_tail_bytes();
if (pdu_size > nof_bytes) { if (pdu_size > nof_bytes) {
log->error("TX %s PDU size larger than MAC opportunity\n", rrc->get_rb_name(lcid).c_str()); log->error("TX %s PDU size larger than MAC opportunity (%d > %d)\n", rrc->get_rb_name(lcid).c_str(), pdu_size, nof_bytes);
return -1; return -1;
} }
byte_buffer_t *buf; byte_buffer_t *buf;

@ -37,6 +37,7 @@ namespace srslte {
rlc_um::rlc_um(uint32_t queue_len) rlc_um::rlc_um(uint32_t queue_len)
:lcid(0) :lcid(0)
,tx(queue_len) ,tx(queue_len)
,pool(byte_buffer_pool::get_instance())
,rrc(NULL) ,rrc(NULL)
,log(NULL) ,log(NULL)
{ {
@ -375,6 +376,12 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
{ {
pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex);
log->debug("MAC opportunity - %d bytes\n", nof_bytes); log->debug("MAC opportunity - %d bytes\n", nof_bytes);
if (not tx_enabled) {
pthread_mutex_unlock(&mutex);
return 0;
}
if(!tx_sdu && tx_sdu_queue.size() == 0) { if(!tx_sdu && tx_sdu_queue.size() == 0) {
log->info("No data available to be sent\n"); log->info("No data available to be sent\n");
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
@ -399,7 +406,7 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
uint8_t *pdu_ptr = pdu->msg; uint8_t *pdu_ptr = pdu->msg;
int head_len = rlc_um_packed_length(&header); int head_len = rlc_um_packed_length(&header);
int pdu_space = nof_bytes; int pdu_space = SRSLTE_MIN(nof_bytes, pdu->get_tailroom());;
if(pdu_space <= head_len + 1) if(pdu_space <= head_len + 1)
{ {
@ -430,7 +437,7 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
pool->deallocate(tx_sdu); pool->deallocate(tx_sdu);
tx_sdu = NULL; tx_sdu = NULL;
} }
pdu_space -= to_move; pdu_space -= SRSLTE_MIN(to_move, pdu->get_tailroom());
header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU
} }
@ -474,7 +481,7 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
memcpy(payload, pdu->msg, pdu->N_bytes); memcpy(payload, pdu->msg, pdu->N_bytes);
uint32_t ret = pdu->N_bytes; uint32_t ret = pdu->N_bytes;
log->info("%s Transmitting PDU SN=%d (%d B)\n", get_rb_name(), header.sn, pdu->N_bytes); log->info_hex(payload, ret, "%s Tx PDU SN=%d (%d B)\n", get_rb_name(), header.sn, pdu->N_bytes);
pool->deallocate(pdu); pool->deallocate(pdu);
debug_state(); debug_state();
@ -595,7 +602,7 @@ void rlc_um::rlc_um_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header);
log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d", get_rb_name(), header.sn); log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d (%d B)", get_rb_name(), header.sn, nof_bytes);
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) && if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) &&
RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur)) RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur))
@ -673,15 +680,18 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
// First catch up with lower edge of reordering window // First catch up with lower edge of reordering window
while(!inside_reordering_window(vr_ur)) while(!inside_reordering_window(vr_ur))
{ {
log->debug("SN=%d is not inside reordering windows\n", vr_ur);
if(rx_window.end() == rx_window.find(vr_ur)) if(rx_window.end() == rx_window.find(vr_ur))
{ {
log->debug("SN=%d not in rx_window. Reset received SDU\n", vr_ur);
rx_sdu->reset(); rx_sdu->reset();
}else{ }else{
// Handle any SDU segments // Handle any SDU segments
for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++) for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++)
{ {
int len = rx_window[vr_ur].header.li[i]; int len = rx_window[vr_ur].header.li[i];
log->debug_hex(rx_window[vr_ur].buf->msg, len, "Handling segment %d/%d of length %d B of SN=%d\n", i+1, rx_window[vr_ur].header.N_li, len, vr_ur);
// Check if we received a middle or end segment // Check if we received a middle or end segment
if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
log->warning("Dropping PDU %d due to lost start segment\n", vr_ur); log->warning("Dropping PDU %d due to lost start segment\n", vr_ur);
@ -758,35 +768,34 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
// Now update vr_ur until we reach an SN we haven't yet received // Now update vr_ur until we reach an SN we haven't yet received
while(rx_window.end() != rx_window.find(vr_ur)) { while(rx_window.end() != rx_window.find(vr_ur)) {
log->debug("Reassemble loop for vr_ur=%d\n", vr_ur); log->debug("Reassemble loop for vr_ur=%d\n", vr_ur);
if ((vr_ur_in_rx_sdu+1)%cfg.rx_mod != vr_ur) {
log->warning("PDU SN=%d lost, dropping remainder of %d\n", vr_ur_in_rx_sdu+1, vr_ur); if (not pdu_belongs_to_rx_sdu()) {
log->warning("PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d)\n", vr_ur_in_rx_sdu+1, vr_ur_in_rx_sdu);
pdu_lost = false; // Reset flag to not prevent reassembling of further segments
rx_sdu->reset(); rx_sdu->reset();
} }
// Handle any SDU segments // Handle any SDU segments
for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++) { for(uint32_t i=0; i<rx_window[vr_ur].header.N_li; i++) {
int len = rx_window[vr_ur].header.li[i]; uint16_t len = rx_window[vr_ur].header.li[i];
log->debug("Handling SDU segment i=%d with len=%d of vr_ur=%d N_li=%d [%s]\n", i, len, vr_ur, rx_window[vr_ur].header.N_li, rlc_fi_field_text[rx_window[vr_ur].header.fi]);
// Check if the first part of the PDU is a middle or end segment // Check if the first part of the PDU is a middle or end segment
if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
log->warning_hex(rx_window[vr_ur].buf->msg, len, "Dropping first part of SN %d due to lost start segment\n", vr_ur); log->warning_hex(rx_window[vr_ur].buf->msg, len, "Dropping first %d B of SN %d due to lost start segment\n", len, vr_ur);
if (rx_window[vr_ur].buf->N_bytes < len) {
log->error("Dropping remaining remainder of SN %d too (N_bytes=%u < len=%d)\n", vr_ur, rx_window[vr_ur].buf->N_bytes, len);
goto clean_up_rx_window;
}
// Advance data pointers and continue with next segment // Advance data pointers and continue with next segment
rx_window[vr_ur].buf->msg += len; rx_window[vr_ur].buf->msg += len;
rx_window[vr_ur].buf->N_bytes -= len; rx_window[vr_ur].buf->N_bytes -= len;
rx_sdu->reset(); rx_sdu->reset();
// beginning of next SDU? // Reset flag, it is safe to process all remaining segments of this PDU
if (rx_window[vr_ur].header.fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED) { pdu_lost = false;
len = rx_window[vr_ur].buf->N_bytes; continue;
log->info_hex(rx_window[vr_ur].buf->msg, len, "Copying first %d bytes of new SDU\n", len);
memcpy(rx_sdu->msg, rx_window[vr_ur].buf->msg, len);
rx_sdu->N_bytes = len;
rx_window[vr_ur].buf->msg += len;
rx_window[vr_ur].buf->N_bytes -= len;
log->info("Updating vr_ur_in_rx_sdu. old=%d, new=%d\n", vr_ur_in_rx_sdu, vr_ur);
vr_ur_in_rx_sdu = vr_ur;
goto clean_up_rx_window;
}
} }
// Check available space in SDU // Check available space in SDU
@ -796,16 +805,22 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
goto clean_up_rx_window; goto clean_up_rx_window;
} }
log->info_hex(rx_window[vr_ur].buf->msg, len, "Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n", if (not pdu_belongs_to_rx_sdu()) {
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod); log->info_hex(rx_window[vr_ur].buf->msg, len, "Copying first %d bytes of new SDU\n", len);
log->info("Updating vr_ur_in_rx_sdu. old=%d, new=%d\n", vr_ur_in_rx_sdu, vr_ur);
vr_ur_in_rx_sdu = vr_ur;
} else {
log->info_hex(rx_window[vr_ur].buf->msg, len, "Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n",
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod);
}
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
rx_sdu->N_bytes += len; rx_sdu->N_bytes += len;
rx_window[vr_ur].buf->msg += len; rx_window[vr_ur].buf->msg += len;
rx_window[vr_ur].buf->N_bytes -= len; rx_window[vr_ur].buf->N_bytes -= len;
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) { vr_ur_in_rx_sdu = vr_ur;
log->warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
rx_sdu->reset(); if (pdu_belongs_to_rx_sdu()) {
} else {
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", get_rb_name(), vr_ur, i); log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", get_rb_name(), vr_ur, i);
rx_sdu->set_timestamp(); rx_sdu->set_timestamp();
if(cfg.is_mrb){ if(cfg.is_mrb){
@ -818,6 +833,11 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n"); log->error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus().\n");
return; return;
} }
} else {
log->warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
// Advance data pointers and continue with next segment
rx_window[vr_ur].buf->msg += len;
rx_window[vr_ur].buf->N_bytes -= len;
} }
pdu_lost = false; pdu_lost = false;
} }
@ -833,8 +853,8 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
rx_window[vr_ur].buf->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES && rx_window[vr_ur].buf->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES &&
rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES) rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSLTE_MAX_BUFFER_SIZE_BYTES)
{ {
log->info_hex(rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes, "Writing last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n", log->info_hex(rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes, "Writing last segment in SDU buffer. Updating vr_ur=%d, vr_ur_in_rx_sdu=%d, Buffer size=%d, segment size=%d\n",
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes); vr_ur, vr_ur_in_rx_sdu, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes);
rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes;
} else { } else {
@ -872,6 +892,18 @@ clean_up_rx_window:
} }
} }
// Only called when lock is hold
bool rlc_um::rlc_um_rx::pdu_belongs_to_rx_sdu()
{
// return true if the currently received SDU
if (((vr_ur_in_rx_sdu + 1)%cfg.rx_mod == vr_ur) || (vr_ur == vr_ur_in_rx_sdu)) {
return true;
}
return false;
}
// Only called when lock is hold // Only called when lock is hold
// 36.322 Section 5.1.2.2.1 // 36.322 Section 5.1.2.2.1
bool rlc_um::rlc_um_rx::inside_reordering_window(uint16_t sn) bool rlc_um::rlc_um_rx::inside_reordering_window(uint16_t sn)
@ -907,9 +939,8 @@ void rlc_um::rlc_um_rx::reset_metrics()
void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id) void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
{ {
if (reordering_timer_id == timeout_id) { pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex); if (reordering_timer != NULL && reordering_timer_id == timeout_id) {
// 36.322 v10 Section 5.1.2.2.4 // 36.322 v10 Section 5.1.2.2.4
log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n", log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n",
get_rb_name()); get_rb_name());
@ -925,7 +956,7 @@ void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
reassemble_rx_sdus(); reassemble_rx_sdus();
log->debug("Finished reassemble from timeout id=%d\n", timeout_id); log->debug("Finished reassemble from timeout id=%d\n", timeout_id);
} }
reordering_timer->stop();
if (RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur)) { if (RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur)) {
reordering_timer->reset(); reordering_timer->reset();
reordering_timer->run(); reordering_timer->run();
@ -933,13 +964,8 @@ void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
} }
debug_state(); debug_state();
pthread_mutex_unlock(&mutex);
} }
} pthread_mutex_unlock(&mutex);
bool rlc_um::rlc_um_rx::reordering_timeout_running()
{
return reordering_timer->is_running();
} }
/**************************************************************************** /****************************************************************************

@ -34,7 +34,7 @@ add_executable(rlc_stress_test rlc_stress_test.cc)
target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES}) target_link_libraries(rlc_stress_test srslte_upper srslte_phy srslte_common ${Boost_LIBRARIES})
add_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_delay 250) add_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_delay 250)
add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1) add_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1)
add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --opp_sdu_ratio=1.0) add_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --random_opp=false)
# Run clang-tidy if available # Run clang-tidy if available
if(CLANG_TIDY_BIN) if(CLANG_TIDY_BIN)

@ -732,11 +732,11 @@ bool resegment_test_2()
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx1.msg, retx1.N_bytes); rlc2.write_pdu(retx1.msg, retx1.N_bytes);
assert(16 == rlc1.get_buffer_state()); assert(18 == rlc1.get_buffer_state());
// Read the remaining segment // Read the remaining segment
byte_buffer_t retx2; byte_buffer_t retx2;
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 16); // 6 byte header + 10 data retx2.N_bytes = rlc1.read_pdu(retx2.msg, 18); // 6 byte header + 12 data
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx2.msg, retx2.N_bytes); rlc2.write_pdu(retx2.msg, retx2.N_bytes);
@ -959,9 +959,11 @@ bool resegment_test_4()
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx1.msg, retx1.N_bytes); rlc2.write_pdu(retx1.msg, retx1.N_bytes);
assert(23 == rlc1.get_buffer_state());
// Read the remaining segment // Read the remaining segment
byte_buffer_t retx2; byte_buffer_t retx2;
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 21); // 6 byte header + 15 data retx2.N_bytes = rlc1.read_pdu(retx2.msg, 23); // 6 byte header + 18 data
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx2.msg, retx2.N_bytes); rlc2.write_pdu(retx2.msg, retx2.N_bytes);
@ -1069,9 +1071,11 @@ bool resegment_test_5()
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx1.msg, retx1.N_bytes); rlc2.write_pdu(retx1.msg, retx1.N_bytes);
assert(31 == rlc1.get_buffer_state());
// Read the remaining segment // Read the remaining segment
byte_buffer_t retx2; byte_buffer_t retx2;
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 27); // 7 byte header + 20 data retx2.N_bytes = rlc1.read_pdu(retx2.msg, 34); // 7 byte header + 24 data
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx2.msg, retx2.N_bytes); rlc2.write_pdu(retx2.msg, retx2.N_bytes);
@ -1195,11 +1199,11 @@ bool resegment_test_6()
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
rlc2.write_pdu(retx1.msg, retx1.N_bytes); rlc2.write_pdu(retx1.msg, retx1.N_bytes);
assert(155 == rlc1.get_buffer_state()); assert(159 == rlc1.get_buffer_state());
// Read the remaining segment // Read the remaining segment
byte_buffer_t retx2; byte_buffer_t retx2;
len = rlc1.read_pdu(retx2.msg, 157); len = rlc1.read_pdu(retx2.msg, 162);
retx2.N_bytes = len; retx2.N_bytes = len;
// Write the retx PDU to RLC2 // Write the retx PDU to RLC2
@ -1214,6 +1218,7 @@ bool resegment_test_6()
} }
for(int i=3;i<9;i++) for(int i=3;i<9;i++)
{ {
if (i >= tester.n_sdus) return -1;
if(tester.sdus[i]->N_bytes != 54) return -1; if(tester.sdus[i]->N_bytes != 54) return -1;
for(int j=0;j<54;j++) { for(int j=0;j<54;j++) {
if (tester.sdus[i]->msg[j] != j) return -1; if (tester.sdus[i]->msg[j] != j) return -1;

@ -36,6 +36,7 @@
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <cassert> #include <cassert>
#include <srslte/upper/rlc_interface.h> #include <srslte/upper/rlc_interface.h>
#include "srslte/common/crash_handler.h"
#define LOG_HEX_LIMIT (-1) #define LOG_HEX_LIMIT (-1)
@ -55,9 +56,9 @@ typedef struct {
uint32_t log_level; uint32_t log_level;
bool single_tx; bool single_tx;
bool write_pcap; bool write_pcap;
float opp_sdu_ratio; uint32_t avg_opp_size;
bool random_opp;
bool zero_seed; bool zero_seed;
bool pedantic;
} stress_test_args_t; } stress_test_args_t;
void parse_args(stress_test_args_t *args, int argc, char *argv[]) { void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
@ -75,16 +76,16 @@ void parse_args(stress_test_args_t *args, int argc, char *argv[]) {
("mode", bpo::value<std::string>(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)") ("mode", bpo::value<std::string>(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM)")
("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(5), "Duration (sec)") ("duration", bpo::value<uint32_t>(&args->test_duration_sec)->default_value(5), "Duration (sec)")
("sdu_size", bpo::value<uint32_t>(&args->sdu_size)->default_value(1500), "Size of SDUs") ("sdu_size", bpo::value<uint32_t>(&args->sdu_size)->default_value(1500), "Size of SDUs")
("avg_opp_size", bpo::value<uint32_t>(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)")
("random_opp", bpo::value<bool>(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities")
("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") ("sdu_gen_delay", bpo::value<uint32_t>(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)")
("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") ("pdu_tx_delay", bpo::value<uint32_t>(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)")
("error_rate", bpo::value<float>(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") ("error_rate", bpo::value<float>(&args->error_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped")
("opp_sdu_ratio", bpo::value<float>(&args->opp_sdu_ratio)->default_value(0.0), "Ratio between MAC opportunity and SDU size (0==random)")
("reestablish", bpo::value<bool>(&args->reestablish)->default_value(false), "Mimic RLC reestablish during execution") ("reestablish", bpo::value<bool>(&args->reestablish)->default_value(false), "Mimic RLC reestablish during execution")
("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)") ("loglevel", bpo::value<uint32_t>(&args->log_level)->default_value(srslte::LOG_LEVEL_DEBUG), "Log level (1=Error,2=Warning,3=Info,4=Debug)")
("singletx", bpo::value<bool>(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") ("singletx", bpo::value<bool>(&args->single_tx)->default_value(false), "If set to true, only one node is generating data")
("pcap", bpo::value<bool>(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") ("pcap", bpo::value<bool>(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file")
("zeroseed", bpo::value<bool>(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") ("zeroseed", bpo::value<bool>(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero");
("pedantic", bpo::value<bool>(&args->pedantic)->default_value(true), "Whether to perform strict SDU size checking at receiver");
// these options are allowed on the command line // these options are allowed on the command line
bpo::options_description cmdline_options; bpo::options_description cmdline_options;
@ -156,8 +157,11 @@ private:
exit(-1); exit(-1);
} }
float r = args.opp_sdu_ratio ? args.opp_sdu_ratio : static_cast<float>(rand())/RAND_MAX; float factor = 1.0;
int opp_size = r*args.sdu_size; if (args.random_opp) {
factor = 0.5 + static_cast<float>(rand())/RAND_MAX;
}
int opp_size = args.avg_opp_size * factor;
uint32_t buf_state = tx_rlc->get_buffer_state(lcid); uint32_t buf_state = tx_rlc->get_buffer_state(lcid);
if (buf_state > 0) { if (buf_state > 0) {
int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size);
@ -235,10 +239,7 @@ public:
assert(rx_lcid == lcid); assert(rx_lcid == lcid);
if (sdu->N_bytes != args.sdu_size) { if (sdu->N_bytes != args.sdu_size) {
log.error_hex(sdu->msg, sdu->N_bytes, "Received SDU with size %d, expected %d.\n", sdu->N_bytes, args.sdu_size); log.error_hex(sdu->msg, sdu->N_bytes, "Received SDU with size %d, expected %d.\n", sdu->N_bytes, args.sdu_size);
// exit if in pedantic mode or SDU is not a multiple of the expected size exit(-1);
if (args.pedantic || sdu->N_bytes % args.sdu_size != 0) {
exit(-1);
}
} }
byte_buffer_pool::get_instance()->deallocate(sdu); byte_buffer_pool::get_instance()->deallocate(sdu);
@ -407,7 +408,10 @@ void stress_test(stress_test_args_t args)
} }
int main(int argc, char **argv) { int main(int argc, char **argv)
{
srslte_debug_handle_crash(argc, argv);
stress_test_args_t args = {}; stress_test_args_t args = {};
parse_args(&args, argc, argv); parse_args(&args, argc, argv);

@ -150,6 +150,9 @@ nof_ctrl_symbols = 3
# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. # link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered.
# a link failure is when SNR<0 and CRC=KO # a link failure is when SNR<0 and CRC=KO
# max_prach_offset_us: Maximum allowed RACH offset (in us) # max_prach_offset_us: Maximum allowed RACH offset (in us)
# enable_mbsfn: Enable MBMS transmission in the eNB
# m1u_multiaddr: Multicast addres the M1-U socket will register to
# m1u_if_addr: Address of the inteferface the M1-U interface will listen for multicast packets.
# #
##################################################################### #####################################################################
[expert] [expert]
@ -162,21 +165,5 @@ nof_ctrl_symbols = 3
#rrc_inactivity_timer = 60000 #rrc_inactivity_timer = 60000
#max_prach_offset_us = 30 #max_prach_offset_us = 30
#enable_mbsfn = false #enable_mbsfn = false
#m1u_multiaddr = 239.255.0.1
##################################################################### #m1u_if_addr = 127.0.1.201
# Manual RF calibration
#
# Applies DC offset and IQ imbalance to TX and RX modules.
# Currently this configuration is only used if the detected device is a bladeRF
#
# tx_corr_dc_gain: TX DC offset gain correction
# tx_corr_dc_phase: TX DC offset phase correction
# tx_corr_iq_i: TX IQ imbalance inphase correction
# tx_corr_iq_q: TX IQ imbalance quadrature correction
# same can be configured for rx_*
#####################################################################
[rf_calibration]
tx_corr_dc_gain = 20
tx_corr_dc_phase = 184
tx_corr_iq_i = 19
tx_corr_iq_q = 97

@ -128,13 +128,14 @@ typedef struct {
float metrics_period_secs; float metrics_period_secs;
bool enable_mbsfn; bool enable_mbsfn;
bool print_buffer_state; bool print_buffer_state;
std::string m1u_multiaddr;
std::string m1u_if_addr;
}expert_args_t; }expert_args_t;
typedef struct { typedef struct {
enb_args_t enb; enb_args_t enb;
enb_files_t enb_files; enb_files_t enb_files;
rf_args_t rf; rf_args_t rf;
rf_cal_t rf_cal;
pcap_args_t pcap; pcap_args_t pcap;
log_args_t log; log_args_t log;
gui_args_t gui; gui_args_t gui;
@ -142,7 +143,7 @@ typedef struct {
}all_args_t; }all_args_t;
/******************************************************************************* /*******************************************************************************
Main UE class Main eNB class
*******************************************************************************/ *******************************************************************************/
class enb class enb
@ -223,6 +224,10 @@ private:
int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg); int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg);
bool sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num); bool sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num);
int parse_cell_cfg(all_args_t *args, srslte_cell_t *cell); int parse_cell_cfg(all_args_t *args, srslte_cell_t *cell);
std::string get_build_mode();
std::string get_build_info();
std::string get_build_string();
}; };
} // namespace srsenb } // namespace srsenb

@ -153,6 +153,7 @@ private:
rrc_interface_mac *rrc; rrc_interface_mac *rrc;
pthread_rwlock_t rwlock; pthread_rwlock_t rwlock;
pthread_mutex_t sched_mutex;
cell_cfg_t cfg; cell_cfg_t cfg;
sched_args_t sched_cfg; sched_args_t sched_cfg;

@ -34,6 +34,7 @@ namespace srsenb {
class dl_metric_rr : public sched::metric_dl class dl_metric_rr : public sched::metric_dl
{ {
public: public:
//interface
void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t start_rbg, uint32_t nof_rbg, uint32_t nof_ctrl_symbols, uint32_t tti); void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t start_rbg, uint32_t nof_rbg, uint32_t nof_ctrl_symbols, uint32_t tti);
dl_harq_proc* get_user_allocation(sched_ue *user); dl_harq_proc* get_user_allocation(sched_ue *user);
private: private:
@ -62,6 +63,7 @@ private:
class ul_metric_rr : public sched::metric_ul class ul_metric_rr : public sched::metric_ul
{ {
public: public:
// interface
void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t nof_rb, uint32_t tti); void new_tti(std::map<uint16_t,sched_ue> &ue_db, uint32_t nof_rb, uint32_t tti);
ul_harq_proc* get_user_allocation(sched_ue *user); ul_harq_proc* get_user_allocation(sched_ue *user);
bool update_allocation(ul_harq_proc::ul_alloc_t alloc); bool update_allocation(ul_harq_proc::ul_alloc_t alloc);

@ -73,7 +73,6 @@ private:
cf_t samples[sf_buffer_sz]; cf_t samples[sf_buffer_sz];
uint32_t nof_samples; uint32_t nof_samples;
uint32_t tti; uint32_t tti;
char debug_name[SRSLTE_BUFFER_POOL_LOG_NAME_LEN];
}; };
srslte::buffer_pool<sf_buffer> buffer_pool; srslte::buffer_pool<sf_buffer> buffer_pool;
srslte::block_queue<sf_buffer*> pending_buffers; srslte::block_queue<sf_buffer*> pending_buffers;

@ -40,24 +40,6 @@
namespace srsenb { namespace srsenb {
/****************************************************************************
* 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
class gtpu class gtpu
:public gtpu_interface_rrc :public gtpu_interface_rrc
,public gtpu_interface_pdcp ,public gtpu_interface_pdcp
@ -67,7 +49,7 @@ public:
gtpu(); gtpu();
bool init(std::string gtp_bind_addr_, std::string mme_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_, bool enable_mbsfn = false); bool init(std::string gtp_bind_addr_, std::string mme_addr_, std::string m1u_multiaddr_, std::string m1u_if_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_, bool enable_mbsfn = false);
void stop(); void stop();
// gtpu_interface_rrc // gtpu_interface_rrc
@ -95,7 +77,7 @@ private:
class mch_thread : public thread { class mch_thread : public thread {
public: public:
mch_thread() : initiated(false),running(false),run_enable(false),pool(NULL) {} mch_thread() : initiated(false),running(false),run_enable(false),pool(NULL) {}
bool init(pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_); bool init(std::string m1u_multiaddr_, std::string m1u_if_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_);
void stop(); void stop();
private: private:
void run_thread(); void run_thread();
@ -110,6 +92,8 @@ private:
srslte::log *gtpu_log; srslte::log *gtpu_log;
int m1u_sd; int m1u_sd;
int lcid_counter; int lcid_counter;
std::string m1u_multiaddr;
std::string m1u_if_addr;
srslte::byte_buffer_pool *pool; srslte::byte_buffer_pool *pool;
}; };
@ -124,12 +108,11 @@ private:
}bearer_map; }bearer_map;
std::map<uint16_t, bearer_map> rnti_bearers; std::map<uint16_t, bearer_map> rnti_bearers;
// Socket file descriptors // Socket file descriptor
int snk_fd; int fd;
int src_fd;
//Threading
void run_thread(); void run_thread();
void echo_response(in_addr_t addr, in_port_t port, uint16_t seq);
pthread_mutex_t mutex; pthread_mutex_t mutex;

@ -198,6 +198,7 @@ public:
void send_connection_setup(bool is_setup = true); void send_connection_setup(bool is_setup = true);
void send_connection_reest(); void send_connection_reest();
void send_connection_reject();
void send_connection_release(); void send_connection_release();
void send_connection_reest_rej(); void send_connection_reest_rej();
void send_connection_reconf(srslte::byte_buffer_t *sdu); void send_connection_reconf(srslte::byte_buffer_t *sdu);
@ -296,6 +297,7 @@ public:
int cqi_sched_sf_idx; int cqi_sched_sf_idx;
int cqi_sched_prb_idx; int cqi_sched_prb_idx;
int get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drbid); int get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drbid);
bool nas_pending;
srslte::byte_buffer_t erab_info; srslte::byte_buffer_t erab_info;
}; };
@ -363,6 +365,7 @@ private:
uint32_t nof_users[100][80]; uint32_t nof_users[100][80];
} sr_sched_t; } sr_sched_t;
sr_sched_t sr_sched; sr_sched_t sr_sched;
sr_sched_t cqi_sched; sr_sched_t cqi_sched;
LIBLTE_RRC_MCCH_MSG_STRUCT mcch; LIBLTE_RRC_MCCH_MSG_STRUCT mcch;

@ -80,6 +80,7 @@ public:
bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio); bool user_release(uint16_t rnti, LIBLTE_S1AP_CAUSERADIONETWORK_ENUM cause_radio);
void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res); void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res);
void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res); void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res);
bool is_mme_connected();
//void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps); //void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps);
private: private:

@ -111,8 +111,6 @@ sib2 =
additional_spectrum_emission = 1; additional_spectrum_emission = 1;
}; };
mbsfnSubframeConfigList = mbsfnSubframeConfigList =
{ {
radioframeAllocationPeriod = "1"; radioframeAllocationPeriod = "1";

@ -26,6 +26,9 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include "srsenb/hdr/enb.h" #include "srsenb/hdr/enb.h"
#include "srslte/build_info.h"
#include <iostream>
#include <sstream>
namespace srsenb { namespace srsenb {
@ -54,6 +57,9 @@ void enb::cleanup(void)
} }
enb::enb() : started(false) { enb::enb() : started(false) {
// print build info
std::cout << std::endl << get_build_string() << std::endl;
srslte_dft_load(); srslte_dft_load();
pool = srslte::byte_buffer_pool::get_instance(ENB_POOL_SIZE); pool = srslte::byte_buffer_pool::get_instance(ENB_POOL_SIZE);
@ -79,6 +85,7 @@ bool enb::init(all_args_t *args_)
} else { } else {
logger_file.init(args->log.filename, args->log.file_max_size); logger_file.init(args->log.filename, args->log.file_max_size);
logger_file.log("\n\n"); logger_file.log("\n\n");
logger_file.log(get_build_string().c_str());
logger = &logger_file; logger = &logger_file;
} }
@ -160,8 +167,6 @@ bool enb::init(all_args_t *args_)
radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str())); radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str()));
} }
radio.set_manual_calibration(&args->rf_cal);
radio.set_rx_gain(args->rf.rx_gain); radio.set_rx_gain(args->rf.rx_gain);
radio.set_tx_gain(args->rf.tx_gain); radio.set_tx_gain(args->rf.tx_gain);
@ -212,15 +217,23 @@ bool enb::init(all_args_t *args_)
uint32_t prach_freq_offset = rrc_cfg.sibs[1].sib.sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset; uint32_t prach_freq_offset = rrc_cfg.sibs[1].sib.sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset;
if (prach_freq_offset + 6 > cell_cfg.nof_prb) { if(cell_cfg.nof_prb>10) {
fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", prach_freq_offset); if (prach_freq_offset + 6 > cell_cfg.nof_prb - SRSLTE_MAX(rrc_cfg.cqi_cfg.nof_prb, rrc_cfg.sr_cfg.nof_prb)) {
return false; fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", prach_freq_offset);
} return false;
}
if (prach_freq_offset < rrc_cfg.cqi_cfg.nof_prb || prach_freq_offset < rrc_cfg.sr_cfg.nof_prb ) { if (prach_freq_offset < SRSLTE_MAX(rrc_cfg.cqi_cfg.nof_prb, rrc_cfg.sr_cfg.nof_prb)) {
fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d lower than CQI offset: %d or SR offset: %d\n", fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d lower than CQI offset: %d or SR offset: %d\n",
prach_freq_offset, rrc_cfg.cqi_cfg.nof_prb, rrc_cfg.sr_cfg.nof_prb); prach_freq_offset, rrc_cfg.cqi_cfg.nof_prb, rrc_cfg.sr_cfg.nof_prb);
return false; return false;
}
} else { // 6 PRB case
if (prach_freq_offset+6 > cell_cfg.nof_prb) {
fprintf(stderr, "Invalid PRACH configuration: frequency interval=(%d, %d) does not fit into the eNB PRBs=(0,%d)\n",
prach_freq_offset, prach_freq_offset+6, cell_cfg.nof_prb);
return false;
}
} }
rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer; rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer;
@ -237,7 +250,7 @@ bool enb::init(all_args_t *args_)
pdcp.init(&rlc, &rrc, &gtpu, &pdcp_log); pdcp.init(&rlc, &rrc, &gtpu, &pdcp_log);
rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, &gtpu, &rrc_log); rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, &gtpu, &rrc_log);
s1ap.init(args->enb.s1ap, &rrc, &s1ap_log); s1ap.init(args->enb.s1ap, &rrc, &s1ap_log);
gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, &pdcp, &gtpu_log, args->expert.enable_mbsfn); gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, args->expert.m1u_multiaddr, args->expert.m1u_if_addr, &pdcp, &gtpu_log, args->expert.enable_mbsfn);
started = true; started = true;
return true; return true;
@ -342,4 +355,24 @@ srslte::LOG_LEVEL_ENUM enb::level(std::string l)
} }
} }
std::string enb::get_build_mode()
{
return std::string(srslte_get_build_mode());
}
std::string enb::get_build_info()
{
if (std::string(srslte_get_build_info()) == "") {
return std::string(srslte_get_version());
}
return std::string(srslte_get_build_info());
}
std::string enb::get_build_string()
{
std::stringstream ss;
ss << "Built in " << get_build_mode() << " mode using " << get_build_info() << "." << std::endl;
return ss.str();
}
} // namespace srsenb } // namespace srsenb

@ -905,7 +905,7 @@ int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_
} }
// Update MBSFN list counter. Only 1 supported // Update MBSFN list counter. Only 1 supported
if (mbsfn_section_present) { if (mbsfn_section_present && args->expert.enable_mbsfn) {
sib2->mbsfn_subfr_cnfg_list_size = 1; sib2->mbsfn_subfr_cnfg_list_size = 1;
} }

@ -64,6 +64,7 @@ sched::sched() : bc_aggr_level(0), rar_aggr_level(0), avail_rbg(0), P(0), start_
reset(); reset();
pthread_rwlock_init(&rwlock, NULL); pthread_rwlock_init(&rwlock, NULL);
pthread_mutex_init(&sched_mutex, NULL);
} }
sched::~sched() sched::~sched()
@ -72,6 +73,7 @@ sched::~sched()
pthread_rwlock_wrlock(&rwlock); pthread_rwlock_wrlock(&rwlock);
pthread_rwlock_unlock(&rwlock); pthread_rwlock_unlock(&rwlock);
pthread_rwlock_destroy(&rwlock); pthread_rwlock_destroy(&rwlock);
pthread_mutex_destroy(&sched_mutex);
} }
void sched::init(rrc_interface_mac *rrc_, srslte::log* log) void sched::init(rrc_interface_mac *rrc_, srslte::log* log)
@ -677,6 +679,11 @@ int sched::dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST])
// Schedules data to users // Schedules data to users
int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]) int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST])
{ {
// NOTE: In case of 6 PRBs, do not transmit if there is going to be a PRACH in the UL to avoid collisions
if (cfg.cell.nof_prb<10 and srslte_prach_tti_opportunity_config(cfg.prach_config, current_tti+4, -1)) {
start_rbg = avail_rbg;
}
uint32_t nof_ctrl_symbols = (cfg.cell.nof_prb<10)?(current_cfi+1):current_cfi; uint32_t nof_ctrl_symbols = (cfg.cell.nof_prb<10)?(current_cfi+1):current_cfi;
dl_metric->new_tti(ue_db, start_rbg, avail_rbg, nof_ctrl_symbols, current_tti); dl_metric->new_tti(ue_db, start_rbg, avail_rbg, nof_ctrl_symbols, current_tti);
@ -768,6 +775,7 @@ int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result)
rar_aggr_level = 2; rar_aggr_level = 2;
bzero(sched_result, sizeof(sched_interface::dl_sched_res_t)); bzero(sched_result, sizeof(sched_interface::dl_sched_res_t));
pthread_mutex_lock(&sched_mutex);
pthread_rwlock_rdlock(&rwlock); pthread_rwlock_rdlock(&rwlock);
/* Schedule Broadcast data */ /* Schedule Broadcast data */
@ -780,6 +788,7 @@ int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result)
sched_result->nof_data_elems += dl_sched_data(sched_result->data); sched_result->nof_data_elems += dl_sched_data(sched_result->data);
pthread_rwlock_unlock(&rwlock); pthread_rwlock_unlock(&rwlock);
pthread_mutex_unlock(&sched_mutex);
/* Set CFI */ /* Set CFI */
sched_result->cfi = current_cfi; sched_result->cfi = current_cfi;
@ -796,7 +805,7 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
return 0; return 0;
} }
if (cfg.prach_freq_offset + 6 > cfg.cell.nof_prb) { if (cfg.prach_freq_offset + 6 > cfg.cell.nof_prb and cfg.cell.nof_prb>10) {
fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", cfg.prach_freq_offset); fprintf(stderr, "Invalid PRACH configuration: frequency offset=%d outside bandwidth limits\n", cfg.prach_freq_offset);
return -1; return -1;
} }
@ -817,12 +826,13 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
int nof_dci_elems = 0; int nof_dci_elems = 0;
int nof_phich_elems = 0; int nof_phich_elems = 0;
pthread_mutex_lock(&sched_mutex);
pthread_rwlock_rdlock(&rwlock);
// current_cfi is set in dl_sched() // current_cfi is set in dl_sched()
bzero(sched_result, sizeof(sched_interface::ul_sched_res_t)); bzero(sched_result, sizeof(sched_interface::ul_sched_res_t));
ul_metric->reset_allocation(cfg.cell.nof_prb); ul_metric->reset_allocation(cfg.cell.nof_prb);
pthread_rwlock_rdlock(&rwlock);
// Get HARQ process for this TTI // Get HARQ process for this TTI
for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
sched_ue *user = (sched_ue*) &iter->second; sched_ue *user = (sched_ue*) &iter->second;
@ -840,7 +850,19 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
} }
} }
// reserve PRBs for PRACH
if(srslte_prach_tti_opportunity_config(cfg.prach_config, tti, -1)) {
ul_harq_proc::ul_alloc_t prach = {cfg.prach_freq_offset, 6};
if(!ul_metric->update_allocation(prach)) {
log_h->warning("SCHED: Failed to allocate PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L);
}
else {
log_h->debug("SCHED: Allocated PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L);
}
}
// Update available allocation if there's a pending RAR // Update available allocation if there's a pending RAR
// NOTE: It has priority over PUCCH.
if (pending_msg3[tti%10].enabled) { if (pending_msg3[tti%10].enabled) {
ul_harq_proc::ul_alloc_t msg3 = {pending_msg3[tti%10].n_prb, pending_msg3[tti%10].L}; ul_harq_proc::ul_alloc_t msg3 = {pending_msg3[tti%10].n_prb, pending_msg3[tti%10].L};
if(ul_metric->update_allocation(msg3)) { if(ul_metric->update_allocation(msg3)) {
@ -854,15 +876,13 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
// Allocate PUCCH resources // Allocate PUCCH resources
if (cfg.nrb_pucch >= 0) { if (cfg.nrb_pucch >= 0) {
ul_harq_proc::ul_alloc_t pucch = {0, (uint32_t) cfg.nrb_pucch}; ul_harq_proc::ul_alloc_t pucch = {0, (uint32_t) cfg.nrb_pucch};
if(!ul_metric->update_allocation(pucch)) { if (!ul_metric->update_allocation(pucch) and cfg.cell.nof_prb != 6) {
log_h->warning("SCHED: Failed to allocate PUCCH\n"); log_h->warning("SCHED: There was a collision with the PUCCH (%d, %d)\n", pucch.RB_start, pucch.RB_start+pucch.L);
} }
pucch.RB_start = cfg.cell.nof_prb-cfg.nrb_pucch; pucch.RB_start = cfg.cell.nof_prb-cfg.nrb_pucch;
pucch.L = (uint32_t) cfg.nrb_pucch; pucch.L = (uint32_t) cfg.nrb_pucch;
if(!ul_metric->update_allocation(pucch)) { if (!ul_metric->update_allocation(pucch) and cfg.cell.nof_prb != 6) {
log_h->warning("SCHED: Failed to allocate PUCCH\n"); log_h->warning("SCHED: There was a collision with the PUCCH (%d, %d)\n", pucch.RB_start, pucch.RB_start+pucch.L);
} else {
log_h->debug("Allocating PUCCH (%d,%d)\n", pucch.RB_start, pucch.RB_start+pucch.L);
} }
} else { } else {
for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { for(it_t iter=ue_db.begin(); iter!=ue_db.end(); ++iter) {
@ -880,17 +900,6 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
} }
} }
// reserve PRBs for PRACH
if(srslte_prach_tti_opportunity_config(cfg.prach_config, tti, -1)) {
ul_harq_proc::ul_alloc_t prach = {cfg.prach_freq_offset, 6};
if(!ul_metric->update_allocation(prach)) {
log_h->warning("SCHED: Failed to allocate PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L);
}
else {
log_h->debug("SCHED: Allocated PRACH RBs within (%d,%d)\n", prach.RB_start, prach.RB_start + prach.L);
}
}
ul_metric->new_tti(ue_db, cfg.cell.nof_prb, current_tti); ul_metric->new_tti(ue_db, cfg.cell.nof_prb, current_tti);
// Now allocate PUSCH // Now allocate PUSCH
@ -994,6 +1003,7 @@ int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched
} }
pthread_rwlock_unlock(&rwlock); pthread_rwlock_unlock(&rwlock);
pthread_mutex_unlock(&sched_mutex);
sched_result->nof_dci_elems = nof_dci_elems; sched_result->nof_dci_elems = nof_dci_elems;
sched_result->nof_phich_elems = nof_phich_elems; sched_result->nof_phich_elems = nof_phich_elems;

@ -301,13 +301,14 @@ bool ul_metric_rr::new_allocation(uint32_t L, ul_harq_proc::ul_alloc_t* alloc)
bool ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc) bool ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc)
{ {
bool ret = false;
if(allocation_is_valid(alloc)) { if(allocation_is_valid(alloc)) {
for (uint32_t n=alloc.RB_start;n<alloc.RB_start+alloc.L;n++) { ret = true;
used_rb[n] = true; }
} for (uint32_t n=alloc.RB_start;n<alloc.RB_start+alloc.L;n++) {
return true; used_rb[n] = true;
} }
return false; return ret;
} }
ul_harq_proc* ul_metric_rr::allocate_user_retx_prbs(sched_ue *user) ul_harq_proc* ul_metric_rr::allocate_user_retx_prbs(sched_ue *user)

@ -102,6 +102,7 @@ void sched_ue::set_cfg(uint16_t rnti_, sched_interface::ue_cfg_t *cfg_, sched_in
sched::generate_cce_location(regs, &dci_locations[cfi][sf_idx], cfi+1, sf_idx, rnti); sched::generate_cce_location(regs, &dci_locations[cfi][sf_idx], cfi+1, sf_idx, rnti);
} }
} }
pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex);
for (int i=0;i<sched_interface::MAX_LC;i++) { for (int i=0;i<sched_interface::MAX_LC;i++) {
@ -498,7 +499,10 @@ int sched_ue::generate_format1(dl_harq_proc *h,
srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb); srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb);
uint32_t nof_ctrl_symbols = cfi+(cell.nof_prb<10?1:0); uint32_t nof_ctrl_symbols = cfi+(cell.nof_prb<10?1:0);
uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols); uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols);
if (fixed_mcs_dl < 0) { if(need_conres_ce and cell.nof_prb<10) { // SRB0 Tx. Use a higher MCS for the PRACH to fit in 6 PRBs
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(4), nof_prb)/8;
mcs = 4;
} else if (fixed_mcs_dl < 0) {
tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs); tbs = alloc_tbs_dl(nof_prb, nof_re, req_bytes, &mcs);
} else { } else {
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb)/8; tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb)/8;
@ -1206,7 +1210,6 @@ int sched_ue::alloc_tbs(uint32_t nof_prb,
uint32_t max_Qm = is_ul?4:6; // Allow 16-QAM in PUSCH Only uint32_t max_Qm = is_ul?4:6; // Allow 16-QAM in PUSCH Only
// TODO: Compute real spectral efficiency based on PUSCH-UCI configuration // TODO: Compute real spectral efficiency based on PUSCH-UCI configuration
int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, max_Qm, &sel_mcs)/8; int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, max_Qm, &sel_mcs)/8;
/* If less bytes are requested, lower the MCS */ /* If less bytes are requested, lower the MCS */
@ -1221,6 +1224,7 @@ int sched_ue::alloc_tbs(uint32_t nof_prb,
// Avoid the unusual case n_prb=1, mcs=6 tbs=328 (used in voip) // Avoid the unusual case n_prb=1, mcs=6 tbs=328 (used in voip)
if (nof_prb == 1 && sel_mcs == 6) { if (nof_prb == 1 && sel_mcs == 6) {
sel_mcs--; sel_mcs--;
tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(sel_mcs), nof_prb)/8;
} }
if (mcs && tbs >= 0) { if (mcs && tbs >= 0) {

@ -145,9 +145,7 @@ void parse_args(all_args_t *args, int argc, char* argv[]) {
bpo::value<int>(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3), bpo::value<int>(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3),
"Number of control symbols") "Number of control symbols")
/* Expert section */ /* Expert section */
("expert.metrics_period_secs", ("expert.metrics_period_secs",
bpo::value<float>(&args->expert.metrics_period_secs)->default_value(1.0), bpo::value<float>(&args->expert.metrics_period_secs)->default_value(1.0),
"Periodicity for metrics in seconds") "Periodicity for metrics in seconds")
@ -194,17 +192,19 @@ void parse_args(all_args_t *args, int argc, char* argv[]) {
("expert.enable_mbsfn", ("expert.enable_mbsfn",
bpo::value<bool>(&args->expert.enable_mbsfn)->default_value(false), bpo::value<bool>(&args->expert.enable_mbsfn)->default_value(false),
"enables mbms in the enodeb") "Enables MBMS in the eNB")
("expert.print_buffer_state", ("expert.print_buffer_state",
bpo::value<bool>(&args->expert.print_buffer_state)->default_value(false), bpo::value<bool>(&args->expert.print_buffer_state)->default_value(false),
"Prints on the console the buffer state every 10 seconds") "Prints on the console the buffer state every 10 seconds")
("rf_calibration.tx_corr_dc_gain", bpo::value<float>(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), "TX DC offset gain correction") ("expert.m1u_multiaddr",
("rf_calibration.tx_corr_dc_phase", bpo::value<float>(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), "TX DC offset phase correction") bpo::value<string>(&args->expert.m1u_multiaddr)->default_value("239.255.0.1"),
("rf_calibration.tx_corr_iq_i", bpo::value<float>(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), "TX IQ imbalance inphase correction") "M1-U Multicast address the eNB joins.")
("rf_calibration.tx_corr_iq_q", bpo::value<float>(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), "TX IQ imbalance quadrature correction")
("expert.m1u_if_addr",
bpo::value<string>(&args->expert.m1u_if_addr)->default_value("127.0.1.201"),
"IP address of the interface the eNB will listen for M1-U traffic.")
; ;
// Positional options - config file location // Positional options - config file location

@ -43,44 +43,27 @@ gtpu::gtpu():mchthread()
} }
bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_interface_gtpu* pdcp_, srslte::log* gtpu_log_, bool enable_mbsfn) bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, std::string m1u_multiaddr_, std::string m1u_if_addr_, srsenb::pdcp_interface_gtpu* pdcp_, srslte::log* gtpu_log_, bool enable_mbsfn)
{ {
pdcp = pdcp_; pdcp = pdcp_;
gtpu_log = gtpu_log_; gtpu_log = gtpu_log_;
gtp_bind_addr = gtp_bind_addr_; gtp_bind_addr = gtp_bind_addr_;
mme_addr = mme_addr_; mme_addr = mme_addr_;
pool = byte_buffer_pool::get_instance(); pool = byte_buffer_pool::get_instance();
// Set up sink socket // Set up socket
snk_fd = socket(AF_INET, SOCK_DGRAM, 0); fd = socket(AF_INET, SOCK_DGRAM, 0);
if (snk_fd < 0) { if (fd < 0) {
gtpu_log->error("Failed to create sink socket\n"); gtpu_log->error("Failed to create socket\n");
return false; return false;
} }
int enable = 1; int enable = 1;
#if defined (SO_REUSEADDR) #if defined (SO_REUSEADDR)
if (setsockopt(snk_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
gtpu_log->error("setsockopt(SO_REUSEADDR) failed\n");
#endif
#if defined (SO_REUSEPORT)
if (setsockopt(snk_fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0)
gtpu_log->error("setsockopt(SO_REUSEPORT) failed\n");
#endif
// Set up source socket
src_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (src_fd < 0) {
gtpu_log->error("Failed to create source socket\n");
return false;
}
#if defined (SO_REUSEADDR)
if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
gtpu_log->error("setsockopt(SO_REUSEADDR) failed\n"); gtpu_log->error("setsockopt(SO_REUSEADDR) failed\n");
#endif #endif
#if defined (SO_REUSEPORT) #if defined (SO_REUSEPORT)
if (setsockopt(src_fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0)
gtpu_log->error("setsockopt(SO_REUSEPORT) failed\n"); gtpu_log->error("setsockopt(SO_REUSEPORT) failed\n");
#endif #endif
@ -90,7 +73,7 @@ bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_
bindaddr.sin_addr.s_addr = inet_addr(gtp_bind_addr.c_str()); bindaddr.sin_addr.s_addr = inet_addr(gtp_bind_addr.c_str());
bindaddr.sin_port = htons(GTPU_PORT); bindaddr.sin_port = htons(GTPU_PORT);
if (bind(src_fd, (struct sockaddr *)&bindaddr, sizeof(struct sockaddr_in))) { if (bind(fd, (struct sockaddr *)&bindaddr, sizeof(struct sockaddr_in))) {
gtpu_log->error("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT); gtpu_log->error("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT);
gtpu_log->console("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT); gtpu_log->console("Failed to bind on address %s, port %d\n", gtp_bind_addr.c_str(), GTPU_PORT);
return false; return false;
@ -102,15 +85,14 @@ bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_
// Start MCH thread if enabled // Start MCH thread if enabled
this->enable_mbsfn = enable_mbsfn; this->enable_mbsfn = enable_mbsfn;
if(enable_mbsfn) { if(enable_mbsfn) {
mchthread.init(pdcp, gtpu_log); mchthread.init(m1u_multiaddr_, m1u_if_addr_, pdcp, gtpu_log);
} }
return true; return true;
} }
void gtpu::stop() void gtpu::stop()
{ {
if(enable_mbsfn){
if(enable_mbsfn){
mchthread.stop(); mchthread.stop();
} }
@ -128,11 +110,8 @@ void gtpu::stop()
wait_thread_finish(); wait_thread_finish();
} }
if (snk_fd) { if (fd) {
close(snk_fd); close(fd);
}
if (src_fd) {
close(src_fd);
} }
} }
@ -141,8 +120,8 @@ void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu)
{ {
gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d, n_bytes=%d", rnti, lcid, pdu->N_bytes); gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d, n_bytes=%d", rnti, lcid, pdu->N_bytes);
gtpu_header_t header; gtpu_header_t header;
header.flags = 0x30; header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = 0xFF; header.message_type = GTPU_MSG_DATA_PDU;
header.length = pdu->N_bytes; header.length = pdu->N_bytes;
header.teid = rnti_bearers[rnti].teids_out[lcid]; header.teid = rnti_bearers[rnti].teids_out[lcid];
@ -151,8 +130,11 @@ void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu)
servaddr.sin_addr.s_addr = htonl(rnti_bearers[rnti].spgw_addrs[lcid]); servaddr.sin_addr.s_addr = htonl(rnti_bearers[rnti].spgw_addrs[lcid]);
servaddr.sin_port = htons(GTPU_PORT); servaddr.sin_port = htons(GTPU_PORT);
gtpu_write_header(&header, pdu, gtpu_log); if(!gtpu_write_header(&header, pdu, gtpu_log)){
if (sendto(snk_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))<0) { gtpu_log->error("Error writing GTP-U Header. Flags 0x%x, Message Type 0x%x\n", header.flags, header.message_type);
return;
}
if (sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))<0) {
perror("sendto"); perror("sendto");
} }
@ -223,6 +205,10 @@ void gtpu::run_thread()
} }
run_enable = true; run_enable = true;
sockaddr_in client;
socklen_t client_len = sizeof(client);
size_t buflen = SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET;
running=true; running=true;
while(run_enable) { while(run_enable) {
@ -230,51 +216,92 @@ void gtpu::run_thread()
gtpu_log->debug("Waiting for read...\n"); gtpu_log->debug("Waiting for read...\n");
int n = 0; int n = 0;
do{ do{
n = recv(src_fd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0); n = recvfrom(fd, pdu->msg, buflen, 0, (struct sockaddr *)&client, &client_len);
} while (n == -1 && errno == EAGAIN); } while (n == -1 && errno == EAGAIN);
if (n < 0) { if (n < 0) {
gtpu_log->error("Failed to read from socket\n"); gtpu_log->error("Failed to read from socket\n");
} }
gtpu_log->debug("Received %d bytes from S1-U interface\n", n);
pdu->N_bytes = (uint32_t) n; pdu->N_bytes = (uint32_t) n;
gtpu_header_t header; gtpu_header_t header;
gtpu_read_header(pdu, &header,gtpu_log); if(!gtpu_read_header(pdu, &header,gtpu_log)){
continue;
}
uint16_t rnti = 0; switch(header.message_type) {
uint16_t lcid = 0;
teidin_to_rntilcid(header.teid, &rnti, &lcid);
pthread_mutex_lock(&mutex); case GTPU_MSG_ECHO_REQUEST:
bool user_exists = (rnti_bearers.count(rnti) > 0); // Echo request - send response
pthread_mutex_unlock(&mutex); echo_response(client.sin_addr.s_addr, client.sin_port, header.seq_number);
break;
if(!user_exists) { case GTPU_MSG_DATA_PDU:
gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti);
continue;
}
if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { uint16_t rnti = 0;
gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); uint16_t lcid = 0;
continue; teidin_to_rntilcid(header.teid, &rnti, &lcid);
}
gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes); pthread_mutex_lock(&mutex);
bool user_exists = (rnti_bearers.count(rnti) > 0);
pthread_mutex_unlock(&mutex);
pdcp->write_sdu(rnti, lcid, pdu); if(!user_exists) {
gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti);
continue;
}
do { if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) {
pdu = pool_allocate; gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid);
if (!pdu) { continue;
gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); }
usleep(10000);
} gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d, n_bytes=%d", rnti, lcid, pdu->N_bytes);
} while(!pdu);
pdcp->write_sdu(rnti, lcid, pdu);
do {
pdu = pool_allocate;
if (!pdu) {
gtpu_log->console("GTPU Buffer pool empty. Trying again...\n");
usleep(10000);
}
} while(!pdu);
break;
}
} }
running = false; running = false;
} }
void gtpu::echo_response(in_addr_t addr, in_port_t port, uint16_t seq)
{
gtpu_log->info("TX GTPU Echo Response, Seq: %d\n", seq);
gtpu_header_t header;
srslte::byte_buffer_t *pdu = pool_allocate;
//header
header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL | GTPU_FLAGS_SEQUENCE;
header.message_type = GTPU_MSG_ECHO_RESPONSE;
header.teid = 0;
header.length = 4;
header.seq_number = seq;
header.n_pdu = 0;
header.next_ext_hdr_type = 0;
gtpu_write_header(&header,pdu,gtpu_log);
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = addr;
servaddr.sin_port = port;
sendto(fd, pdu->msg, 12, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in));
pool->deallocate(pdu);
}
/**************************************************************************** /****************************************************************************
* TEID to RNIT/LCID helper functions * TEID to RNIT/LCID helper functions
***************************************************************************/ ***************************************************************************/
@ -293,11 +320,13 @@ void gtpu::rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin)
/**************************************************************************** /****************************************************************************
* Class to run the MCH thread * Class to run the MCH thread
***************************************************************************/ ***************************************************************************/
bool gtpu::mch_thread::init(pdcp_interface_gtpu *pdcp, srslte::log *gtpu_log) bool gtpu::mch_thread::init(std::string m1u_multiaddr_, std::string m1u_if_addr_, pdcp_interface_gtpu *pdcp, srslte::log *gtpu_log)
{ {
pool = byte_buffer_pool::get_instance(); pool = byte_buffer_pool::get_instance();
this->pdcp = pdcp; this->pdcp = pdcp;
this->gtpu_log = gtpu_log; this->gtpu_log = gtpu_log;
m1u_multiaddr = m1u_multiaddr_;
m1u_if_addr = m1u_if_addr_;
struct sockaddr_in bindaddr; struct sockaddr_in bindaddr;
@ -322,11 +351,12 @@ bool gtpu::mch_thread::init(pdcp_interface_gtpu *pdcp, srslte::log *gtpu_log)
/* Send an ADD MEMBERSHIP message via setsockopt */ /* Send an ADD MEMBERSHIP message via setsockopt */
struct ip_mreq mreq; struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = inet_addr("239.255.0.1"); //Multicast address of the service mreq.imr_multiaddr.s_addr = inet_addr(m1u_multiaddr.c_str()); //Multicast address of the service
mreq.imr_interface.s_addr = inet_addr("127.0.1.200"); //Address of the IF the socket will listen to. mreq.imr_interface.s_addr = inet_addr(m1u_if_addr.c_str()); //Address of the IF the socket will listen to.
if (setsockopt(m1u_sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, if (setsockopt(m1u_sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
&mreq, sizeof(mreq)) < 0) { &mreq, sizeof(mreq)) < 0) {
gtpu_log->error("Register musticast group for M1-U\n"); gtpu_log->error("Register musticast group for M1-U\n");
gtpu_log->error("M1-U infterface IP: %s, M1-U Multicast Address %s\n", m1u_if_addr.c_str(),m1u_multiaddr.c_str());
return false; return false;
} }
gtpu_log->info("M1-U initialized\n"); gtpu_log->info("M1-U initialized\n");
@ -372,12 +402,12 @@ void gtpu::mch_thread::run_thread()
do{ do{
n = recvfrom(m1u_sd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0, (struct sockaddr *) &src_addr, &addrlen); n = recvfrom(m1u_sd, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET, 0, (struct sockaddr *) &src_addr, &addrlen);
} while (n == -1 && errno == EAGAIN); } while (n == -1 && errno == EAGAIN);
gtpu_log->debug("Received %d bytes from M1-U interface\n", n);
pdu->N_bytes = (uint32_t) n; pdu->N_bytes = (uint32_t) n;
gtpu_header_t header; gtpu_header_t header;
gtpu_read_header(pdu, &header, gtpu_log); gtpu_read_header(pdu, &header, gtpu_log);
pdcp->write_sdu(SRSLTE_MRNTI, lcid, pdu); pdcp->write_sdu(SRSLTE_MRNTI, lcid, pdu);
do { do {
pdu = pool_allocate; pdu = pool_allocate;

@ -885,24 +885,25 @@ void rrc::activity_monitor::run_thread()
*******************************************************************************/ *******************************************************************************/
rrc::ue::ue() rrc::ue::ue()
{ {
parent = NULL; parent = NULL;
set_activity(); set_activity();
has_tmsi = false; has_tmsi = false;
connect_notified = false; connect_notified = false;
transaction_id = 0; transaction_id = 0;
sr_allocated = false; sr_allocated = false;
sr_sched_sf_idx = 0; sr_sched_sf_idx = 0;
sr_sched_prb_idx = 0; sr_sched_prb_idx = 0;
sr_N_pucch = 0; sr_N_pucch = 0;
sr_I = 0; sr_I = 0;
cqi_allocated = false; cqi_allocated = false;
cqi_pucch = 0; cqi_pucch = 0;
cqi_idx = 0; cqi_idx = 0;
cqi_sched_sf_idx = 0; cqi_sched_sf_idx = 0;
cqi_sched_prb_idx = 0; cqi_sched_prb_idx = 0;
rlf_cnt = 0; rlf_cnt = 0;
state = RRC_STATE_IDLE; nas_pending = false;
pool = srslte::byte_buffer_pool::get_instance(); state = RRC_STATE_IDLE;
pool = srslte::byte_buffer_pool::get_instance();
} }
rrc_state_t rrc::ue::get_state() rrc_state_t rrc::ue::get_state()
@ -1043,6 +1044,12 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, byte_buffer_t *pdu)
void rrc::ue::handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg) void rrc::ue::handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg)
{ {
if (not parent->s1ap->is_mme_connected()) {
printf("send reject\n");
parent->rrc_log->error("MME isn't connected. Sending Connection Reject\n");
send_connection_reject();
}
set_activity(); set_activity();
if(msg->ue_id_type == LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI) { if(msg->ue_id_type == LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI) {
@ -1227,9 +1234,12 @@ void rrc::ue::setup_erab(uint8_t id, LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT
parent->gtpu->add_bearer(rnti, lcid, addr_, erabs[id].teid_out, &(erabs[id].teid_in)); parent->gtpu->add_bearer(rnti, lcid, addr_, erabs[id].teid_out, &(erabs[id].teid_in));
if(nas_pdu) { if(nas_pdu) {
nas_pending = true;
memcpy(erab_info.buffer, nas_pdu->buffer, nas_pdu->n_octets); memcpy(erab_info.buffer, nas_pdu->buffer, nas_pdu->n_octets);
erab_info.N_bytes = nas_pdu->n_octets; erab_info.N_bytes = nas_pdu->n_octets;
parent->rrc_log->info_hex(erab_info.buffer, erab_info.N_bytes, "setup_erab nas_pdu -> erab_info rnti 0x%x", rnti); parent->rrc_log->info_hex(erab_info.buffer, erab_info.N_bytes, "setup_erab nas_pdu -> erab_info rnti 0x%x", rnti);
} else {
nas_pending = false;
} }
} }
@ -1299,7 +1309,17 @@ void rrc::ue::send_connection_reest_rej()
dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ; dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ;
send_dl_ccch(&dl_ccch_msg); send_dl_ccch(&dl_ccch_msg);
}
void rrc::ue::send_connection_reject()
{
LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg;
bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT));
dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ;
dl_ccch_msg.msg.rrc_con_rej.wait_time = 10;
send_dl_ccch(&dl_ccch_msg);
} }
void rrc::ue::send_connection_setup(bool is_setup) void rrc::ue::send_connection_setup(bool is_setup)
@ -1667,12 +1687,17 @@ void rrc::ue::send_connection_reconf(srslte::byte_buffer_t *pdu)
// DRB1 has already been configured in GTPU through bearer setup // DRB1 has already been configured in GTPU through bearer setup
// Add NAS Attach accept // Add NAS Attach accept
conn_reconf->N_ded_info_nas = 1; if(nas_pending){
parent->rrc_log->debug("Adding NAS message to connection reconfiguration\n");
parent->rrc_log->info_hex(erab_info.buffer, erab_info.N_bytes, "connection_reconf erab_info -> nas_info rnti 0x%x\n", rnti); conn_reconf->N_ded_info_nas = 1;
conn_reconf->ded_info_nas_list[0].N_bytes = erab_info.N_bytes;
memcpy(conn_reconf->ded_info_nas_list[0].msg, erab_info.buffer, erab_info.N_bytes);
parent->rrc_log->info_hex(erab_info.buffer, erab_info.N_bytes, "connection_reconf erab_info -> nas_info rnti 0x%x\n", rnti);
conn_reconf->ded_info_nas_list[0].N_bytes = erab_info.N_bytes;
memcpy(conn_reconf->ded_info_nas_list[0].msg, erab_info.buffer, erab_info.N_bytes);
} else {
parent->rrc_log->debug("Not adding NAS message to connection reconfiguration\n");
conn_reconf->N_ded_info_nas = 0;
}
// Reuse same PDU // Reuse same PDU
pdu->reset(); pdu->reset();

@ -259,6 +259,11 @@ void s1ap::ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPR
//} //}
bool s1ap::is_mme_connected()
{
return mme_connected;
}
/******************************************************************************* /*******************************************************************************
/* S1AP connection helpers /* S1AP connection helpers
********************************************************************************/ ********************************************************************************/

@ -40,13 +40,16 @@ db_file = user_db.csv
##################################################################### #####################################################################
# SP-GW configuration # SP-GW configuration
# #
# gtpu_bind_addr: GTP-U bind adress. # gtpu_bind_addr: GTP-U bind address.
# sgi_if_addr: SGi TUN interface IP address.
# sgi_if_name: SGi TUN interface name.
# #
##################################################################### #####################################################################
[spgw] [spgw]
gtpu_bind_addr = 127.0.1.100 gtpu_bind_addr = 127.0.1.100
sgi_if_addr = 172.16.0.1 sgi_if_addr = 172.16.0.1
sgi_if_name = srs_spgw_sgi
#################################################################### ####################################################################
# PCAP configuration # PCAP configuration

@ -74,7 +74,7 @@ enum hss_auth_algo {
HSS_ALGO_MILENAGE HSS_ALGO_MILENAGE
}; };
class hss : public hss_interface_s1ap class hss : public hss_interface_nas
{ {
public: public:
static hss* get_instance(void); static hss* get_instance(void);
@ -82,10 +82,10 @@ public:
int init(hss_args_t *hss_args, srslte::log_filter* hss_log); int init(hss_args_t *hss_args, srslte::log_filter* hss_log);
void stop(void); void stop(void);
bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres); virtual bool gen_auth_info_answer(uint64_t imsi, uint8_t *k_asme, uint8_t *autn, uint8_t *rand, uint8_t *xres);
bool gen_update_loc_answer(uint64_t imsi, uint8_t* qci); virtual bool gen_update_loc_answer(uint64_t imsi, uint8_t* qci);
bool resync_sqn(uint64_t imsi, uint8_t *auts); virtual bool resync_sqn(uint64_t imsi, uint8_t *auts);
private: private:

@ -48,9 +48,12 @@ const uint16_t GTPU_RX_PORT = 2152;
typedef struct { typedef struct {
std::string name; std::string name;
std::string sgi_mb_if_name;
std::string sgi_mb_if_addr; std::string sgi_mb_if_addr;
std::string sgi_mb_if_mask; std::string sgi_mb_if_mask;
std::string m1u_multi_addr; std::string m1u_multi_addr;
std::string m1u_multi_if;
int m1u_multi_ttl;
} mbms_gw_args_t; } mbms_gw_args_t;
struct pseudo_hdr struct pseudo_hdr

@ -44,15 +44,6 @@
namespace srsepc{ namespace srsepc{
/*
typedef struct {
std::string s1ap_level;
std::string all_level;
int s1ap_hex_limit;
std::string filename;
}log_args_t;
*/
typedef struct{ typedef struct{
s1ap_args_t s1ap_args; s1ap_args_t s1ap_args;
//diameter_args_t diameter_args; //diameter_args_t diameter_args;
@ -66,7 +57,7 @@ class mme:
public: public:
static mme* get_instance(void); static mme* get_instance(void);
static void cleanup(void); static void cleanup(void);
int init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_s1ap * hss_); int init(mme_args_t* args, srslte::log_filter *nas_log, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_nas * hss);
void stop(); void stop();
int get_s1_mme(); int get_s1_mme();
void run_thread(); void run_thread();
@ -83,6 +74,7 @@ private:
srslte::byte_buffer_pool *m_pool; srslte::byte_buffer_pool *m_pool;
/*Logs*/ /*Logs*/
srslte::log_filter *m_nas_log;
srslte::log_filter *m_s1ap_log; srslte::log_filter *m_s1ap_log;
srslte::log_filter *m_mme_gtpc_log; srslte::log_filter *m_mme_gtpc_log;
}; };

@ -30,7 +30,8 @@
#include "srslte/common/log_filter.h" #include "srslte/common/log_filter.h"
#include "srslte/common/buffer_pool.h" #include "srslte/common/buffer_pool.h"
#include "srslte/asn1/gtpc.h" #include "srslte/asn1/gtpc.h"
#include "s1ap_common.h" #include "nas.h"
namespace srsepc namespace srsepc
{ {
@ -38,6 +39,7 @@ class spgw;
class s1ap; class s1ap;
class mme_gtpc class mme_gtpc
: public gtpc_interface_nas
{ {
public: public:
@ -51,12 +53,12 @@ public:
bool init(srslte::log_filter *mme_gtpc_log); bool init(srslte::log_filter *mme_gtpc_log);
uint32_t get_new_ctrl_teid(); uint32_t get_new_ctrl_teid();
void send_create_session_request(uint64_t imsi); virtual bool send_create_session_request(uint64_t imsi);
void handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu); bool handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu);
void send_modify_bearer_request(uint64_t imsi, erab_ctx_t *bearer_ctx); virtual bool send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify, srslte::gtp_fteid_t *enb_fteid);
void handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu); void handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu);
void send_release_access_bearers_request(uint64_t imsi); void send_release_access_bearers_request(uint64_t imsi);
void send_delete_session_request(uint64_t imsi); virtual bool send_delete_session_request(uint64_t imsi);
private: private:

@ -0,0 +1,294 @@
/*
* \section LICENSE
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSEPC_NAS_H
#define SRSEPC_NAS_H
#include <netinet/sctp.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 "srslte/common/buffer_pool.h"
#include "srslte/interfaces/epc_interfaces.h"
namespace srsepc{
static const uint8_t MAX_ERABS_PER_UE = 16;
// MME EMM states (3GPP 24.301 v10.0.0, section 5.1.3.4)
typedef enum {
EMM_STATE_DEREGISTERED = 0,
EMM_STATE_COMMON_PROCEDURE_INITIATED,
EMM_STATE_REGISTERED,
EMM_STATE_DEREGISTERED_INITIATED,
EMM_STATE_N_ITEMS,
} emm_state_t;
static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"DEREGISTERED",
"COMMON PROCEDURE INITIATED",
"REGISTERED",
"DEREGISTERED INITIATED"};
// MME ECM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ECM_STATE_IDLE = 0,
ECM_STATE_CONNECTED,
ECM_STATE_N_ITEMS,
} ecm_state_t;
static const char ecm_state_text[ECM_STATE_N_ITEMS][100] = {"IDLE",
"CONNECTED"};
/*
// MME ESM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ESM_BEARER_CONTEXT_INACTIVE = 0,
ESM_BEARER_CONTEXT_ACTIVE_PENDING,
ESM_BEARER_CONTEXT_ACTIVE,
ESM_BEARER_CONTEXT_INACTIVE_PENDING,
ESM_BEARER_CONTEXT_MODIFY_PENDING,
ESM_BEARER_PROCEDURE_TRANSACTION_INACTIVE,
ESM_BEARER_PROCEDURE_TRANSACTION_PENDING,
ESM_STATE_N_ITEMS,
} esm_state_t;
static const char esm_state_text[ESM_STATE_N_ITEMS][100] = {"CONTEXT INACTIVE",
"CONTEXT ACTIVE PENDING",
"CONTEXT ACTIVE",
"CONTEXT_INACTIVE_PENDING",
"CONTEXT_MODIFY_PENDING",
"PROCEDURE_TRANSACTION_INACTIVE"
"PROCEDURE_TRANSACTION_PENDING"};
*/
typedef enum
{
ERAB_DEACTIVATED,
ERAB_CTX_REQUESTED,
ERAB_CTX_SETUP,
ERAB_ACTIVE
} esm_state_t;
/*
* EMM, ECM, ESM and EPS Security context definitions
*/
typedef struct{
uint64_t imsi;
emm_state_t state;
uint8_t procedure_transaction_id;
uint8_t attach_type;
struct in_addr ue_ip;
srslte::gtpc_f_teid_ie sgw_ctrl_fteid;
} emm_ctx_t;
typedef struct{
ecm_state_t state;
uint32_t enb_ue_s1ap_id;
uint32_t mme_ue_s1ap_id;
struct sctp_sndrcvinfo enb_sri;
bool eit;
} ecm_ctx_t;
typedef struct{
uint8_t erab_id;
esm_state_t state;
uint8_t qci;
srslte::gtpc_f_teid_ie enb_fteid;
srslte::gtpc_f_teid_ie sgw_s1u_fteid;
srslte::gtpc_pdn_address_allocation_ie pdn_addr_alloc;
} esm_ctx_t;
typedef struct{
uint8_t eksi;
uint8_t k_asme[32];
uint8_t autn[16];
uint8_t rand[16];
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];
uint8_t k_enb[32];
LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap;
bool ms_network_cap_present;
LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap;
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
} sec_ctx_t;
/*
* NAS Initialization Arguments
*/
typedef struct {
uint16_t mcc;
uint16_t mnc;
uint8_t mme_code;
uint16_t mme_group;
uint16_t tac;
std::string apn;
std::string dns;
} nas_init_t;
class nas
{
public:
nas();
void init(nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
/***********************
* Initial UE messages *
***********************/
//Attach request messages
static bool handle_attach_request( uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
static bool handle_imsi_attach_request_unknown_ue( uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
static bool handle_imsi_attach_request_known_ue( nas *nas_ctx,
uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
static bool handle_guti_attach_request_unknown_ue( uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
static bool handle_guti_attach_request_known_ue( nas *nas_ctx,
uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
const LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT &attach_req,
const LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT &pdn_con_req,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
//Service request messages
static bool handle_service_request( uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
//Dettach request messages
static bool handle_detach_request( uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
//Tracking area update request messages
static bool handle_tracking_area_update_request( uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id,
struct sctp_sndrcvinfo *enb_sri,
srslte::byte_buffer_t *nas_rx,
nas_init_t args,
s1ap_interface_nas *s1ap,
gtpc_interface_nas *gtpc,
hss_interface_nas *hss,
srslte::log *nas_log);
/* Uplink NAS messages handling */
bool handle_authentication_response (srslte::byte_buffer_t *nas_rx);
bool handle_security_mode_complete (srslte::byte_buffer_t *nas_rx);
bool handle_attach_complete (srslte::byte_buffer_t *nas_rx);
bool handle_esm_information_response (srslte::byte_buffer_t *nas_rx);
bool handle_identity_response (srslte::byte_buffer_t *nas_rx);
bool handle_tracking_area_update_request (srslte::byte_buffer_t *nas_rx);
bool handle_authentication_failure (srslte::byte_buffer_t *nas_rx);
bool handle_detach_request (srslte::byte_buffer_t *nas_rx);
/* Downlink NAS messages packing */
bool pack_authentication_request (srslte::byte_buffer_t *nas_buffer);
bool pack_authentication_reject (srslte::byte_buffer_t *nas_buffer);
bool pack_security_mode_command (srslte::byte_buffer_t *nas_buffer);
bool pack_esm_information_request (srslte::byte_buffer_t *nas_buffer);
bool pack_identity_request (srslte::byte_buffer_t *nas_buffer);
bool pack_emm_information (srslte::byte_buffer_t *nas_buffer);
bool pack_service_reject (srslte::byte_buffer_t *nas_buffer);
bool pack_attach_accept (srslte::byte_buffer_t *nas_buffer);
/* Security functions */
bool integrity_check (srslte::byte_buffer_t *pdu);
bool short_integrity_check (srslte::byte_buffer_t *pdu);
/* UE Context */
emm_ctx_t m_emm_ctx;
ecm_ctx_t m_ecm_ctx;
esm_ctx_t m_esm_ctx[MAX_ERABS_PER_UE];
sec_ctx_t m_sec_ctx;
private:
srslte::byte_buffer_pool *m_pool;
srslte::log *m_nas_log;
gtpc_interface_nas *m_gtpc;
s1ap_interface_nas *m_s1ap;
hss_interface_nas *m_hss;
uint16_t m_mcc;
uint16_t m_mnc;
uint16_t m_mme_group;
uint16_t m_mme_code;
uint16_t m_tac;
std::string m_apn;
std::string m_dns;
};
}//namespace
#endif // SRSEPC_NAS_H

@ -32,6 +32,7 @@
#include "srslte/common/common.h" #include "srslte/common/common.h"
#include "srslte/common/log.h" #include "srslte/common/log.h"
#include "srslte/common/s1ap_pcap.h" #include "srslte/common/s1ap_pcap.h"
#include "srslte/interfaces/epc_interfaces.h"
#include <strings.h> #include <strings.h>
#include <arpa/inet.h> #include <arpa/inet.h>
@ -41,7 +42,7 @@
#include <unistd.h> #include <unistd.h>
#include <map> #include <map>
#include <set> #include <set>
#include "s1ap_common.h" #include "nas.h"
#include "s1ap_mngmt_proc.h" #include "s1ap_mngmt_proc.h"
#include "s1ap_nas_transport.h" #include "s1ap_nas_transport.h"
#include "s1ap_ctx_mngmt_proc.h" #include "s1ap_ctx_mngmt_proc.h"
@ -52,7 +53,9 @@ namespace srsepc{
const uint16_t S1MME_PORT = 36412; const uint16_t S1MME_PORT = 36412;
class s1ap class s1ap:
public s1ap_interface_nas,
public s1ap_interface_gtpc
{ {
public: public:
@ -60,7 +63,7 @@ public:
static void cleanup(); static void cleanup();
int enb_listen(); int enb_listen();
int init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1ap * hss_); int init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, srslte::log_filter *nas_log, hss_interface_nas * hss_);
void stop(); void stop();
int get_s1_mme(); int get_s1_mme();
@ -82,28 +85,34 @@ public:
void add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo* enb_sri); void add_new_enb_ctx(const enb_ctx_t &enb_ctx, const struct sctp_sndrcvinfo* enb_sri);
void get_enb_ctx(uint16_t sctp_stream); void get_enb_ctx(uint16_t sctp_stream);
bool add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx); bool add_nas_ctx_to_imsi_map(nas *nas_ctx);
bool add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx); bool add_nas_ctx_to_mme_ue_s1ap_id_map(nas *nas_ctx);
bool add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id); bool add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id);
ue_ctx_t* find_ue_ctx_from_imsi(uint64_t imsi); virtual nas* find_nas_ctx_from_imsi(uint64_t imsi);
ue_ctx_t* find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id); nas* find_nas_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id);
bool release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id); bool release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id);
void release_ues_ecm_ctx_in_enb(int32_t enb_assoc); void release_ues_ecm_ctx_in_enb(int32_t enb_assoc);
bool delete_ue_ctx(uint64_t imsi); virtual bool delete_ue_ctx(uint64_t imsi);
uint32_t allocate_m_tmsi(uint64_t imsi); uint32_t allocate_m_tmsi(uint64_t imsi);
virtual uint64_t find_imsi_from_m_tmsi(uint32_t m_tmsi);
s1ap_args_t m_s1ap_args; s1ap_args_t m_s1ap_args;
srslte::log_filter *m_s1ap_log; srslte::log_filter *m_s1ap_log;
srslte::log_filter *m_nas_log;
s1ap_mngmt_proc* m_s1ap_mngmt_proc; s1ap_mngmt_proc* m_s1ap_mngmt_proc;
s1ap_nas_transport* m_s1ap_nas_transport; s1ap_nas_transport* m_s1ap_nas_transport;
s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc; s1ap_ctx_mngmt_proc* m_s1ap_ctx_mngmt_proc;
std::map<uint32_t, uint64_t> m_tmsi_to_imsi; std::map<uint32_t, uint64_t> m_tmsi_to_imsi;
//Interfaces
virtual bool send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup);
virtual bool send_ue_context_release_command(uint32_t mme_ue_s1ap_id);
virtual bool send_downlink_nas_transport(uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id, srslte::byte_buffer_t *nas_msg, struct sctp_sndrcvinfo enb_sri);
private: private:
s1ap(); s1ap();
virtual ~s1ap(); virtual ~s1ap();
@ -113,14 +122,14 @@ private:
uint32_t m_plmn; uint32_t m_plmn;
srslte::byte_buffer_pool *m_pool; srslte::byte_buffer_pool *m_pool;
hss_interface_s1ap *m_hss; hss_interface_nas *m_hss;
int m_s1mme; int m_s1mme;
std::map<uint16_t, enb_ctx_t*> m_active_enbs; std::map<uint16_t, enb_ctx_t*> m_active_enbs;
std::map<int32_t, uint16_t> m_sctp_to_enb_id; std::map<int32_t, uint16_t> m_sctp_to_enb_id;
std::map<int32_t,std::set<uint32_t> > m_enb_assoc_to_ue_ids; std::map<int32_t,std::set<uint32_t> > m_enb_assoc_to_ue_ids;
std::map<uint64_t, ue_ctx_t*> m_imsi_to_ue_ctx; std::map<uint64_t, nas*> m_imsi_to_nas_ctx;
std::map<uint32_t, ue_ctx_t*> m_mme_ue_s1ap_id_to_ue_ctx; std::map<uint32_t, nas*> m_mme_ue_s1ap_id_to_nas_ctx;
uint32_t m_next_mme_ue_s1ap_id; uint32_t m_next_mme_ue_s1ap_id;
uint32_t m_next_m_tmsi; uint32_t m_next_m_tmsi;

@ -31,57 +31,6 @@ namespace srsepc{
static const uint8_t MAX_TA=255; //Maximum TA supported 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_BPLMN=6; //Maximum broadcasted PLMNs per TAC
static const uint8_t MAX_ERABS_PER_UE = 16;
// MME EMM states (3GPP 24.301 v10.0.0, section 5.1.3.4)
typedef enum {
EMM_STATE_DEREGISTERED = 0,
EMM_STATE_COMMON_PROCEDURE_INITIATED,
EMM_STATE_REGISTERED,
EMM_STATE_DEREGISTERED_INITIATED,
EMM_STATE_N_ITEMS,
} emm_state_t;
static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"DEREGISTERED",
"COMMON PROCEDURE INITIATED",
"REGISTERED",
"DEREGISTERED INITIATED"};
// MME ECM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ECM_STATE_IDLE = 0,
ECM_STATE_CONNECTED,
ECM_STATE_N_ITEMS,
} ecm_state_t;
static const char ecm_state_text[ECM_STATE_N_ITEMS][100] = {"IDLE",
"CONNECTED"};
// MME ESM states (3GPP 23.401 v10.0.0, section 4.6.3)
typedef enum {
ESM_BEARER_CONTEXT_INACTIVE = 0,
ESM_BEARER_CONTEXT_ACTIVE_PENDING,
ESM_BEARER_CONTEXT_ACTIVE,
ESM_BEARER_CONTEXT_INACTIVE_PENDING,
ESM_BEARER_CONTEXT_MODIFY_PENDING,
ESM_BEARER_PROCEDURE_TRANSACTION_INACTIVE,
ESM_BEARER_PROCEDURE_TRANSACTION_PENDING,
ESM_STATE_N_ITEMS,
} esm_state_t;
static const char esm_state_text[ESM_STATE_N_ITEMS][100] = {"CONTEXT INACTIVE",
"CONTEXT ACTIVE PENDING",
"CONTEXT ACTIVE",
"CONTEXT_INACTIVE_PENDING",
"CONTEXT_MODIFY_PENDING",
"PROCEDURE_TRANSACTION_INACTIVE"
"PROCEDURE_TRANSACTION_PENDING"};
enum erab_state
{
ERAB_DEACTIVATED,
ERAB_CTX_REQUESTED,
ERAB_CTX_SETUP,
ERAB_ACTIVE
};
typedef struct{ typedef struct{
uint8_t mme_code; uint8_t mme_code;
@ -111,59 +60,6 @@ typedef struct{
struct sctp_sndrcvinfo sri; struct sctp_sndrcvinfo sri;
} enb_ctx_t; } enb_ctx_t;
typedef struct{
uint8_t eksi;
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];
uint8_t k_enb[32];
LIBLTE_MME_UE_NETWORK_CAPABILITY_STRUCT ue_network_cap;
bool ms_network_cap_present;
LIBLTE_MME_MS_NETWORK_CAPABILITY_STRUCT ms_network_cap;
} eps_sec_ctx_t;
typedef struct{
enum erab_state state;
uint8_t erab_id;
uint8_t qci;
srslte::gtpc_f_teid_ie enb_fteid;
srslte::gtpc_f_teid_ie sgw_s1u_fteid;
srslte::gtpc_pdn_address_allocation_ie pdn_addr_alloc;
} erab_ctx_t;
typedef struct{
uint64_t imsi;
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
eps_sec_ctx_t security_ctxt;
uint8_t procedure_transaction_id;
emm_state_t state;
uint32_t mme_ue_s1ap_id;
uint8_t attach_type;
struct in_addr ue_ip;
srslte::gtpc_f_teid_ie sgw_ctrl_fteid;
} ue_emm_ctx_t;
typedef struct{
uint64_t imsi;
uint32_t enb_ue_s1ap_id;
uint32_t mme_ue_s1ap_id;
struct sctp_sndrcvinfo enb_sri;
ecm_state_t state;
erab_ctx_t erabs_ctx[MAX_ERABS_PER_UE];
bool eit;
} ue_ecm_ctx_t;
typedef struct{
ue_emm_ctx_t emm_ctx;
eps_sec_ctx_t sec_ctx;
ue_ecm_ctx_t ecm_ctx;
} ue_ctx_t;
}//namespace }//namespace
#endif // SRSEPC_S1AP_COMMON_H #endif // SRSEPC_S1AP_COMMON_H

@ -47,11 +47,10 @@ public:
void init(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 send_initial_context_setup_request(nas *nas_ctx, uint16_t erab_to_setup);
bool send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, ue_ecm_ctx_t *ecm_ctx, erab_ctx_t *erab_ctx);
bool handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp); 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); 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);
bool send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer); bool send_ue_context_release_command(nas *nas_ctx);
bool handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp); bool handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp);
private: private:

@ -42,57 +42,25 @@ public:
static s1ap_nas_transport* m_instance; static s1ap_nas_transport* m_instance;
static s1ap_nas_transport* get_instance(void); static s1ap_nas_transport* get_instance(void);
static void cleanup(void); static void cleanup(void);
void init(hss_interface_s1ap * hss_); void init(hss_interface_nas * hss_);
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_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_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 send_downlink_nas_transport(uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id, srslte::byte_buffer_t *nas_msg, struct sctp_sndrcvinfo enb_sri);
bool pack_attach_accept(ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx, LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctxt, struct srslte::gtpc_pdn_address_allocation_ie *paa, srslte::byte_buffer_t *nas_buffer); bool handle_nas_detach_request( uint32_t m_tmsi,
private:
s1ap_nas_transport();
virtual ~s1ap_nas_transport();
srslte::log *m_s1ap_log;
srslte::byte_buffer_pool *m_pool;
s1ap* m_s1ap;
hss_interface_s1ap* m_hss;
mme_gtpc* m_mme_gtpc;
//Initial UE messages
bool handle_nas_attach_request( uint32_t enb_ue_s1ap_id,
srslte::byte_buffer_t *nas_msg,
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 *nas_msg,
srslte::byte_buffer_t *reply_buffer,
bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_service_request(uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id, uint32_t enb_ue_s1ap_id,
srslte::byte_buffer_t *nas_msg, srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer, srslte::byte_buffer_t *reply_buffer,
bool* reply_flag, bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri); struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_detach_request(uint32_t m_tmsi, bool handle_nas_service_request( uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id, uint32_t enb_ue_s1ap_id,
srslte::byte_buffer_t *nas_msg, srslte::byte_buffer_t *nas_msg,
srslte::byte_buffer_t *reply_buffer, srslte::byte_buffer_t *reply_buffer,
bool* reply_flag, bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri); struct sctp_sndrcvinfo *enb_sri);
bool handle_nas_tracking_area_update_request( uint32_t m_tmsi, bool handle_nas_tracking_area_update_request( uint32_t m_tmsi,
uint32_t enb_ue_s1ap_id, uint32_t enb_ue_s1ap_id,
@ -100,34 +68,17 @@ private:
srslte::byte_buffer_t *reply_buffer, srslte::byte_buffer_t *reply_buffer,
bool* reply_flag, bool* reply_flag,
struct sctp_sndrcvinfo *enb_sri); struct sctp_sndrcvinfo *enb_sri);
private:
s1ap_nas_transport();
virtual ~s1ap_nas_transport();
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); srslte::log *m_s1ap_log;
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); srslte::byte_buffer_pool *m_pool;
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 handle_authentication_failure(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_buffer, bool *reply_flag);
bool handle_nas_detach_request(srslte::byte_buffer_t *nas_msg, ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg, bool *reply_flag);
bool integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu);
bool short_integrity_check(ue_emm_ctx_t *emm_ctx, srslte::byte_buffer_t *pdu);
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 eksi, 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_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx);
bool pack_esm_information_request(srslte::byte_buffer_t *reply_msg, ue_emm_ctx_t *ue_emm_ctx, ue_ecm_ctx_t *ue_ecm_ctx);
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(ue_ctx_t* ue_ctx, srslte::byte_buffer_t *reply_msg); s1ap* m_s1ap;
bool pack_service_reject(srslte::byte_buffer_t *reply_msg, uint8_t emm_cause, uint32_t enb_ue_s1ap_id); hss_interface_nas* m_hss;
mme_gtpc* m_mme_gtpc;
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);
}; };
} //namespace srsepc } //namespace srsepc
#endif // SRSEPC_S1AP_NAS_TRANSPORT_H #endif // SRSEPC_S1AP_NAS_TRANSPORT_H

@ -50,6 +50,7 @@ const uint16_t GTPU_RX_PORT = 2152;
typedef struct { typedef struct {
std::string gtpu_bind_addr; std::string gtpu_bind_addr;
std::string sgi_if_addr; std::string sgi_if_addr;
std::string sgi_if_name;
} spgw_args_t; } spgw_args_t;

@ -1,20 +1,27 @@
##################################################################### #####################################################################
# srsEPC configuration file # srsMBMS configuration file
##################################################################### #####################################################################
##################################################################### #####################################################################
# MBMS-GW configuration # MBMS-GW configuration
# #
# name: MBMS-GW name # name: MBMS-GW name
# sgi_mb_if_name: SGi-mb TUN interface name
# sgi_mb_if_addr: SGi-mb interface IP address # sgi_mb_if_addr: SGi-mb interface IP address
# m1u_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3) # sgi_mb_if_mask: SGi-mb interface IP mask
# m1u_multi_addr: Multicast group for eNBs (FIXME this should be setup with M2/M3)
# m1u_multi_if: IP of local interface for multicast traffic
# m1u_multi_ttl: TTL for M1-U multicast traffic
# #
##################################################################### #####################################################################
[mbms_gw] [mbms_gw]
name = srsmbmsgw01 name = srsmbmsgw01
sgi_mb_if_name = sgi_mb
sgi_mb_if_addr = 172.16.0.254 sgi_mb_if_addr = 172.16.0.254
sgi_mb_if_mask = 255.255.255.255 sgi_mb_if_mask = 255.255.255.255
m1u_multi_addr = 239.255.0.1 m1u_multi_addr = 239.255.0.1
m1u_multi_if = 127.0.1.200
m1u_multi_ttl = 1
#################################################################### ####################################################################
# Log configuration # Log configuration

@ -618,7 +618,7 @@ hss::increment_ue_sqn(uint64_t imsi)
} }
increment_sqn(ue_ctx->sqn,ue_ctx->sqn); increment_sqn(ue_ctx->sqn,ue_ctx->sqn);
m_hss_log->debug("Incremented SQN (IMSI: %" PRIu64 ")" PRIu64 "\n", imsi); m_hss_log->debug("Incremented SQN -- IMSI: %" PRIu64 "\n", imsi);
m_hss_log->debug_hex(ue_ctx->sqn, 6, "SQN: "); m_hss_log->debug_hex(ue_ctx->sqn, 6, "SQN: ");
} }

@ -22,6 +22,7 @@
* *
*/ */
#include <iostream> #include <iostream>
#include <sstream>
#include <fstream> #include <fstream>
#include <errno.h> #include <errno.h>
#include <signal.h> #include <signal.h>
@ -30,6 +31,7 @@
#include "srslte/common/crash_handler.h" #include "srslte/common/crash_handler.h"
#include "srslte/common/bcd_helpers.h" #include "srslte/common/bcd_helpers.h"
#include "srslte/common/config_file.h" #include "srslte/common/config_file.h"
#include "srslte/build_info.h"
#include "srsepc/hdr/mme/mme.h" #include "srsepc/hdr/mme/mme.h"
#include "srsepc/hdr/hss/hss.h" #include "srsepc/hdr/hss/hss.h"
#include "srsepc/hdr/spgw/spgw.h" #include "srsepc/hdr/spgw/spgw.h"
@ -46,6 +48,8 @@ sig_int_handler(int signo){
} }
typedef struct { typedef struct {
std::string nas_level;
int nas_hex_limit;
std::string s1ap_level; std::string s1ap_level;
int s1ap_hex_limit; int s1ap_hex_limit;
std::string gtpc_level; std::string gtpc_level;
@ -85,6 +89,7 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
string mme_apn; string mme_apn;
string spgw_bind_addr; string spgw_bind_addr;
string sgi_if_addr; string sgi_if_addr;
string sgi_if_name;
string dns_addr; string dns_addr;
string hss_db_file; string hss_db_file;
string hss_auth_algo; string hss_auth_algo;
@ -114,10 +119,13 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
("hss.auth_algo", bpo::value<string>(&hss_auth_algo)->default_value("milenage"), "HSS uthentication algorithm.") ("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.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") ("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")
("spgw.sgi_if_name", bpo::value<string>(&sgi_if_name)->default_value("srs_spgw_sgi"), "Name of TUN interface for the SGi connection")
("pcap.enable", bpo::value<bool>(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP") ("pcap.enable", bpo::value<bool>(&args->mme_args.s1ap_args.pcap_enable)->default_value(false), "Enable S1AP PCAP")
("pcap.filename", bpo::value<string>(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename") ("pcap.filename", bpo::value<string>(&args->mme_args.s1ap_args.pcap_filename)->default_value("/tmp/epc.pcap"), "PCAP filename")
("log.nas_level", bpo::value<string>(&args->log_args.nas_level), "MME NAS log level")
("log.nas_hex_limit", bpo::value<int>(&args->log_args.nas_hex_limit), "MME NAS log hex dump limit")
("log.s1ap_level", bpo::value<string>(&args->log_args.s1ap_level), "MME S1AP log level") ("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.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_level", bpo::value<string>(&args->log_args.gtpc_level), "MME GTPC log level")
@ -217,11 +225,15 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
args->mme_args.s1ap_args.mme_apn = mme_apn; args->mme_args.s1ap_args.mme_apn = mme_apn;
args->spgw_args.gtpu_bind_addr = spgw_bind_addr; args->spgw_args.gtpu_bind_addr = spgw_bind_addr;
args->spgw_args.sgi_if_addr = sgi_if_addr; args->spgw_args.sgi_if_addr = sgi_if_addr;
args->spgw_args.sgi_if_name = sgi_if_name;
args->hss_args.db_file = hss_db_file; args->hss_args.db_file = hss_db_file;
args->hss_args.auth_algo = hss_auth_algo; args->hss_args.auth_algo = hss_auth_algo;
// Apply all_level to any unset layers // Apply all_level to any unset layers
if (vm.count("log.all_level")) { if (vm.count("log.all_level")) {
if(!vm.count("log.nas_level")) {
args->log_args.nas_level = args->log_args.all_level;
}
if(!vm.count("log.s1ap_level")) { if(!vm.count("log.s1ap_level")) {
args->log_args.s1ap_level = args->log_args.all_level; args->log_args.s1ap_level = args->log_args.all_level;
} }
@ -250,6 +262,9 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
if(!vm.count("log.hss_hex_limit")) { if(!vm.count("log.hss_hex_limit")) {
args->log_args.hss_hex_limit = args->log_args.all_hex_limit; args->log_args.hss_hex_limit = args->log_args.all_hex_limit;
} }
if(!vm.count("log.nas_hex_limit")) {
args->log_args.nas_hex_limit = args->log_args.all_hex_limit;
}
} }
// Check user database // Check user database
@ -280,6 +295,26 @@ level(std::string l)
} }
} }
std::string get_build_mode()
{
return std::string(srslte_get_build_mode());
}
std::string get_build_info()
{
if (std::string(srslte_get_build_info()) == "") {
return std::string(srslte_get_version());
}
return std::string(srslte_get_build_info());
}
std::string get_build_string()
{
std::stringstream ss;
ss << "Built in " << get_build_mode() << " mode using " << get_build_info() << "." << std::endl;
return ss.str();
}
int int
main (int argc,char * argv[] ) main (int argc,char * argv[] )
{ {
@ -287,6 +322,9 @@ main (int argc,char * argv[] )
signal(SIGTERM, sig_int_handler); signal(SIGTERM, sig_int_handler);
signal(SIGKILL, sig_int_handler); signal(SIGKILL, sig_int_handler);
// print build info
cout << endl << get_build_string() << endl;
cout << endl <<"--- Software Radio Systems EPC ---" << endl << endl; cout << endl <<"--- Software Radio Systems EPC ---" << endl << endl;
srslte_debug_handle_crash(argc, argv); srslte_debug_handle_crash(argc, argv);
@ -302,10 +340,17 @@ main (int argc,char * argv[] )
logger = &logger_stdout; logger = &logger_stdout;
} else { } else {
logger_file.init(args.log_args.filename); logger_file.init(args.log_args.filename);
logger_file.log("\n\n");
logger_file.log(get_build_string().c_str());
logger_file.log("\n--- Software Radio Systems EPC log ---\n\n"); logger_file.log("\n--- Software Radio Systems EPC log ---\n\n");
logger = &logger_file; logger = &logger_file;
} }
srslte::log_filter nas_log;
nas_log.init("NAS ",logger);
nas_log.set_level(level(args.log_args.nas_level));
nas_log.set_hex_limit(args.log_args.nas_hex_limit);
srslte::log_filter s1ap_log; srslte::log_filter s1ap_log;
s1ap_log.init("S1AP",logger); s1ap_log.init("S1AP",logger);
s1ap_log.set_level(level(args.log_args.s1ap_level)); s1ap_log.set_level(level(args.log_args.s1ap_level));
@ -334,7 +379,7 @@ main (int argc,char * argv[] )
} }
mme *mme = mme::get_instance(); mme *mme = mme::get_instance();
if (mme->init(&args.mme_args, &s1ap_log, &mme_gtpc_log, hss)) { if (mme->init(&args.mme_args, &nas_log, &s1ap_log, &mme_gtpc_log, hss)) {
cout << "Error initializing MME" << endl; cout << "Error initializing MME" << endl;
exit(1); exit(1);
} }

@ -82,9 +82,11 @@ void
parse_args(all_args_t *args, int argc, char* argv[]) { parse_args(all_args_t *args, int argc, char* argv[]) {
string mbms_gw_name; string mbms_gw_name;
string mbms_gw_sgi_mb_if_name;
string mbms_gw_sgi_mb_if_addr; string mbms_gw_sgi_mb_if_addr;
string mbms_gw_sgi_mb_if_mask; string mbms_gw_sgi_mb_if_mask;
string mbms_gw_m1u_multi_addr; string mbms_gw_m1u_multi_addr;
string mbms_gw_m1u_multi_if;
string log_filename; string log_filename;
@ -100,9 +102,12 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
common.add_options() common.add_options()
("mbms_gw.name", bpo::value<string>(&mbms_gw_name)->default_value("srsmbmsgw01"), "MBMS-GW Name") ("mbms_gw.name", bpo::value<string>(&mbms_gw_name)->default_value("srsmbmsgw01"), "MBMS-GW Name")
("mbms_gw.sgi_mb_if_name", bpo::value<string>(&mbms_gw_sgi_mb_if_name)->default_value("sgi_mb"), "SGi-mb TUN interface Address.")
("mbms_gw.sgi_mb_if_addr", bpo::value<string>(&mbms_gw_sgi_mb_if_addr)->default_value("172.16.1.1"), "SGi-mb TUN interface Address.") ("mbms_gw.sgi_mb_if_addr", bpo::value<string>(&mbms_gw_sgi_mb_if_addr)->default_value("172.16.1.1"), "SGi-mb TUN interface Address.")
("mbms_gw.sgi_mb_if_mask", bpo::value<string>(&mbms_gw_sgi_mb_if_mask)->default_value("255.255.255.255"), "SGi-mb TUN interface mask.") ("mbms_gw.sgi_mb_if_mask", bpo::value<string>(&mbms_gw_sgi_mb_if_mask)->default_value("255.255.255.255"), "SGi-mb TUN interface mask.")
("mbms_gw.m1u_multi_addr", bpo::value<string>(&mbms_gw_m1u_multi_addr)->default_value("239.255.0.1"), "M1-u GTPu destination multicast address.") ("mbms_gw.m1u_multi_addr", bpo::value<string>(&mbms_gw_m1u_multi_addr)->default_value("239.255.0.1"), "M1-u GTPu destination multicast address.")
("mbms_gw.m1u_multi_if", bpo::value<string>(&mbms_gw_m1u_multi_if)->default_value("127.0.1.200"), "Local interface IP for M1-U multicast packets.")
("mbms_gw.m1u_multi_ttl", bpo::value<int>(&args->mbms_gw_args.m1u_multi_ttl)->default_value(1), "TTL for M1-U multicast packets.")
("log.all_level", bpo::value<string>(&args->log_args.all_level)->default_value("info"), "ALL log level") ("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.all_hex_limit", bpo::value<int>(&args->log_args.all_hex_limit)->default_value(32), "ALL log hex dump limit")
@ -153,9 +158,11 @@ parse_args(all_args_t *args, int argc, char* argv[]) {
bpo::notify(vm); bpo::notify(vm);
args->mbms_gw_args.name = mbms_gw_name; args->mbms_gw_args.name = mbms_gw_name;
args->mbms_gw_args.sgi_mb_if_name = mbms_gw_sgi_mb_if_name;
args->mbms_gw_args.sgi_mb_if_addr = mbms_gw_sgi_mb_if_addr; args->mbms_gw_args.sgi_mb_if_addr = mbms_gw_sgi_mb_if_addr;
args->mbms_gw_args.sgi_mb_if_mask = mbms_gw_sgi_mb_if_mask; args->mbms_gw_args.sgi_mb_if_mask = mbms_gw_sgi_mb_if_mask;
args->mbms_gw_args.m1u_multi_addr = mbms_gw_m1u_multi_addr; args->mbms_gw_args.m1u_multi_addr = mbms_gw_m1u_multi_addr;
args->mbms_gw_args.m1u_multi_if = mbms_gw_m1u_multi_if;
// Apply all_level to any unset layers // Apply all_level to any unset layers
if (vm.count("log.all_level")) { if (vm.count("log.all_level")) {

@ -126,7 +126,6 @@ mbms_gw::stop()
srslte::error_t srslte::error_t
mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args) mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args)
{ {
char dev[IFNAMSIZ] = "sgi_mb";
struct ifreq ifr; struct ifreq ifr;
if(m_sgi_mb_up) if(m_sgi_mb_up)
@ -138,22 +137,22 @@ mbms_gw::init_sgi_mb_if(mbms_gw_args_t *args)
// Construct the TUN device // Construct the TUN device
m_sgi_mb_if = open("/dev/net/tun", O_RDWR); m_sgi_mb_if = open("/dev/net/tun", O_RDWR);
m_mbms_gw_log->info("TUN file descriptor = %d\n", m_sgi_mb_if); m_mbms_gw_log->info("TUN file descriptor = %d\n", m_sgi_mb_if);
if(m_sgi_mb_if < 0) if (m_sgi_mb_if < 0) {
{
m_mbms_gw_log->error("Failed to open TUN device: %s\n", strerror(errno)); m_mbms_gw_log->error("Failed to open TUN device: %s\n", strerror(errno));
return(srslte::ERROR_CANT_START); return(srslte::ERROR_CANT_START);
} }
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_mb_if_name.c_str(), std::min(args->sgi_mb_if_name.length(), (size_t)IFNAMSIZ-1) );
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0'; ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0';
if(ioctl(m_sgi_mb_if, TUNSETIFF, &ifr) < 0) if (ioctl(m_sgi_mb_if, TUNSETIFF, &ifr) < 0) {
{ m_mbms_gw_log->error("Failed to set TUN device name: %s\n", strerror(errno));
m_mbms_gw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); close(m_sgi_mb_if);
close(m_sgi_mb_if); return(srslte::ERROR_CANT_START);
return(srslte::ERROR_CANT_START); } else {
m_mbms_gw_log->debug("Set TUN device name: %s\n", args->sgi_mb_if_name.c_str());
} }
// Bring up the interface // Bring up the interface
@ -234,11 +233,18 @@ mbms_gw::init_m1_u(mbms_gw_args_t *args)
/* Set local interface for outbound multicast packets*/ /* Set local interface for outbound multicast packets*/
/* The IP must be associated with a local multicast capable interface */ /* The IP must be associated with a local multicast capable interface */
struct in_addr local_if; struct in_addr local_if;
local_if.s_addr = inet_addr("127.0.1.200"); local_if.s_addr = inet_addr(args->m1u_multi_if.c_str());
if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr))<0){ if(setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr))<0){
perror("Error setting multicast interface.\n"); m_mbms_gw_log->error("Error %s setting multicast interface %s.\n", strerror(errno), args->m1u_multi_if.c_str());
return srslte::ERROR_CANT_START;
} else { } else {
printf("Multicast interface specified.\n"); printf("Multicast interface specified. Address: %s\n", args->m1u_multi_if.c_str());
}
/*Set Multicast TTL*/
if ( setsockopt(m_m1u, IPPROTO_IP,IP_MULTICAST_TTL,&args->m1u_multi_ttl,sizeof(args->m1u_multi_ttl)) <0 ) {
perror("Error setting multicast ttl.\n");
return srslte::ERROR_CANT_START;
} }
bzero(&m_m1u_multi_addr,sizeof(m_m1u_multi_addr)); bzero(&m_m1u_multi_addr,sizeof(m_m1u_multi_addr));
@ -287,29 +293,26 @@ mbms_gw::handle_sgi_md_pdu(srslte::byte_buffer_t *msg)
srslte::gtpu_header_t header; srslte::gtpu_header_t header;
//Setup GTP-U header //Setup GTP-U header
header.flags = 0x30; header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = 0xFF; header.message_type = GTPU_MSG_DATA_PDU;
header.length = msg->N_bytes; header.length = msg->N_bytes;
header.teid = 0xAAAA; //FIXME Harcoded TEID for now header.teid = 0xAAAA; //FIXME Harcoded TEID for now
//Sanity Check IP packet //Sanity Check IP packet
if(msg->N_bytes < 20) if (msg->N_bytes < 20) {
{
m_mbms_gw_log->error("IPv4 min len: %d, drop msg len %d\n", 20, msg->N_bytes); m_mbms_gw_log->error("IPv4 min len: %d, drop msg len %d\n", 20, msg->N_bytes);
return; return;
} }
//IP Headers //IP Headers
struct iphdr *iph = (struct iphdr *) msg->msg; struct iphdr *iph = (struct iphdr *) msg->msg;
if(iph->version != 4) if(iph->version != 4) {
{
m_mbms_gw_log->warning("IPv6 not supported yet.\n"); m_mbms_gw_log->warning("IPv6 not supported yet.\n");
return; return;
} }
//Write GTP-U header into packet //Write GTP-U header into packet
if(!srslte::gtpu_write_header(&header, msg, m_mbms_gw_log)) if (!srslte::gtpu_write_header(&header, msg, m_mbms_gw_log)) {
{
m_mbms_gw_log->console("Error writing GTP-U header on PDU\n"); m_mbms_gw_log->console("Error writing GTP-U header on PDU\n");
} }

@ -71,38 +71,38 @@ mme::cleanup(void)
} }
int int
mme::init(mme_args_t* args, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_s1ap * hss_) mme::init(mme_args_t* args, srslte::log_filter *nas_log, srslte::log_filter *s1ap_log, srslte::log_filter *mme_gtpc_log, hss_interface_nas * hss)
{ {
/*Init logger*/ /*Init logger*/
m_nas_log = nas_log;
m_s1ap_log = s1ap_log; m_s1ap_log = s1ap_log;
m_mme_gtpc_log = mme_gtpc_log; m_mme_gtpc_log = mme_gtpc_log;
/*Init S1AP*/ /*Init S1AP*/
m_s1ap = s1ap::get_instance(); m_s1ap = s1ap::get_instance();
if(m_s1ap->init(args->s1ap_args, s1ap_log, hss_)){ if (m_s1ap->init(args->s1ap_args, nas_log, s1ap_log, hss)) {
m_s1ap_log->error("Error initializing MME S1APP\n"); m_s1ap_log->error("Error initializing MME S1APP\n");
exit(-1); exit(-1);
} }
/*Init GTP-C*/ /*Init GTP-C*/
m_mme_gtpc = mme_gtpc::get_instance(); m_mme_gtpc = mme_gtpc::get_instance();
if(!m_mme_gtpc->init(m_mme_gtpc_log)) if (!m_mme_gtpc->init(m_mme_gtpc_log)) {
{
m_s1ap_log->console("Error initializing GTP-C\n"); m_s1ap_log->console("Error initializing GTP-C\n");
exit(-1); exit(-1);
} }
/*Log successful initialization*/ /*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->info("MME Initialized. MCC: 0x%x, MNC: 0x%x\n", args->s1ap_args.mcc, args->s1ap_args.mnc);
m_s1ap_log->console("MME Initialized. \n"); m_s1ap_log->console("MME Initialized. MCC: 0x%x, MNC: 0x%x\n", args->s1ap_args.mcc, args->s1ap_args.mnc);
return 0; return 0;
} }
void void
mme::stop() mme::stop()
{ {
if(m_running) if (m_running) {
{
m_s1ap->stop(); m_s1ap->stop();
m_s1ap->cleanup(); m_s1ap->cleanup();
m_running = false; m_running = false;
@ -140,22 +140,17 @@ mme::run_thread()
} }
else if (rd_sz == -1 && errno == EAGAIN){ else if (rd_sz == -1 && errno == EAGAIN){
m_s1ap_log->debug("Socket timeout reached"); m_s1ap_log->debug("Socket timeout reached");
} } else {
else{ if (msg_flags & MSG_NOTIFICATION) {
if(msg_flags & MSG_NOTIFICATION)
{
//Received notification //Received notification
union sctp_notification *notification = (union sctp_notification*)pdu->msg; union sctp_notification *notification = (union sctp_notification*)pdu->msg;
m_s1ap_log->debug("SCTP Notification %d\n", notification->sn_header.sn_type); m_s1ap_log->debug("SCTP Notification %d\n", notification->sn_header.sn_type);
if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) 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->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_log->console("SCTP Association Shutdown. Association: %d\n",sri.sinfo_assoc_id);
m_s1ap->delete_enb_ctx(sri.sinfo_assoc_id); m_s1ap->delete_enb_ctx(sri.sinfo_assoc_id);
} }
} } else {
else
{
//Received data //Received data
pdu->N_bytes = rd_sz; pdu->N_bytes = rd_sz;
m_s1ap_log->info("Received S1AP msg. Size: %d\n", pdu->N_bytes); m_s1ap_log->info("Received S1AP msg. Size: %d\n", pdu->N_bytes);
@ -165,5 +160,4 @@ mme::run_thread()
} }
return; return;
} }
} //namespace srsepc } //namespace srsepc

@ -91,7 +91,8 @@ mme_gtpc::get_new_ctrl_teid()
{ {
return m_next_ctrl_teid++; //FIXME Use a Id pool? return m_next_ctrl_teid++; //FIXME Use a Id pool?
} }
void
bool
mme_gtpc::send_create_session_request(uint64_t imsi) mme_gtpc::send_create_session_request(uint64_t imsi)
{ {
m_mme_gtpc_log->info("Sending Create Session Request.\n"); m_mme_gtpc_log->info("Sending Create Session Request.\n");
@ -133,17 +134,13 @@ mme_gtpc::send_create_session_request(uint64_t imsi)
//Check whether this UE is already registed //Check whether this UE is already registed
std::map<uint64_t, struct gtpc_ctx>::iterator it = m_imsi_to_gtpc_ctx.find(imsi); std::map<uint64_t, struct gtpc_ctx>::iterator it = m_imsi_to_gtpc_ctx.find(imsi);
if(it != m_imsi_to_gtpc_ctx.end()) if (it != m_imsi_to_gtpc_ctx.end()) {
{
m_mme_gtpc_log->warning("Create Session Request being called for an UE with an active GTP-C connection.\n"); m_mme_gtpc_log->warning("Create Session Request being called for an UE with an active GTP-C connection.\n");
m_mme_gtpc_log->warning("Deleting previous GTP-C connection.\n"); m_mme_gtpc_log->warning("Deleting previous GTP-C connection.\n");
std::map<uint32_t, uint64_t>::iterator jt = m_mme_ctr_teid_to_imsi.find(it->second.mme_ctr_fteid.teid); std::map<uint32_t, uint64_t>::iterator jt = m_mme_ctr_teid_to_imsi.find(it->second.mme_ctr_fteid.teid);
if(jt == m_mme_ctr_teid_to_imsi.end()) if (jt == m_mme_ctr_teid_to_imsi.end()) {
{
m_mme_gtpc_log->error("Could not find IMSI from MME Ctrl TEID. MME Ctr TEID: %d\n", it->second.mme_ctr_fteid.teid); m_mme_gtpc_log->error("Could not find IMSI from MME Ctrl TEID. MME Ctr TEID: %d\n", it->second.mme_ctr_fteid.teid);
} } else {
else
{
m_mme_ctr_teid_to_imsi.erase(jt); m_mme_ctr_teid_to_imsi.erase(jt);
} }
m_imsi_to_gtpc_ctx.erase(it); m_imsi_to_gtpc_ctx.erase(it);
@ -160,33 +157,31 @@ mme_gtpc::send_create_session_request(uint64_t imsi)
gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid; gtpc_ctx.mme_ctr_fteid = cs_req->sender_f_teid;
m_imsi_to_gtpc_ctx.insert(std::pair<uint64_t,gtpc_ctx_t>(imsi,gtpc_ctx)); m_imsi_to_gtpc_ctx.insert(std::pair<uint64_t,gtpc_ctx_t>(imsi,gtpc_ctx));
m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu); m_spgw->handle_create_session_request(cs_req, &cs_resp_pdu);
return true;
} }
void bool
mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu) 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; 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->info("Received Create Session Response\n");
m_mme_gtpc_log->console("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) 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"); m_mme_gtpc_log->warning("Could not create GTPC session. Not a create session response\n");
//TODO Handle error //TODO Handle error
return; return false;
} }
if (cs_resp->cause.cause_value != srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED){ 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"); m_mme_gtpc_log->warning("Could not create GTPC session. Create Session Request not accepted\n");
//TODO Handle error //TODO Handle error
return; return false;
} }
//Get IMSI from the control TEID //Get IMSI from the control TEID
std::map<uint32_t,uint64_t>::iterator id_it = m_mme_ctr_teid_to_imsi.find(cs_resp_pdu->header.teid); std::map<uint32_t,uint64_t>::iterator id_it = m_mme_ctr_teid_to_imsi.find(cs_resp_pdu->header.teid);
if(id_it == m_mme_ctr_teid_to_imsi.end()) if(id_it == m_mme_ctr_teid_to_imsi.end()) {
{
m_mme_gtpc_log->warning("Could not find IMSI from Ctrl TEID.\n"); m_mme_gtpc_log->warning("Could not find IMSI from Ctrl TEID.\n");
return; return false;
} }
uint64_t imsi = id_it->second; uint64_t imsi = id_it->second;
@ -200,7 +195,7 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
//Get S-GW S1-u F-TEID //Get S-GW S1-u F-TEID
if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){ if (cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid_present == false){
m_mme_gtpc_log->error("Did not receive SGW S1-U F-TEID in create session response\n"); m_mme_gtpc_log->error("Did not receive SGW S1-U F-TEID in create session response\n");
return; return false;
} }
m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid); m_mme_gtpc_log->console("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid);
m_mme_gtpc_log->info("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid); m_mme_gtpc_log->info("Create Session Response -- SPGW control TEID %d\n", sgw_ctr_fteid.teid);
@ -210,36 +205,33 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
m_mme_gtpc_log->info("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr)); m_mme_gtpc_log->info("Create Session Response -- SPGW S1-U Address: %s\n", inet_ntoa(s1u_addr));
//Check UE Ipv4 address was allocated //Check UE Ipv4 address was allocated
if(cs_resp->paa_present != true) if (cs_resp->paa_present != true) {
{
m_mme_gtpc_log->error("PDN Adress Allocation not present\n"); m_mme_gtpc_log->error("PDN Adress Allocation not present\n");
return; return false;
} }
if(cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) if (cs_resp->paa.pdn_type != srslte::GTPC_PDN_TYPE_IPV4) {
{
m_mme_gtpc_log->error("IPv6 not supported yet\n"); m_mme_gtpc_log->error("IPv6 not supported yet\n");
return; return false;
} }
//Save create session response info to E-RAB context //Save create session response info to E-RAB context
ue_ctx_t *ue_ctx = m_s1ap->find_ue_ctx_from_imsi(imsi); nas *nas_ctx = m_s1ap->find_nas_ctx_from_imsi(imsi);
if(ue_ctx == NULL){ if(nas_ctx == NULL){
m_mme_gtpc_log->error("Could not find UE context. IMSI %015lu\n", imsi); m_mme_gtpc_log->error("Could not find UE context. IMSI %015lu\n", imsi);
return; return false;
} }
ue_emm_ctx_t *emm_ctx = &ue_ctx->emm_ctx; emm_ctx_t *emm_ctx = &nas_ctx->m_emm_ctx;
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; ecm_ctx_t *ecm_ctx = &nas_ctx->m_ecm_ctx;
//Save UE IP to nas ctxt //Save UE IP to nas ctxt
emm_ctx->ue_ip.s_addr = cs_resp->paa.ipv4; emm_ctx->ue_ip.s_addr = cs_resp->paa.ipv4;
m_mme_gtpc_log->console("SPGW Allocated IP %s to ISMI %015lu\n",inet_ntoa(emm_ctx->ue_ip),emm_ctx->imsi); m_mme_gtpc_log->console("SPGW Allocated IP %s to ISMI %015lu\n",inet_ntoa(emm_ctx->ue_ip),emm_ctx->imsi);
//Save SGW ctrl F-TEID in GTP-C context //Save SGW ctrl F-TEID in GTP-C context
std::map<uint64_t,struct gtpc_ctx>::iterator it_g = m_imsi_to_gtpc_ctx.find(imsi); std::map<uint64_t,struct gtpc_ctx>::iterator it_g = m_imsi_to_gtpc_ctx.find(imsi);
if(it_g == m_imsi_to_gtpc_ctx.end()) if(it_g == m_imsi_to_gtpc_ctx.end()) {
{
//Could not find GTP-C Context //Could not find GTP-C Context
m_mme_gtpc_log->error("Could not find GTP-C context\n"); m_mme_gtpc_log->error("Could not find GTP-C context\n");
return; return false;
} }
gtpc_ctx_t *gtpc_ctx = &it_g->second; gtpc_ctx_t *gtpc_ctx = &it_g->second;
gtpc_ctx->sgw_ctr_fteid = sgw_ctr_fteid; gtpc_ctx->sgw_ctr_fteid = sgw_ctr_fteid;
@ -247,26 +239,24 @@ mme_gtpc::handle_create_session_response(srslte::gtpc_pdu *cs_resp_pdu)
//Set EPS bearer context //Set EPS bearer context
//FIXME default EPS bearer is hard-coded //FIXME default EPS bearer is hard-coded
int default_bearer=5; int default_bearer=5;
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[default_bearer]; esm_ctx_t *esm_ctx = &nas_ctx->m_esm_ctx[default_bearer];
erab_ctx->pdn_addr_alloc= cs_resp->paa; esm_ctx->pdn_addr_alloc= cs_resp->paa;
erab_ctx->sgw_s1u_fteid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid; esm_ctx->sgw_s1u_fteid = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid;
m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(emm_ctx, ecm_ctx, erab_ctx); m_s1ap->m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(nas_ctx, default_bearer);
return; return true;
} }
void bool
mme_gtpc::send_modify_bearer_request(uint64_t imsi, erab_ctx_t *erab_ctx) mme_gtpc::send_modify_bearer_request(uint64_t imsi, uint16_t erab_to_modify, srslte::gtp_fteid_t *enb_fteid)
{ {
m_mme_gtpc_log->info("Sending GTP-C Modify bearer request\n"); m_mme_gtpc_log->info("Sending GTP-C Modify bearer request\n");
srslte::gtpc_pdu mb_req_pdu; srslte::gtpc_pdu mb_req_pdu;
srslte::gtp_fteid_t *enb_fteid = &erab_ctx->enb_fteid;
std::map<uint64_t,gtpc_ctx_t>::iterator it = m_imsi_to_gtpc_ctx.find(imsi); std::map<uint64_t,gtpc_ctx_t>::iterator it = m_imsi_to_gtpc_ctx.find(imsi);
if(it == m_imsi_to_gtpc_ctx.end()) if (it == m_imsi_to_gtpc_ctx.end()) {
{
m_mme_gtpc_log->error("Modify bearer request for UE without GTP-C connection\n"); m_mme_gtpc_log->error("Modify bearer request for UE without GTP-C connection\n");
return; return false;
} }
srslte::gtp_fteid_t sgw_ctr_fteid = it->second.sgw_ctr_fteid; srslte::gtp_fteid_t sgw_ctr_fteid = it->second.sgw_ctr_fteid;
@ -276,7 +266,7 @@ mme_gtpc::send_modify_bearer_request(uint64_t imsi, erab_ctx_t *erab_ctx)
header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST; header->type = srslte::GTPC_MSG_TYPE_MODIFY_BEARER_REQUEST;
srslte::gtpc_modify_bearer_request *mb_req = &mb_req_pdu.choice.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.ebi = erab_to_modify;
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.ipv4 = enb_fteid->ipv4;
mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid = enb_fteid->teid; mb_req->eps_bearer_context_to_modify.s1_u_enb_f_teid.teid = enb_fteid->teid;
@ -289,7 +279,7 @@ mme_gtpc::send_modify_bearer_request(uint64_t imsi, erab_ctx_t *erab_ctx)
srslte::gtpc_pdu mb_resp_pdu; srslte::gtpc_pdu mb_resp_pdu;
m_spgw->handle_modify_bearer_request(&mb_req_pdu,&mb_resp_pdu); m_spgw->handle_modify_bearer_request(&mb_req_pdu,&mb_resp_pdu);
handle_modify_bearer_response(&mb_resp_pdu); handle_modify_bearer_response(&mb_resp_pdu);
return; return true;
} }
void void
@ -297,8 +287,7 @@ mme_gtpc::handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu)
{ {
uint32_t mme_ctrl_teid = mb_resp_pdu->header.teid; uint32_t mme_ctrl_teid = mb_resp_pdu->header.teid;
std::map<uint32_t,uint64_t>::iterator imsi_it = m_mme_ctr_teid_to_imsi.find(mme_ctrl_teid); std::map<uint32_t,uint64_t>::iterator imsi_it = m_mme_ctr_teid_to_imsi.find(mme_ctrl_teid);
if(imsi_it == m_mme_ctr_teid_to_imsi.end()) if (imsi_it == m_mme_ctr_teid_to_imsi.end()) {
{
m_mme_gtpc_log->error("Could not find IMSI from control TEID\n"); m_mme_gtpc_log->error("Could not find IMSI from control TEID\n");
return; return;
} }
@ -310,20 +299,21 @@ mme_gtpc::handle_modify_bearer_response(srslte::gtpc_pdu *mb_resp_pdu)
return; return;
} }
void bool
mme_gtpc::send_delete_session_request(uint64_t imsi) mme_gtpc::send_delete_session_request(uint64_t imsi)
{ {
m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request. IMSI %" PRIu64 "\n",imsi); m_mme_gtpc_log->info("Sending GTP-C Delete Session Request request. IMSI %" PRIu64 "\n",imsi);
srslte::gtpc_pdu del_req_pdu; srslte::gtpc_pdu del_req_pdu;
srslte::gtp_fteid_t sgw_ctr_fteid; srslte::gtp_fteid_t sgw_ctr_fteid;
srslte::gtp_fteid_t mme_ctr_fteid; srslte::gtp_fteid_t mme_ctr_fteid;
//Get S-GW Ctr TEID //Get S-GW Ctr TEID
std::map<uint64_t,gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi); std::map<uint64_t,gtpc_ctx_t>::iterator it_ctx = m_imsi_to_gtpc_ctx.find(imsi);
if(it_ctx == m_imsi_to_gtpc_ctx.end()) if (it_ctx == m_imsi_to_gtpc_ctx.end()) {
{
m_mme_gtpc_log->error("Could not find GTP-C context to remove\n"); m_mme_gtpc_log->error("Could not find GTP-C context to remove\n");
return; return false;
} }
sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid; sgw_ctr_fteid = it_ctx->second.sgw_ctr_fteid;
mme_ctr_fteid = it_ctx->second.mme_ctr_fteid; mme_ctr_fteid = it_ctx->second.mme_ctr_fteid;
srslte::gtpc_header *header = &del_req_pdu.header; srslte::gtpc_header *header = &del_req_pdu.header;
@ -342,16 +332,13 @@ mme_gtpc::send_delete_session_request(uint64_t imsi)
//Delete GTP-C context //Delete GTP-C context
std::map<uint32_t,uint64_t>::iterator it_imsi = m_mme_ctr_teid_to_imsi.find(mme_ctr_fteid.teid); std::map<uint32_t,uint64_t>::iterator it_imsi = m_mme_ctr_teid_to_imsi.find(mme_ctr_fteid.teid);
if(it_imsi == m_mme_ctr_teid_to_imsi.end()) if (it_imsi == m_mme_ctr_teid_to_imsi.end()) {
{
m_mme_gtpc_log->error("Could not find IMSI from MME ctr TEID"); m_mme_gtpc_log->error("Could not find IMSI from MME ctr TEID");
} } else {
else
{
m_mme_ctr_teid_to_imsi.erase(it_imsi); m_mme_ctr_teid_to_imsi.erase(it_imsi);
} }
m_imsi_to_gtpc_ctx.erase(it_ctx); m_imsi_to_gtpc_ctx.erase(it_ctx);
return; return true;
} }
void void

File diff suppressed because it is too large Load Diff

@ -52,7 +52,6 @@ s1ap::~s1ap()
s1ap* s1ap*
s1ap::get_instance(void) s1ap::get_instance(void)
{ {
pthread_mutex_lock(&s1ap_instance_mutex); pthread_mutex_lock(&s1ap_instance_mutex);
if(m_instance == NULL) { if(m_instance == NULL) {
m_instance = new s1ap(); m_instance = new s1ap();
@ -73,7 +72,7 @@ s1ap::cleanup(void)
} }
int int
s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1ap * hss_) s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *nas_log, srslte::log_filter *s1ap_log, hss_interface_nas * hss)
{ {
m_pool = srslte::byte_buffer_pool::get_instance(); m_pool = srslte::byte_buffer_pool::get_instance();
@ -81,10 +80,11 @@ s1ap::init(s1ap_args_t s1ap_args, srslte::log_filter *s1ap_log, hss_interface_s1
srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn); srslte::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn);
m_next_m_tmsi = rand(); m_next_m_tmsi = rand();
//Init log //Init log
m_nas_log = nas_log;
m_s1ap_log = s1ap_log; m_s1ap_log = s1ap_log;
//Get pointer to the HSS //Get pointer to the HSS
m_hss = hss_; m_hss = hss;
//Init message handlers //Init message handlers
m_s1ap_mngmt_proc = s1ap_mngmt_proc::get_instance(); //Managment procedures m_s1ap_mngmt_proc = s1ap_mngmt_proc::get_instance(); //Managment procedures
@ -115,22 +115,21 @@ s1ap::stop()
close(m_s1mme); close(m_s1mme);
} }
std::map<uint16_t,enb_ctx_t*>::iterator enb_it = m_active_enbs.begin(); std::map<uint16_t,enb_ctx_t*>::iterator enb_it = m_active_enbs.begin();
while(enb_it!=m_active_enbs.end()) while (enb_it!=m_active_enbs.end()) {
{
m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id); m_s1ap_log->info("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id);
m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id); m_s1ap_log->console("Deleting eNB context. eNB Id: 0x%x\n", enb_it->second->enb_id);
delete enb_it->second; delete enb_it->second;
m_active_enbs.erase(enb_it++); m_active_enbs.erase(enb_it++);
} }
std::map<uint64_t,ue_ctx_t*>::iterator ue_it = m_imsi_to_ue_ctx.begin(); std::map<uint64_t, nas*>::iterator ue_it = m_imsi_to_nas_ctx.begin();
while(ue_it!=m_imsi_to_ue_ctx.end()) while (ue_it!=m_imsi_to_nas_ctx.end()) {
{
m_s1ap_log->info("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first); m_s1ap_log->info("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first);
m_s1ap_log->console("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first); m_s1ap_log->console("Deleting UE EMM context. IMSI: %015lu\n", ue_it->first);
delete ue_it->second; delete ue_it->second;
m_imsi_to_ue_ctx.erase(ue_it++); m_imsi_to_nas_ctx.erase(ue_it++);
} }
//Cleanup message handlers //Cleanup message handlers
s1ap_mngmt_proc::cleanup(); s1ap_mngmt_proc::cleanup();
s1ap_nas_transport::cleanup(); s1ap_nas_transport::cleanup();
@ -371,63 +370,54 @@ s1ap::delete_enb_ctx(int32_t assoc_id)
//UE Context Management //UE Context Management
bool bool
s1ap::add_ue_ctx_to_imsi_map(ue_ctx_t *ue_ctx) s1ap::add_nas_ctx_to_imsi_map(nas *nas_ctx)
{ {
std::map<uint64_t, ue_ctx_t*>::iterator ctx_it = m_imsi_to_ue_ctx.find(ue_ctx->emm_ctx.imsi); std::map<uint64_t, nas*>::iterator ctx_it = m_imsi_to_nas_ctx.find(nas_ctx->m_emm_ctx.imsi);
if(ctx_it != m_imsi_to_ue_ctx.end()) if (ctx_it != m_imsi_to_nas_ctx.end()) {
{ m_s1ap_log->error("UE Context already exists. IMSI %015lu",nas_ctx->m_emm_ctx.imsi);
m_s1ap_log->error("UE Context already exists. IMSI %015lu",ue_ctx->emm_ctx.imsi);
return false; return false;
} }
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0) if (nas_ctx->m_ecm_ctx.mme_ue_s1ap_id != 0) {
{ std::map<uint32_t,nas*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_nas_ctx.find(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
std::map<uint32_t,ue_ctx_t*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); if(ctx_it2 != m_mme_ue_s1ap_id_to_nas_ctx.end() && ctx_it2->second != nas_ctx) {
if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx)
{
m_s1ap_log->error("Context identified with IMSI does not match context identified by MME UE S1AP Id.\n"); m_s1ap_log->error("Context identified with IMSI does not match context identified by MME UE S1AP Id.\n");
return false; return false;
} }
} }
m_imsi_to_ue_ctx.insert(std::pair<uint64_t,ue_ctx_t*>(ue_ctx->emm_ctx.imsi, ue_ctx)); m_imsi_to_nas_ctx.insert(std::pair<uint64_t,nas*>(nas_ctx->m_emm_ctx.imsi, nas_ctx));
m_s1ap_log->debug("Saved UE context corresponding to IMSI %015lu\n",ue_ctx->emm_ctx.imsi); m_s1ap_log->debug("Saved UE context corresponding to IMSI %015lu\n",nas_ctx->m_emm_ctx.imsi);
return true; return true;
} }
bool bool
s1ap::add_ue_ctx_to_mme_ue_s1ap_id_map(ue_ctx_t *ue_ctx) s1ap::add_nas_ctx_to_mme_ue_s1ap_id_map(nas *nas_ctx)
{ {
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id == 0) if (nas_ctx->m_ecm_ctx.mme_ue_s1ap_id == 0) {
{
m_s1ap_log->error("Could not add UE context to MME UE S1AP map. MME UE S1AP ID 0 is not valid."); m_s1ap_log->error("Could not add UE context to MME UE S1AP map. MME UE S1AP ID 0 is not valid.");
return false; return false;
} }
std::map<uint32_t, ue_ctx_t*>::iterator ctx_it = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); std::map<uint32_t, nas*>::iterator ctx_it = m_mme_ue_s1ap_id_to_nas_ctx.find(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
if(ctx_it != m_mme_ue_s1ap_id_to_ue_ctx.end()) if (ctx_it != m_mme_ue_s1ap_id_to_nas_ctx.end()) {
{ m_s1ap_log->error("UE Context already exists. MME UE S1AP Id %015lu",nas_ctx->m_emm_ctx.imsi);
m_s1ap_log->error("UE Context already exists. MME UE S1AP Id %015lu",ue_ctx->emm_ctx.imsi);
return false; return false;
} }
if(ue_ctx->ecm_ctx.imsi != 0) if (nas_ctx->m_emm_ctx.imsi != 0) {
{ std::map<uint32_t,nas*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_nas_ctx.find(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
std::map<uint32_t,ue_ctx_t*>::iterator ctx_it2 = m_mme_ue_s1ap_id_to_ue_ctx.find(ue_ctx->ecm_ctx.mme_ue_s1ap_id); if (ctx_it2 != m_mme_ue_s1ap_id_to_nas_ctx.end() && ctx_it2->second != nas_ctx) {
if(ctx_it2 != m_mme_ue_s1ap_id_to_ue_ctx.end() && ctx_it2->second != ue_ctx)
{
m_s1ap_log->error("Context identified with MME UE S1AP Id does not match context identified by IMSI.\n"); m_s1ap_log->error("Context identified with MME UE S1AP Id does not match context identified by IMSI.\n");
return false; return false;
} }
} }
m_mme_ue_s1ap_id_to_ue_ctx.insert(std::pair<uint32_t,ue_ctx_t*>(ue_ctx->ecm_ctx.mme_ue_s1ap_id, ue_ctx)); m_mme_ue_s1ap_id_to_nas_ctx.insert(std::pair<uint32_t, nas*>(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id, nas_ctx));
m_s1ap_log->debug("Saved UE context corresponding to MME UE S1AP Id %d\n",ue_ctx->ecm_ctx.mme_ue_s1ap_id); m_s1ap_log->debug("Saved UE context corresponding to MME UE S1AP Id %d\n",nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
return true; return true;
} }
bool bool
s1ap::add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id) s1ap::add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id)
{ {
std::map<int32_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc); std::map<int32_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc);
if(ues_in_enb == m_enb_assoc_to_ue_ids.end()) if (ues_in_enb == m_enb_assoc_to_ue_ids.end()) {
{
m_s1ap_log->error("Could not find eNB from eNB SCTP association %d",enb_assoc); m_s1ap_log->error("Could not find eNB from eNB SCTP association %d",enb_assoc);
return false; return false;
} }
@ -442,30 +432,24 @@ s1ap::add_ue_to_enb_set(int32_t enb_assoc, uint32_t mme_ue_s1ap_id)
return true; return true;
} }
ue_ctx_t* nas*
s1ap::find_ue_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id) s1ap::find_nas_ctx_from_mme_ue_s1ap_id(uint32_t mme_ue_s1ap_id)
{ {
std::map<uint32_t, ue_ctx_t*>::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id); std::map<uint32_t, nas*>::iterator it = m_mme_ue_s1ap_id_to_nas_ctx.find(mme_ue_s1ap_id);
if(it == m_mme_ue_s1ap_id_to_ue_ctx.end()) if (it == m_mme_ue_s1ap_id_to_nas_ctx.end()) {
{
return NULL; return NULL;
} } else {
else
{
return it->second; return it->second;
} }
} }
ue_ctx_t* nas*
s1ap::find_ue_ctx_from_imsi(uint64_t imsi) s1ap::find_nas_ctx_from_imsi(uint64_t imsi)
{ {
std::map<uint64_t, ue_ctx_t*>::iterator it = m_imsi_to_ue_ctx.find(imsi); std::map<uint64_t, nas*>::iterator it = m_imsi_to_nas_ctx.find(imsi);
if(it == m_imsi_to_ue_ctx.end()) if (it == m_imsi_to_nas_ctx.end()) {
{
return NULL; return NULL;
} } else {
else
{
return it->second; return it->second;
} }
} }
@ -474,22 +458,18 @@ void
s1ap::release_ues_ecm_ctx_in_enb(int32_t enb_assoc) s1ap::release_ues_ecm_ctx_in_enb(int32_t enb_assoc)
{ {
m_s1ap_log->console("Releasing UEs context\n"); m_s1ap_log->console("Releasing UEs context\n");
//delete UEs ctx
std::map<int32_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc); std::map<int32_t,std::set<uint32_t> >::iterator ues_in_enb = m_enb_assoc_to_ue_ids.find(enb_assoc);
std::set<uint32_t>::iterator ue_id = ues_in_enb->second.begin(); std::set<uint32_t>::iterator ue_id = ues_in_enb->second.begin();
if(ue_id == ues_in_enb->second.end()) if (ue_id == ues_in_enb->second.end()) {
{
m_s1ap_log->console("No UEs to be released\n"); m_s1ap_log->console("No UEs to be released\n");
} else { } else {
while(ue_id != ues_in_enb->second.end() ) while (ue_id != ues_in_enb->second.end() ) {
{ std::map<uint32_t, nas*>::iterator nas_ctx = m_mme_ue_s1ap_id_to_nas_ctx.find(*ue_id);
std::map<uint32_t, ue_ctx_t*>::iterator ue_ctx = m_mme_ue_s1ap_id_to_ue_ctx.find(*ue_id); emm_ctx_t *emm_ctx = &nas_ctx->second->m_emm_ctx;
ue_emm_ctx_t *emm_ctx = &ue_ctx->second->emm_ctx; ecm_ctx_t *ecm_ctx = &nas_ctx->second->m_ecm_ctx;
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->second->ecm_ctx;
m_s1ap_log->info("Releasing UE context. IMSI: %015lu, UE-MME S1AP Id: %d\n", emm_ctx->imsi, ecm_ctx->mme_ue_s1ap_id); m_s1ap_log->info("Releasing UE context. IMSI: %015lu, UE-MME S1AP Id: %d\n", emm_ctx->imsi, ecm_ctx->mme_ue_s1ap_id);
if(emm_ctx->state == EMM_STATE_REGISTERED) if(emm_ctx->state == EMM_STATE_REGISTERED) {
{
m_mme_gtpc->send_delete_session_request(emm_ctx->imsi); m_mme_gtpc->send_delete_session_request(emm_ctx->imsi);
emm_ctx->state = EMM_STATE_DEREGISTERED; emm_ctx->state = EMM_STATE_DEREGISTERED;
} }
@ -505,32 +485,29 @@ s1ap::release_ues_ecm_ctx_in_enb(int32_t enb_assoc)
bool bool
s1ap::release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id) s1ap::release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id)
{ {
ue_ctx_t *ue_ctx = find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); nas *nas_ctx = find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL) if (nas_ctx == NULL) {
{
m_s1ap_log->error("Cannot release UE ECM context, UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->error("Cannot release UE ECM context, UE not found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false; return false;
} }
ue_ecm_ctx_t* ecm_ctx = &ue_ctx->ecm_ctx; ecm_ctx_t* ecm_ctx = &nas_ctx->m_ecm_ctx;
//Delete UE within eNB UE set //Delete UE within eNB UE set
std::map<int32_t,uint16_t>::iterator it = m_sctp_to_enb_id.find(ecm_ctx->enb_sri.sinfo_assoc_id); std::map<int32_t,uint16_t>::iterator it = m_sctp_to_enb_id.find(ecm_ctx->enb_sri.sinfo_assoc_id);
if(it == m_sctp_to_enb_id.end() ) if (it == m_sctp_to_enb_id.end() ) {
{
m_s1ap_log->error("Could not find eNB for UE release request.\n"); m_s1ap_log->error("Could not find eNB for UE release request.\n");
return false; return false;
} }
uint16_t enb_id = it->second; uint16_t enb_id = it->second;
std::map<int32_t,std::set<uint32_t> >::iterator ue_set = m_enb_assoc_to_ue_ids.find(ecm_ctx->enb_sri.sinfo_assoc_id); std::map<int32_t,std::set<uint32_t> >::iterator ue_set = m_enb_assoc_to_ue_ids.find(ecm_ctx->enb_sri.sinfo_assoc_id);
if(ue_set == m_enb_assoc_to_ue_ids.end()) if (ue_set == m_enb_assoc_to_ue_ids.end()) {
{
m_s1ap_log->error("Could not find the eNB's UEs.\n"); m_s1ap_log->error("Could not find the eNB's UEs.\n");
return false; return false;
} }
ue_set->second.erase(mme_ue_s1ap_id); ue_set->second.erase(mme_ue_s1ap_id);
//Release UE ECM context //Release UE ECM context
m_mme_ue_s1ap_id_to_ue_ctx.erase(mme_ue_s1ap_id); m_mme_ue_s1ap_id_to_nas_ctx.erase(mme_ue_s1ap_id);
ecm_ctx->state = ECM_STATE_IDLE; ecm_ctx->state = ECM_STATE_IDLE;
ecm_ctx->mme_ue_s1ap_id = 0; ecm_ctx->mme_ue_s1ap_id = 0;
ecm_ctx->enb_ue_s1ap_id = 0; ecm_ctx->enb_ue_s1ap_id = 0;
@ -542,22 +519,20 @@ s1ap::release_ue_ecm_ctx(uint32_t mme_ue_s1ap_id)
bool bool
s1ap::delete_ue_ctx(uint64_t imsi) s1ap::delete_ue_ctx(uint64_t imsi)
{ {
ue_ctx_t *ue_ctx = find_ue_ctx_from_imsi(imsi); nas *nas_ctx = find_nas_ctx_from_imsi(imsi);
if(ue_ctx == NULL) if (nas_ctx == NULL) {
{
m_s1ap_log->info("Cannot delete UE context, UE not found. IMSI: %" PRIu64 "\n", imsi); m_s1ap_log->info("Cannot delete UE context, UE not found. IMSI: %" PRIu64 "\n", imsi);
return false; return false;
} }
//Make sure to release ECM ctx //Make sure to release ECM ctx
if(ue_ctx->ecm_ctx.mme_ue_s1ap_id != 0) if (nas_ctx->m_ecm_ctx.mme_ue_s1ap_id != 0) {
{ release_ue_ecm_ctx(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id);
} }
//Delete UE context //Delete UE context
m_imsi_to_ue_ctx.erase(imsi); m_imsi_to_nas_ctx.erase(imsi);
delete ue_ctx; delete nas_ctx;
m_s1ap_log->info("Deleted UE Context.\n"); m_s1ap_log->info("Deleted UE Context.\n");
return true; return true;
} }
@ -569,30 +544,28 @@ s1ap::delete_ue_ctx(uint64_t imsi)
void void
s1ap::activate_eps_bearer(uint64_t imsi, uint8_t ebi) s1ap::activate_eps_bearer(uint64_t imsi, uint8_t ebi)
{ {
std::map<uint64_t,ue_ctx_t*>::iterator ue_ctx_it = m_imsi_to_ue_ctx.find(imsi); std::map<uint64_t,nas*>::iterator ue_ctx_it = m_imsi_to_nas_ctx.find(imsi);
if(ue_ctx_it == m_imsi_to_ue_ctx.end()) if (ue_ctx_it == m_imsi_to_nas_ctx.end()) {
{
m_s1ap_log->error("Could not activate EPS bearer: Could not find UE context\n"); m_s1ap_log->error("Could not activate EPS bearer: Could not find UE context\n");
return; return;
} }
//Make sure NAS is active //Make sure NAS is active
uint32_t mme_ue_s1ap_id = ue_ctx_it->second->ecm_ctx.mme_ue_s1ap_id; uint32_t mme_ue_s1ap_id = ue_ctx_it->second->m_ecm_ctx.mme_ue_s1ap_id;
std::map<uint32_t,ue_ctx_t*>::iterator it = m_mme_ue_s1ap_id_to_ue_ctx.find(mme_ue_s1ap_id); std::map<uint32_t,nas*>::iterator it = m_mme_ue_s1ap_id_to_nas_ctx.find(mme_ue_s1ap_id);
if(it == m_mme_ue_s1ap_id_to_ue_ctx.end()) if (it == m_mme_ue_s1ap_id_to_nas_ctx.end()) {
{
m_s1ap_log->error("Could not activate EPS bearer: ECM context seems to be missing\n"); m_s1ap_log->error("Could not activate EPS bearer: ECM context seems to be missing\n");
return; return;
} }
ue_ecm_ctx_t * ecm_ctx = &ue_ctx_it->second->ecm_ctx; ecm_ctx_t * ecm_ctx = &ue_ctx_it->second->m_ecm_ctx;
if (ecm_ctx->erabs_ctx[ebi].state != ERAB_CTX_SETUP) esm_ctx_t * esm_ctx = &ue_ctx_it->second->m_esm_ctx[ebi];
{ if (esm_ctx->state != ERAB_CTX_SETUP) {
m_s1ap_log->error("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, ecm_ctx->erabs_ctx[ebi].state); m_s1ap_log->error("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, esm_ctx->state);
m_s1ap_log->console("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, ecm_ctx->erabs_ctx[ebi].state); m_s1ap_log->console("Could not be activate EPS Bearer, bearer in wrong state: MME S1AP Id %d, EPS Bearer id %d, state %d\n", mme_ue_s1ap_id, ebi, esm_ctx->state);
return; return;
} }
ecm_ctx->erabs_ctx[ebi].state = ERAB_ACTIVE; esm_ctx->state = ERAB_ACTIVE;
ecm_ctx->state = ECM_STATE_CONNECTED; ecm_ctx->state = ECM_STATE_CONNECTED;
m_s1ap_log->info("Activated EPS Bearer: Bearer id %d\n",ebi); m_s1ap_log->info("Activated EPS Bearer: Bearer id %d\n",ebi);
return; return;
@ -609,18 +582,28 @@ s1ap::allocate_m_tmsi(uint64_t imsi)
return m_tmsi; return m_tmsi;
} }
uint64_t
s1ap::find_imsi_from_m_tmsi(uint32_t m_tmsi)
{
std::map<uint32_t,uint64_t>::iterator it = m_tmsi_to_imsi.find(m_tmsi);
if (it != m_tmsi_to_imsi.end()) {
m_s1ap_log->debug("Found IMSI %015lu from M-TMSI 0x%x\n", it->second, m_tmsi);
return it->second;
} else {
m_s1ap_log->debug("Could not find IMSI from M-TMSI 0x%x\n", m_tmsi);
return 0;
}
}
void void
s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx) s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx)
{ {
std::string mnc_str, mcc_str; std::string mnc_str, mcc_str;
if(enb_ctx.enb_name_present) 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->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); 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 {
else
{
m_s1ap_log->console("%s - eNB Id 0x%x\n",prefix.c_str(), enb_ctx.enb_id); 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); m_s1ap_log->info("%s - eNB Id 0x%x\n", prefix.c_str(), enb_ctx.enb_id);
} }
@ -628,10 +611,8 @@ s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx)
srslte::mnc_to_string(enb_ctx.mnc, &mnc_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->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); 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 i=0;i<enb_ctx.nof_supported_ta;i++) {
{ for(int j=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->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 - TAC %d, B-PLMN %d\n",prefix.c_str(), enb_ctx.tac[i],enb_ctx.bplmns[i][j]);
} }
@ -640,5 +621,38 @@ s1ap::print_enb_ctx_info(const std::string &prefix, const enb_ctx_t &enb_ctx)
return; return;
} }
} //namespace srsepc /*
* Interfaces
*/
/*GTP-C||NAS -> S1AP interface*/
bool
s1ap::send_initial_context_setup_request(uint64_t imsi, uint16_t erab_to_setup)
{
nas* nas_ctx = find_nas_ctx_from_imsi(imsi);
if (nas_ctx == NULL) {
m_s1ap_log->error("Error finding NAS context when sending initial context Setup Request\n");
return false;
}
m_s1ap_ctx_mngmt_proc->send_initial_context_setup_request(nas_ctx, erab_to_setup);
return true;
}
/*NAS -> S1AP interface*/
bool
s1ap::send_ue_context_release_command(uint32_t mme_ue_s1ap_id)
{
nas* nas_ctx = find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if (nas_ctx == NULL) {
m_s1ap_log->error("Error finding NAS context when sending UE Context Setup Release\n");
return false;
}
m_s1ap_ctx_mngmt_proc->send_ue_context_release_command(nas_ctx);
return true;
}
bool
s1ap::send_downlink_nas_transport(uint32_t enb_ue_s1ap_id, uint32_t mme_ue_s1ap_id, srslte::byte_buffer_t *nas_msg, struct sctp_sndrcvinfo enb_sri)
{
return m_s1ap_nas_transport->send_downlink_nas_transport(enb_ue_s1ap_id, mme_ue_s1ap_id, nas_msg, enb_sri);
}
} //namespace srsepc

@ -79,13 +79,8 @@ s1ap_ctx_mngmt_proc::init(void)
} }
bool bool
s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx, s1ap_ctx_mngmt_proc::send_initial_context_setup_request(nas *nas_ctx, uint16_t erab_to_setup)
ue_ecm_ctx_t *ecm_ctx,
erab_ctx_t *erab_ctx)
{ {
int s1mme = m_s1ap->get_s1_mme();
//Prepare reply PDU //Prepare reply PDU
LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; LIBLTE_S1AP_S1AP_PDU_STRUCT pdu;
bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT)); bzero(&pdu, sizeof(LIBLTE_S1AP_S1AP_PDU_STRUCT));
@ -96,52 +91,55 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST; init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST;
LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *in_ctxt_req = &init->choice.InitialContextSetupRequest; LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *in_ctxt_req = &init->choice.InitialContextSetupRequest;
LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctx_req = &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"); m_s1ap_log->info("Preparing to send Initial Context Setup request\n");
//Get UE Context/E-RAB Context to setup
emm_ctx_t *emm_ctx = &nas_ctx->m_emm_ctx;
ecm_ctx_t *ecm_ctx = &nas_ctx->m_ecm_ctx;
esm_ctx_t *esm_ctx = &nas_ctx->m_esm_ctx[erab_to_setup];
sec_ctx_t *sec_ctx = &nas_ctx->m_sec_ctx;
//Add MME and eNB S1AP Ids //Add MME and eNB S1AP Ids
in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id; in_ctxt_req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id;
in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id; in_ctxt_req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id;
//Set UE-AMBR //Set UE-AMBR
in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000; in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateDL.BitRate=1000000000;
in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL.BitRate=1000000000;//FIXME Get UE-AMBR from HSS in_ctxt_req->uEaggregateMaximumBitrate.uEaggregateMaximumBitRateUL.BitRate=1000000000;
//Setup eRAB context //Number of E-RABs to be setup
in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1; in_ctxt_req->E_RABToBeSetupListCtxtSUReq.len = 1;
erab_ctx_req->e_RAB_ID.E_RAB_ID = erab_ctx->erab_id;
//Setup eRAB context
LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab_ctx_req = &in_ctxt_req->E_RABToBeSetupListCtxtSUReq.buffer[0];
erab_ctx_req->e_RAB_ID.E_RAB_ID = esm_ctx->erab_id;
//Setup E-RAB QoS parameters //Setup E-RAB QoS parameters
erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = erab_ctx->qci; erab_ctx_req->e_RABlevelQoSParameters.qCI.QCI = esm_ctx->qci;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.priorityLevel.PriorityLevel = 15 ;//Lowest
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION; erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionCapability = LIBLTE_S1AP_PRE_EMPTIONCAPABILITY_SHALL_NOT_TRIGGER_PRE_EMPTION;
erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE; erab_ctx_req->e_RABlevelQoSParameters.allocationRetentionPriority.pre_emptionVulnerability = LIBLTE_S1AP_PRE_EMPTIONVULNERABILITY_PRE_EMPTABLE;
erab_ctx_req->e_RABlevelQoSParameters.gbrQosInformation_present=false; erab_ctx_req->e_RABlevelQoSParameters.gbrQosInformation_present=false;
//Set E-RAB S-GW F-TEID //Set E-RAB S-GW F-TEID
erab_ctx_req->transportLayerAddress.n_bits = 32; //IPv4 erab_ctx_req->transportLayerAddress.n_bits = 32; //IPv4
uint32_t sgw_s1u_ip = htonl(erab_ctx->sgw_s1u_fteid.ipv4); uint32_t sgw_s1u_ip = htonl(esm_ctx->sgw_s1u_fteid.ipv4);
//uint32_t sgw_s1u_ip = cs_resp->eps_bearer_context_created.s1_u_sgw_f_teid.ipv4; uint8_t *tmp_ptr = erab_ctx_req->transportLayerAddress.buffer;
uint8_t *tmp_ptr = erab_ctx_req->transportLayerAddress.buffer; liblte_value_2_bits(sgw_s1u_ip, &tmp_ptr, 32);
liblte_value_2_bits(sgw_s1u_ip, &tmp_ptr, 32);//FIXME consider ipv6
uint32_t sgw_s1u_teid = erab_ctx->sgw_s1u_fteid.teid; uint32_t sgw_s1u_teid = esm_ctx->sgw_s1u_fteid.teid;
srslte::uint32_to_uint8(sgw_s1u_teid,erab_ctx_req->gTP_TEID.buffer); srslte::uint32_to_uint8(sgw_s1u_teid,erab_ctx_req->gTP_TEID.buffer);
//Set UE security capabilities and k_enb //Set UE security capabilities and k_enb
bzero(in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer,sizeof(uint8_t)*16); bzero(in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer,sizeof(uint8_t)*16);
bzero(in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer,sizeof(uint8_t)*16); bzero(in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer,sizeof(uint8_t)*16);
for (int i = 0; i<3; i++) { for (int i = 0; i<3; i++) {
if(emm_ctx->security_ctxt.ue_network_cap.eea[i+1] == true){ if(sec_ctx->ue_network_cap.eea[i+1] == true){
in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 1; //EEA supported in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 1; //EEA supported
} else { } else {
in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 0; //EEA not supported in_ctxt_req->UESecurityCapabilities.encryptionAlgorithms.buffer[i] = 0; //EEA not supported
} }
if(emm_ctx->security_ctxt.ue_network_cap.eia[i+1] == true){ if(sec_ctx->ue_network_cap.eia[i+1] == true){
in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 1; //EEA supported in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 1; //EEA supported
} else { } else {
in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 0; //EEA not supported in_ctxt_req->UESecurityCapabilities.integrityProtectionAlgorithms.buffer[i] = 0; //EEA not supported
@ -149,31 +147,35 @@ s1ap_ctx_mngmt_proc::send_initial_context_setup_request(ue_emm_ctx_t *emm_ctx,
} }
//Get K eNB //Get K eNB
liblte_unpack(emm_ctx->security_ctxt.k_enb, 32, in_ctxt_req->SecurityKey.buffer); liblte_unpack(sec_ctx->k_enb, 32, in_ctxt_req->SecurityKey.buffer);
m_s1ap_log->info_hex(emm_ctx->security_ctxt.k_enb, 32, "Initial Context Setup Request -- Key eNB (k_enb)\n"); m_s1ap_log->info_hex(sec_ctx->k_enb, 32, "Initial Context Setup Request -- Key eNB (k_enb)\n");
srslte::byte_buffer_t *nas_buffer = m_pool->allocate(); srslte::byte_buffer_t *nas_buffer = m_pool->allocate();
if (emm_ctx->state == EMM_STATE_DEREGISTERED) { if (emm_ctx->state == EMM_STATE_DEREGISTERED) {
//Attach procedure initiated from an attach request //Attach procedure initiated from an attach request
m_s1ap_log->console("Adding attach accept to Initial Context Setup Request\n"); m_s1ap_log->console("Adding attach accept to Initial Context Setup Request\n");
m_s1ap_log->info("Adding attach accept to Initial Context Setup Request\n"); m_s1ap_log->info("Adding attach accept to Initial Context Setup Request\n");
m_s1ap_nas_transport->pack_attach_accept(emm_ctx, ecm_ctx, erab_ctx_req, &erab_ctx->pdn_addr_alloc, nas_buffer); nas_ctx->pack_attach_accept(nas_buffer);
}
//Add nas message to context setup request
erab_ctx_req->nAS_PDU_present = true;
memcpy(erab_ctx_req->nAS_PDU.buffer, nas_buffer->msg, nas_buffer->N_bytes);
erab_ctx_req->nAS_PDU.n_octets = nas_buffer->N_bytes;
}
srslte::byte_buffer_t *reply_buffer = m_pool->allocate();
LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer); LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer);
if (err != LIBLTE_SUCCESS) { if (err != LIBLTE_SUCCESS) {
m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n"); m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n");
return false; return false;
} }
if (!m_s1ap->s1ap_tx_pdu(reply_buffer,&ecm_ctx->enb_sri)) { if (!m_s1ap->s1ap_tx_pdu(reply_buffer,&ecm_ctx->enb_sri)) {
m_s1ap_log->error("Error sending Initial Context Setup Request.\n"); m_s1ap_log->error("Error sending Initial Context Setup Request.\n");
return false; return false;
} }
//Change E-RAB state to Context Setup Requested and save S-GW control F-TEID //Change E-RAB state to Context Setup Requested and save S-GW control F-TEID
ecm_ctx->erabs_ctx[erab_ctx_req->e_RAB_ID.E_RAB_ID].state = ERAB_CTX_REQUESTED; esm_ctx->state = ERAB_CTX_REQUESTED;
struct in_addr addr; struct in_addr addr;
addr.s_addr = htonl(sgw_s1u_ip); addr.s_addr = htonl(sgw_s1u_ip);
@ -194,53 +196,50 @@ bool
s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *in_ctxt_resp) 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; 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_from_mme_ue_s1ap_id(mme_ue_s1ap_id); nas *nas_ctx = m_s1ap->find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if (ue_ctx == NULL) if (nas_ctx == NULL){
{
m_s1ap_log->error("Could not find UE's context in active UE's map\n"); m_s1ap_log->error("Could not find UE's context in active UE's map\n");
return false; return false;
} }
ue_emm_ctx_t * emm_ctx = &ue_ctx->emm_ctx;
ue_ecm_ctx_t * ecm_ctx = &ue_ctx->ecm_ctx; emm_ctx_t * emm_ctx = &nas_ctx->m_emm_ctx;
ecm_ctx_t * ecm_ctx = &nas_ctx->m_ecm_ctx;
m_s1ap_log->console("Received Initial Context Setup Response\n"); m_s1ap_log->console("Received Initial Context Setup Response\n");
//Setup E-RABs //Setup E-RABs
for(uint32_t i=0; i<in_ctxt_resp->E_RABSetupListCtxtSURes.len;i++) 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; uint8_t erab_id = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].e_RAB_ID.E_RAB_ID;
erab_ctx_t *erab_ctx = &ecm_ctx->erabs_ctx[erab_id]; esm_ctx_t *esm_ctx = &nas_ctx->m_esm_ctx[erab_id];
if (erab_ctx->state != ERAB_CTX_REQUESTED) if (esm_ctx->state != ERAB_CTX_REQUESTED) {
{
m_s1ap_log->error("E-RAB requested was not previously requested %d\n",erab_id); m_s1ap_log->error("E-RAB requested was not previously requested %d\n",erab_id);
return false; return false;
} }
//Mark E-RAB with context setup //Mark E-RAB with context setup
erab_ctx->state = ERAB_CTX_SETUP; esm_ctx->state = ERAB_CTX_SETUP;
//Set the GTP information //Set the GTP information
uint8_t *bit_ptr = in_ctxt_resp->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer; 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)); esm_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); memcpy(&esm_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); esm_ctx->enb_fteid.teid = ntohl(esm_ctx->enb_fteid.teid);
char enb_addr_str[INET_ADDRSTRLEN+1]; 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)); const char *err = inet_ntop(AF_INET, &esm_ctx->enb_fteid.ipv4,enb_addr_str,sizeof(enb_addr_str));
if(err == NULL) if (err == NULL) {
{
m_s1ap_log->error("Error converting IP to string\n"); 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 Setup. E-RAB id %d\n",esm_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->info("E-RAB Context -- eNB TEID 0x%x, eNB Address %s\n", esm_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 Setup. E-RAB id %d\n",esm_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); m_s1ap_log->console("E-RAB Context -- eNB TEID 0x%x; eNB GTP-U Address %s\n", esm_ctx->enb_fteid.teid, enb_addr_str);
} }
if(emm_ctx->state == EMM_STATE_REGISTERED)
{ if (emm_ctx->state == EMM_STATE_REGISTERED) {
m_s1ap_log->console("Initial Context Setup Response triggered from Service Request.\n"); m_s1ap_log->console("Initial Context Setup Response triggered from Service Request.\n");
m_s1ap_log->console("Sending Modify Bearer Request.\n"); m_s1ap_log->console("Sending Modify Bearer Request.\n");
m_mme_gtpc->send_modify_bearer_request(emm_ctx->imsi, &ecm_ctx->erabs_ctx[5]); m_mme_gtpc->send_modify_bearer_request(emm_ctx->imsi, 5, &nas_ctx->m_esm_ctx[5].enb_fteid);
} }
return true; return true;
} }
@ -248,49 +247,45 @@ s1ap_ctx_mngmt_proc::handle_initial_context_setup_response(LIBLTE_S1AP_MESSAGE_I
bool 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) 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)
{ {
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT ue_rel_req; LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT ue_rel_req;
uint32_t mme_ue_s1ap_id = ue_rel->MME_UE_S1AP_ID.MME_UE_S1AP_ID; 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->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); 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_from_mme_ue_s1ap_id(mme_ue_s1ap_id); nas * nas_ctx = m_s1ap->find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL) if (nas_ctx == NULL) {
{
m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false; return false;
} }
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx;
emm_ctx_t *emm_ctx = &nas_ctx->m_emm_ctx;
ecm_ctx_t *ecm_ctx = &nas_ctx->m_ecm_ctx;
//Delete user plane context at the SPGW (but keep GTP-C connection). //Delete user plane context at the SPGW (but keep GTP-C connection).
if (ecm_ctx->state == ECM_STATE_CONNECTED) if (ecm_ctx->state == ECM_STATE_CONNECTED) {
{
//There are active E-RABs, send release access mearers request //There are active E-RABs, send release access mearers request
m_s1ap_log->console("There are active E-RABs, send release access mearers request\n"); m_s1ap_log->console("There are active E-RABs, send release access bearers request\n");
m_s1ap_log->info("There are active E-RABs, send release access mearers request\n"); m_s1ap_log->info("There are active E-RABs, send release access bearers request\n");
//The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED //The handle_release_access_bearers_response function will make sure to mark E-RABS DEACTIVATED
//It will release the UEs downstream S1-u and keep the upstream S1-U connection active. //It will release the UEs downstream S1-u and keep the upstream S1-U connection active.
m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi); m_mme_gtpc->send_release_access_bearers_request(emm_ctx->imsi);
//Send release context command to enb, so that it can release it's bearers //Send release context command to enb, so that it can release it's bearers
send_ue_context_release_command(ecm_ctx,reply_buffer); send_ue_context_release_command(nas_ctx);
} } else {
else
{
//No ECM Context to release //No ECM Context to release
m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); m_s1ap_log->info("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
//Make sure E-RABS are merked as DEACTIVATED. //Make sure E-RABS are merked as DEACTIVATED.
for(int i=0;i<MAX_ERABS_PER_UE;i++) for (int i=0;i<MAX_ERABS_PER_UE;i++) {
{ nas_ctx->m_esm_ctx[i].state = ERAB_DEACTIVATED;
ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED;
} }
} }
//Delete UE context //Set UE context to ECM Idle
ecm_ctx->state = ECM_STATE_IDLE; ecm_ctx->state = ECM_STATE_IDLE;
ecm_ctx->enb_ue_s1ap_id = 0; ecm_ctx->enb_ue_s1ap_id = 0;
ecm_ctx->mme_ue_s1ap_id = 0; ecm_ctx->mme_ue_s1ap_id = 0;
@ -300,10 +295,9 @@ s1ap_ctx_mngmt_proc::handle_ue_context_release_request(LIBLTE_S1AP_MESSAGE_UECON
} }
bool bool
s1ap_ctx_mngmt_proc::send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srslte::byte_buffer_t *reply_buffer) s1ap_ctx_mngmt_proc::send_ue_context_release_command(nas *nas_ctx)
{ {
srslte::byte_buffer_t *reply_buffer = m_pool->allocate();
int s1mme = m_s1ap->get_s1_mme();
//Prepare reply PDU //Prepare reply PDU
LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; LIBLTE_S1AP_S1AP_PDU_STRUCT pdu;
@ -315,54 +309,54 @@ s1ap_ctx_mngmt_proc::send_ue_context_release_command(ue_ecm_ctx_t *ecm_ctx, srsl
init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND; init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND;
LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ctx_rel_cmd = &init->choice.UEContextReleaseCommand; LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *ctx_rel_cmd = &init->choice.UEContextReleaseCommand;
ctx_rel_cmd->UE_S1AP_IDs.choice_type = LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR; ctx_rel_cmd->UE_S1AP_IDs.choice_type = LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR;
ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.mME_UE_S1AP_ID.MME_UE_S1AP_ID = ecm_ctx->mme_ue_s1ap_id; ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.mME_UE_S1AP_ID.MME_UE_S1AP_ID = nas_ctx->m_ecm_ctx.mme_ue_s1ap_id;
ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ecm_ctx->enb_ue_s1ap_id; ctx_rel_cmd->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = nas_ctx->m_ecm_ctx.enb_ue_s1ap_id;
ctx_rel_cmd->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_NAS; ctx_rel_cmd->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_NAS;
ctx_rel_cmd->Cause.choice.nas.ext = false; ctx_rel_cmd->Cause.choice.nas.ext = false;
ctx_rel_cmd->Cause.choice.nas.e = LIBLTE_S1AP_CAUSENAS_NORMAL_RELEASE; ctx_rel_cmd->Cause.choice.nas.e = LIBLTE_S1AP_CAUSENAS_NORMAL_RELEASE;
LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer); LIBLTE_ERROR_ENUM err = liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)reply_buffer);
if(err != LIBLTE_SUCCESS) if (err != LIBLTE_SUCCESS) {
{ m_s1ap_log->error("Could not pack Context Release Command Message\n");
m_s1ap_log->error("Could not pack Initial Context Setup Request Message\n"); m_pool->deallocate(reply_buffer);
return false; return false;
} }
//Send Reply to eNB //Send Reply to eNB
if(!m_s1ap->s1ap_tx_pdu(reply_buffer,&ecm_ctx->enb_sri)) if(!m_s1ap->s1ap_tx_pdu(reply_buffer,&nas_ctx->m_ecm_ctx.enb_sri)){
{ m_s1ap_log->error("Error sending UE Context Release Command.\n");
m_s1ap_log->error("Error sending UE Context Release command.\n"); m_pool->deallocate(reply_buffer);
return false; return false;
} }
m_pool->deallocate(reply_buffer);
return true; return true;
} }
bool bool
s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp) s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *rel_comp)
{ {
uint32_t mme_ue_s1ap_id = rel_comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID; uint32_t mme_ue_s1ap_id = rel_comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID;
m_s1ap_log->info("Received UE Context Release Complete. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->info("Received UE Context Release Complete. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("Received UE Context Release Complete. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); m_s1ap_log->console("Received UE Context Release Complete. MME-UE S1AP Id %d\n", mme_ue_s1ap_id);
ue_ctx_t * ue_ctx = m_s1ap->find_ue_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); nas * nas_ctx = m_s1ap->find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id);
if(ue_ctx == NULL) if (nas_ctx == NULL) {
{
m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->info("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id); m_s1ap_log->console("No UE context to release found. MME-UE S1AP Id: %d\n", mme_ue_s1ap_id);
return false; return false;
} }
ue_ecm_ctx_t *ecm_ctx = &ue_ctx->ecm_ctx; emm_ctx_t *emm_ctx = &nas_ctx->m_emm_ctx;
ecm_ctx_t *ecm_ctx = &nas_ctx->m_ecm_ctx;
//Delete user plane context at the SPGW (but keep GTP-C connection). //Delete user plane context at the SPGW (but keep GTP-C connection).
if (ecm_ctx->state == ECM_STATE_CONNECTED) { if (ecm_ctx->state == ECM_STATE_CONNECTED) {
//There are active E-RABs, send release access mearers request //There are active E-RABs, send release access mearers request
m_s1ap_log->console("There are active E-RABs, send release access mearers request"); m_s1ap_log->console("There are active E-RABs, send release access mearers request");
m_s1ap_log->info("There are active E-RABs, send release access mearers request"); m_s1ap_log->info("There are active E-RABs, send release access mearers request");
m_mme_gtpc->send_release_access_bearers_request(ecm_ctx->imsi); m_mme_gtpc->send_release_access_bearers_request(emm_ctx->imsi);
//The handle_releease_access_bearers_response function will make sure to mark E-RABS DEACTIVATED //The handle_releease_access_bearers_response function will make sure to mark E-RABS DEACTIVATED
//It will release the UEs downstream S1-u and keep the upstream S1-U connection active. //It will release the UEs downstream S1-u and keep the upstream S1-U connection active.
} else { } else {
@ -371,12 +365,12 @@ s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(LIBLTE_S1AP_MESSAGE_UECO
m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id); m_s1ap_log->console("UE is not ECM connected. No need to release S1-U. MME UE S1AP Id %d\n", mme_ue_s1ap_id);
//Make sure E-RABS are marked as DEACTIVATED. //Make sure E-RABS are marked as DEACTIVATED.
for (int i=0;i<MAX_ERABS_PER_UE;i++) { for (int i=0;i<MAX_ERABS_PER_UE;i++) {
ecm_ctx->erabs_ctx[i].state = ERAB_DEACTIVATED; nas_ctx->m_esm_ctx[i].state = ERAB_DEACTIVATED;
} }
} }
//Delete UE context //Delete UE context
m_s1ap->release_ue_ecm_ctx(ue_ctx->ecm_ctx.mme_ue_s1ap_id); m_s1ap->release_ue_ecm_ctx(nas_ctx->m_ecm_ctx.mme_ue_s1ap_id);
m_s1ap_log->info("UE Context Release Completed.\n"); m_s1ap_log->info("UE Context Release Completed.\n");
m_s1ap_log->console("UE Context Release Completed.\n"); m_s1ap_log->console("UE Context Release Completed.\n");
return true; return true;

File diff suppressed because it is too large Load Diff

@ -157,11 +157,9 @@ spgw::stop()
srslte::error_t srslte::error_t
spgw::init_sgi_if(spgw_args_t *args) spgw::init_sgi_if(spgw_args_t *args)
{ {
char dev[IFNAMSIZ] = "srs_spgw_sgi";
struct ifreq ifr; struct ifreq ifr;
if(m_sgi_up) if (m_sgi_up) {
{
return(srslte::ERROR_ALREADY_STARTED); return(srslte::ERROR_ALREADY_STARTED);
} }
@ -169,39 +167,36 @@ spgw::init_sgi_if(spgw_args_t *args)
// Construct the TUN device // Construct the TUN device
m_sgi_if = open("/dev/net/tun", O_RDWR); m_sgi_if = open("/dev/net/tun", O_RDWR);
m_spgw_log->info("TUN file descriptor = %d\n", m_sgi_if); m_spgw_log->info("TUN file descriptor = %d\n", m_sgi_if);
if(m_sgi_if < 0) if (m_sgi_if < 0) {
{ m_spgw_log->error("Failed to open TUN device: %s\n", strerror(errno));
m_spgw_log->error("Failed to open TUN device: %s\n", strerror(errno)); return(srslte::ERROR_CANT_START);
return(srslte::ERROR_CANT_START);
} }
memset(&ifr, 0, sizeof(ifr)); memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI; ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ-1); strncpy(ifr.ifr_ifrn.ifrn_name, args->sgi_if_name.c_str(), std::min(args->sgi_if_name.length(), (size_t)(IFNAMSIZ-1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0'; ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1]='\0';
if(ioctl(m_sgi_if, TUNSETIFF, &ifr) < 0) if (ioctl(m_sgi_if, TUNSETIFF, &ifr) < 0) {
{ m_spgw_log->error("Failed to set TUN device name: %s\n", strerror(errno));
m_spgw_log->error("Failed to set TUN device name: %s\n", strerror(errno)); close(m_sgi_if);
close(m_sgi_if); return(srslte::ERROR_CANT_START);
return(srslte::ERROR_CANT_START);
} }
// Bring up the interface // Bring up the interface
m_sgi_sock = socket(AF_INET, SOCK_DGRAM, 0); m_sgi_sock = socket(AF_INET, SOCK_DGRAM, 0);
if(ioctl(m_sgi_sock, SIOCGIFFLAGS, &ifr) < 0) if (ioctl(m_sgi_sock, SIOCGIFFLAGS, &ifr) < 0) {
{ m_spgw_log->error("Failed to bring up socket: %s\n", strerror(errno));
m_spgw_log->error("Failed to bring up socket: %s\n", strerror(errno)); close(m_sgi_if);
close(m_sgi_if); return(srslte::ERROR_CANT_START);
return(srslte::ERROR_CANT_START);
} }
ifr.ifr_flags |= IFF_UP | IFF_RUNNING; ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if(ioctl(m_sgi_sock, SIOCSIFFLAGS, &ifr) < 0) if (ioctl(m_sgi_sock, SIOCSIFFLAGS, &ifr) < 0) {
{ m_spgw_log->error("Failed to set socket flags: %s\n", strerror(errno));
m_spgw_log->error("Failed to set socket flags: %s\n", strerror(errno)); close(m_sgi_if);
close(m_sgi_if); return(srslte::ERROR_CANT_START);
return(srslte::ERROR_CANT_START);
} }
//Set IP of the interface //Set IP of the interface
@ -238,8 +233,7 @@ spgw::init_s1u(spgw_args_t *args)
{ {
//Open S1-U socket //Open S1-U socket
m_s1u = socket(AF_INET,SOCK_DGRAM,0); m_s1u = socket(AF_INET,SOCK_DGRAM,0);
if (m_s1u == -1) if (m_s1u == -1) {
{
m_spgw_log->error("Failed to open socket: %s\n", strerror(errno)); m_spgw_log->error("Failed to open socket: %s\n", strerror(errno));
return srslte::ERROR_CANT_START; return srslte::ERROR_CANT_START;
} }
@ -283,8 +277,7 @@ spgw::run_thread()
fd_set set; fd_set set;
//struct timeval to; //struct timeval to;
int max_fd = std::max(m_s1u,sgi); int max_fd = std::max(m_s1u,sgi);
while (m_running) while (m_running) {
{
msg->reset(); msg->reset();
FD_ZERO(&set); FD_ZERO(&set);
FD_SET(m_s1u, &set); FD_SET(m_s1u, &set);
@ -292,26 +285,18 @@ spgw::run_thread()
//m_spgw_log->info("Waiting for S1-U or SGi packets.\n"); //m_spgw_log->info("Waiting for S1-U or SGi packets.\n");
int n = select(max_fd+1, &set, NULL, NULL, NULL); int n = select(max_fd+1, &set, NULL, NULL, NULL);
if (n == -1) if (n == -1) {
{
m_spgw_log->error("Error from select\n"); m_spgw_log->error("Error from select\n");
} } else if (n) {
else if (n) if (FD_ISSET(m_s1u, &set)) {
{
//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 ); msg->N_bytes = recvfrom(m_s1u, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES, 0, &src_addr, &addrlen );
handle_s1u_pdu(msg); handle_s1u_pdu(msg);
} }
if (FD_ISSET(m_sgi_if, &set)) if (FD_ISSET(m_sgi_if, &set)) {
{
msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES); msg->N_bytes = read(sgi, msg->msg, SRSLTE_MAX_BUFFER_SIZE_BYTES);
handle_sgi_pdu(msg); handle_sgi_pdu(msg);
} }
} } else {
else
{
m_spgw_log->debug("No data from select.\n"); m_spgw_log->debug("No data from select.\n");
} }
} }
@ -330,62 +315,54 @@ spgw::handle_sgi_pdu(srslte::byte_buffer_t *msg)
srslte::gtpc_f_teid_ie enb_fteid; srslte::gtpc_f_teid_ie enb_fteid;
struct iphdr *iph = (struct iphdr *) msg->msg; struct iphdr *iph = (struct iphdr *) msg->msg;
if(iph->version != 4) if (iph->version != 4) {
{
m_spgw_log->warning("IPv6 not supported yet.\n"); m_spgw_log->warning("IPv6 not supported yet.\n");
return; return;
} }
if(iph->tot_len < 20) if (iph->tot_len < 20) {
{
m_spgw_log->warning("Invalid IP header length.\n"); m_spgw_log->warning("Invalid IP header length.\n");
return; return;
} }
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
gtp_fteid_it = m_ip_to_teid.find(iph->daddr); gtp_fteid_it = m_ip_to_teid.find(iph->daddr);
if(gtp_fteid_it != m_ip_to_teid.end()) if (gtp_fteid_it != m_ip_to_teid.end()) {
{
ip_found = true; ip_found = true;
enb_fteid = gtp_fteid_it->second; enb_fteid = gtp_fteid_it->second;
} }
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
if(ip_found == false) if (ip_found == false) {
{
//m_spgw_log->console("IP Packet is not for any UE\n");
return; return;
} }
struct sockaddr_in enb_addr; struct sockaddr_in enb_addr;
enb_addr.sin_family = AF_INET; enb_addr.sin_family = AF_INET;
enb_addr.sin_port = htons(GTPU_RX_PORT); enb_addr.sin_port = htons(GTPU_RX_PORT);
enb_addr.sin_addr.s_addr = enb_fteid.ipv4; 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 //Setup GTP-U header
srslte::gtpu_header_t header; srslte::gtpu_header_t header;
header.flags = 0x30; header.flags = GTPU_FLAGS_VERSION_V1 | GTPU_FLAGS_GTP_PROTOCOL;
header.message_type = 0xFF; header.message_type = GTPU_MSG_DATA_PDU;
header.length = msg->N_bytes; header.length = msg->N_bytes;
header.teid = enb_fteid.teid; header.teid = enb_fteid.teid;
//Write header into packet //Write header into packet
if(!srslte::gtpu_write_header(&header, msg, m_spgw_log)) if (!srslte::gtpu_write_header(&header, msg, m_spgw_log)) {
{
m_spgw_log->console("Error writing GTP-U header on PDU\n"); m_spgw_log->console("Error writing GTP-U header on PDU\n");
} }
//Send packet to destination //Send packet to destination
int n = sendto(m_s1u,msg->msg,msg->N_bytes,0,(struct sockaddr*) &enb_addr,sizeof(enb_addr)); int n = sendto(m_s1u,msg->msg,msg->N_bytes,0,(struct sockaddr*) &enb_addr,sizeof(enb_addr));
if(n<0) if (n<0) {
{
m_spgw_log->error("Error sending packet to eNB\n"); m_spgw_log->error("Error sending packet to eNB\n");
return; return;
} } else if((unsigned int) n!=msg->N_bytes) {
else if((unsigned int) n!=msg->N_bytes)
{
m_spgw_log->error("Mis-match between packet bytes and sent bytes: Sent: %d, Packet: %d \n",n,msg->N_bytes); m_spgw_log->error("Mis-match between packet bytes and sent bytes: Sent: %d, Packet: %d \n",n,msg->N_bytes);
} }
return; return;
} }
@ -399,12 +376,9 @@ spgw::handle_s1u_pdu(srslte::byte_buffer_t *msg)
//m_spgw_log->console("TEID 0x%x. Bytes=%d\n", header.teid, msg->N_bytes); //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); int n = write(m_sgi_if, msg->msg, msg->N_bytes);
if(n<0) if (n<0) {
{
m_spgw_log->error("Could not write to TUN interface.\n"); m_spgw_log->error("Could not write to TUN interface.\n");
} } else {
else
{
//m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes); //m_spgw_log->console("Forwarded packet to TUN interface. Bytes= %d/%d\n", n, msg->N_bytes);
} }
return; return;
@ -473,19 +447,19 @@ bool
spgw::delete_gtp_ctx(uint32_t ctrl_teid) spgw::delete_gtp_ctx(uint32_t ctrl_teid)
{ {
spgw_tunnel_ctx_t *tunnel_ctx; spgw_tunnel_ctx_t *tunnel_ctx;
if(!m_teid_to_tunnel_ctx.count(ctrl_teid)){ if (!m_teid_to_tunnel_ctx.count(ctrl_teid)) {
m_spgw_log->error("Could not find GTP context to delete.\n"); m_spgw_log->error("Could not find GTP context to delete.\n");
return false; return false;
} }
tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid]; tunnel_ctx = m_teid_to_tunnel_ctx[ctrl_teid];
//Remove GTP-U connections, if any. //Remove GTP-U connections, if any.
if(m_ip_to_teid.count(tunnel_ctx->ue_ipv4)) if (m_ip_to_teid.count(tunnel_ctx->ue_ipv4)) {
{
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
m_ip_to_teid.erase(tunnel_ctx->ue_ipv4); m_ip_to_teid.erase(tunnel_ctx->ue_ipv4);
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
} }
//Remove Ctrl TEID from IMSI to control TEID map //Remove Ctrl TEID from IMSI to control TEID map
m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi); m_imsi_to_ctr_teid.erase(tunnel_ctx->imsi);
@ -503,8 +477,7 @@ spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *
int default_bearer_id = 5; int default_bearer_id = 5;
//Check if IMSI has active GTP-C and/or GTP-U //Check if IMSI has active GTP-C and/or GTP-U
bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req->imsi); bool gtpc_present = m_imsi_to_ctr_teid.count(cs_req->imsi);
if(gtpc_present) if (gtpc_present) {
{
m_spgw_log->console("SPGW: GTP-C context for IMSI %015lu already exists.\n", cs_req->imsi); m_spgw_log->console("SPGW: GTP-C context for IMSI %015lu already exists.\n", cs_req->imsi);
delete_gtp_ctx(m_imsi_to_ctr_teid[cs_req->imsi]); delete_gtp_ctx(m_imsi_to_ctr_teid[cs_req->imsi]);
m_spgw_log->console("SPGW: Deleted previous context.\n"); m_spgw_log->console("SPGW: Deleted previous context.\n");
@ -547,9 +520,6 @@ spgw::handle_create_session_request(struct srslte::gtpc_create_session_request *
return; return;
} }
void void
spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct srslte::gtpc_pdu *mb_resp_pdu) spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct srslte::gtpc_pdu *mb_resp_pdu)
{ {
@ -558,8 +528,7 @@ spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct s
//Get control tunnel info from mb_req PDU //Get control tunnel info from mb_req PDU
uint32_t ctrl_teid = mb_req_pdu->header.teid; 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); 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()) if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
{
m_spgw_log->warning("Could not find TEID %d to modify\n",ctrl_teid); m_spgw_log->warning("Could not find TEID %d to modify\n",ctrl_teid);
return; return;
} }
@ -604,8 +573,9 @@ spgw::handle_modify_bearer_request(struct srslte::gtpc_pdu *mb_req_pdu, struct s
srslte::gtpc_modify_bearer_response *mb_resp = &mb_resp_pdu->choice.modify_bearer_response; 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->cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
mb_resp->eps_bearer_context_modified.ebi = tunnel_ctx->ebi; mb_resp->eps_bearer_context_modified.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; mb_resp->eps_bearer_context_modified.cause.cause_value = srslte::GTPC_CAUSE_VALUE_REQUEST_ACCEPTED;
return;
} }
void void
@ -614,8 +584,7 @@ spgw::handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct
//Find tunel ctxt //Find tunel ctxt
uint32_t ctrl_teid = del_req_pdu->header.teid; 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); 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()) if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
{
m_spgw_log->warning("Could not find TEID %d to delete\n",ctrl_teid); m_spgw_log->warning("Could not find TEID %d to delete\n",ctrl_teid);
return; return;
} }
@ -625,8 +594,7 @@ spgw::handle_delete_session_request(struct srslte::gtpc_pdu *del_req_pdu, struct
//Delete data tunnel //Delete data tunnel
pthread_mutex_lock(&m_mutex); pthread_mutex_lock(&m_mutex);
std::map<in_addr_t,srslte::gtp_fteid_t>::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4); std::map<in_addr_t,srslte::gtp_fteid_t>::iterator data_it = m_ip_to_teid.find(tunnel_ctx->ue_ipv4);
if(data_it != m_ip_to_teid.end()) if (data_it != m_ip_to_teid.end()) {
{
m_ip_to_teid.erase(data_it); m_ip_to_teid.erase(data_it);
} }
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);
@ -642,8 +610,7 @@ spgw::handle_release_access_bearers_request(struct srslte::gtpc_pdu *rel_req_pdu
//Find tunel ctxt //Find tunel ctxt
uint32_t ctrl_teid = rel_req_pdu->header.teid; uint32_t ctrl_teid = rel_req_pdu->header.teid;
std::map<uint32_t,spgw_tunnel_ctx_t*>::iterator tunnel_it = m_teid_to_tunnel_ctx.find(ctrl_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()) if (tunnel_it == m_teid_to_tunnel_ctx.end()) {
{
m_spgw_log->warning("Could not find TEID %d to release bearers from\n",ctrl_teid); m_spgw_log->warning("Could not find TEID %d to release bearers from\n",ctrl_teid);
return; return;
} }
@ -653,8 +620,7 @@ spgw::handle_release_access_bearers_request(struct srslte::gtpc_pdu *rel_req_pdu
//Delete data tunnel //Delete data tunnel
pthread_mutex_lock(&m_mutex); 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); 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()) if (data_it != m_ip_to_teid.end()) {
{
m_ip_to_teid.erase(data_it); m_ip_to_teid.erase(data_it);
} }
pthread_mutex_unlock(&m_mutex); pthread_mutex_unlock(&m_mutex);

@ -111,6 +111,7 @@ typedef struct {
typedef struct { typedef struct {
std::string ip_netmask; std::string ip_netmask;
std::string ip_devname;
phy_args_t phy; phy_args_t phy;
float metrics_period_secs; float metrics_period_secs;
bool pregenerate_signals; bool pregenerate_signals;
@ -122,7 +123,6 @@ typedef struct {
typedef struct { typedef struct {
rf_args_t rf; rf_args_t rf;
rf_cal_t rf_cal;
pcap_args_t pcap; pcap_args_t pcap;
trace_args_t trace; trace_args_t trace;
log_args_t log; log_args_t log;

@ -27,6 +27,7 @@
#ifndef SRSUE_GW_H #ifndef SRSUE_GW_H
#define SRSUE_GW_H #define SRSUE_GW_H
#include <net/if.h>
#include "srslte/common/buffer_pool.h" #include "srslte/common/buffer_pool.h"
#include "srslte/common/log.h" #include "srslte/common/log.h"
#include "srslte/common/common.h" #include "srslte/common/common.h"
@ -35,8 +36,6 @@
#include "srslte/common/threads.h" #include "srslte/common/threads.h"
#include "gw_metrics.h" #include "gw_metrics.h"
#include <linux/if.h>
namespace srsue { namespace srsue {
class gw class gw
@ -52,13 +51,14 @@ public:
void get_metrics(gw_metrics_t &m); void get_metrics(gw_metrics_t &m);
void set_netmask(std::string netmask); void set_netmask(std::string netmask);
void set_tundevname(const std::string & devname);
// PDCP interface // PDCP interface
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu);
void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu); void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu);
// NAS interface // NAS interface
srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str); srslte::error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_addr, char *err_str);
// RRC interface // RRC interface
void add_mch_port(uint32_t lcid, uint32_t port); void add_mch_port(uint32_t lcid, uint32_t port);
@ -67,6 +67,7 @@ private:
bool default_netmask; bool default_netmask;
std::string netmask; std::string netmask;
std::string tundevname;
static const int GW_THREAD_PRIO = 7; static const int GW_THREAD_PRIO = 7;
@ -86,6 +87,7 @@ private:
bool if_up; bool if_up;
uint32_t current_ip_addr; uint32_t current_ip_addr;
uint8_t current_if_id[8];
long ul_tput_bytes; long ul_tput_bytes;
long dl_tput_bytes; long dl_tput_bytes;
@ -93,6 +95,10 @@ private:
void run_thread(); void run_thread();
srslte::error_t init_if(char *err_str); srslte::error_t init_if(char *err_str);
srslte::error_t setup_if_addr4(uint32_t ip_addr, char *err_str);
srslte::error_t setup_if_addr6(uint8_t *ipv6_if_id, char *err_str);
bool find_ipv6_addr(struct in6_addr *in6_out);
void del_ipv6_addr(struct in6_addr *in6p);
// MBSFN // MBSFN
int mbsfn_sock_fd; // Sink UDP socket file descriptor int mbsfn_sock_fd; // Sink UDP socket file descriptor

@ -41,6 +41,7 @@ namespace srsue {
typedef struct { typedef struct {
std::string apn_name; std::string apn_name;
std::string apn_protocol;
std::string apn_user; std::string apn_user;
std::string apn_pass; std::string apn_pass;
bool force_imsi_attach; bool force_imsi_attach;
@ -87,6 +88,8 @@ public:
uint32_t get_ul_count(); uint32_t get_ul_count();
bool is_attached(); bool is_attached();
bool get_k_asme(uint8_t *k_asme_, uint32_t n); bool get_k_asme(uint8_t *k_asme_, uint32_t n);
uint32_t get_ipv4_addr();
bool get_ipv6_addr(uint8_t *ipv6_addr);
// UE interface // UE interface
bool attach_request(); bool attach_request();
@ -133,6 +136,7 @@ private:
bool auth_request; bool auth_request;
uint32_t ip_addr; uint32_t ip_addr;
uint8_t ipv6_if_id[8];
uint8_t eps_bearer_id; uint8_t eps_bearer_id;
uint8_t chap_id; uint8_t chap_id;

@ -359,7 +359,7 @@ private:
byte_buffer_t* byte_align_and_pack(); byte_buffer_t* byte_align_and_pack();
void send_ul_ccch_msg(); void send_ul_ccch_msg();
void send_ul_dcch_msg(); void send_ul_dcch_msg(uint32_t lcid);
srslte::bit_buffer_t bit_buf; srslte::bit_buffer_t bit_buf;
pthread_mutex_t mutex; pthread_mutex_t mutex;
@ -603,7 +603,7 @@ private:
void send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM cause); void send_con_restablish_request(LIBLTE_RRC_CON_REEST_REQ_CAUSE_ENUM cause);
void send_con_restablish_complete(); void send_con_restablish_complete();
void send_con_setup_complete(byte_buffer_t *nas_msg); void send_con_setup_complete(byte_buffer_t *nas_msg);
void send_ul_info_transfer(byte_buffer_t *nas_msg); void send_ul_info_transfer(uint32_t lcid, byte_buffer_t *nas_msg);
void send_security_mode_complete(); void send_security_mode_complete();
void send_rrc_con_reconfig_complete(); void send_rrc_con_reconfig_complete();
void send_rrc_ue_cap_info(); void send_rrc_ue_cap_info();

@ -43,6 +43,13 @@ bsr_proc::bsr_proc()
next_tx_tti = 0; next_tx_tti = 0;
triggered_bsr_type=NONE; triggered_bsr_type=NONE;
for (int i=0;i<MAX_LCID;i++) {
lcg[i] = -1;
priorities[i] = -1;
last_pending_data[i] = 0;
}
lcg[0] = 0;
priorities[0] = 99;
} }
void bsr_proc::init(rlc_interface_mac *rlc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_) void bsr_proc::init(rlc_interface_mac *rlc_, srslte::log* log_h_, mac_interface_rrc::mac_cfg_t *mac_cfg_, srslte::timers *timers_db_)
@ -69,13 +76,6 @@ void bsr_proc::reset()
reset_sr = false; reset_sr = false;
sr_is_sent = false; sr_is_sent = false;
triggered_bsr_type = NONE; triggered_bsr_type = NONE;
for (int i=0;i<MAX_LCID;i++) {
lcg[i] = -1;
priorities[i] = -1;
last_pending_data[i] = 0;
}
lcg[0] = 0;
priorities[0] = 99;
next_tx_tti = 0; next_tx_tti = 0;
} }

@ -85,18 +85,17 @@ void parse_args(all_args_t *args, int argc, char *argv[]) {
"UECapabilityInformation message. Default 0xe6041000") "UECapabilityInformation message. Default 0xe6041000")
("rrc.ue_category", bpo::value<string>(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)") ("rrc.ue_category", bpo::value<string>(&args->ue_category_str)->default_value("4"), "UE Category (1 to 5)")
("nas.apn", bpo::value<string>(&args->nas.apn_name)->default_value(""), "Set Access Point Name (APN) for data services") ("nas.apn", bpo::value<string>(&args->nas.apn_name)->default_value(""), "Set Access Point Name (APN) for data services")
("nas.apn_protocol", bpo::value<string>(&args->nas.apn_protocol)->default_value(""), "Set Access Point Name (APN) protocol for data services")
("nas.user", bpo::value<string>(&args->nas.apn_user)->default_value(""), "Username for CHAP authentication") ("nas.user", bpo::value<string>(&args->nas.apn_user)->default_value(""), "Username for CHAP authentication")
("nas.pass", bpo::value<string>(&args->nas.apn_pass)->default_value(""), "Password for CHAP authentication") ("nas.pass", bpo::value<string>(&args->nas.apn_pass)->default_value(""), "Password for CHAP authentication")
("nas.force_imsi_attach", bpo::value<bool>(&args->nas.force_imsi_attach)->default_value(false), "Whether to always perform an IMSI attach") ("nas.force_imsi_attach", bpo::value<bool>(&args->nas.force_imsi_attach)->default_value(false), "Whether to always perform an IMSI attach")
("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") ("pcap.enable", bpo::value<bool>(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark")
("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") ("pcap.filename", bpo::value<string>(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename")
("pcap.nas_enable", bpo::value<bool>(&args->pcap.nas_enable)->default_value(false), "Enable NAS packet captures for wireshark") ("pcap.nas_enable", bpo::value<bool>(&args->pcap.nas_enable)->default_value(false), "Enable NAS packet captures for wireshark")
("pcap.nas_filename", bpo::value<string>(&args->pcap.nas_filename)->default_value("ue_nas.pcap"), "NAS layer capture filename (useful when NAS encryption is enabled)") ("pcap.nas_filename", bpo::value<string>(&args->pcap.nas_filename)->default_value("ue_nas.pcap"), "NAS layer capture filename (useful when NAS encryption is enabled)")
("trace.enable", bpo::value<bool>(&args->trace.enable)->default_value(false), "Enable PHY and radio timing traces") ("trace.enable", bpo::value<bool>(&args->trace.enable)->default_value(false), "Enable PHY and radio timing traces")
("trace.phy_filename", bpo::value<string>(&args->trace.phy_filename)->default_value("ue.phy_trace"), ("trace.phy_filename", bpo::value<string>(&args->trace.phy_filename)->default_value("ue.phy_trace"),
"PHY timing traces filename") "PHY timing traces filename")
@ -123,7 +122,6 @@ void parse_args(all_args_t *args, int argc, char *argv[]) {
("log.usim_level", bpo::value<string>(&args->log.usim_level), "USIM log level") ("log.usim_level", bpo::value<string>(&args->log.usim_level), "USIM log level")
("log.usim_hex_limit", bpo::value<int>(&args->log.usim_hex_limit), "USIM log hex dump limit") ("log.usim_hex_limit", bpo::value<int>(&args->log.usim_hex_limit), "USIM log hex dump limit")
("log.all_level", bpo::value<string>(&args->log.all_level)->default_value("info"), "ALL log level") ("log.all_level", bpo::value<string>(&args->log.all_level)->default_value("info"), "ALL log level")
("log.all_hex_limit", bpo::value<int>(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit") ("log.all_hex_limit", bpo::value<int>(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit")
@ -145,6 +143,10 @@ void parse_args(all_args_t *args, int argc, char *argv[]) {
bpo::value<string>(&args->expert.ip_netmask)->default_value("255.255.255.0"), bpo::value<string>(&args->expert.ip_netmask)->default_value("255.255.255.0"),
"Netmask of the tun_srsue device") "Netmask of the tun_srsue device")
("expert.ip_devname",
bpo::value<string>(&args->expert.ip_devname)->default_value("tun_srsue"),
"Name of the tun_srsue device")
("expert.mbms_service", ("expert.mbms_service",
bpo::value<int>(&args->expert.mbms_service)->default_value(-1), bpo::value<int>(&args->expert.mbms_service)->default_value(-1),
"automatically starts an mbms service of the number given") "automatically starts an mbms service of the number given")
@ -309,16 +311,7 @@ void parse_args(all_args_t *args, int argc, char *argv[]) {
("expert.pdsch_8bit_decoder", ("expert.pdsch_8bit_decoder",
bpo::value<bool>(&args->expert.phy.pdsch_8bit_decoder)->default_value(false), bpo::value<bool>(&args->expert.phy.pdsch_8bit_decoder)->default_value(false),
"Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)") "Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)");
("rf_calibration.tx_corr_dc_gain", bpo::value<float>(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0),
"TX DC offset gain correction")
("rf_calibration.tx_corr_dc_phase", bpo::value<float>(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0),
"TX DC offset phase correction")
("rf_calibration.tx_corr_iq_i", bpo::value<float>(&args->rf_cal.tx_corr_iq_i)->default_value(0.0),
"TX IQ imbalance inphase correction")
("rf_calibration.tx_corr_iq_q", bpo::value<float>(&args->rf_cal.tx_corr_iq_q)->default_value(0.0),
"TX IQ imbalance quadrature correction");
// Positional options - config file location // Positional options - config file location
bpo::options_description position("Positional options"); bpo::options_description position("Positional options");

@ -463,11 +463,6 @@ void phch_worker::work_imp()
tr_log_end(); tr_log_end();
if (next_offset > 0) {
phy->worker_end(tx_tti, signal_ready, signal_ptr, SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time);
} else {
phy->worker_end(tx_tti, signal_ready, &signal_ptr[-next_offset], SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time);
}
if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) { if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type) {
if (!dl_action.generate_ack_callback) { if (!dl_action.generate_ack_callback) {
@ -491,6 +486,13 @@ void phch_worker::work_imp()
phy->set_mch_period_stop(0); phy->set_mch_period_stop(0);
} }
} }
if (next_offset > 0) {
phy->worker_end(tx_tti, signal_ready, signal_ptr, SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time);
} else {
phy->worker_end(tx_tti, signal_ready, &signal_ptr[-next_offset], SRSLTE_SF_LEN_PRB(cell.nof_prb)+next_offset, tx_time);
}
if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type){ if(SUBFRAME_TYPE_REGULAR == sf_cfg.sf_type){
update_measurements(); update_measurements();
} }
@ -1269,6 +1271,22 @@ void phch_worker::encode_pusch(srslte_ra_ul_grant_t *grant, uint8_t *payload, ui
char timestr[64]; char timestr[64];
timestr[0]='\0'; timestr[0]='\0';
/* Check input values ranges */
if (rnti == 0) {
Warning("Encode PUSCH: Invalid RNTI (= 0)\n");
return;
} else if (rv > 3) {
Warning("Encode PUSCH: Invalid RV (= %ud)\n", rv);
return;
} else if (payload == NULL) {
Warning("Encode PUSCH: NULL payload\n");
return;
} else if (softbuffer == NULL) {
Warning("Encode PUSCH: NULL softbuffer\n");
return;
}
/* Configure and encode */
if (srslte_ue_ul_cfg_grant(&ue_ul, grant, TTI_TX(tti), rv, current_tx_nb)) { if (srslte_ue_ul_cfg_grant(&ue_ul, grant, TTI_TX(tti), rv, current_tx_nb)) {
Error("Configuring UL grant\n"); Error("Configuring UL grant\n");
} }

@ -193,10 +193,7 @@ bool ue::init(all_args_t *args_) {
radio.set_continuous_tx(args->rf.continuous_tx.compare("yes")?false:true); radio.set_continuous_tx(args->rf.continuous_tx.compare("yes")?false:true);
} }
radio.set_manual_calibration(&args->rf_cal);
// Set PHY options // Set PHY options
if (args->rf.tx_gain > 0) { if (args->rf.tx_gain > 0) {
args->expert.phy.ul_pwr_ctrl_en = false; args->expert.phy.ul_pwr_ctrl_en = false;
} else { } else {
@ -224,10 +221,11 @@ bool ue::init(all_args_t *args_) {
rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */); rlc.init(&pdcp, &rrc, this, &rlc_log, &mac, 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK); pdcp.init(&rlc, &rrc, &gw, &pdcp_log, 0 /* RB_ID_SRB0 */, SECURITY_DIRECTION_UPLINK);
srslte_nas_config_t nas_cfg(1, args->nas.apn_name, args->nas.apn_user, args->nas.apn_pass, args->nas.force_imsi_attach); /* RB_ID_SRB1 */ srslte_nas_config_t nas_cfg(1, args->nas.apn_name, args->nas.apn_protocol, args->nas.apn_user, args->nas.apn_pass, args->nas.force_imsi_attach); /* RB_ID_SRB1 */
nas.init(usim, &rrc, &gw, &nas_log, nas_cfg); nas.init(usim, &rrc, &gw, &nas_log, nas_cfg);
gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */); gw.init(&pdcp, &nas, &gw_log, 3 /* RB_ID_DRB1 */);
gw.set_netmask(args->expert.ip_netmask); gw.set_netmask(args->expert.ip_netmask);
gw.set_tundevname(args->expert.ip_devname);
// Get current band from provided EARFCN // Get current band from provided EARFCN
args->rrc.supported_bands[0] = srslte_band_get_band(args->rf.dl_earfcn); args->rrc.supported_bands[0] = srslte_band_get_band(args->rf.dl_earfcn);
@ -235,8 +233,8 @@ bool ue::init(all_args_t *args_) {
args->rrc.ue_category = atoi(args->ue_category_str.c_str()); args->rrc.ue_category = atoi(args->ue_category_str.c_str());
// set args and initialize RRC // set args and initialize RRC
rrc.set_args(args->rrc);
rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &gw, &mac, &rrc_log); rrc.init(&phy, &mac, &rlc, &pdcp, &nas, usim, &gw, &mac, &rrc_log);
rrc.set_args(args->rrc);
// Currently EARFCN list is set to only one frequency as indicated in ue.conf // Currently EARFCN list is set to only one frequency as indicated in ue.conf
std::vector<uint32_t> earfcn_list; std::vector<uint32_t> earfcn_list;

@ -32,11 +32,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/if.h> #include <linux/ipv6.h>
#include <linux/if_tun.h> #include <linux/if_tun.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
namespace srsue { namespace srsue {
@ -45,6 +46,7 @@ gw::gw()
{ {
current_ip_addr = 0; current_ip_addr = 0;
default_netmask = true; default_netmask = true;
tundevname = "";
} }
void gw::init(pdcp_interface_gw *pdcp_, nas_interface_gw *nas_, srslte::log *gw_log_, srslte::srslte_gw_config_t cfg_) void gw::init(pdcp_interface_gw *pdcp_, nas_interface_gw *nas_, srslte::log *gw_log_, srslte::srslte_gw_config_t cfg_)
@ -125,6 +127,11 @@ void gw::set_netmask(std::string netmask)
this->netmask = netmask; this->netmask = netmask;
} }
void gw::set_tundevname(const std::string & devname)
{
tundevname = devname;
}
/******************************************************************************* /*******************************************************************************
PDCP interface PDCP interface
@ -159,29 +166,14 @@ void gw::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu)
struct in_addr dst_addr; struct in_addr dst_addr;
memcpy(&dst_addr.s_addr, &pdu->msg[16],4); memcpy(&dst_addr.s_addr, &pdu->msg[16],4);
if(!if_up) if (!if_up) {
{
gw_log->warning("TUN/TAP not up - dropping gw RX message\n"); gw_log->warning("TUN/TAP not up - dropping gw RX message\n");
}else{ } else {
int n = write(tun_fd, pdu->msg, pdu->N_bytes); int n = write(tun_fd, pdu->msg, pdu->N_bytes);
if(n > 0 && (pdu->N_bytes != (uint32_t)n)) if(n > 0 && (pdu->N_bytes != (uint32_t) n) ) {
{
gw_log->warning("DL TUN/TAP write failure\n"); gw_log->warning("DL TUN/TAP write failure\n");
} }
} }
/*
// Strip IP/UDP header
pdu->msg += 28;
pdu->N_bytes -= 28;
if(mbsfn_sock_fd) {
if(lcid > 0 && lcid < SRSLTE_N_MCH_LCIDS) {
mbsfn_sock_addr.sin_port = htons(mbsfn_ports[lcid]);
if(sendto(mbsfn_sock_fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&mbsfn_sock_addr, sizeof(struct sockaddr_in))<0) {
gw_log->error("Failed to send MCH PDU to port %d\n", mbsfn_ports[lcid]);
}
}
}*/
} }
pool->deallocate(pdu); pool->deallocate(pdu);
} }
@ -189,104 +181,27 @@ void gw::write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *pdu)
/******************************************************************************* /*******************************************************************************
NAS interface NAS interface
*******************************************************************************/ *******************************************************************************/
srslte::error_t gw::setup_if_addr(uint32_t ip_addr, char *err_str) srslte::error_t gw::setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_addr, char *err_str)
{ {
if (ip_addr != current_ip_addr) { srslte::error_t err;
if(!if_up) if(pdn_type == LIBLTE_MME_PDN_TYPE_IPV4 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6 ){
{ err = setup_if_addr4(ip_addr, err_str);
if(init_if(err_str)) if(err!= srslte::ERROR_NONE){
{ return err;
gw_log->error("init_if failed\n");
return(srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip_addr);
if(0 > ioctl(sock, SIOCSIFADDR, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket address: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
} }
ifr.ifr_netmask.sa_family = AF_INET; }
const char *mask = "255.255.255.0"; if(pdn_type == LIBLTE_MME_PDN_TYPE_IPV6 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6 ){
if (!default_netmask) { err = setup_if_addr6(ipv6_if_addr, err_str);
mask = netmask.c_str(); if(err!= srslte::ERROR_NONE){
} return err;
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(mask);
if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket netmask: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
} }
current_ip_addr = ip_addr;
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
} }
return(srslte::ERROR_NONE); // Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
return srslte::ERROR_NONE;
} }
srslte::error_t gw::init_if(char *err_str)
{
if(if_up)
{
return(srslte::ERROR_ALREADY_STARTED);
}
char dev[IFNAMSIZ] = "tun_srsue";
// Construct the TUN device
tun_fd = open("/dev/net/tun", O_RDWR);
gw_log->info("TUN file descriptor = %d\n", tun_fd);
if(0 > tun_fd)
{
err_str = strerror(errno);
gw_log->debug("Failed to open TUN device: %s\n", err_str);
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-1);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = 0;
if(0 > ioctl(tun_fd, TUNSETIFF, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set TUN device name: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
// Bring up the interface
sock = socket(AF_INET, SOCK_DGRAM, 0);
if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to bring up socket: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr))
{
err_str = strerror(errno);
gw_log->debug("Failed to set socket flags: %s\n", err_str);
close(tun_fd);
return(srslte::ERROR_CANT_START);
}
if_up = true;
return(srslte::ERROR_NONE);
}
/******************************************************************************* /*******************************************************************************
@ -299,17 +214,15 @@ void gw::add_mch_port(uint32_t lcid, uint32_t port)
} }
} }
/********************/ /********************/
/* GW Receive */ /* GW Receive */
/********************/ /********************/
void gw::run_thread() void gw::run_thread()
{ {
struct iphdr *ip_pkt; uint32 idx = 0;
uint32 idx = 0; int32 N_bytes = 0;
int32 N_bytes;
srslte::byte_buffer_t *pdu = pool_allocate; srslte::byte_buffer_t *pdu = pool_allocate_blocking;
if (!pdu) { if (!pdu) {
gw_log->error("Fatal Error: Couldn't allocate PDU in run_thread().\n"); gw_log->error("Fatal Error: Couldn't allocate PDU in run_thread().\n");
return; return;
@ -331,16 +244,23 @@ void gw::run_thread()
break; break;
} }
gw_log->debug("Read %d bytes from TUN fd=%d, idx=%d\n", N_bytes, tun_fd, idx); gw_log->debug("Read %d bytes from TUN fd=%d, idx=%d\n", N_bytes, tun_fd, idx);
if(N_bytes > 0) if (N_bytes > 0) {
{ struct iphdr *ip_pkt = (struct iphdr*)pdu->msg;
struct ipv6hdr *ip6_pkt = (struct ipv6hdr*)pdu->msg;
uint16_t pkt_len = 0;
pdu->N_bytes = idx + N_bytes; pdu->N_bytes = idx + N_bytes;
ip_pkt = (struct iphdr*)pdu->msg; if (ip_pkt->version == 4 || ip_pkt->version == 6) {
if (ip_pkt->version == 4){
// Warning: Accept only IPv4 packets pkt_len = ntohs(ip_pkt->tot_len);
if (ip_pkt->version == 4) { } else if (ip_pkt->version == 6){
pkt_len = ntohs(ip6_pkt->payload_len)+40;
} else {
gw_log->error_hex(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet.\n");
continue;
}
gw_log->debug("IPv%d packet total length: %d Bytes\n", ip_pkt->version, pkt_len);
// Check if entire packet was received // Check if entire packet was received
if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) if (pkt_len == pdu->N_bytes) {
{
gw_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU"); gw_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU");
while(run_enable && !pdcp->is_lcid_enabled(cfg.lcid) && attach_wait < ATTACH_WAIT_TOUT) { while(run_enable && !pdcp->is_lcid_enabled(cfg.lcid) && attach_wait < ATTACH_WAIT_TOUT) {
@ -376,7 +296,10 @@ void gw::run_thread()
} }
}else{ }else{
idx += N_bytes; idx += N_bytes;
gw_log->debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.\n", ip_pkt->tot_len, pdu->N_bytes);
} }
} else {
gw_log->error("IP Version not handled. Version %d\n", ip_pkt->version);
} }
}else{ }else{
gw_log->error("Failed to read from TUN interface - gw receive thread exiting.\n"); gw_log->error("Failed to read from TUN interface - gw receive thread exiting.\n");
@ -388,4 +311,315 @@ void gw::run_thread()
gw_log->info("GW IP receiver thread exiting.\n"); gw_log->info("GW IP receiver thread exiting.\n");
} }
/**************************/
/* TUN Interface Helpers */
/**************************/
srslte::error_t gw::init_if(char *err_str)
{
if (if_up) {
return (srslte::ERROR_ALREADY_STARTED);
}
// Construct the TUN device
tun_fd = open("/dev/net/tun", O_RDWR);
gw_log->info("TUN file descriptor = %d\n", tun_fd);
if (0 > tun_fd) {
err_str = strerror(errno);
gw_log->debug("Failed to open TUN device: %s\n", err_str);
return (srslte::ERROR_CANT_START);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, tundevname.c_str(), std::min(tundevname.length(), (size_t)(IFNAMSIZ-1)));
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ-1] = 0;
if (0 > ioctl(tun_fd, TUNSETIFF, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set TUN device name: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
// Bring up the interface
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to bring up socket: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket flags: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
// Delete link-local IPv6 address.
struct in6_addr in6p;
char addr_str[INET6_ADDRSTRLEN];
if(find_ipv6_addr(&in6p)){
gw_log->debug("Found link-local IPv6 address: %s\n",inet_ntop(AF_INET6, &in6p, addr_str,INET6_ADDRSTRLEN) );
del_ipv6_addr(&in6p);
} else {
gw_log->warning("Could not find link-local IPv6 address.\n");
}
if_up = true;
return(srslte::ERROR_NONE);
}
srslte::error_t gw::setup_if_addr4(uint32_t ip_addr, char *err_str)
{
if (ip_addr != current_ip_addr) {
if (!if_up) {
if (init_if(err_str)) {
gw_log->error("init_if failed\n");
return (srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip_addr);
if (0 > ioctl(sock, SIOCSIFADDR, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket address: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
ifr.ifr_netmask.sa_family = AF_INET;
const char *mask = "255.255.255.0";
if (!default_netmask) {
mask = netmask.c_str();
}
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(mask);
if (0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) {
err_str = strerror(errno);
gw_log->debug("Failed to set socket netmask: %s\n", err_str);
close(tun_fd);
return (srslte::ERROR_CANT_START);
}
current_ip_addr = ip_addr;
}
return(srslte::ERROR_NONE);
}
srslte::error_t gw::setup_if_addr6(uint8_t *ipv6_if_id, char *err_str)
{
struct sockaddr_in6 sai;
struct in6_ifreq ifr6;
bool match = true;
for (int i=0; i<8; i++){
if(ipv6_if_id[i] != current_if_id[i]){
match = false;
break;
}
}
if (!match) {
if (!if_up) {
if( init_if(err_str) ) {
gw_log->error("init_if failed\n");
return(srslte::ERROR_CANT_START);
}
}
// Setup the IP address
sock = socket(AF_INET6, SOCK_DGRAM, 0);
ifr.ifr_addr.sa_family = AF_INET6;
if(inet_pton(AF_INET6, "fe80::", (void *)&sai.sin6_addr) <= 0) {
gw_log->error("Bad address\n");
return srslte::ERROR_CANT_START;
}
memcpy(&sai.sin6_addr.s6_addr[8], ipv6_if_id, 8);
if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) {
perror("SIOGIFINDEX");
return srslte::ERROR_CANT_START;
}
ifr6.ifr6_ifindex = ifr.ifr_ifindex;
ifr6.ifr6_prefixlen = 64;
memcpy((char *) &ifr6.ifr6_addr, (char *) &sai.sin6_addr,
sizeof(struct in6_addr));
if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) {
err_str = strerror(errno);
gw_log->error("Could not set IPv6 Link local address. Error %s\n", err_str);
return srslte::ERROR_CANT_START;
}
for (int i=0; i<8; i++){
current_if_id[i] = ipv6_if_id[i];
}
}
return(srslte::ERROR_NONE);
}
bool gw::find_ipv6_addr(struct in6_addr *in6_out)
{
int n, rtattrlen, fd = -1;
unsigned int if_index;
struct rtattr *rta, *rtatp;
struct nlmsghdr *nlmp;
struct ifaddrmsg *rtmp;
struct in6_addr *in6p;
char buf[2048];
struct {
struct nlmsghdr n;
struct ifaddrmsg r;
char buf[1024];
} req;
gw_log->debug("Trying to obtain IPv6 addr of %s interface\n", tundevname.c_str());
//Get Interface Index
if_index = if_nametoindex(tundevname.c_str());
if(if_index == 0){
gw_log->error("Could not find interface index\n");
goto err_out;
}
// Open NETLINK socket
fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (fd < 0) {
gw_log->error("Error openning NETLINK socket -- %s\n", strerror(errno));
goto err_out;
}
// We use RTM_GETADDR to get the ip address from the kernel
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH;
req.n.nlmsg_type = RTM_GETADDR;
// AF_INET6 is used to signify the kernel to fetch only ipv6 entires.
req.r.ifa_family = AF_INET6;
// Fill up all the attributes for the rtnetlink header.
// The lenght is important. 16 signifies we are requesting IPv6 addresses
rta = (struct rtattr *)(((char *)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
rta->rta_len = RTA_LENGTH(16);
// Time to send and recv the message from kernel
n = send(fd, &req, req.n.nlmsg_len, 0);
if (n < 0) {
gw_log->error("Error sending NETLINK message to kernel -- %s", strerror(errno));
goto err_out;
}
n = recv(fd, buf, sizeof(buf), 0);
if (n < 0) {
gw_log->error("Error receiving from NETLINK socket\n");
goto err_out;
}
if (n == 0) {
gw_log->error("Nothing received from NETLINK Socket\n");
goto err_out;
}
// Parse the reply
for (nlmp = (struct nlmsghdr *)buf; NLMSG_OK (nlmp, n); nlmp = NLMSG_NEXT (nlmp, n)){
//Chack NL message type
if (nlmp->nlmsg_type == NLMSG_DONE){
gw_log->error("Reach end of NETLINK message without finding IPv6 address.\n");
goto err_out;
}
if (nlmp->nlmsg_type == NLMSG_ERROR) {
gw_log->error("NLMSG_ERROR in NETLINK reply\n");
goto err_out;
}
gw_log->debug("NETLINK message type %d\n", nlmp->nlmsg_type);
//Get IFA message
rtmp = (struct ifaddrmsg *)NLMSG_DATA(nlmp);
rtatp = (struct rtattr *)IFA_RTA(rtmp);
rtattrlen = IFA_PAYLOAD(nlmp);
for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) {
// We are looking IFA_ADDRESS rt_attribute type.
// For more info on the different types see man(7) rtnetlink.
if (rtatp->rta_type == IFA_ADDRESS) {
in6p = (struct in6_addr *)RTA_DATA(rtatp);
if (if_index == rtmp->ifa_index) {
for (int i = 0; i < 16; i++) {
in6_out->s6_addr[i] = in6p->s6_addr[i];
}
goto out;
}
}
}
}
err_out:
if (fd > 0) {
close(fd);
}
return false;
out:
close(fd);
return true;
}
void gw::del_ipv6_addr(struct in6_addr *in6p)
{
int status, fd =-1;
unsigned int if_index;
struct {
struct nlmsghdr n;
struct ifaddrmsg ifa;
char buf[1024];
} req;
//Get Interface Index
if_index = if_nametoindex(tundevname.c_str());
if(if_index == 0){
gw_log->error("Could not find interface index\n");
goto out;
}
// Open netlink socket
fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (fd < 0) {
gw_log->error("Error openning NETLINK socket -- %s\n", strerror(errno));
goto out;
}
// We use RTM_DELADDR to delete the ip address from the interface
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_type = RTM_DELADDR;
req.n.nlmsg_flags = NLM_F_REQUEST;
req.ifa.ifa_family = AF_INET6;
req.ifa.ifa_prefixlen = 64;
req.ifa.ifa_index = if_index; // set the tun_srsue index
req.ifa.ifa_scope = 0;
//Add RT atribute
struct rtattr *rta;
rta = (struct rtattr *)(((char *)&req.n) + NLMSG_ALIGN(req.n.nlmsg_len));
rta->rta_type = IFA_LOCAL;
rta->rta_len = RTA_LENGTH(16);
memcpy(RTA_DATA(rta), in6p, 16);
req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + rta->rta_len;
status = send(fd, &req, req.n.nlmsg_len, 0);
if (status < 0) {
gw_log->error("Error sending NETLINK message\n");
goto out;
}
out:
if (fd<0){
close(fd);
}
return;
}
} // namespace srsue } // namespace srsue

@ -54,6 +54,7 @@ nas::nas()
ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; ctxt.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0;
plmn_is_selected = false; plmn_is_selected = false;
chap_id = 0; chap_id = 0;
memset(ipv6_if_id, 0, sizeof(ipv6_if_id));
} }
void nas::init(usim_interface_nas *usim_, void nas::init(usim_interface_nas *usim_,
@ -183,7 +184,7 @@ bool nas::detach_request() {
case EMM_STATE_REGISTERED: case EMM_STATE_REGISTERED:
// send detach request // send detach request
send_detach_request(true); send_detach_request(true);
state = EMM_STATE_DEREGISTERED_INITIATED; state = EMM_STATE_DEREGISTERED;
break; break;
case EMM_STATE_DEREGISTERED_INITIATED: case EMM_STATE_DEREGISTERED_INITIATED:
// do nothing .. // do nothing ..
@ -411,6 +412,21 @@ bool nas::get_k_asme(uint8_t *k_asme_, uint32_t n) {
return true; return true;
} }
uint32_t nas::get_ipv4_addr()
{
return ip_addr;
}
bool nas::get_ipv6_addr(uint8_t *ipv6_addr)
{
uint8_t null_addr[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (memcmp(ipv6_addr, null_addr, 8) != 0) {
memcpy(ipv6_addr, ipv6_if_id, 8);
return true;
}
return false;
}
/******************************************************************************* /*******************************************************************************
PCAP PCAP
*******************************************************************************/ *******************************************************************************/
@ -587,6 +603,7 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
if (pdu->N_bytes <= 5) { if (pdu->N_bytes <= 5) {
nas_log->error("Invalid attach accept PDU size (%d)\n", pdu->N_bytes); nas_log->error("Invalid attach accept PDU size (%d)\n", pdu->N_bytes);
pool->deallocate(pdu);
return; return;
} }
@ -629,6 +646,17 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg,
&act_def_eps_bearer_context_req); &act_def_eps_bearer_context_req);
if ( (cfg.apn_protocol == "ipv4" && LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) ||
(cfg.apn_protocol == "ipv6" && LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) ){
nas_log->error("Failed to attach -- Mismatch between PDN protocol and PDN type in attach accept.\n");
pool->deallocate(pdu);
return;
}
if ( ("ipv4v6" == cfg.apn_protocol && LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) ||
("ipv4v6" == cfg.apn_protocol && LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) ){
nas_log->warning("Requested IPv4v6, but only received a single PDN address.\n");
nas_log->warning("EMM Cause: %d\n", attach_accept.emm_cause );
}
if (LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) { if (LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) {
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[0] << 24; ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[0] << 24;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[1] << 16; ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[1] << 16;
@ -650,11 +678,83 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
// Setup GW // Setup GW
char *err_str = NULL; char *err_str = NULL;
if (gw->setup_if_addr(ip_addr, err_str)) { if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV4, ip_addr, NULL, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
} else if (LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type){
memcpy(ipv6_if_id, act_def_eps_bearer_context_req.pdn_addr.addr, 8);
nas_log->info("Network attach successful. APN: %s, IPv6 interface id: %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
act_def_eps_bearer_context_req.apn.apn,
act_def_eps_bearer_context_req.pdn_addr.addr[0],
act_def_eps_bearer_context_req.pdn_addr.addr[1],
act_def_eps_bearer_context_req.pdn_addr.addr[2],
act_def_eps_bearer_context_req.pdn_addr.addr[3],
act_def_eps_bearer_context_req.pdn_addr.addr[4],
act_def_eps_bearer_context_req.pdn_addr.addr[5],
act_def_eps_bearer_context_req.pdn_addr.addr[6],
act_def_eps_bearer_context_req.pdn_addr.addr[7]);
nas_log->console("Network attach successful. IPv6 interface Id: %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
act_def_eps_bearer_context_req.pdn_addr.addr[0],
act_def_eps_bearer_context_req.pdn_addr.addr[1],
act_def_eps_bearer_context_req.pdn_addr.addr[2],
act_def_eps_bearer_context_req.pdn_addr.addr[3],
act_def_eps_bearer_context_req.pdn_addr.addr[4],
act_def_eps_bearer_context_req.pdn_addr.addr[5],
act_def_eps_bearer_context_req.pdn_addr.addr[6],
act_def_eps_bearer_context_req.pdn_addr.addr[7]);
// Setup GW
char *err_str = NULL;
if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV6, 0, ipv6_if_id, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
} else if (LIBLTE_MME_PDN_TYPE_IPV4V6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type){
memcpy(ipv6_if_id, act_def_eps_bearer_context_req.pdn_addr.addr, 8);
//IPv6
nas_log->info("Network attach successful. APN: %s, IPv6 interface id: %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
act_def_eps_bearer_context_req.apn.apn,
act_def_eps_bearer_context_req.pdn_addr.addr[0],
act_def_eps_bearer_context_req.pdn_addr.addr[1],
act_def_eps_bearer_context_req.pdn_addr.addr[2],
act_def_eps_bearer_context_req.pdn_addr.addr[3],
act_def_eps_bearer_context_req.pdn_addr.addr[4],
act_def_eps_bearer_context_req.pdn_addr.addr[5],
act_def_eps_bearer_context_req.pdn_addr.addr[6],
act_def_eps_bearer_context_req.pdn_addr.addr[7]);
nas_log->console("Network attach successful. IPv6 interface Id: %02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
act_def_eps_bearer_context_req.pdn_addr.addr[0],
act_def_eps_bearer_context_req.pdn_addr.addr[1],
act_def_eps_bearer_context_req.pdn_addr.addr[2],
act_def_eps_bearer_context_req.pdn_addr.addr[3],
act_def_eps_bearer_context_req.pdn_addr.addr[4],
act_def_eps_bearer_context_req.pdn_addr.addr[5],
act_def_eps_bearer_context_req.pdn_addr.addr[6],
act_def_eps_bearer_context_req.pdn_addr.addr[7]);
//IPv4
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[8] << 24;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[9] << 16;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[10] << 8;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[11];
nas_log->info("Network attach successful. APN: %s, IP: %u.%u.%u.%u\n",
act_def_eps_bearer_context_req.apn.apn,
act_def_eps_bearer_context_req.pdn_addr.addr[8],
act_def_eps_bearer_context_req.pdn_addr.addr[9],
act_def_eps_bearer_context_req.pdn_addr.addr[10],
act_def_eps_bearer_context_req.pdn_addr.addr[11]);
nas_log->console("Network attach successful. IP: %u.%u.%u.%u\n",
act_def_eps_bearer_context_req.pdn_addr.addr[8],
act_def_eps_bearer_context_req.pdn_addr.addr[9],
act_def_eps_bearer_context_req.pdn_addr.addr[10],
act_def_eps_bearer_context_req.pdn_addr.addr[11]);
char *err_str = NULL;
if (gw->setup_if_addr(LIBLTE_MME_PDN_TYPE_IPV4V6, ip_addr, ipv6_if_id, err_str)) {
nas_log->error("Failed to set gateway address - %s\n", err_str); nas_log->error("Failed to set gateway address - %s\n", err_str);
} }
} else { } else {
nas_log->error("Not handling IPV6 or IPV4V6\n"); nas_log->error("PDN type not IPv4, IPv6 nor IPv4v6\n");
pool->deallocate(pdu); pool->deallocate(pdu);
return; return;
} }
@ -731,7 +831,6 @@ void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu) {
nas_log->info("Sending Attach Complete\n"); nas_log->info("Sending Attach Complete\n");
rrc->write_sdu(lcid, pdu); rrc->write_sdu(lcid, pdu);
ctxt.tx_count++; ctxt.tx_count++;
} else { } else {
nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result); nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result);
state = EMM_STATE_DEREGISTERED; state = EMM_STATE_DEREGISTERED;
@ -1036,6 +1135,8 @@ void nas::gen_attach_request(byte_buffer_t *msg) {
// GUTI or IMSI attach // GUTI or IMSI attach
if(have_guti && have_ctxt) { if(have_guti && have_ctxt) {
attach_req.tmsi_status_present = true;
attach_req.tmsi_status = LIBLTE_MME_TMSI_STATUS_VALID_TMSI;
attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI; attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_GUTI;
memcpy(&attach_req.eps_mobile_id.guti, &ctxt.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT)); memcpy(&attach_req.eps_mobile_id.guti, &ctxt.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT));
attach_req.old_guti_type = LIBLTE_MME_GUTI_TYPE_NATIVE; attach_req.old_guti_type = LIBLTE_MME_GUTI_TYPE_NATIVE;
@ -1124,10 +1225,25 @@ void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg) {
// Set the PDN con req parameters // Set the PDN con req parameters
pdn_con_req.eps_bearer_id = 0x00; // Unassigned bearer ID pdn_con_req.eps_bearer_id = 0x00; // Unassigned bearer ID
pdn_con_req.proc_transaction_id = 0x01; // First transaction ID pdn_con_req.proc_transaction_id = 0x01; // First transaction ID
pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4;
pdn_con_req.request_type = LIBLTE_MME_REQUEST_TYPE_INITIAL_REQUEST; pdn_con_req.request_type = LIBLTE_MME_REQUEST_TYPE_INITIAL_REQUEST;
pdn_con_req.apn_present = false; pdn_con_req.apn_present = false;
//Set PDN protocol type
if (cfg.apn_protocol == "ipv4" || cfg.apn_protocol == ""){
nas_log->console("Setting PDN protocol to IPv4\n");
pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4;
} else if (cfg.apn_protocol == "ipv6") {
nas_log->console("Setting PDN protocol to IPv6\n");
pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV6;
} else if (cfg.apn_protocol == "ipv4v6") {
nas_log->console("Setting PDN protocol to IPv4v6\n");
pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4V6;
} else {
nas_log->warning("Unsupported PDN prtocol. Defaulting to IPv4\n");
nas_log->console("Unsupported PDN prtocol: %s. Defaulting to IPv4\n", cfg.apn_protocol.c_str());
pdn_con_req.pdn_type = LIBLTE_MME_PDN_TYPE_IPV4;
}
// Set the optional flags // Set the optional flags
if (cfg.apn == "") { if (cfg.apn == "") {
pdn_con_req.esm_info_transfer_flag_present = false; pdn_con_req.esm_info_transfer_flag_present = false;
@ -1187,7 +1303,7 @@ void nas::send_detach_request(bool switch_off)
detach_request.nas_ksi.nas_ksi = ctxt.ksi; detach_request.nas_ksi.nas_ksi = ctxt.ksi;
nas_log->info("Requesting Detach with GUTI\n"); nas_log->info("Requesting Detach with GUTI\n");
liblte_mme_pack_detach_request_msg(&detach_request, liblte_mme_pack_detach_request_msg(&detach_request,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY,
ctxt.tx_count, ctxt.tx_count,
(LIBLTE_BYTE_MSG_STRUCT *) pdu); (LIBLTE_BYTE_MSG_STRUCT *) pdu);
@ -1221,7 +1337,11 @@ void nas::send_detach_request(bool switch_off)
} }
nas_log->info("Sending detach request\n"); nas_log->info("Sending detach request\n");
rrc->write_sdu(cfg.lcid, pdu); if (rrc->is_connected()) {
rrc->write_sdu(cfg.lcid, pdu);
} else {
rrc->connection_request(LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING, pdu);
}
} }
void nas::send_detach_accept() void nas::send_detach_accept()
@ -1490,7 +1610,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) {
esm_info_resp.protocol_cnfg_opts_present = false; esm_info_resp.protocol_cnfg_opts_present = false;
} }
byte_buffer_t *pdu = pool_allocate; byte_buffer_t *pdu = pool_allocate_blocking;
if (!pdu) { if (!pdu) {
nas_log->error("Fatal Error: Couldn't allocate PDU in send_attach_request().\n"); nas_log->error("Fatal Error: Couldn't allocate PDU in send_attach_request().\n");
return; return;

@ -1376,7 +1376,7 @@ void rrc::send_con_restablish_complete() {
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE; ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE;
ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id; ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id;
send_ul_dcch_msg(); send_ul_dcch_msg(RB_ID_SRB1);
} }
void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) { void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
@ -1393,13 +1393,13 @@ void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) {
pool->deallocate(nas_msg); pool->deallocate(nas_msg);
send_ul_dcch_msg(); send_ul_dcch_msg(RB_ID_SRB1);
} }
void rrc::send_ul_info_transfer(byte_buffer_t *nas_msg) { void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *nas_msg) {
bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT));
rrc_log->debug("Preparing RX Info Transfer\n"); rrc_log->debug("%s Preparing UL Info Transfer\n", get_rb_name(lcid).c_str());
// Prepare RX INFO packet // Prepare RX INFO packet
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER; ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER;
@ -1409,7 +1409,7 @@ void rrc::send_ul_info_transfer(byte_buffer_t *nas_msg) {
pool->deallocate(nas_msg); pool->deallocate(nas_msg);
send_ul_dcch_msg(); send_ul_dcch_msg(rlc->has_bearer(RB_ID_SRB2) ? RB_ID_SRB2 : RB_ID_SRB1);
} }
void rrc::send_security_mode_complete() { void rrc::send_security_mode_complete() {
@ -1419,7 +1419,7 @@ void rrc::send_security_mode_complete() {
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE; ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE;
ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id; ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id;
send_ul_dcch_msg(); send_ul_dcch_msg(RB_ID_SRB1);
} }
void rrc::send_rrc_con_reconfig_complete() { void rrc::send_rrc_con_reconfig_complete() {
@ -1429,7 +1429,7 @@ void rrc::send_rrc_con_reconfig_complete() {
ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE; ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE;
ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id; ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id;
send_ul_dcch_msg(); send_ul_dcch_msg(RB_ID_SRB1);
} }
bool rrc::ho_prepare() { bool rrc::ho_prepare() {
@ -1583,7 +1583,7 @@ bool rrc::con_reconfig(LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig) {
byte_buffer_t *nas_sdu; byte_buffer_t *nas_sdu;
for (uint32_t i = 0; i < reconfig->N_ded_info_nas; i++) { for (uint32_t i = 0; i < reconfig->N_ded_info_nas; i++) {
nas_sdu = pool_allocate; nas_sdu = pool_allocate_blocking;
if (nas_sdu) { if (nas_sdu) {
memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes); memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes);
nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes; nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes;
@ -1888,7 +1888,7 @@ byte_buffer_t* rrc::byte_align_and_pack()
} }
// Reset and reuse sdu buffer if provided // Reset and reuse sdu buffer if provided
byte_buffer_t *pdcp_buf = pool_allocate; byte_buffer_t *pdcp_buf = pool_allocate_blocking;
if (pdcp_buf) { if (pdcp_buf) {
srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits);
pdcp_buf->N_bytes = bit_buf.N_bits / 8; pdcp_buf->N_bytes = bit_buf.N_bits / 8;
@ -1920,13 +1920,13 @@ void rrc::send_ul_ccch_msg()
} }
} }
void rrc::send_ul_dcch_msg() void rrc::send_ul_dcch_msg(uint32_t lcid)
{ {
liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf); liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT *) &bit_buf);
byte_buffer_t *pdu = byte_align_and_pack(); byte_buffer_t *pdu = byte_align_and_pack();
if (pdu) { if (pdu) {
rrc_log->info("Sending %s\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); rrc_log->info_hex(pdu->msg, pdu->N_bytes, "%s Sending %s\n", get_rb_name(lcid).c_str(), liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]);
pdcp->write_sdu(RB_ID_SRB1, pdu); pdcp->write_sdu(lcid, pdu);
} }
} }
@ -1937,7 +1937,7 @@ void rrc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) {
return; return;
} }
rrc_log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU", get_rb_name(lcid).c_str()); rrc_log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU", get_rb_name(lcid).c_str());
send_ul_info_transfer(sdu); send_ul_info_transfer(lcid, sdu);
} }
void rrc::write_pdu(uint32_t lcid, byte_buffer_t *pdu) { void rrc::write_pdu(uint32_t lcid, byte_buffer_t *pdu) {
@ -2052,7 +2052,7 @@ void rrc::parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu) {
switch (dl_dcch_msg.msg_type) { switch (dl_dcch_msg.msg_type) {
case LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER: case LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER:
pdu = pool_allocate; pdu = pool_allocate_blocking;
if (!pdu) { if (!pdu) {
rrc_log->error("Fatal error: out of buffers in pool\n"); rrc_log->error("Fatal error: out of buffers in pool\n");
return; return;
@ -2179,7 +2179,7 @@ void rrc::send_rrc_ue_cap_info() {
cap->inter_rat_params.cdma2000_hrpd_present = false; cap->inter_rat_params.cdma2000_hrpd_present = false;
cap->inter_rat_params.cdma2000_1xrtt_present = false; cap->inter_rat_params.cdma2000_1xrtt_present = false;
send_ul_dcch_msg(); send_ul_dcch_msg(RB_ID_SRB1);
} }
@ -2980,7 +2980,7 @@ void rrc::rrc_meas::generate_report(uint32_t meas_id)
} }
// Send to lower layers // Send to lower layers
parent->send_ul_dcch_msg(); parent->send_ul_dcch_msg(RB_ID_SRB1);
} }
/* Handle entering/leaving event conditions 5.5.4.1 */ /* Handle entering/leaving event conditions 5.5.4.1 */

@ -29,6 +29,7 @@
#include "srsue/hdr/upper/usim_base.h" #include "srsue/hdr/upper/usim_base.h"
#include "srsue/hdr/upper/usim.h" #include "srsue/hdr/upper/usim.h"
#include "srsue/hdr/upper/nas.h" #include "srsue/hdr/upper/nas.h"
#include "srsue/hdr/upper/gw.h"
#include "srslte/upper/rlc.h" #include "srslte/upper/rlc.h"
#include "srsue/hdr/upper/rrc.h" #include "srsue/hdr/upper/rrc.h"
#include "srsue/hdr/mac/mac.h" #include "srsue/hdr/mac/mac.h"
@ -70,15 +71,17 @@ using namespace srslte;
namespace srslte { namespace srslte {
// fake classes // fake classes
class pdcp_dummy : public rrc_interface_pdcp class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_gw
{ {
public: public:
void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {} void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {}
void write_pdu_bcch_bch(byte_buffer_t *pdu) {} void write_pdu_bcch_bch(byte_buffer_t *pdu) {}
void write_pdu_bcch_dlsch(byte_buffer_t *pdu) {} void write_pdu_bcch_dlsch(byte_buffer_t *pdu) {}
void write_pdu_pcch(byte_buffer_t *pdu) {} void write_pdu_pcch(byte_buffer_t *pdu) {}
void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {}
std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); } std::string get_rb_name(uint32_t lcid) { return std::string("lcid"); }
void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu, bool blocking) {}
bool is_lcid_enabled(uint32_t lcid) { return false; }
}; };
class rrc_dummy : public rrc_interface_nas class rrc_dummy : public rrc_interface_nas
@ -126,7 +129,7 @@ private:
class gw_dummy : public gw_interface_nas, public gw_interface_pdcp class gw_dummy : public gw_interface_nas, public gw_interface_pdcp
{ {
error_t setup_if_addr(uint32_t ip_addr, char *err_str) { return ERROR_NONE; } error_t setup_if_addr(uint8_t pdn_type, uint32_t ip_addr, uint8_t *ipv6_if_id, char *err_str) { return ERROR_NONE; }
void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {} void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {}
void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {} void write_pdu_mch(uint32_t lcid, srslte::byte_buffer_t *sdu) {}
}; };
@ -175,6 +178,7 @@ int security_command_test()
nas.write_pdu(LCID, tmp); nas.write_pdu(LCID, tmp);
// TODO: add check for authentication response // TODO: add check for authentication response
rrc_dummy.reset();
// reuse buffer for security mode command // reuse buffer for security mode command
memcpy(tmp->msg, sec_mode_command_pdu, sizeof(sec_mode_command_pdu)); memcpy(tmp->msg, sec_mode_command_pdu, sizeof(sec_mode_command_pdu));
@ -199,16 +203,20 @@ int mme_attach_request_test()
srslte::log_filter rrc_log("RRC"); srslte::log_filter rrc_log("RRC");
srslte::log_filter mac_log("MAC"); srslte::log_filter mac_log("MAC");
srslte::log_filter usim_log("USIM"); srslte::log_filter usim_log("USIM");
srslte::log_filter gw_log("GW");
nas_log.set_level(srslte::LOG_LEVEL_DEBUG); nas_log.set_level(srslte::LOG_LEVEL_DEBUG);
rrc_log.set_level(srslte::LOG_LEVEL_DEBUG); rrc_log.set_level(srslte::LOG_LEVEL_DEBUG);
usim_log.set_level(srslte::LOG_LEVEL_DEBUG); usim_log.set_level(srslte::LOG_LEVEL_DEBUG);
gw_log.set_level(srslte::LOG_LEVEL_DEBUG);
nas_log.set_hex_limit(100000); nas_log.set_hex_limit(100000);
rrc_log.set_hex_limit(100000); rrc_log.set_hex_limit(100000);
usim_log.set_hex_limit(100000); usim_log.set_hex_limit(100000);
gw_log.set_hex_limit(100000);
rrc_dummy rrc_dummy; rrc_dummy rrc_dummy;
gw_dummy gw; pdcp_dummy pdcp_dummy;
srsue::usim usim; srsue::usim usim;
usim_args_t args; usim_args_t args;
args.mode = "soft"; args.mode = "soft";
@ -224,8 +232,15 @@ int mme_attach_request_test()
nas_cfg.force_imsi_attach = true; nas_cfg.force_imsi_attach = true;
nas_cfg.apn = "test123"; nas_cfg.apn = "test123";
srsue::nas nas; srsue::nas nas;
srsue::gw gw;
nas.init(&usim, &rrc_dummy, &gw, &nas_log, nas_cfg); nas.init(&usim, &rrc_dummy, &gw, &nas_log, nas_cfg);
srslte_gw_config_t gw_config(3);
gw.init(&pdcp_dummy, &nas, &gw_log, gw_config);
gw.set_tundevname("tun0");
// trigger test
nas.attach_request(); nas.attach_request();
// this will time out in the first place // this will time out in the first place

@ -16,7 +16,7 @@
# device_args: Arguments for the device driver. Options are "auto" or any string. # device_args: Arguments for the device driver. Options are "auto" or any string.
# Default for UHD: "recv_frame_size=9232,send_frame_size=9232" # Default for UHD: "recv_frame_size=9232,send_frame_size=9232"
# Default for bladeRF: "" # Default for bladeRF: ""
# #time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay # time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay
# from antenna to timestamp insertion. # from antenna to timestamp insertion.
# Default "auto". B210 USRP: 100 samples, bladeRF: 27. # Default "auto". B210 USRP: 100 samples, bladeRF: 27.
# burst_preamble_us: Preamble length to transmit before start of burst. # burst_preamble_us: Preamble length to transmit before start of burst.
@ -120,12 +120,14 @@ imei = 353490069873319
# NAS configuration # NAS configuration
# #
# apn: Set Access Point Name (APN) # apn: Set Access Point Name (APN)
# apn_protocol: Set APN protocol (IPv4, IPv6 or IPv4v6.)
# user: Username for CHAP authentication # user: Username for CHAP authentication
# pass: Password for CHAP authentication # pass: Password for CHAP authentication
# force_imsi_attach: Whether to always perform an IMSI attach # force_imsi_attach: Whether to always perform an IMSI attach
##################################################################### #####################################################################
[nas] [nas]
#apn = internetinternet #apn = internetinternet
#apn_protocol = ipv4
#user = srsuser #user = srsuser
#pass = srspass #pass = srspass
#force_imsi_attach = false #force_imsi_attach = false
@ -137,6 +139,7 @@ enable = false
# Expert configuration options # Expert configuration options
# #
# ip_netmask: Netmask of the tun_srsue device. Default: 255.255.255.0 # ip_netmask: Netmask of the tun_srsue device. Default: 255.255.255.0
# ip_devname: Nanme of the tun_srsue device. Default: tun_srsue
# rssi_sensor_enabled: Enable or disable RF frontend RSSI sensor. Required for RSRP metrics but # rssi_sensor_enabled: Enable or disable RF frontend RSSI sensor. Required for RSRP metrics but
# can cause UHD instability for long-duration testing. Default true. # can cause UHD instability for long-duration testing. Default true.
# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings # rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings
@ -209,6 +212,7 @@ enable = false
##################################################################### #####################################################################
[expert] [expert]
#ip_netmask = 255.255.255.0 #ip_netmask = 255.255.255.0
#ip_devname = tun_srsue
#mbms_service = -1 #mbms_service = -1
#rssi_sensor_enabled = false #rssi_sensor_enabled = false
#rx_gain_offset = 62 #rx_gain_offset = 62
@ -249,21 +253,3 @@ enable = false
#cfo_loop_pss_tol = 400 #cfo_loop_pss_tol = 400
#cfo_loop_ref_min = 0 #cfo_loop_ref_min = 0
#cfo_loop_pss_conv = 20 #cfo_loop_pss_conv = 20
#####################################################################
# Manual RF calibration
#
# Applies DC offset and IQ imbalance to TX and RX modules.
# Currently this configuration is only used if the detected device is a bladeRF
#
# tx_corr_dc_gain: TX DC offset gain correction
# tx_corr_dc_phase: TX DC offset phase correction
# tx_corr_iq_i: TX IQ imbalance inphase correction
# tx_corr_iq_q: TX IQ imbalance quadrature correction
# same can be configured for rx_*
#####################################################################
[rf_calibration]
tx_corr_dc_gain = 20
tx_corr_dc_phase = 184
tx_corr_iq_i = 19
tx_corr_iq_q = 97

Loading…
Cancel
Save