add upper layer code including tests

master
Andre Puschmann 8 years ago
parent ae186d8195
commit 3ea8d7c721

@ -0,0 +1,87 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef GW_H
#define GW_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/msg_queue.h"
#include "common/interfaces.h"
#include "common/threads.h"
#include "upper/gw_metrics.h"
#include <linux/if.h>
namespace srsue {
class gw
:public gw_interface_pdcp
,public gw_interface_nas
,public thread
{
public:
gw();
void init(pdcp_interface_gw *pdcp_, rrc_interface_gw *rrc_, ue_interface *ue_, srslte::log *gw_log_);
void stop();
void get_metrics(gw_metrics_t &m);
// PDCP interface
void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu);
// NAS interface
srslte::error_t setup_if_addr(uint32_t ip_addr, char *err_str);
private:
static const int GW_THREAD_PRIO = 7;
srslte::byte_buffer_pool *pool;
srslte::log *gw_log;
pdcp_interface_gw *pdcp;
rrc_interface_gw *rrc;
ue_interface *ue;
bool running;
bool run_enable;
int32 tun_fd;
struct ifreq ifr;
int32 sock;
bool if_up;
long ul_tput_bytes;
long dl_tput_bytes;
struct timeval metrics_time[3];
void run_thread();
srslte::error_t init_if(char *err_str);
};
} // namespace srsue
#endif // GW_H

@ -0,0 +1,41 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef UE_GW_METRICS_H
#define UE_GW_METRICS_H
namespace srsue {
struct gw_metrics_t
{
double dl_tput_mbps;
double ul_tput_mbps;
};
} // namespace srsue
#endif // UE_GW_METRICS_H

@ -0,0 +1,146 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef NAS_H
#define NAS_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/security.h"
#include "liblte_mme.h"
using srslte::byte_buffer_t;
namespace srsue {
// EMM states (3GPP 24.302 v10.0.0)
typedef enum{
EMM_STATE_NULL = 0,
EMM_STATE_DEREGISTERED,
EMM_STATE_REGISTERED_INITIATED,
EMM_STATE_REGISTERED,
EMM_STATE_SERVICE_REQUEST_INITIATED,
EMM_STATE_DEREGISTERED_INITIATED,
EMM_STATE_TAU_INITIATED,
EMM_STATE_N_ITEMS,
}emm_state_t;
static const char emm_state_text[EMM_STATE_N_ITEMS][100] = {"NULL",
"DEREGISTERED",
"REGISTERED INITIATED",
"REGISTERED",
"SERVICE REQUEST INITIATED",
"DEREGISTERED INITIATED",
"TRACKING AREA UPDATE INITIATED"};
class nas
:public nas_interface_rrc
{
public:
nas();
void init(usim_interface_nas *usim_,
rrc_interface_nas *rrc_,
gw_interface_nas *gw_,
srslte::log *nas_log_);
void stop();
emm_state_t get_state();
// RRC interface
void notify_connection_setup();
void write_pdu(uint32_t lcid, byte_buffer_t *pdu);
uint32_t get_ul_count();
bool is_attached();
bool get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi);
private:
srslte::byte_buffer_pool *pool;
srslte::log *nas_log;
rrc_interface_nas *rrc;
usim_interface_nas *usim;
gw_interface_nas *gw;
emm_state_t state;
// Save short MAC
// Identifiers
LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti;
bool is_guti_set;
uint32_t ip_addr;
uint8_t eps_bearer_id;
uint8_t transaction_id;
// NAS counters - incremented for each security-protected message recvd/sent
uint32_t count_ul;
uint32_t count_dl;
// Security
uint8_t ksi;
uint8_t k_nas_enc[32];
uint8_t k_nas_int[32];
CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
void integrity_generate(uint8_t *key_128,
uint32_t count,
uint8_t rb_id,
uint8_t direction,
uint8_t *msg,
uint32_t msg_len,
uint8_t *mac);
void integrity_check();
void cipher_encrypt();
void cipher_decrypt();
// Parsers
void parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu);
void parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu);
void parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu);
void parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu);
void parse_identity_request(uint32_t lcid, byte_buffer_t *pdu);
void parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu);
void parse_service_reject(uint32_t lcid, byte_buffer_t *pdu);
void parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu);
void parse_emm_information(uint32_t lcid, byte_buffer_t *pdu);
// Senders
void send_attach_request();
void send_identity_response();
void send_service_request();
void send_esm_information_response();
void gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg);
};
} // namespace srsue
#endif // NAS_H

@ -0,0 +1,85 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef PDCP_H
#define PDCP_H
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "upper/pdcp_entity.h"
using srslte::byte_buffer_t;
namespace srsue {
class pdcp
:public pdcp_interface_gw
,public pdcp_interface_rlc
,public pdcp_interface_rrc
{
public:
pdcp();
void init(rlc_interface_pdcp *rlc_,
rrc_interface_pdcp *rrc_,
gw_interface_pdcp *gw_,
srslte::log *pdcp_log_,
uint8_t direction_);
void stop();
// RRC interface
void reset();
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
void add_bearer(uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg = NULL);
void config_security(uint32_t lcid,
uint8_t *k_rrc_enc,
uint8_t *k_rrc_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo);
// RLC interface
void write_pdu(uint32_t lcid, byte_buffer_t *sdu);
void write_pdu_bcch_bch(byte_buffer_t *sdu);
void write_pdu_bcch_dlsch(byte_buffer_t *sdu);
void write_pdu_pcch(byte_buffer_t *sdu);
private:
srslte::log *pdcp_log;
pdcp_entity pdcp_array[SRSUE_N_RADIO_BEARERS];
rlc_interface_pdcp *rlc;
rrc_interface_pdcp *rrc;
gw_interface_pdcp *gw;
uint8_t direction;
bool valid_lcid(uint32_t lcid);
};
} // namespace srsue
#endif // PDCP_H

@ -0,0 +1,139 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef PDCP_ENTITY_H
#define PDCP_ENTITY_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/security.h"
using srslte::byte_buffer_t;
namespace srsue {
/****************************************************************************
* Structs and Defines
* Ref: 3GPP TS 36.323 v10.1.0
***************************************************************************/
#define PDCP_CONTROL_MAC_I 0x00000000
#define PDCP_PDU_TYPE_PDCP_STATUS_REPORT 0x0
#define PDCP_PDU_TYPE_INTERSPERSED_ROHC_FEEDBACK_PACKET 0x1
typedef enum{
PDCP_D_C_CONTROL_PDU = 0,
PDCP_D_C_DATA_PDU,
PDCP_D_C_N_ITEMS,
}pdcp_d_c_t;
static const char pdcp_d_c_text[PDCP_D_C_N_ITEMS][20] = {"Control PDU",
"Data PDU"};
/****************************************************************************
* PDCP Entity interface
* Common interface for all PDCP entities
***************************************************************************/
class pdcp_entity
{
public:
pdcp_entity();
void init(rlc_interface_pdcp *rlc_,
rrc_interface_pdcp *rrc_,
gw_interface_pdcp *gw_,
srslte::log *log_,
uint32_t lcid_,
uint8_t direction_,
LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg = NULL
);
void reset();
bool is_active();
// RRC interface
void write_sdu(byte_buffer_t *sdu);
void config_security(uint8_t *k_rrc_enc_,
uint8_t *k_rrc_int_,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo_,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo_);
// RLC interface
void write_pdu(byte_buffer_t *pdu);
private:
srslte::byte_buffer_pool *pool;
srslte::log *log;
rlc_interface_pdcp *rlc;
rrc_interface_pdcp *rrc;
gw_interface_pdcp *gw;
bool active;
uint32_t lcid;
bool do_security;
u_int8_t direction;
uint8_t sn_len;
// TODO: Support the following configurations
// bool do_rohc;
uint32_t rx_count;
uint32_t tx_count;
uint8_t k_rrc_enc[32];
uint8_t k_rrc_int[32];
CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
void integrity_generate(uint8_t *key_128,
uint32_t count,
uint8_t rb_id,
uint8_t direction,
uint8_t *msg,
uint32_t msg_len,
uint8_t *mac);
};
/****************************************************************************
* Pack/Unpack helper functions
* Ref: 3GPP TS 36.323 v10.1.0
***************************************************************************/
void pdcp_pack_control_pdu(uint32_t sn, byte_buffer_t *sdu);
void pdcp_unpack_control_pdu(byte_buffer_t *sdu, uint32_t *sn);
void pdcp_pack_data_pdu_short_sn(uint32_t sn, byte_buffer_t *sdu);
void pdcp_unpack_data_pdu_short_sn(byte_buffer_t *sdu, uint32_t *sn);
void pdcp_pack_data_pdu_long_sn(uint32_t sn, byte_buffer_t *sdu);
void pdcp_unpack_data_pdu_long_sn(byte_buffer_t *sdu, uint32_t *sn);
} // namespace srsue
#endif // PDCP_ENTITY_H

@ -0,0 +1,100 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_H
#define RLC_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/msg_queue.h"
#include "upper/rlc_entity.h"
#include "upper/rlc_metrics.h"
namespace srsue {
/****************************************************************************
* RLC Layer
* Ref: 3GPP TS 36.322 v10.0.0
* Single interface for RLC layer - contains separate RLC entities for
* each bearer.
***************************************************************************/
class rlc
:public rlc_interface_mac
,public rlc_interface_pdcp
,public rlc_interface_rrc
{
public:
rlc();
void init(pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
ue_interface *ue_,
srslte::log *rlc_log_,
srslte::mac_interface_timers *mac_timers_);
void stop();
void get_metrics(rlc_metrics_t &m);
// PDCP interface
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
// MAC interface
uint32_t get_buffer_state(uint32_t lcid);
uint32_t get_total_buffer_state(uint32_t lcid);
int read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes);
void write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes);
void write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes);
void write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes);
void write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes);
// RRC interface
void reset();
void add_bearer(uint32_t lcid);
void add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
private:
void reset_metrics();
srslte::byte_buffer_pool *pool;
srslte::log *rlc_log;
pdcp_interface_rlc *pdcp;
rrc_interface_rlc *rrc;
srslte::mac_interface_timers *mac_timers;
ue_interface *ue;
rlc_entity rlc_array[SRSUE_N_RADIO_BEARERS];
long ul_tput_bytes[SRSUE_N_RADIO_BEARERS];
long dl_tput_bytes[SRSUE_N_RADIO_BEARERS];
struct timeval metrics_time[3];
bool valid_lcid(uint32_t lcid);
};
} // namespace srsue
#endif // RLC_H

@ -0,0 +1,230 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_AM_H
#define RLC_AM_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/msg_queue.h"
#include "common/timeout.h"
#include "upper/rlc_common.h"
#include <map>
#include <deque>
#include <list>
using srslte::byte_buffer_t;
namespace srsue {
struct rlc_amd_rx_pdu_t{
rlc_amd_pdu_header_t header;
byte_buffer_t *buf;
};
struct rlc_amd_rx_pdu_segments_t{
std::list<rlc_amd_rx_pdu_t> segments;
};
struct rlc_amd_tx_pdu_t{
rlc_amd_pdu_header_t header;
byte_buffer_t *buf;
uint32_t retx_count;
bool is_acked;
};
struct rlc_amd_retx_t{
uint32_t sn;
bool is_segment;
uint32_t so_start;
uint32_t so_end;
};
class rlc_am
:public rlc_common
{
public:
rlc_am();
void init(srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers);
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
void reset();
void empty_queue();
rlc_mode_t get_mode();
uint32_t get_bearer();
// PDCP interface
void write_sdu(byte_buffer_t *sdu);
// MAC interface
uint32_t get_buffer_state();
uint32_t get_total_buffer_state();
int read_pdu(uint8_t *payload, uint32_t nof_bytes);
void write_pdu(uint8_t *payload, uint32_t nof_bytes);
private:
srslte::byte_buffer_pool *pool;
srslte::log *log;
uint32_t lcid;
pdcp_interface_rlc *pdcp;
rrc_interface_rlc *rrc;
// TX SDU buffers
srslte::msg_queue tx_sdu_queue;
byte_buffer_t *tx_sdu;
// PDU being resegmented
rlc_amd_tx_pdu_t tx_pdu_segments;
// Tx and Rx windows
std::map<uint32_t, rlc_amd_tx_pdu_t> tx_window;
std::deque<rlc_amd_retx_t> retx_queue;
std::map<uint32_t, rlc_amd_rx_pdu_t> rx_window;
std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
// RX SDU buffers
byte_buffer_t *rx_sdu;
// Mutexes
pthread_mutex_t mutex;
bool poll_received;
bool do_status;
rlc_status_pdu_t status;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
// TX configs
int32_t t_poll_retx; // Poll retx timeout (ms)
int32_t poll_pdu; // Insert poll bit after this many PDUs
int32_t poll_byte; // Insert poll bit after this much data (KB)
int32_t max_retx_thresh; // Max number of retx
// RX configs
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms)
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
// Tx state variables
uint32_t vt_a; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window.
uint32_t vt_ms; // Max send state. High edge of tx window. vt_a + window_size.
uint32_t vt_s; // Send state. SN to be assigned for next PDU.
uint32_t poll_sn; // Poll send state. SN of most recent PDU txed with poll bit set.
// Tx counters
uint32_t pdu_without_poll;
uint32_t byte_without_poll;
// Rx state variables
uint32_t vr_r; // Receive state. SN following last in-sequence received PDU. Low edge of rx window
uint32_t vr_mr; // Max acceptable receive state. High edge of rx window. vr_r + window size.
uint32_t vr_x; // t_reordering state. SN following PDU which triggered t_reordering.
uint32_t vr_ms; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU.
uint32_t vr_h; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
srslte::timeout poll_retx_timeout;
srslte::timeout reordering_timeout;
srslte::timeout status_prohibit_timeout;
static const int reordering_timeout_id = 1;
// Timer checks
bool status_prohibited();
bool poll_retx();
void check_reordering_timeout();
// Helpers
bool poll_required();
int prepare_status();
int build_status_pdu(uint8_t *payload, uint32_t nof_bytes);
int build_retx_pdu(uint8_t *payload, uint32_t nof_bytes);
int build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx);
int build_data_pdu(uint8_t *payload, uint32_t nof_bytes);
void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t header);
void handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t header);
void handle_control_pdu(uint8_t *payload, uint32_t nof_bytes);
void reassemble_rx_sdus();
bool inside_tx_window(uint16_t sn);
bool inside_rx_window(uint16_t sn);
void debug_state();
bool add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pdu_t *segment);
int required_buffer_size(rlc_amd_retx_t retx);
bool retx_queue_has_sn(uint32_t sn);
};
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
void rlc_am_read_data_pdu_header(byte_buffer_t *pdu, rlc_amd_pdu_header_t *header);
void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd_pdu_header_t *header);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, byte_buffer_t *pdu);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payload);
void rlc_am_read_status_pdu(byte_buffer_t *pdu, rlc_status_pdu_t *status);
void rlc_am_read_status_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_status_pdu_t *status);
void rlc_am_write_status_pdu(rlc_status_pdu_t *status, byte_buffer_t *pdu );
int rlc_am_write_status_pdu(rlc_status_pdu_t *status, uint8_t *payload);
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header);
uint32_t rlc_am_packed_length(rlc_status_pdu_t *status);
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(uint8_t *payload);
bool rlc_am_is_pdu_segment(uint8_t *payload);
std::string rlc_am_to_string(rlc_status_pdu_t *status);
bool rlc_am_start_aligned(uint8_t fi);
bool rlc_am_end_aligned(uint8_t fi);
} // namespace srsue
#endif // RLC_AM_H

@ -0,0 +1,184 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_COMMON_H
#define RLC_COMMON_H
namespace srsue {
/****************************************************************************
* Structs and Defines
* Ref: 3GPP TS 36.322 v10.0.0
***************************************************************************/
#define RLC_AM_WINDOW_SIZE 512
typedef enum{
RLC_MODE_TM = 0,
RLC_MODE_UM,
RLC_MODE_AM,
RLC_MODE_N_ITEMS,
}rlc_mode_t;
static const char rlc_mode_text[RLC_MODE_N_ITEMS][20] = {"Transparent Mode",
"Unacknowledged Mode",
"Acknowledged Mode"};
typedef enum{
RLC_FI_FIELD_START_AND_END_ALIGNED = 0,
RLC_FI_FIELD_NOT_END_ALIGNED,
RLC_FI_FIELD_NOT_START_ALIGNED,
RLC_FI_FIELD_NOT_START_OR_END_ALIGNED,
RLC_FI_FIELD_N_ITEMS,
}rlc_fi_field_t;
static const char rlc_fi_field_text[RLC_FI_FIELD_N_ITEMS][32] = {"Start and end aligned",
"Not end aligned",
"Not start aligned",
"Not start or end aligned"};
typedef enum{
RLC_DC_FIELD_CONTROL_PDU = 0,
RLC_DC_FIELD_DATA_PDU,
RLC_DC_FIELD_N_ITEMS,
}rlc_dc_field_t;
static const char rlc_dc_field_text[RLC_DC_FIELD_N_ITEMS][20] = {"Control PDU",
"Data PDU"};
typedef enum{
RLC_UMD_SN_SIZE_5_BITS = 0,
RLC_UMD_SN_SIZE_10_BITS,
RLC_UMD_SN_SIZE_N_ITEMS,
}rlc_umd_sn_size_t;
static const char rlc_umd_sn_size_text[RLC_UMD_SN_SIZE_N_ITEMS][20] = {"5 bits", "10 bits"};
static const uint16_t rlc_umd_sn_size_num[RLC_UMD_SN_SIZE_N_ITEMS] = {5, 10};
// UMD PDU Header
typedef struct{
uint8_t fi; // Framing info
rlc_umd_sn_size_t sn_size; // Sequence number size (5 or 10 bits)
uint16_t sn; // Sequence number
uint32_t N_li; // Number of length indicators
uint16_t li[RLC_AM_WINDOW_SIZE]; // Array of length indicators
}rlc_umd_pdu_header_t;
// AMD PDU Header
struct rlc_amd_pdu_header_t{
rlc_dc_field_t dc; // Data or control
uint8_t rf; // Resegmentation flag
uint8_t p; // Polling bit
uint8_t fi; // Framing info
uint16_t sn; // Sequence number
uint8_t lsf; // Last segment flag
uint16_t so; // Segment offset
uint32_t N_li; // Number of length indicators
uint16_t li[RLC_AM_WINDOW_SIZE]; // Array of length indicators
rlc_amd_pdu_header_t(){
dc = RLC_DC_FIELD_CONTROL_PDU;
rf = 0;
p = 0;
fi = 0;
sn = 0;
lsf = 0;
so = 0;
N_li=0;
for(int i=0;i<RLC_AM_WINDOW_SIZE;i++)
li[i] = 0;
}
rlc_amd_pdu_header_t(const rlc_amd_pdu_header_t& h)
{
copy(h);
}
rlc_amd_pdu_header_t& operator= (const rlc_amd_pdu_header_t& h)
{
copy(h);
return *this;
}
void copy(const rlc_amd_pdu_header_t& h)
{
dc = h.dc;
rf = h.rf;
p = h.p;
fi = h.fi;
sn = h.sn;
lsf = h.lsf;
so = h.so;
N_li = h.N_li;
for(int i=0;i<h.N_li;i++)
li[i] = h.li[i];
}
};
// NACK helper
struct rlc_status_nack_t{
uint16_t nack_sn;
bool has_so;
uint16_t so_start;
uint16_t so_end;
rlc_status_nack_t(){has_so=false; nack_sn=0; so_start=0; so_end=0;}
};
// STATUS PDU
struct rlc_status_pdu_t{
uint16_t ack_sn;
uint32_t N_nack;
rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE];
rlc_status_pdu_t(){N_nack=0; ack_sn=0;}
};
/****************************************************************************
* RLC Common interface
* Common interface for all RLC entities
***************************************************************************/
class rlc_common
{
public:
virtual void init(srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers_) = 0;
virtual void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) = 0;
virtual void reset() = 0;
virtual void empty_queue() = 0;
virtual rlc_mode_t get_mode() = 0;
virtual uint32_t get_bearer() = 0;
// PDCP interface
virtual void write_sdu(srslte::byte_buffer_t *sdu) = 0;
// MAC interface
virtual uint32_t get_buffer_state() = 0;
virtual uint32_t get_total_buffer_state() = 0;
virtual int read_pdu(uint8_t *payload, uint32_t nof_bytes) = 0;
virtual void write_pdu(uint8_t *payload, uint32_t nof_bytes) = 0;
};
} // namespace srsue
#endif // RLC_COMMON_H

@ -0,0 +1,85 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_ENTITY_H
#define RLC_ENTITY_H
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "upper/rlc_common.h"
#include "upper/rlc_tm.h"
#include "upper/rlc_um.h"
#include "upper/rlc_am.h"
namespace srsue {
/****************************************************************************
* RLC Entity
* Common container for all RLC entities
***************************************************************************/
class rlc_entity
{
public:
rlc_entity();
void init(rlc_mode_t mode,
srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers_);
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
void reset();
bool active();
rlc_mode_t get_mode();
uint32_t get_bearer();
// PDCP interface
void write_sdu(byte_buffer_t *sdu);
// MAC interface
uint32_t get_buffer_state();
uint32_t get_total_buffer_state();
int read_pdu(uint8_t *payload, uint32_t nof_bytes);
void write_pdu(uint8_t *payload, uint32_t nof_bytes);
private:
rlc_tm tm;
rlc_um um;
rlc_am am;
rlc_common *rlc;
};
} // namespace srsue
#endif // RLC_ENTITY_H

@ -0,0 +1,41 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef UE_RLC_METRICS_H
#define UE_RLC_METRICS_H
namespace srsue {
struct rlc_metrics_t
{
float dl_tput_mbps;
float ul_tput_mbps;
};
} // namespace srsue
#endif // UE_RLC_METRICS_H

@ -0,0 +1,80 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_TM_H
#define RLC_TM_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/msg_queue.h"
#include "upper/rlc_common.h"
namespace srsue {
class rlc_tm
:public rlc_common
{
public:
rlc_tm();
void init(srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers);
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
void reset();
void empty_queue();
rlc_mode_t get_mode();
uint32_t get_bearer();
// PDCP interface
void write_sdu(srslte::byte_buffer_t *sdu);
// MAC interface
uint32_t get_buffer_state();
uint32_t get_total_buffer_state();
int read_pdu(uint8_t *payload, uint32_t nof_bytes);
void write_pdu(uint8_t *payload, uint32_t nof_bytes);
private:
srslte::byte_buffer_pool *pool;
srslte::log *log;
uint32_t lcid;
pdcp_interface_rlc *pdcp;
rrc_interface_rlc *rrc;
// Thread-safe queues for MAC messages
srslte::msg_queue ul_queue;
};
} // namespace srsue
#endif // RLC_TM_H

@ -0,0 +1,158 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RLC_UM_H
#define RLC_UM_H
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/msg_queue.h"
#include "upper/rlc_common.h"
#include <pthread.h>
#include <map>
#include <queue>
namespace srsue {
struct rlc_umd_pdu_t{
rlc_umd_pdu_header_t header;
srslte::byte_buffer_t *buf;
};
class rlc_um
:public srslte::timer_callback
,public rlc_common
{
public:
rlc_um();
void init(srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers_);
void configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg);
void reset();
void empty_queue();
rlc_mode_t get_mode();
uint32_t get_bearer();
// PDCP interface
void write_sdu(srslte::byte_buffer_t *sdu);
// MAC interface
uint32_t get_buffer_state();
uint32_t get_total_buffer_state();
int read_pdu(uint8_t *payload, uint32_t nof_bytes);
void write_pdu(uint8_t *payload, uint32_t nof_bytes);
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
bool reordering_timeout_running();
private:
srslte::byte_buffer_pool *pool;
srslte::log *log;
uint32_t lcid;
pdcp_interface_rlc *pdcp;
rrc_interface_rlc *rrc;
srslte::mac_interface_timers *mac_timers;
// TX SDU buffers
srslte::msg_queue tx_sdu_queue;
srslte::byte_buffer_t *tx_sdu;
// Rx window
std::map<uint32_t, rlc_umd_pdu_t> rx_window;
uint32_t rx_window_size;
uint32_t rx_mod; // Rx counter modulus
uint32_t tx_mod; // Tx counter modulus
// RX SDU buffers
srslte::byte_buffer_t *rx_sdu;
uint32_t vr_ur_in_rx_sdu;
// Mutexes
pthread_mutex_t mutex;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
int32_t t_reordering; // Timer used by rx to detect PDU loss (ms)
rlc_umd_sn_size_t tx_sn_field_length; // Number of bits used for tx (UL) sequence number
rlc_umd_sn_size_t rx_sn_field_length; // Number of bits used for rx (DL) sequence number
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
// Tx state variables
uint32_t vt_us; // Send state. SN to be assigned for next PDU.
// Rx state variables
uint32_t vr_ur; // Receive state. SN of earliest PDU still considered for reordering.
uint32_t vr_ux; // t_reordering state. SN following PDU which triggered t_reordering.
uint32_t vr_uh; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
uint32_t reordering_timeout_id;
bool pdu_lost;
int build_data_pdu(uint8_t *payload, uint32_t nof_bytes);
void handle_data_pdu(uint8_t *payload, uint32_t nof_bytes);
void reassemble_rx_sdus();
bool inside_reordering_window(uint16_t sn);
void debug_state();
};
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
void rlc_um_read_data_pdu_header(srslte::byte_buffer_t *pdu, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header);
void rlc_um_read_data_pdu_header(uint8_t *payload, uint32_t nof_bytes, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header);
void rlc_um_write_data_pdu_header(rlc_umd_pdu_header_t *header, srslte::byte_buffer_t *pdu);
uint32_t rlc_um_packed_length(rlc_umd_pdu_header_t *header);
bool rlc_um_start_aligned(uint8_t fi);
bool rlc_um_end_aligned(uint8_t fi);
} // namespace srsue
#endif // RLC_UM_H

@ -0,0 +1,211 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef RRC_H
#define RRC_H
#include "pthread.h"
#include "common/buffer_pool.h"
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/security.h"
#include <map>
using srslte::byte_buffer_t;
namespace srsue {
// RRC states (3GPP 36.331 v10.0.0)
typedef enum{
RRC_STATE_IDLE = 0,
RRC_STATE_SIB1_SEARCH,
RRC_STATE_SIB2_SEARCH,
RRC_STATE_WAIT_FOR_CON_SETUP,
RRC_STATE_COMPLETING_SETUP,
RRC_STATE_RRC_CONNECTED,
RRC_STATE_N_ITEMS,
}rrc_state_t;
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
"SIB1_SEARCH",
"SIB2_SEARCH",
"WAIT FOR CON SETUP",
"COMPLETING SETUP",
"RRC CONNECTED"};
class rrc
:public rrc_interface_nas
,public rrc_interface_phy
,public rrc_interface_mac
,public rrc_interface_gw
,public rrc_interface_pdcp
,public rrc_interface_rlc
,public srslte::timer_callback
{
public:
rrc();
void init(phy_interface_rrc *phy_,
mac_interface_rrc *mac_,
rlc_interface_rrc *rlc_,
pdcp_interface_rrc *pdcp_,
nas_interface_rrc *nas_,
usim_interface_rrc *usim_,
srslte::mac_interface_timers *mac_timers_,
srslte::log *rrc_log_);
void stop();
rrc_state_t get_state();
void set_ue_category(int category);
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
void test_con_restablishment();
void liblte_rrc_log(char* str);
private:
srslte::byte_buffer_pool *pool;
srslte::log *rrc_log;
phy_interface_rrc *phy;
mac_interface_rrc *mac;
rlc_interface_rrc *rlc;
pdcp_interface_rrc *pdcp;
nas_interface_rrc *nas;
usim_interface_rrc *usim;
srslte::bit_buffer_t bit_buf;
pthread_mutex_t mutex;
rrc_state_t state;
uint8_t transaction_id;
bool drb_up;
uint8_t k_rrc_enc[32];
uint8_t k_rrc_int[32];
uint8_t k_up_enc[32];
uint8_t k_up_int[32]; // Not used: only for relay nodes (3GPP 33.401 Annex A.7)
CIPHERING_ALGORITHM_ID_ENUM cipher_algo;
INTEGRITY_ALGORITHM_ID_ENUM integ_algo;
LIBLTE_RRC_MIB_STRUCT mib;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT sib1;
LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2;
std::map<uint32_t, LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT> srbs;
std::map<uint32_t, LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT> drbs;
LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg;
LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg;
pthread_t sib_search_thread;
// RRC constants and timers
srslte::mac_interface_timers *mac_timers;
uint32_t n310_cnt, N310;
uint32_t n311_cnt, N311;
uint32_t t301, t310, t311;
uint32_t safe_reset_timer;
int ue_category;
// NAS interface
void write_sdu(uint32_t lcid, byte_buffer_t *sdu);
uint16_t get_mcc();
uint16_t get_mnc();
void enable_capabilities();
// PHY interface
void in_sync();
void out_of_sync();
// MAC interface
void release_pucch_srs();
void ra_problem();
// GW interface
bool rrc_connected();
void rrc_connect();
bool have_drb();
// PDCP interface
void write_pdu(uint32_t lcid, 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_pcch(byte_buffer_t *pdu);
// RLC interface
void max_retx_attempted();
// Senders
void send_con_request();
void send_con_restablish_request();
void send_con_restablish_complete();
void send_con_setup_complete(byte_buffer_t *nas_msg);
void send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu);
void send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu);
void send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu);
void send_rrc_ue_cap_info(uint32_t lcid, byte_buffer_t *pdu);
// Parsers
void parse_dl_ccch(byte_buffer_t *pdu);
void parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu);
void parse_dl_info_transfer(uint32_t lcid, byte_buffer_t *pdu);
// Helpers
void reset_ue();
void rrc_connection_release();
void radio_link_failure();
static void* start_sib_thread(void *rrc_);
void sib_search();
uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t x);
void apply_sib2_configs();
void handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup);
void handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup);
void handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig, byte_buffer_t *pdu);
void add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg);
void add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg);
void release_drb(uint8_t lcid);
void apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg);
void apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults);
void apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg, bool apply_defaults);
// Helpers for setting default values
void set_phy_default_pucch_srs();
void set_phy_default();
void set_mac_default();
void set_rrc_default();
};
} // namespace srsue
#endif // RRC_H

@ -0,0 +1,127 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef USIM_H
#define USIM_H
#include <string>
#include "common/log.h"
#include "common/common.h"
#include "common/interfaces.h"
#include "common/security.h"
namespace srsue {
typedef enum{
auth_algo_milenage = 0,
auth_algo_xor,
}auth_algo_t;
typedef struct{
std::string algo;
std::string op;
std::string amf;
std::string imsi;
std::string imei;
std::string k;
}usim_args_t;
class usim
:public usim_interface_nas
,public usim_interface_rrc
{
public:
usim();
void init(usim_args_t *args, srslte::log *usim_log_);
void stop();
// NAS interface
void get_imsi_vec(uint8_t* imsi_, uint32_t n);
void get_imei_vec(uint8_t* imei_, uint32_t n);
void generate_authentication_response(uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res);
void generate_nas_keys(uint8_t *k_nas_enc,
uint8_t *k_nas_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo);
// RRC interface
void generate_as_keys(uint32_t count_ul,
uint8_t *k_rrc_enc,
uint8_t *k_rrc_int,
uint8_t *k_up_enc,
uint8_t *k_up_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo);
private:
void gen_auth_res_milenage( uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res);
void gen_auth_res_xor( uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res);
void str_to_hex(std::string str, uint8_t *hex);
srslte::log *usim_log;
// User data
auth_algo_t auth_algo;
uint8_t amf[2]; // 3GPP 33.102 v10.0.0 Annex H
uint8_t op[16];
uint64_t imsi;
uint64_t imei;
uint8_t k[16];
// Security variables
uint8_t rand[16];
uint8_t ck[16];
uint8_t ik[16];
uint8_t ak[6];
uint8_t mac[8];
uint8_t autn[16];
uint8_t k_asme[32];
uint8_t k_enb[32];
};
} // namespace srsue
#endif // USIM_H

@ -0,0 +1,24 @@
# Copyright 2015 Software Radio Systems Limited
#
# This file is part of srsUE
#
# srsUE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsUE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
file(GLOB SOURCES "*.cc")
add_library(srslte_upper SHARED ${SOURCES})
target_link_libraries(srslte_upper srslte_common)
INSTALL(TARGETS srslte_upper DESTINATION ${LIBRARY_DIR})
SRSLTE_SET_PIC(srslte_upper)

@ -0,0 +1,290 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/gw.h"
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
using namespace srslte;
namespace srsue{
gw::gw()
:if_up(false)
{}
void gw::init(pdcp_interface_gw *pdcp_, rrc_interface_gw *rrc_, ue_interface *ue_, srslte::log *gw_log_)
{
pool = byte_buffer_pool::get_instance();
pdcp = pdcp_;
rrc = rrc_;
ue = ue_;
gw_log = gw_log_;
run_enable = true;
gettimeofday(&metrics_time[1], NULL);
dl_tput_bytes = 0;
ul_tput_bytes = 0;
}
void gw::stop()
{
if(run_enable)
{
run_enable = false;
if(if_up)
{
close(tun_fd);
// Wait thread to exit gracefully otherwise might leave a mutex locked
int cnt=0;
while(running && cnt<100) {
usleep(10000);
cnt++;
}
if (running) {
thread_cancel();
}
wait_thread_finish();
}
// TODO: tear down TUN device?
}
}
void gw::get_metrics(gw_metrics_t &m)
{
gettimeofday(&metrics_time[2], NULL);
get_time_interval(metrics_time);
double secs = (double) metrics_time[0].tv_sec+metrics_time[0].tv_usec*1e-6;
m.dl_tput_mbps = (dl_tput_bytes*8/(double)1e6)/secs;
m.ul_tput_mbps = (ul_tput_bytes*8/(double)1e6)/secs;
gw_log->info("RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n",
m.dl_tput_mbps, m.ul_tput_mbps);
memcpy(&metrics_time[1], &metrics_time[2], sizeof(struct timeval));
dl_tput_bytes = 0;
ul_tput_bytes = 0;
}
/*******************************************************************************
PDCP interface
*******************************************************************************/
void gw::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu)
{
gw_log->info_hex(pdu->msg, pdu->N_bytes, "RX PDU");
gw_log->info("RX PDU. Stack latency: %ld us\n", pdu->get_latency_us());
dl_tput_bytes += pdu->N_bytes;
if(!if_up)
{
gw_log->warning("TUN/TAP not up - dropping gw RX message\n");
}else{
int n = write(tun_fd, pdu->msg, pdu->N_bytes);
if(pdu->N_bytes != n)
{
gw_log->warning("DL TUN/TAP write failure\n");
}
}
pool->deallocate(pdu);
}
/*******************************************************************************
NAS interface
*******************************************************************************/
srslte::error_t gw::setup_if_addr(uint32_t ip_addr, char *err_str)
{
if(!if_up)
{
if(init_if(err_str))
{
gw_log->error("init_if failed\n");
return(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(ERROR_CANT_START);
}
ifr.ifr_netmask.sa_family = AF_INET;
((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0");
if(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(ERROR_CANT_START);
}
// Setup a thread to receive packets from the TUN device
start(GW_THREAD_PRIO);
return(ERROR_NONE);
}
srslte::error_t gw::init_if(char *err_str)
{
if(if_up)
{
return(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(ERROR_CANT_START);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ);
if(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(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(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(ERROR_CANT_START);
}
if_up = true;
return(ERROR_NONE);
}
/********************/
/* GW Receive */
/********************/
void gw::run_thread()
{
struct iphdr *ip_pkt;
uint32 idx = 0;
int32 N_bytes;
byte_buffer_t *pdu = pool->allocate();
gw_log->info("GW IP packet receiver thread run_enable\n");
running = true;
while(run_enable)
{
if (SRSUE_MAX_BUFFER_SIZE_BYTES-SRSUE_BUFFER_HEADER_OFFSET > idx) {
N_bytes = read(tun_fd, &pdu->msg[idx], SRSUE_MAX_BUFFER_SIZE_BYTES-SRSUE_BUFFER_HEADER_OFFSET - idx);
} else {
gw_log->error("GW pdu buffer full - gw receive thread exiting.\n");
gw_log->console("GW pdu buffer full - gw receive thread exiting.\n");
break;
}
gw_log->debug("Read %d bytes from TUN fd=%d, idx=%d\n", N_bytes, tun_fd, idx);
if(N_bytes > 0)
{
pdu->N_bytes = idx + N_bytes;
ip_pkt = (struct iphdr*)pdu->msg;
// Warning: Accept only IPv4 packets
if (ip_pkt->version == 4) {
// Check if entire packet was received
if(ntohs(ip_pkt->tot_len) == pdu->N_bytes)
{
gw_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU");
while(run_enable && (!rrc->rrc_connected() || !rrc->have_drb())) {
rrc->rrc_connect();
usleep(1000);
}
if (!run_enable) {
break;
}
// Send PDU directly to PDCP
pdu->set_timestamp();
ul_tput_bytes += pdu->N_bytes;
pdcp->write_sdu(RB_ID_DRB1, pdu);
do {
pdu = pool->allocate();
if (!pdu) {
printf("Not enough buffers in pool\n");
usleep(100000);
}
} while(!pdu);
idx = 0;
}else{
idx += N_bytes;
}
}
}else{
gw_log->error("Failed to read from TUN interface - gw receive thread exiting.\n");
gw_log->console("Failed to read from TUN interface - gw receive thread exiting.\n");
break;
}
}
running = false;
gw_log->info("GW IP receiver thread exiting.\n");
}
} // namespace srsue

@ -0,0 +1,633 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/nas.h"
using namespace srslte;
namespace srsue{
nas::nas()
:state(EMM_STATE_DEREGISTERED)
,is_guti_set(false)
,ip_addr(0)
,eps_bearer_id(0)
,count_ul(0)
,count_dl(0)
{}
void nas::init(usim_interface_nas *usim_,
rrc_interface_nas *rrc_,
gw_interface_nas *gw_,
srslte::log *nas_log_)
{
pool = byte_buffer_pool::get_instance();
usim = usim_;
rrc = rrc_;
gw = gw_;
nas_log = nas_log_;
}
void nas::stop()
{}
emm_state_t nas::get_state()
{
return state;
}
/*******************************************************************************
RRC interface
*******************************************************************************/
bool nas::is_attached()
{
return state == EMM_STATE_REGISTERED;
}
void nas::notify_connection_setup()
{
nas_log->debug("State = %s\n", emm_state_text[state]);
if(EMM_STATE_DEREGISTERED == state) {
send_attach_request();
} else {
send_service_request();
}
}
void nas::write_pdu(uint32_t lcid, byte_buffer_t *pdu)
{
uint8 pd;
uint8 msg_type;
nas_log->info_hex(pdu->msg, pdu->N_bytes, "DL %s PDU", rb_id_text[lcid]);
// Parse the message
liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)pdu, &pd, &msg_type);
switch(msg_type)
{
case LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT:
parse_attach_accept(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT:
parse_attach_reject(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST:
parse_authentication_request(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT:
parse_authentication_reject(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST:
parse_identity_request(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND:
parse_security_mode_command(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT:
parse_service_reject(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST:
parse_esm_information_request(lcid, pdu);
break;
case LIBLTE_MME_MSG_TYPE_EMM_INFORMATION:
parse_emm_information(lcid, pdu);
break;
default:
nas_log->error("Not handling NAS message with MSG_TYPE=%02X\n",msg_type);
pool->deallocate(pdu);
break;
}
}
uint32_t nas::get_ul_count()
{
return count_ul;
}
bool nas::get_s_tmsi(LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi)
{
if(is_guti_set) {
s_tmsi->mmec = guti.mme_code;
s_tmsi->m_tmsi = guti.m_tmsi;
return true;
} else {
return false;
}
}
/*******************************************************************************
Security
*******************************************************************************/
void nas::integrity_generate(uint8_t *key_128,
uint32_t count,
uint8_t rb_id,
uint8_t direction,
uint8_t *msg,
uint32_t msg_len,
uint8_t *mac)
{
switch(integ_algo)
{
case INTEGRITY_ALGORITHM_ID_EIA0:
break;
case INTEGRITY_ALGORITHM_ID_128_EIA1:
security_128_eia1(key_128,
count,
rb_id,
direction,
msg,
msg_len,
mac);
break;
case INTEGRITY_ALGORITHM_ID_128_EIA2:
security_128_eia2(key_128,
count,
rb_id,
direction,
msg,
msg_len,
mac);
break;
default:
break;
}
}
void nas::integrity_check()
{
}
void nas::cipher_encrypt()
{
}
void nas::cipher_decrypt()
{
}
/*******************************************************************************
Parsers
*******************************************************************************/
void nas::parse_attach_accept(uint32_t lcid, byte_buffer_t *pdu)
{
LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept;
LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req;
LIBLTE_MME_ATTACH_COMPLETE_MSG_STRUCT attach_complete;
LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_ACCEPT_MSG_STRUCT act_def_eps_bearer_context_accept;
nas_log->info("Received Attach Accept\n");
count_dl++;
liblte_mme_unpack_attach_accept_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu, &attach_accept);
if(attach_accept.eps_attach_result == LIBLTE_MME_EPS_ATTACH_RESULT_EPS_ONLY)
{
//FIXME: Handle t3412.unit
//FIXME: Handle tai_list
if(attach_accept.guti_present)
{
memcpy(&guti, &attach_accept.guti.guti, sizeof(LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT));
is_guti_set = true;
// TODO: log message to console
}
if(attach_accept.lai_present)
{
}
if(attach_accept.ms_id_present)
{}
if(attach_accept.emm_cause_present)
{}
if(attach_accept.t3402_present)
{}
if(attach_accept.t3423_present)
{}
if(attach_accept.equivalent_plmns_present)
{}
if(attach_accept.emerg_num_list_present)
{}
if(attach_accept.eps_network_feature_support_present)
{}
if(attach_accept.additional_update_result_present)
{}
if(attach_accept.t3412_ext_present)
{}
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, &act_def_eps_bearer_context_req);
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[1] << 16;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[2] << 8;
ip_addr |= act_def_eps_bearer_context_req.pdn_addr.addr[3];
nas_log->info("IP allocated by network %u.%u.%u.%u\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]);
nas_log->console("Network attach successful. IP: %u.%u.%u.%u\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]);
// Setup GW
char *err_str = NULL;
if(gw->setup_if_addr(ip_addr, err_str))
{
nas_log->error("Failed to set gateway address - %s\n", err_str);
}
}
else
{
nas_log->error("Not handling IPV6 or IPV4V6\n");
pool->deallocate(pdu);
return;
}
eps_bearer_id = act_def_eps_bearer_context_req.eps_bearer_id;
if(act_def_eps_bearer_context_req.transaction_id_present)
{
transaction_id = act_def_eps_bearer_context_req.proc_transaction_id;
}
//FIXME: Handle the following parameters
// act_def_eps_bearer_context_req.eps_qos.qci
// act_def_eps_bearer_context_req.eps_qos.br_present
// act_def_eps_bearer_context_req.eps_qos.br_ext_present
// act_def_eps_bearer_context_req.apn.apn
// act_def_eps_bearer_context_req.negotiated_qos_present
// act_def_eps_bearer_context_req.llc_sapi_present
// act_def_eps_bearer_context_req.radio_prio_present
// act_def_eps_bearer_context_req.packet_flow_id_present
// act_def_eps_bearer_context_req.apn_ambr_present
// act_def_eps_bearer_context_req.protocol_cnfg_opts_present
// act_def_eps_bearer_context_req.connectivity_type_present
// FIXME: Setup the default EPS bearer context
state = EMM_STATE_REGISTERED;
// Send EPS bearer context accept and attach complete
count_ul++;
act_def_eps_bearer_context_accept.eps_bearer_id = eps_bearer_id;
act_def_eps_bearer_context_accept.proc_transaction_id = transaction_id;
act_def_eps_bearer_context_accept.protocol_cnfg_opts_present = false;
liblte_mme_pack_activate_default_eps_bearer_context_accept_msg(&act_def_eps_bearer_context_accept, &attach_complete.esm_msg);
liblte_mme_pack_attach_complete_msg(&attach_complete,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED,
count_ul,
(LIBLTE_BYTE_MSG_STRUCT*)pdu);
integrity_generate(&k_nas_int[16],
count_ul,
lcid-1,
SECURITY_DIRECTION_UPLINK,
&pdu->msg[5],
pdu->N_bytes-5,
&pdu->msg[1]);
// Instruct RRC to enable capabilities
rrc->enable_capabilities();
nas_log->info("Sending Attach Complete\n");
rrc->write_sdu(lcid, pdu);
}
else
{
nas_log->info("Not handling attach type %u\n", attach_accept.eps_attach_result);
state = EMM_STATE_DEREGISTERED;
pool->deallocate(pdu);
}
}
void nas::parse_attach_reject(uint32_t lcid, byte_buffer_t *pdu)
{
LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT attach_rej;
liblte_mme_unpack_attach_reject_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu, &attach_rej);
nas_log->warning("Received Attach Reject. Cause= %02X\n", attach_rej.emm_cause);
nas_log->console("Received Attach Reject. Cause= %02X\n", attach_rej.emm_cause);
state = EMM_STATE_DEREGISTERED;
pool->deallocate(pdu);
// FIXME: Command RRC to release?
}
void nas::parse_authentication_request(uint32_t lcid, byte_buffer_t *pdu)
{
LIBLTE_MME_AUTHENTICATION_REQUEST_MSG_STRUCT auth_req;
LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_res;
nas_log->info("Received Authentication Request\n");;
liblte_mme_unpack_authentication_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu, &auth_req);
// Reuse the pdu for the response message
pdu->reset();
// Generate authentication response using RAND, AUTN & KSI-ASME
uint16 mcc, mnc;
mcc = rrc->get_mcc();
mnc = rrc->get_mnc();
nas_log->info("MCC=%d, MNC=%d\n", mcc, mnc);
bool net_valid;
uint8_t res[16];
usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, &net_valid, res);
if(net_valid)
{
nas_log->info("Network authentication successful\n");
for(int i=0; i<8; i++)
{
auth_res.res[i] = res[i];
}
liblte_mme_pack_authentication_response_msg(&auth_res, (LIBLTE_BYTE_MSG_STRUCT*)pdu);
nas_log->info("Sending Authentication Response\n");
rrc->write_sdu(lcid, pdu);
}
else
{
nas_log->warning("Network authentication failure\n");
nas_log->console("Warning: Network authentication failure\n");
pool->deallocate(pdu);
}
}
void nas::parse_authentication_reject(uint32_t lcid, byte_buffer_t *pdu)
{
nas_log->warning("Received Authentication Reject\n");
pool->deallocate(pdu);
state = EMM_STATE_DEREGISTERED;
// FIXME: Command RRC to release?
}
void nas::parse_identity_request(uint32_t lcid, byte_buffer_t *pdu)
{
nas_log->error("TODO:parse_identity_request\n");
}
void nas::parse_security_mode_command(uint32_t lcid, byte_buffer_t *pdu)
{
bool success;
LIBLTE_MME_SECURITY_MODE_COMMAND_MSG_STRUCT sec_mode_cmd;
LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp;
LIBLTE_MME_SECURITY_MODE_REJECT_MSG_STRUCT sec_mode_rej;
nas_log->info("Received Security Mode Command\n");
liblte_mme_unpack_security_mode_command_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu, &sec_mode_cmd);
ksi = sec_mode_cmd.nas_ksi.nas_ksi;
cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eea;
integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eia;
// FIXME: Handle nonce_ue, nonce_mme
// FIXME: Currently only handling ciphering EEA0 (null) and integrity EIA1,EIA2
// FIXME: Use selected_nas_sec_algs to choose correct algos
nas_log->debug("Security details: ksi=%d, eea=%s, eia=%s\n",
ksi, ciphering_algorithm_id_text[cipher_algo], integrity_algorithm_id_text[integ_algo]);
if(CIPHERING_ALGORITHM_ID_EEA0 != cipher_algo ||
(INTEGRITY_ALGORITHM_ID_128_EIA2 != integ_algo &&
INTEGRITY_ALGORITHM_ID_128_EIA1 != integ_algo) ||
sec_mode_cmd.nas_ksi.tsc_flag != LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE)
{
sec_mode_rej.emm_cause = LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH;
nas_log->warning("Sending Security Mode Reject due to security capabilities mismatch\n");
success = false;
}
else
{
// Generate NAS encryption key and integrity protection key
usim->generate_nas_keys(k_nas_enc, k_nas_int, cipher_algo, integ_algo);
nas_log->debug_hex(k_nas_enc, 32, "NAS encryption key - k_nas_enc");
nas_log->debug_hex(k_nas_int, 32, "NAS integrity key - k_nas_int");
// Check incoming MAC
uint8_t *inMAC = &pdu->msg[1];
uint8_t genMAC[4];
integrity_generate(&k_nas_int[16],
count_dl,
lcid-1,
SECURITY_DIRECTION_DOWNLINK,
&pdu->msg[5],
pdu->N_bytes-5,
genMAC);
nas_log->info_hex(inMAC, 4, "Incoming PDU MAC:");
nas_log->info_hex(genMAC, 4, "Generated PDU MAC:");
bool match=true;
for(int i=0;i<4;i++) {
if(inMAC[i] != genMAC[i]) {
match = false;
}
}
if(!match) {
sec_mode_rej.emm_cause = LIBLTE_MME_EMM_CAUSE_SECURITY_MODE_REJECTED_UNSPECIFIED;
nas_log->warning("Sending Security Mode Reject due to integrity check failure\n");
success = false;
} else {
if(sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req)
{
sec_mode_comp.imeisv_present = true;
sec_mode_comp.imeisv.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_IMEISV;
usim->get_imei_vec(sec_mode_comp.imeisv.imeisv, 15);
sec_mode_comp.imeisv.imeisv[14] = 5;
sec_mode_comp.imeisv.imeisv[15] = 3;
}
else
{
sec_mode_comp.imeisv_present = false;
}
// Reuse pdu for response
pdu->reset();
liblte_mme_pack_security_mode_complete_msg(&sec_mode_comp,
LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED,
count_ul,
(LIBLTE_BYTE_MSG_STRUCT*)pdu);
integrity_generate(&k_nas_int[16],
count_ul,
lcid-1,
SECURITY_DIRECTION_UPLINK,
&pdu->msg[5],
pdu->N_bytes-5,
&pdu->msg[1]);
nas_log->info("Sending Security Mode Complete nas_count_ul=%d, RB=%s\n",
count_ul,
rb_id_text[lcid]);
success = true;
}
}
if(!success) {
// Reuse pdu for response
pdu->reset();
liblte_mme_pack_security_mode_reject_msg(&sec_mode_rej, (LIBLTE_BYTE_MSG_STRUCT*)pdu);
}
rrc->write_sdu(lcid, pdu);
}
void nas::parse_service_reject(uint32_t lcid, byte_buffer_t *pdu)
{
nas_log->error("TODO:parse_service_reject\n");
}
void nas::parse_esm_information_request(uint32_t lcid, byte_buffer_t *pdu)
{
nas_log->error("TODO:parse_esm_information_request\n");
}
void nas::parse_emm_information(uint32_t lcid, byte_buffer_t *pdu)
{
nas_log->error("TODO:parse_emm_information\n");
}
/*******************************************************************************
Senders
*******************************************************************************/
void nas::send_attach_request()
{
LIBLTE_MME_ATTACH_REQUEST_MSG_STRUCT attach_req;
byte_buffer_t *msg = pool->allocate();
u_int32_t i;
attach_req.eps_attach_type = LIBLTE_MME_EPS_ATTACH_TYPE_EPS_ATTACH;
for(i=0; i<8; i++)
{
attach_req.ue_network_cap.eea[i] = false;
attach_req.ue_network_cap.eia[i] = false;
}
attach_req.ue_network_cap.eea[0] = true; // EEA0 supported
attach_req.ue_network_cap.eia[0] = true; // EIA0 supported
attach_req.ue_network_cap.eia[1] = true; // EIA1 supported
attach_req.ue_network_cap.eia[2] = true; // EIA2 supported
attach_req.ue_network_cap.uea_present = false; // UMTS encryption algos
attach_req.ue_network_cap.uia_present = false; // UMTS integrity algos
attach_req.ms_network_cap_present = false; // A/Gb mode (2G) or Iu mode (3G)
attach_req.eps_mobile_id.type_of_id = LIBLTE_MME_EPS_MOBILE_ID_TYPE_IMSI;
usim->get_imsi_vec(attach_req.eps_mobile_id.imsi, 15);
// ESM message (PDN connectivity request) for first default bearer
gen_pdn_connectivity_request(&attach_req.esm_msg);
attach_req.old_p_tmsi_signature_present = false;
attach_req.additional_guti_present = false;
attach_req.last_visited_registered_tai_present = false;
attach_req.drx_param_present = false;
attach_req.ms_network_cap_present = false;
attach_req.old_lai_present = false;
attach_req.tmsi_status_present = false;
attach_req.ms_cm2_present = false;
attach_req.ms_cm3_present = false;
attach_req.supported_codecs_present = false;
attach_req.additional_update_type_present = false;
attach_req.voice_domain_pref_and_ue_usage_setting_present = false;
attach_req.device_properties_present = false;
attach_req.old_guti_type_present = false;
// Pack the message
liblte_mme_pack_attach_request_msg(&attach_req, (LIBLTE_BYTE_MSG_STRUCT*)msg);
nas_log->info("Sending attach request\n");
rrc->write_sdu(RB_ID_SRB1, msg);
}
void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT *msg)
{
LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT pdn_con_req;
nas_log->info("Generating PDN Connectivity Request\n");
// Set the PDN con req parameters
pdn_con_req.eps_bearer_id = 0x00; // Unassigned bearer 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;
// Set the optional flags
pdn_con_req.esm_info_transfer_flag_present = false; //FIXME: Check if this is needed
pdn_con_req.apn_present = false;
pdn_con_req.protocol_cnfg_opts_present = false;
pdn_con_req.device_properties_present = false;
// Pack the message
liblte_mme_pack_pdn_connectivity_request_msg(&pdn_con_req, msg);
}
void nas::send_identity_response(){}
void nas::send_service_request()
{
byte_buffer_t *msg = pool->allocate();
count_ul++;
// Pack the service request message directly
msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT);
msg->N_bytes++;
msg->msg[1] = (ksi & 0x07) << 5;
msg->msg[1] |= count_ul & 0x1F;
msg->N_bytes++;
uint8_t mac[4];
integrity_generate(&k_nas_int[16],
count_ul,
RB_ID_SRB1-1,
SECURITY_DIRECTION_UPLINK,
&msg->msg[0],
2,
&mac[0]);
// Set the short MAC
msg->msg[2] = mac[2];
msg->N_bytes++;
msg->msg[3] = mac[3];
msg->N_bytes++;
nas_log->info("Sending service request\n");
rrc->write_sdu(RB_ID_SRB1, msg);
}
void nas::send_esm_information_response(){}
} // namespace srsue

@ -0,0 +1,132 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/pdcp.h"
using namespace srslte;
namespace srsue{
pdcp::pdcp()
{}
void pdcp::init(rlc_interface_pdcp *rlc_, rrc_interface_pdcp *rrc_, gw_interface_pdcp *gw_, srslte::log *pdcp_log_, uint8_t direction_)
{
rlc = rlc_;
rrc = rrc_;
gw = gw_;
pdcp_log = pdcp_log_;
direction = direction_;
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, RB_ID_SRB0, direction); // SRB0
}
void pdcp::stop()
{}
void pdcp::reset()
{
for(uint32_t i=0;i<SRSUE_N_RADIO_BEARERS;i++) {
pdcp_array[i].reset();
}
pdcp_array[0].init(rlc, rrc, gw, pdcp_log, RB_ID_SRB0, direction); // SRB0
}
/*******************************************************************************
RRC/GW interface
*******************************************************************************/
void pdcp::write_sdu(uint32_t lcid, byte_buffer_t *sdu)
{
if(valid_lcid(lcid))
pdcp_array[lcid].write_sdu(sdu);
}
void pdcp::add_bearer(uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg)
{
if(lcid < 0 || lcid >= SRSUE_N_RADIO_BEARERS) {
pdcp_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSUE_N_RADIO_BEARERS, lcid);
return;
}
if (!pdcp_array[lcid].is_active()) {
pdcp_array[lcid].init(rlc, rrc, gw, pdcp_log, lcid, direction, cnfg);
pdcp_log->info("Added bearer %s\n", rb_id_text[lcid]);
} else {
pdcp_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rb_id_text[lcid]);
}
}
void pdcp::config_security(uint32_t lcid,
uint8_t *k_rrc_enc,
uint8_t *k_rrc_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo)
{
if(valid_lcid(lcid))
pdcp_array[lcid].config_security(k_rrc_enc, k_rrc_int, cipher_algo, integ_algo);
}
/*******************************************************************************
RLC interface
*******************************************************************************/
void pdcp::write_pdu(uint32_t lcid, byte_buffer_t *pdu)
{
if(valid_lcid(lcid))
pdcp_array[lcid].write_pdu(pdu);
}
void pdcp::write_pdu_bcch_bch(byte_buffer_t *sdu)
{
rrc->write_pdu_bcch_bch(sdu);
}
void pdcp::write_pdu_bcch_dlsch(byte_buffer_t *sdu)
{
rrc->write_pdu_bcch_dlsch(sdu);
}
void pdcp::write_pdu_pcch(byte_buffer_t *sdu)
{
rrc->write_pdu_pcch(sdu);
}
/*******************************************************************************
Helpers
*******************************************************************************/
bool pdcp::valid_lcid(uint32_t lcid)
{
if(lcid < 0 || lcid >= SRSUE_N_RADIO_BEARERS) {
pdcp_log->error("Radio bearer id must be in [0:%d] - %d", SRSUE_N_RADIO_BEARERS, lcid);
return false;
}
if(!pdcp_array[lcid].is_active()) {
pdcp_log->error("PDCP entity for logical channel %d has not been activated\n", lcid);
return false;
}
return true;
}
} // namespace srsue

@ -0,0 +1,286 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/pdcp_entity.h"
#include "common/security.h"
using namespace srslte;
namespace srsue{
pdcp_entity::pdcp_entity()
:active(false)
,tx_count(0)
,rx_count(0)
,do_security(false)
,sn_len(12)
{
pool = byte_buffer_pool::get_instance();
}
void pdcp_entity::init(rlc_interface_pdcp *rlc_,
rrc_interface_pdcp *rrc_,
gw_interface_pdcp *gw_,
srslte::log *log_,
uint32_t lcid_,
u_int8_t direction_,
LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg)
{
rlc = rlc_;
rrc = rrc_;
gw = gw_;
log = log_;
lcid = lcid_;
direction = direction_;
active = true;
tx_count = 0;
rx_count = 0;
do_security = false;
if(cnfg)
{
if(cnfg->rlc_um_pdcp_sn_size_present) {
if(LIBLTE_RRC_PDCP_SN_SIZE_7_BITS == cnfg->rlc_um_pdcp_sn_size) {
sn_len = 7;
}
}
// TODO: handle remainder of cnfg
}
log->debug("Init %s\n", rb_id_text[lcid]);
}
void pdcp_entity::reset()
{
active = false;
if(log)
log->debug("Reset %s\n", rb_id_text[lcid]);
}
bool pdcp_entity::is_active()
{
return active;
}
// RRC interface
void pdcp_entity::write_sdu(byte_buffer_t *sdu)
{
log->info_hex(sdu->msg, sdu->N_bytes, "TX %s SDU, do_security = %s", rb_id_text[lcid], (do_security)?"true":"false");
// Handle SRB messages
switch(lcid)
{
case RB_ID_SRB0:
rlc->write_sdu(lcid, sdu);
break;
case RB_ID_SRB1: // Intentional fall-through
case RB_ID_SRB2:
pdcp_pack_control_pdu(tx_count, sdu);
if(do_security)
{
integrity_generate(&k_rrc_int[16],
tx_count,
lcid-1,
direction,
sdu->msg,
sdu->N_bytes-4,
&sdu->msg[sdu->N_bytes-4]);
}
tx_count++;
rlc->write_sdu(lcid, sdu);
break;
}
// Handle DRB messages
if(lcid >= RB_ID_DRB1)
{
if(12 == sn_len)
{
pdcp_pack_data_pdu_long_sn(tx_count++, sdu);
} else {
pdcp_pack_data_pdu_short_sn(tx_count++, sdu);
}
rlc->write_sdu(lcid, sdu);
}
}
void pdcp_entity::config_security(uint8_t *k_rrc_enc_,
uint8_t *k_rrc_int_,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo_,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo_)
{
do_security = true;
for(int i=0; i<32; i++)
{
k_rrc_enc[i] = k_rrc_enc_[i];
k_rrc_int[i] = k_rrc_int_[i];
}
cipher_algo = cipher_algo_;
integ_algo = integ_algo_;
}
// RLC interface
void pdcp_entity::write_pdu(byte_buffer_t *pdu)
{
// Handle SRB messages
switch(lcid)
{
case RB_ID_SRB0:
// Simply pass on to RRC
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", rb_id_text[lcid]);
rrc->write_pdu(RB_ID_SRB0, pdu);
break;
case RB_ID_SRB1: // Intentional fall-through
case RB_ID_SRB2:
uint32_t sn;
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU", rb_id_text[lcid]);
pdcp_unpack_control_pdu(pdu, &sn);
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s SDU SN: %d",
rb_id_text[lcid], sn);
rrc->write_pdu(lcid, pdu);
break;
}
// Handle DRB messages
if(lcid >= RB_ID_DRB1)
{
uint32_t sn;
if(12 == sn_len)
{
pdcp_unpack_data_pdu_long_sn(pdu, &sn);
} else {
pdcp_unpack_data_pdu_short_sn(pdu, &sn);
}
log->info_hex(pdu->msg, pdu->N_bytes, "RX %s PDU: %d", rb_id_text[lcid], sn);
gw->write_pdu(lcid, pdu);
}
}
void pdcp_entity::integrity_generate( uint8_t *key_128,
uint32_t count,
uint8_t rb_id,
uint8_t direction,
uint8_t *msg,
uint32_t msg_len,
uint8_t *mac)
{
switch(integ_algo)
{
case INTEGRITY_ALGORITHM_ID_EIA0:
break;
case INTEGRITY_ALGORITHM_ID_128_EIA1:
security_128_eia1(key_128,
count,
rb_id,
direction,
msg,
msg_len,
mac);
break;
case INTEGRITY_ALGORITHM_ID_128_EIA2:
security_128_eia2(key_128,
count,
rb_id,
direction,
msg,
msg_len,
mac);
break;
default:
break;
}
}
/****************************************************************************
* Pack/Unpack helper functions
* Ref: 3GPP TS 36.323 v10.1.0
***************************************************************************/
void pdcp_pack_control_pdu(uint32_t sn, byte_buffer_t *sdu)
{
// Make room and add header
sdu->msg--;
sdu->N_bytes++;
*sdu->msg = sn & 0x1F;
// Add MAC
sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 24) & 0xFF;
sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 16) & 0xFF;
sdu->msg[sdu->N_bytes++] = (PDCP_CONTROL_MAC_I >> 8) & 0xFF;
sdu->msg[sdu->N_bytes++] = PDCP_CONTROL_MAC_I & 0xFF;
}
void pdcp_unpack_control_pdu(byte_buffer_t *pdu, uint32_t *sn)
{
// Strip header
*sn = *pdu->msg & 0x1F;
pdu->msg++;
pdu->N_bytes--;
// Strip MAC
pdu->N_bytes -= 4;
// TODO: integrity check MAC
}
void pdcp_pack_data_pdu_short_sn(uint32_t sn, byte_buffer_t *sdu)
{
// Make room and add header
sdu->msg--;
sdu->N_bytes++;
sdu->msg[0] = (PDCP_D_C_DATA_PDU << 7) | (sn & 0x7F);
}
void pdcp_unpack_data_pdu_short_sn(byte_buffer_t *sdu, uint32_t *sn)
{
// Strip header
*sn = sdu->msg[0] & 0x7F;
sdu->msg++;
sdu->N_bytes--;
}
void pdcp_pack_data_pdu_long_sn(uint32_t sn, byte_buffer_t *sdu)
{
// Make room and add header
sdu->msg -= 2;
sdu->N_bytes += 2;
sdu->msg[0] = (PDCP_D_C_DATA_PDU << 7) | ((sn >> 8) & 0x0F);
sdu->msg[1] = sn & 0xFF;
}
void pdcp_unpack_data_pdu_long_sn(byte_buffer_t *sdu, uint32_t *sn)
{
// Strip header
*sn = (sdu->msg[0] & 0x0F) << 8;
*sn |= sdu->msg[1];
sdu->msg += 2;
sdu->N_bytes -= 2;
}
}

@ -0,0 +1,264 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICRXAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/rlc.h"
#include "upper/rlc_tm.h"
#include "upper/rlc_um.h"
#include "upper/rlc_am.h"
using namespace srslte;
namespace srsue{
rlc::rlc()
{
pool = byte_buffer_pool::get_instance();
}
void rlc::init(pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
ue_interface *ue_,
srslte::log *rlc_log_,
mac_interface_timers *mac_timers_)
{
pdcp = pdcp_;
rrc = rrc_;
ue = ue_;
rlc_log = rlc_log_;
mac_timers = mac_timers_;
gettimeofday(&metrics_time[1], NULL);
reset_metrics();
rlc_array[0].init(RLC_MODE_TM, rlc_log, RB_ID_SRB0, pdcp, rrc, mac_timers); // SRB0
}
void rlc::reset_metrics()
{
bzero(dl_tput_bytes, sizeof(long)*SRSUE_N_RADIO_BEARERS);
bzero(ul_tput_bytes, sizeof(long)*SRSUE_N_RADIO_BEARERS);
}
void rlc::stop()
{
reset();
}
void rlc::get_metrics(rlc_metrics_t &m)
{
gettimeofday(&metrics_time[2], NULL);
get_time_interval(metrics_time);
double secs = (double)metrics_time[0].tv_sec + metrics_time[0].tv_usec*1e-6;
m.dl_tput_mbps = 0;
m.ul_tput_mbps = 0;
for (int i=0;i<SRSUE_N_RADIO_BEARERS;i++) {
m.dl_tput_mbps += (dl_tput_bytes[i]*8/(double)1e6)/secs;
m.ul_tput_mbps += (ul_tput_bytes[i]*8/(double)1e6)/secs;
if(rlc_array[i].active()) {
rlc_log->info("LCID=%d, RX throughput: %4.6f Mbps. TX throughput: %4.6f Mbps.\n",
i,
(dl_tput_bytes[i]*8/(double)1e6)/secs,
(ul_tput_bytes[i]*8/(double)1e6)/secs);
}
}
memcpy(&metrics_time[1], &metrics_time[2], sizeof(struct timeval));
reset_metrics();
}
void rlc::reset()
{
for(uint32_t i=0; i<SRSUE_N_RADIO_BEARERS; i++) {
if(rlc_array[i].active())
rlc_array[i].reset();
}
rlc_array[0].init(RLC_MODE_TM, rlc_log, RB_ID_SRB0, pdcp, rrc, mac_timers); // SRB0
}
/*******************************************************************************
PDCP interface
*******************************************************************************/
void rlc::write_sdu(uint32_t lcid, byte_buffer_t *sdu)
{
if(valid_lcid(lcid)) {
rlc_array[lcid].write_sdu(sdu);
}
}
/*******************************************************************************
MAC interface
*******************************************************************************/
uint32_t rlc::get_buffer_state(uint32_t lcid)
{
if(valid_lcid(lcid)) {
return rlc_array[lcid].get_buffer_state();
} else {
return 0;
}
}
uint32_t rlc::get_total_buffer_state(uint32_t lcid)
{
if(valid_lcid(lcid)) {
return rlc_array[lcid].get_total_buffer_state();
} else {
return 0;
}
}
int rlc::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
if(valid_lcid(lcid)) {
ul_tput_bytes[lcid] += nof_bytes;
return rlc_array[lcid].read_pdu(payload, nof_bytes);
}
return 0;
}
void rlc::write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes)
{
if(valid_lcid(lcid)) {
dl_tput_bytes[lcid] += nof_bytes;
rlc_array[lcid].write_pdu(payload, nof_bytes);
}
}
void rlc::write_pdu_bcch_bch(uint8_t *payload, uint32_t nof_bytes)
{
rlc_log->info_hex(payload, nof_bytes, "BCCH BCH message received.");
dl_tput_bytes[0] += nof_bytes;
byte_buffer_t *buf = pool->allocate();
memcpy(buf->msg, payload, nof_bytes);
buf->N_bytes = nof_bytes;
buf->set_timestamp();
pdcp->write_pdu_bcch_bch(buf);
}
void rlc::write_pdu_bcch_dlsch(uint8_t *payload, uint32_t nof_bytes)
{
rlc_log->info_hex(payload, nof_bytes, "BCCH TXSCH message received.");
dl_tput_bytes[0] += nof_bytes;
byte_buffer_t *buf = pool->allocate();
memcpy(buf->msg, payload, nof_bytes);
buf->N_bytes = nof_bytes;
buf->set_timestamp();
pdcp->write_pdu_bcch_dlsch(buf);
}
void rlc::write_pdu_pcch(uint8_t *payload, uint32_t nof_bytes)
{
rlc_log->info_hex(payload, nof_bytes, "PCCH message received.");
dl_tput_bytes[0] += nof_bytes;
byte_buffer_t *buf = pool->allocate();
memcpy(buf->msg, payload, nof_bytes);
buf->N_bytes = nof_bytes;
buf->set_timestamp();
pdcp->write_pdu_pcch(buf);
}
/*******************************************************************************
RRC interface
*******************************************************************************/
void rlc::add_bearer(uint32_t lcid)
{
// No config provided - use defaults for lcid
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
if(RB_ID_SRB1 == lcid || RB_ID_SRB2 == lcid)
{
if (!rlc_array[lcid].active()) {
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM;
cnfg.ul_am_rlc.t_poll_retx = LIBLTE_RRC_T_POLL_RETRANSMIT_MS45;
cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_INFINITY;
cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_INFINITY;
cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4;
cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS35;
cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS0;
add_bearer(lcid, &cnfg);
} else {
rlc_log->warning("Bearer %s already configured. Reconfiguration not supported\n", rb_id_text[lcid]);
}
}else{
rlc_log->error("Radio bearer %s does not support default RLC configuration.",
rb_id_text[lcid]);
}
}
void rlc::add_bearer(uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
{
if(lcid < 0 || lcid >= SRSUE_N_RADIO_BEARERS) {
rlc_log->error("Radio bearer id must be in [0:%d] - %d\n", SRSUE_N_RADIO_BEARERS, lcid);
return;
}
if (!rlc_array[lcid].active()) {
rlc_log->info("Adding radio bearer %s with mode %s\n",
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode]);
switch(cnfg->rlc_mode)
{
case LIBLTE_RRC_RLC_MODE_AM:
rlc_array[lcid].init(RLC_MODE_AM, rlc_log, lcid, pdcp, rrc, mac_timers);
break;
case LIBLTE_RRC_RLC_MODE_UM_BI:
rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers);
break;
case LIBLTE_RRC_RLC_MODE_UM_UNI_DL:
rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers);
break;
case LIBLTE_RRC_RLC_MODE_UM_UNI_UL:
rlc_array[lcid].init(RLC_MODE_UM, rlc_log, lcid, pdcp, rrc, mac_timers);
break;
default:
rlc_log->error("Cannot add RLC entity - invalid mode\n");
return;
}
} else {
rlc_log->warning("Bearer %s already created.\n", rb_id_text[lcid]);
}
rlc_array[lcid].configure(cnfg);
}
/*******************************************************************************
Helpers
*******************************************************************************/
bool rlc::valid_lcid(uint32_t lcid)
{
if(lcid < 0 || lcid >= SRSUE_N_RADIO_BEARERS) {
return false;
}
if(!rlc_array[lcid].active()) {
return false;
}
return true;
}
} // namespace srsue

File diff suppressed because it is too large Load Diff

@ -0,0 +1,137 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/rlc_entity.h"
namespace srsue {
rlc_entity::rlc_entity()
:rlc(NULL)
{
}
void rlc_entity::init(rlc_mode_t mode,
srslte::log *rlc_entity_log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers_)
{
tm.reset();
um.reset();
am.reset();
switch(mode)
{
case RLC_MODE_TM:
rlc = &tm;
break;
case RLC_MODE_UM:
rlc = &um;
break;
case RLC_MODE_AM:
rlc = &am;
break;
default:
rlc_entity_log_->error("Invalid RLC mode - defaulting to TM\n");
rlc = &tm;
break;
}
rlc->init(rlc_entity_log_, lcid_, pdcp_, rrc_, mac_timers_);
}
void rlc_entity::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
{
if(rlc)
rlc->configure(cnfg);
}
void rlc_entity::reset()
{
rlc->reset();
rlc = NULL;
}
bool rlc_entity::active()
{
return (rlc != NULL);
}
rlc_mode_t rlc_entity::get_mode()
{
if(rlc)
return rlc->get_mode();
else
return RLC_MODE_TM;
}
uint32_t rlc_entity::get_bearer()
{
if(rlc)
return rlc->get_bearer();
else
return 0;
}
// PDCP interface
void rlc_entity::write_sdu(byte_buffer_t *sdu)
{
if(rlc)
rlc->write_sdu(sdu);
}
// MAC interface
uint32_t rlc_entity::get_buffer_state()
{
if(rlc)
return rlc->get_buffer_state();
else
return 0;
}
uint32_t rlc_entity::get_total_buffer_state()
{
if(rlc)
return rlc->get_total_buffer_state();
else
return 0;
}
int rlc_entity::read_pdu(uint8_t *payload, uint32_t nof_bytes)
{
if(rlc)
return rlc->read_pdu(payload, nof_bytes);
else
return 0;
}
void rlc_entity::write_pdu(uint8_t *payload, uint32_t nof_bytes)
{
if(rlc)
rlc->write_pdu(payload, nof_bytes);
}
} // namespace srsue

@ -0,0 +1,127 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/rlc_tm.h"
using namespace srslte;
namespace srsue{
rlc_tm::rlc_tm() : ul_queue(16)
{
pool = byte_buffer_pool::get_instance();
}
void rlc_tm::init(srslte::log *log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
mac_interface_timers *mac_timers)
{
log = log_;
lcid = lcid_;
pdcp = pdcp_;
rrc = rrc_;
}
void rlc_tm::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
{
log->error("Attempted to configure TM RLC entity");
}
void rlc_tm::empty_queue()
{
// Drop all messages in TX queue
byte_buffer_t *buf;
while(ul_queue.size() > 0) {
ul_queue.read(&buf);
pool->deallocate(buf);
}
}
void rlc_tm::reset()
{
empty_queue();
}
rlc_mode_t rlc_tm::get_mode()
{
return RLC_MODE_TM;
}
uint32_t rlc_tm::get_bearer()
{
return lcid;
}
// PDCP interface
void rlc_tm::write_sdu(byte_buffer_t *sdu)
{
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rb_id_text[lcid]);
ul_queue.write(sdu);
}
// MAC interface
uint32_t rlc_tm::get_buffer_state()
{
return ul_queue.size_bytes();
}
uint32_t rlc_tm::get_total_buffer_state()
{
return get_buffer_state();
}
int rlc_tm::read_pdu(uint8_t *payload, uint32_t nof_bytes)
{
uint32_t pdu_size = ul_queue.size_tail_bytes();
if(pdu_size > nof_bytes)
{
log->error("TX %s PDU size larger than MAC opportunity\n", rb_id_text[lcid]);
return 0;
}
byte_buffer_t *buf;
ul_queue.read(&buf);
pdu_size = buf->N_bytes;
memcpy(payload, buf->msg, buf->N_bytes);
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
rb_id_text[lcid], buf->get_latency_us());
pool->deallocate(buf);
log->info_hex(payload, pdu_size, "TX %s, %s PDU", rb_id_text[lcid], rlc_mode_text[RLC_MODE_TM]);
return pdu_size;
}
void rlc_tm:: write_pdu(uint8_t *payload, uint32_t nof_bytes)
{
byte_buffer_t *buf = pool->allocate();
memcpy(buf->msg, payload, nof_bytes);
buf->N_bytes = nof_bytes;
buf->set_timestamp();
pdcp->write_pdu(lcid, buf);
}
} // namespace srsue

@ -0,0 +1,701 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/rlc_um.h"
#define RX_MOD_BASE(x) (x-vr_uh-rx_window_size)%rx_mod
using namespace srslte;
namespace srsue{
rlc_um::rlc_um() : tx_sdu_queue(16)
{
tx_sdu = NULL;
rx_sdu = NULL;
pool = byte_buffer_pool::get_instance();
pthread_mutex_init(&mutex, NULL);
vt_us = 0;
vr_ur = 0;
vr_ux = 0;
vr_uh = 0;
vr_ur_in_rx_sdu = 0;
mac_timers = NULL;
pdu_lost = false;
}
void rlc_um::init(srslte::log *log_,
uint32_t lcid_,
pdcp_interface_rlc *pdcp_,
rrc_interface_rlc *rrc_,
srslte::mac_interface_timers *mac_timers_)
{
log = log_;
lcid = lcid_;
pdcp = pdcp_;
rrc = rrc_;
mac_timers = mac_timers_;
reordering_timeout_id = mac_timers->get_unique_id();
}
void rlc_um::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg)
{
switch(cnfg->rlc_mode)
{
case LIBLTE_RRC_RLC_MODE_UM_BI:
t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_bi_rlc.t_reordering];
rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_bi_rlc.sn_field_len;
rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 16 : 512;
rx_mod = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 32 : 1024;
tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_bi_rlc.sn_field_len;
tx_mod = (RLC_UMD_SN_SIZE_5_BITS == tx_sn_field_length) ? 32 : 1024;
log->info("%s configured in %s mode: "
"t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
t_reordering,
rlc_umd_sn_size_num[rx_sn_field_length],
rlc_umd_sn_size_num[tx_sn_field_length]);
break;
case LIBLTE_RRC_RLC_MODE_UM_UNI_UL:
tx_sn_field_length = (rlc_umd_sn_size_t)cnfg->ul_um_uni_rlc.sn_field_len;
tx_mod = (RLC_UMD_SN_SIZE_5_BITS == tx_sn_field_length) ? 32 : 1024;
log->info("%s configured in %s mode: tx_sn_field_length=%u bits\n",
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
rlc_umd_sn_size_num[tx_sn_field_length]);
break;
case LIBLTE_RRC_RLC_MODE_UM_UNI_DL:
t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_um_uni_rlc.t_reordering];
rx_sn_field_length = (rlc_umd_sn_size_t)cnfg->dl_um_uni_rlc.sn_field_len;
rx_window_size = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 16 : 512;
rx_mod = (RLC_UMD_SN_SIZE_5_BITS == rx_sn_field_length) ? 32 : 1024;
log->info("%s configured in %s mode: "
"t_reordering=%d ms, rx_sn_field_length=%u bits\n",
rb_id_text[lcid], liblte_rrc_rlc_mode_text[cnfg->rlc_mode],
liblte_rrc_t_reordering_num[t_reordering],
rlc_umd_sn_size_num[rx_sn_field_length]);
break;
default:
log->error("RLC configuration mode not recognized\n");
}
}
void rlc_um::empty_queue() {
// Drop all messages in TX SDU queue
byte_buffer_t *buf;
while(tx_sdu_queue.size() > 0) {
tx_sdu_queue.read(&buf);
pool->deallocate(buf);
}
}
void rlc_um::reset()
{
// Empty tx_sdu_queue before locking the mutex
empty_queue();
pthread_mutex_lock(&mutex);
vt_us = 0;
vr_ur = 0;
vr_ux = 0;
vr_uh = 0;
pdu_lost = false;
if(rx_sdu)
rx_sdu->reset();
if(tx_sdu)
tx_sdu->reset();
if(mac_timers)
mac_timers->get(reordering_timeout_id)->stop();
// Drop all messages in RX window
std::map<uint32_t, rlc_umd_pdu_t>::iterator it;
for(it = rx_window.begin(); it != rx_window.end(); it++) {
pool->deallocate(it->second.buf);
}
rx_window.clear();
pthread_mutex_unlock(&mutex);
}
rlc_mode_t rlc_um::get_mode()
{
return RLC_MODE_UM;
}
uint32_t rlc_um::get_bearer()
{
return lcid;
}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_um::write_sdu(byte_buffer_t *sdu)
{
log->info_hex(sdu->msg, sdu->N_bytes, "%s Tx SDU", rb_id_text[lcid]);
tx_sdu_queue.write(sdu);
}
/****************************************************************************
* MAC interface
***************************************************************************/
uint32_t rlc_um::get_buffer_state()
{
// Bytes needed for tx SDUs
uint32_t n_sdus = tx_sdu_queue.size();
uint32_t n_bytes = tx_sdu_queue.size_bytes();
if(tx_sdu)
{
n_sdus++;
n_bytes += tx_sdu->N_bytes;
}
// Room needed for header extensions? (integer rounding)
if(n_sdus > 1)
n_bytes += ((n_sdus-1)*1.5)+0.5;
// Room needed for fixed header?
if(n_bytes > 0)
n_bytes += 2;
return n_bytes;
}
uint32_t rlc_um::get_total_buffer_state()
{
return get_buffer_state();
}
int rlc_um::read_pdu(uint8_t *payload, uint32_t nof_bytes)
{
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
pthread_mutex_lock(&mutex);
int r = build_data_pdu(payload, nof_bytes);
pthread_mutex_unlock(&mutex);
return r;
}
void rlc_um::write_pdu(uint8_t *payload, uint32_t nof_bytes)
{
pthread_mutex_lock(&mutex);
handle_data_pdu(payload, nof_bytes);
pthread_mutex_unlock(&mutex);
}
/****************************************************************************
* Timeout callback interface
***************************************************************************/
void rlc_um::timer_expired(uint32_t timeout_id)
{
if(reordering_timeout_id == timeout_id)
{
pthread_mutex_lock(&mutex);
// 36.322 v10 Section 5.1.2.2.4
log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n",
rb_id_text[lcid]);
log->warning("Lost PDU SN: %d\n", vr_ur);
pdu_lost = true;
rx_sdu->reset();
while(RX_MOD_BASE(vr_ur) < RX_MOD_BASE(vr_ux))
{
vr_ur = (vr_ur + 1)%rx_mod;
log->debug("Entering Reassemble from timeout id=%d\n", timeout_id);
reassemble_rx_sdus();
log->debug("Finished reassemble from timeout id=%d\n", timeout_id);
}
mac_timers->get(reordering_timeout_id)->stop();
if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur))
{
mac_timers->get(reordering_timeout_id)->set(this, t_reordering);
mac_timers->get(reordering_timeout_id)->run();
vr_ux = vr_uh;
}
debug_state();
pthread_mutex_unlock(&mutex);
}
}
bool rlc_um::reordering_timeout_running()
{
return mac_timers->get(reordering_timeout_id)->is_running();
}
/****************************************************************************
* Helpers
***************************************************************************/
int rlc_um::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
{
if(!tx_sdu && tx_sdu_queue.size() == 0)
{
log->info("No data available to be sent\n");
return 0;
}
byte_buffer_t *pdu = pool->allocate();
if(!pdu || pdu->N_bytes != 0)
{
log->error("Failed to allocate PDU buffer\n");
return 0;
}
rlc_umd_pdu_header_t header;
header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED;
header.sn = vt_us;
header.N_li = 0;
header.sn_size = tx_sn_field_length;
uint32_t to_move = 0;
uint32_t last_li = 0;
uint8_t *pdu_ptr = pdu->msg;
int head_len = rlc_um_packed_length(&header);
int pdu_space = nof_bytes;
if(pdu_space <= head_len)
{
log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n",
rb_id_text[lcid], nof_bytes, head_len);
return 0;
}
// Check for SDU segment
if(tx_sdu)
{
to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len;
log->debug("%s adding remainder of SDU segment - %d bytes of %d remaining\n",
rb_id_text[lcid], to_move, tx_sdu->N_bytes);
memcpy(pdu_ptr, tx_sdu->msg, to_move);
last_li = to_move;
pdu_ptr += to_move;
pdu->N_bytes += to_move;
tx_sdu->N_bytes -= to_move;
tx_sdu->msg += to_move;
if(tx_sdu->N_bytes == 0)
{
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
rb_id_text[lcid], tx_sdu->get_latency_us());
pool->deallocate(tx_sdu);
tx_sdu = NULL;
}
pdu_space -= to_move;
header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU
}
// Pull SDUs from queue
while(pdu_space > head_len && tx_sdu_queue.size() > 0)
{
log->debug("pdu_space=%d, head_len=%d\n", pdu_space, head_len);
if(last_li > 0)
header.li[header.N_li++] = last_li;
head_len = rlc_um_packed_length(&header);
tx_sdu_queue.read(&tx_sdu);
to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len;
log->debug("%s adding new SDU segment - %d bytes of %d remaining\n",
rb_id_text[lcid], to_move, tx_sdu->N_bytes);
memcpy(pdu_ptr, tx_sdu->msg, to_move);
last_li = to_move;
pdu_ptr += to_move;
pdu->N_bytes += to_move;
tx_sdu->N_bytes -= to_move;
tx_sdu->msg += to_move;
if(tx_sdu->N_bytes == 0)
{
log->info("%s Complete SDU scheduled for tx. Stack latency: %ld us\n",
rb_id_text[lcid], tx_sdu->get_latency_us());
pool->deallocate(tx_sdu);
tx_sdu = NULL;
}
pdu_space -= to_move;
}
if(tx_sdu)
header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU
// Set SN
header.sn = vt_us;
vt_us = (vt_us + 1)%tx_mod;
// Add header and TX
log->debug("%s packing PDU with length %d\n", rb_id_text[lcid], pdu->N_bytes);
rlc_um_write_data_pdu_header(&header, pdu);
memcpy(payload, pdu->msg, pdu->N_bytes);
uint32_t ret = pdu->N_bytes;
log->debug("%sreturning length %d\n", rb_id_text[lcid], pdu->N_bytes);
pool->deallocate(pdu);
debug_state();
return ret;
}
void rlc_um::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
{
std::map<uint32_t, rlc_umd_pdu_t>::iterator it;
rlc_umd_pdu_header_t header;
rlc_um_read_data_pdu_header(payload, nof_bytes, rx_sn_field_length, &header);
log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d",
rb_id_text[lcid], header.sn);
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-rx_window_size) &&
RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur))
{
log->info("%s SN: %d outside rx window [%d:%d] - discarding\n",
rb_id_text[lcid], header.sn, vr_ur, vr_uh);
return;
}
it = rx_window.find(header.sn);
if(rx_window.end() != it)
{
log->info("%s Discarding duplicate SN: %d\n",
rb_id_text[lcid], header.sn);
return;
}
// Write to rx window
rlc_umd_pdu_t pdu;
pdu.buf = pool->allocate();
if (!pdu.buf) {
log->error("Discarting packet: no space in buffer pool\n");
return;
}
memcpy(pdu.buf->msg, payload, nof_bytes);
pdu.buf->N_bytes = nof_bytes;
//Strip header from PDU
int header_len = rlc_um_packed_length(&header);
pdu.buf->msg += header_len;
pdu.buf->N_bytes -= header_len;
pdu.header = header;
rx_window[header.sn] = pdu;
// Update vr_uh
if(!inside_reordering_window(header.sn))
vr_uh = (header.sn + 1)%rx_mod;
// Reassemble and deliver SDUs, while updating vr_ur
log->debug("Entering Reassemble from received PDU\n");
reassemble_rx_sdus();
log->debug("Finished reassemble from received PDU\n");
// Update reordering variables and timers
if(mac_timers->get(reordering_timeout_id)->is_running())
{
if(RX_MOD_BASE(vr_ux) <= RX_MOD_BASE(vr_ur) ||
(!inside_reordering_window(vr_ux) && vr_ux != vr_uh))
{
mac_timers->get(reordering_timeout_id)->stop();
}
}
if(!mac_timers->get(reordering_timeout_id)->is_running())
{
if(RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur))
{
mac_timers->get(reordering_timeout_id)->set(this, t_reordering);
mac_timers->get(reordering_timeout_id)->run();
vr_ux = vr_uh;
}
}
debug_state();
}
void rlc_um::reassemble_rx_sdus()
{
if(!rx_sdu)
rx_sdu = pool->allocate();
// First catch up with lower edge of reordering window
while(!inside_reordering_window(vr_ur))
{
if(rx_window.end() == rx_window.find(vr_ur))
{
rx_sdu->reset();
}else{
// Handle any SDU segments
for(int i=0; i<rx_window[vr_ur].header.N_li; i++)
{
int len = rx_window[vr_ur].header.li[i];
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], 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;
if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi) || vr_ur != ((vr_ur_in_rx_sdu+1)%rx_mod)) {
log->warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
rx_sdu->reset();
} else {
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", rb_id_text[lcid], vr_ur, i);
rx_sdu->set_timestamp();
pdcp->write_pdu(lcid, rx_sdu);
rx_sdu = pool->allocate();
}
pdu_lost = false;
}
// Handle last segment
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;
log->debug("Writting last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d\n",
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
vr_ur_in_rx_sdu = vr_ur;
if(rlc_um_end_aligned(rx_window[vr_ur].header.fi))
{
if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
log->warning("Dropping remainder of lost PDU (lower edge last segments)\n");
rx_sdu->reset();
} else {
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rb_id_text[lcid], vr_ur);
rx_sdu->set_timestamp();
pdcp->write_pdu(lcid, rx_sdu);
rx_sdu = pool->allocate();
}
pdu_lost = false;
}
// Clean up rx_window
pool->deallocate(rx_window[vr_ur].buf);
rx_window.erase(vr_ur);
}
vr_ur = (vr_ur + 1)%rx_mod;
}
// Now update vr_ur until we reach an SN we haven't yet received
while(rx_window.end() != rx_window.find(vr_ur))
{
// Handle any SDU segments
for(int i=0; i<rx_window[vr_ur].header.N_li; i++)
{
int len = rx_window[vr_ur].header.li[i];
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
log->debug("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, rx_mod, (vr_ur_in_rx_sdu+1)%rx_mod);
rx_sdu->N_bytes += len;
rx_window[vr_ur].buf->msg += 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)%rx_mod)) {
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();
} else {
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", rb_id_text[lcid], vr_ur, i);
rx_sdu->set_timestamp();
pdcp->write_pdu(lcid, rx_sdu);
rx_sdu = pool->allocate();
}
pdu_lost = false;
}
// Handle last segment
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;
log->debug("Writting last segment in SDU buffer. Updating vr_ur=%d, Buffer size=%d, segment size=%d\n",
vr_ur, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes);
vr_ur_in_rx_sdu = vr_ur;
if(rlc_um_end_aligned(rx_window[vr_ur].header.fi))
{
if(pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) {
log->warning("Dropping remainder of lost PDU (update vr_ur last segments)\n");
rx_sdu->reset();
} else {
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rb_id_text[lcid], vr_ur);
rx_sdu->set_timestamp();
pdcp->write_pdu(lcid, rx_sdu);
rx_sdu = pool->allocate();
}
pdu_lost = false;
}
// Clean up rx_window
pool->deallocate(rx_window[vr_ur].buf);
rx_window.erase(vr_ur);
vr_ur = (vr_ur + 1)%rx_mod;
}
}
bool rlc_um::inside_reordering_window(uint16_t sn)
{
if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_uh-rx_window_size) &&
RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh))
{
return true;
}else{
return false;
}
}
void rlc_um::debug_state()
{
log->debug("%s vt_us = %d, vr_ur = %d, vr_ux = %d, vr_uh = %d \n",
rb_id_text[lcid], vt_us, vr_ur, vr_ux, vr_uh);
}
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
void rlc_um_read_data_pdu_header(byte_buffer_t *pdu, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header)
{
rlc_um_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header);
}
void rlc_um_read_data_pdu_header(uint8_t *payload, uint32_t nof_bytes, rlc_umd_sn_size_t sn_size, rlc_umd_pdu_header_t *header)
{
uint8_t ext;
uint8_t *ptr = payload;
// Fixed part
if(RLC_UMD_SN_SIZE_5_BITS == sn_size)
{
header->fi = (rlc_fi_field_t)((*ptr >> 6) & 0x03); // 2 bits FI
ext = ((*ptr >> 5) & 0x01); // 1 bit EXT
header->sn = *ptr & 0x1F; // 5 bits SN
ptr++;
}else{
header->fi = (rlc_fi_field_t)((*ptr >> 3) & 0x03); // 2 bits FI
ext = ((*ptr >> 2) & 0x01); // 1 bit EXT
header->sn = (*ptr & 0x03) << 8; // 2 bits SN
ptr++;
header->sn |= (*ptr & 0xFF); // 8 bits SN
ptr++;
}
header->sn_size = sn_size;
// Extension part
header->N_li = 0;
while(ext)
{
if(header->N_li%2 == 0)
{
ext = ((*ptr >> 7) & 0x01);
header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI
header->N_li++;
}
else
{
ext = (*ptr >> 3) & 0x01;
header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI
header->N_li++;
ptr++;
}
}
}
void rlc_um_write_data_pdu_header(rlc_umd_pdu_header_t *header, byte_buffer_t *pdu)
{
uint32_t i;
uint8_t ext = (header->N_li > 0) ? 1 : 0;
// Make room for the header
uint32_t len = rlc_um_packed_length(header);
pdu->msg -= len;
uint8_t *ptr = pdu->msg;
// Fixed part
if(RLC_UMD_SN_SIZE_5_BITS == header->sn_size)
{
*ptr = (header->fi & 0x03) << 6; // 2 bits FI
*ptr |= (ext & 0x01) << 5; // 1 bit EXT
*ptr |= header->sn & 0x1F; // 5 bits SN
ptr++;
}else{
*ptr = (header->fi & 0x03) << 3; // 3 Reserved bits | 2 bits FI
*ptr |= (ext & 0x01) << 2; // 1 bit EXT
*ptr |= (header->sn & 0x300) >> 8; // 2 bits SN
ptr++;
*ptr = (header->sn & 0xFF); // 8 bits SN
ptr++;
}
// Extension part
i = 0;
while(i < header->N_li)
{
ext = ((i+1) == header->N_li) ? 0 : 1;
*ptr = (ext & 0x01) << 7; // 1 bit header
*ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI
ptr++;
*ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI
i++;
if(i < header->N_li)
{
ext = ((i+1) == header->N_li) ? 0 : 1;
*ptr |= (ext & 0x01) << 3; // 1 bit header
*ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI
ptr++;
*ptr = (header->li[i] & 0x0FF); // 8 bits of LI
ptr++;
i++;
}
}
// Pad if N_li is odd
if(header->N_li%2 == 1)
ptr++;
pdu->N_bytes += ptr-pdu->msg;
}
uint32_t rlc_um_packed_length(rlc_umd_pdu_header_t *header)
{
uint32_t len = 0;
if(RLC_UMD_SN_SIZE_5_BITS == header->sn_size)
{
len += 1; // Fixed part is 1 byte
}else{
len += 2; // Fixed part is 2 bytes
}
len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up
return len;
}
bool rlc_um_start_aligned(uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED);
}
bool rlc_um_end_aligned(uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED);
}
} // namespace srsue

File diff suppressed because it is too large Load Diff

@ -0,0 +1,373 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include "upper/usim.h"
using namespace srslte;
namespace srsue{
usim::usim()
{}
void usim::init(usim_args_t *args, srslte::log *usim_log_)
{
usim_log = usim_log_;
const char *imsi_str = args->imsi.c_str();
const char *imei_str = args->imei.c_str();
uint32_t i;
if(32 == args->op.length()) {
str_to_hex(args->op, op);
} else {
usim_log->error("Invalid length for OP: %d should be %d", args->op.length(), 32);
usim_log->console("Invalid length for OP: %d should be %d", args->op.length(), 32);
}
if(4 == args->amf.length()) {
str_to_hex(args->amf, amf);
} else {
usim_log->error("Invalid length for AMF: %d should be %d", args->amf.length(), 4);
usim_log->console("Invalid length for AMF: %d should be %d", args->amf.length(), 4);
}
if(15 == args->imsi.length()) {
imsi = 0;
for(i=0; i<15; i++)
{
imsi *= 10;
imsi += imsi_str[i] - '0';
}
} else {
usim_log->error("Invalid length for ISMI: %d should be %d", args->imsi.length(), 15);
usim_log->console("Invalid length for IMSI: %d should be %d", args->imsi.length(), 15);
}
if(15 == args->imei.length()) {
imei = 0;
for(i=0; i<15; i++)
{
imei *= 10;
imei += imei_str[i] - '0';
}
} else {
usim_log->error("Invalid length for IMEI: %d should be %d", args->imei.length(), 15);
usim_log->console("Invalid length for IMEI: %d should be %d", args->imei.length(), 15);
}
if(32 == args->k.length()) {
str_to_hex(args->k, k);
} else {
usim_log->error("Invalid length for K: %d should be %d", args->k.length(), 32);
usim_log->console("Invalid length for K: %d should be %d", args->k.length(), 32);
}
auth_algo = auth_algo_milenage;
if("xor" == args->algo) {
auth_algo = auth_algo_xor;
}
}
void usim::stop()
{}
/*******************************************************************************
NAS interface
*******************************************************************************/
void usim::get_imsi_vec(uint8_t* imsi_, uint32_t n)
{
if(NULL == imsi_ || n < 15)
{
usim_log->error("Invalid parameters to get_imsi_vec");
return;
}
uint64_t temp = imsi;
for(int i=14;i>=0;i--)
{
imsi_[i] = temp % 10;
temp /= 10;
}
}
void usim::get_imei_vec(uint8_t* imei_, uint32_t n)
{
if(NULL == imei_ || n < 15)
{
usim_log->error("Invalid parameters to get_imei_vec");
return;
}
uint64 temp = imei;
for(int i=14;i>=0;i--)
{
imei_[i] = temp % 10;
temp /= 10;
}
}
void usim::generate_authentication_response(uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res)
{
if(auth_algo_xor == auth_algo) {
gen_auth_res_xor(rand, autn_enb, mcc, mnc, net_valid, res);
} else {
gen_auth_res_milenage(rand, autn_enb, mcc, mnc, net_valid, res);
}
}
void usim::generate_nas_keys(uint8_t *k_nas_enc,
uint8_t *k_nas_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo)
{
// Generate K_nas_enc and K_nas_int
security_generate_k_nas( k_asme,
cipher_algo,
integ_algo,
k_nas_enc,
k_nas_int);
}
/*******************************************************************************
RRC interface
*******************************************************************************/
void usim::generate_as_keys(uint32_t count_ul,
uint8_t *k_rrc_enc,
uint8_t *k_rrc_int,
uint8_t *k_up_enc,
uint8_t *k_up_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo)
{
// Generate K_enb
security_generate_k_enb( k_asme,
count_ul,
k_enb);
// Generate K_rrc_enc and K_rrc_int
security_generate_k_rrc( k_enb,
cipher_algo,
integ_algo,
k_rrc_enc,
k_rrc_int);
// Generate K_up_enc and K_up_int
security_generate_k_up( k_enb,
cipher_algo,
integ_algo,
k_up_enc,
k_up_int);
}
/*******************************************************************************
Helpers
*******************************************************************************/
void usim::gen_auth_res_milenage( uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res)
{
uint32_t i;
uint8_t sqn[6];
*net_valid = true;
// Use RAND and K to compute RES, CK, IK and AK
security_milenage_f2345( k,
op,
rand,
res,
ck,
ik,
ak);
// Extract sqn from autn
for(i=0;i<6;i++)
{
sqn[i] = autn_enb[i] ^ ak[i];
}
// Generate MAC
security_milenage_f1( k,
op,
rand,
sqn,
amf,
mac);
// Construct AUTN
for(i=0; i<6; i++)
{
autn[i] = sqn[i] ^ ak[i];
}
for(i=0; i<2; i++)
{
autn[6+i] = amf[i];
}
for(i=0; i<8; i++)
{
autn[8+i] = mac[i];
}
// Compare AUTNs
for(i=0; i<16; i++)
{
if(autn[i] != autn_enb[i])
{
*net_valid = false;
}
}
// Generate K_asme
security_generate_k_asme( ck,
ik,
ak,
sqn,
mcc,
mnc,
k_asme);
}
// 3GPP TS 34.108 version 10.0.0 Section 8
void usim::gen_auth_res_xor(uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res)
{
uint32_t i;
uint8_t sqn[6];
uint8_t xdout[16];
uint8_t cdout[8];
*net_valid = true;
// Use RAND and K to compute RES, CK, IK and AK
for(i=0; i<16; i++) {
xdout[i] = k[i]^rand[i];
}
for(i=0; i<16; i++) {
res[i] = xdout[i];
ck[i] = xdout[(i+1)%16];
ik[i] = xdout[(i+2)%16];
}
for(i=0; i<6; i++) {
ak[i] = xdout[i+3];
}
// Extract sqn from autn
for(i=0;i<6;i++) {
sqn[i] = autn_enb[i] ^ ak[i];
}
// Generate cdout
for(i=0; i<6; i++) {
cdout[i] = sqn[i];
}
for(i=0; i<2; i++) {
cdout[6+i] = amf[i];
}
// Generate MAC
for(i=0;i<8;i++) {
mac[i] = xdout[i] ^ cdout[i];
}
// Construct AUTN
for(i=0; i<6; i++)
{
autn[i] = sqn[i] ^ ak[i];
}
for(i=0; i<2; i++)
{
autn[6+i] = amf[i];
}
for(i=0; i<8; i++)
{
autn[8+i] = mac[i];
}
// Compare AUTNs
for(i=0; i<16; i++)
{
if(autn[i] != autn_enb[i])
{
*net_valid = false;
}
}
// Generate K_asme
security_generate_k_asme( ck,
ik,
ak,
sqn,
mcc,
mnc,
k_asme);
}
void usim::str_to_hex(std::string str, uint8_t *hex)
{
uint32_t i;
const char *h_str = str.c_str();
uint32_t len = str.length();
for(i=0; i<len/2; i++)
{
if(h_str[i*2+0] >= '0' && h_str[i*2+0] <= '9')
{
hex[i] = ( h_str[i*2+0] - '0') << 4;
}else if( h_str[i*2+0] >= 'A' && h_str[i*2+0] <= 'F'){
hex[i] = (( h_str[i*2+0] - 'A') + 0xA) << 4;
}else{
hex[i] = (( h_str[i*2+0] - 'a') + 0xA) << 4;
}
if( h_str[i*2+1] >= '0' && h_str[i*2+1] <= '9')
{
hex[i] |= h_str[i*2+1] - '0';
}else if( h_str[i*2+1] >= 'A' && h_str[i*2+1] <= 'F'){
hex[i] |= ( h_str[i*2+1] - 'A') + 0xA;
}else{
hex[i] |= ( h_str[i*2+1] - 'a') + 0xA;
}
}
}
} // namespace srsue

@ -0,0 +1,56 @@
# Copyright 2015 Software Radio Systems Limited
#
# This file is part of srsUE
#
# srsUE is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# srsUE is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# A copy of the GNU Affero General Public License can be found in
# the LICENSE file in the top-level directory of this distribution
# and at http://www.gnu.org/licenses/.
#
add_executable(rlc_am_data_test rlc_am_data_test.cc)
target_link_libraries(rlc_am_data_test srslte_upper srslte_phy lte)
add_test(rlc_am_data_test rlc_am_data_test)
add_executable(rlc_am_control_test rlc_am_control_test.cc)
target_link_libraries(rlc_am_control_test srslte_upper srslte_phy lte)
add_test(rlc_am_control_test rlc_am_control_test)
add_executable(rlc_am_test rlc_am_test.cc)
target_link_libraries(rlc_am_test srslte_upper srslte_phy lte)
add_test(rlc_am_test rlc_am_test)
add_executable(rlc_um_data_test rlc_um_data_test.cc)
target_link_libraries(rlc_um_data_test srslte_upper srslte_phy lte)
add_test(rlc_um_data_test rlc_um_data_test)
add_executable(rlc_um_test rlc_um_test.cc)
target_link_libraries(rlc_um_test srslte_upper srslte_phy lte)
add_test(rlc_um_test rlc_um_test)
add_executable(usim_test usim_test.cc)
target_link_libraries(usim_test srslte_upper srslte_phy lte)
add_test(usim_test usim_test)
add_executable(rrc_reconfig_test rrc_reconfig_test.cc)
target_link_libraries(rrc_reconfig_test srslte_upper srslte_phy lte)
add_test(rrc_reconfig_test rrc_reconfig_test)
########################################################################
# Option to run command after build (useful for remote builds)
########################################################################
if (NOT ${BUILD_CMD} STREQUAL "")
message(STATUS "Added custom post-build command: ${BUILD_CMD}")
add_custom_command(TARGET ip_test POST_BUILD COMMAND ${BUILD_CMD})
else(NOT ${BUILD_CMD} STREQUAL "")
message(STATUS "No post-build command defined")
endif (NOT ${BUILD_CMD} STREQUAL "")

@ -0,0 +1,183 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include "upper/usim.h"
#include "upper/nas.h"
#include "upper/rlc.h"
#include "upper/rrc.h"
#include "mac/mac.h"
#include "upper/pdcp_entity.h"
#include "upper/pdcp.h"
#include "common/log_stdout.h"
#include "common/interfaces.h"
using namespace srsue;
uint8_t pdu1[] = {
0x03, 0x22, 0x16, 0x15, 0xe8 , 0x00 , 0x00 , 0x03 , 0x13 , 0xb0 , 0x00 , 0x02 , 0x90 , 0x08,
0x79, 0xf0, 0x00, 0x00, 0x40 , 0xb5 , 0x01 , 0x25 , 0x40 , 0xcc , 0x1d , 0x08 , 0x04 , 0x3c , 0x18 , 0x00,
0x4c, 0x02, 0x20, 0x0f, 0xa8 , 0x00 , 0x65 , 0x48 , 0x07 , 0x04 , 0x04 , 0x24 , 0x1c , 0x19 , 0x05 , 0x41,
0x39, 0x39, 0x4d, 0x38, 0x14 , 0x04 , 0x28 , 0xd1 , 0x5e , 0x6d , 0x78 , 0x13 , 0xfb , 0xf9 , 0x01 , 0xb1,
0x40, 0x2f, 0xd8, 0x4c, 0x02 , 0x20 , 0x00 , 0x5b , 0x78 , 0x00 , 0x07 , 0xa1 , 0x25 , 0xa9 , 0xc1 , 0x3f,
0xd9, 0x40, 0x41, 0xf5, 0x1b , 0x58 , 0x2f , 0x27 , 0x28 , 0xa0 , 0xed , 0xde , 0x54 , 0x43 , 0x48 , 0xc0,
0x56, 0xcc, 0x00, 0x02, 0x84 , 0x00 , 0x42 , 0x0a , 0xf1 , 0x63 };
uint32_t PDU1_LEN = 104;
#define LCID 3
namespace srsue {
// fake classes
class pdcp_dummy : public rrc_interface_pdcp
{
public:
void write_pdu(uint32_t lcid, 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_pcch(byte_buffer_t *pdu) {}
};
class rrc_dummy : public rrc_interface_nas
{
public:
void write_sdu(uint32_t lcid, byte_buffer_t *sdu)
{
}
uint16_t get_mcc() { return 0x11; }
uint16_t get_mnc() { return 0xff; }
void enable_capabilities() {
}
};
class gw_dummy : public gw_interface_nas, public gw_interface_pdcp
{
error_t setup_if_addr(uint32_t ip_addr, char *err_str) {}
void write_pdu(uint32_t lcid, byte_buffer_t *pdu) {}
};
}
class usim_dummy : public usim_interface_nas
{
void get_imsi_vec(uint8_t* imsi_, uint32_t n){
}
void get_imei_vec(uint8_t* imei_, uint32_t n){
}
void generate_authentication_response(uint8_t *rand,
uint8_t *autn_enb,
uint16_t mcc,
uint16_t mnc,
bool *net_valid,
uint8_t *res){
}
void generate_nas_keys(uint8_t *k_nas_enc,
uint8_t *k_nas_int,
CIPHERING_ALGORITHM_ID_ENUM cipher_algo,
INTEGRITY_ALGORITHM_ID_ENUM integ_algo){
}
};
int main(int argc, char **argv)
{
srslte::log_stdout nas_log("NAS");
srslte::log_stdout pdcp_entity_log("PDCP");
srslte::log_stdout rrc_log("RRC");
srslte::log_stdout mac_log("MAC");
nas_log.set_level(srslte::LOG_LEVEL_DEBUG);
pdcp_entity_log.set_level(srslte::LOG_LEVEL_DEBUG);
rrc_log.set_level(srslte::LOG_LEVEL_DEBUG);
nas_log.set_hex_limit(100000);
rrc_log.set_hex_limit(100000);
usim_dummy usim;
rrc_dummy rrc_dummy;
gw_dummy gw;
pdcp_dummy pdcp_dummy;
buffer_pool *pool;
pool = buffer_pool::get_instance();
srsue::nas nas;
nas.init(&usim, &rrc_dummy, &gw, &nas_log);
byte_buffer_t* tmp = pool->allocate();
memcpy(tmp->msg, &pdu1[0], PDU1_LEN);
tmp->N_bytes = PDU1_LEN;
//byte_buffer_t tmp2;
//memcpy(tmp2.msg, &pdu1[0], PDU1_LEN);
//tmp2.N_bytes = PDU1_LEN;
//srsue::mac mac;
//mac.init(NULL, NULL, NULL, &mac_log);
srsue::rrc rrc;
rrc.init(NULL, NULL, NULL, NULL, &nas, NULL, NULL, &rrc_log);
//rrc.init(&phy, &mac, &rlc, &pdcp, &nas, &usim, &mac, &rrc_log);
srsue::pdcp_entity pdcp_entity;
pdcp_entity.init(NULL, &rrc, &gw, &pdcp_entity_log, RB_ID_SRB1, NULL);
pdcp_entity.write_pdu(tmp);
//rrc.write_sdu(RB_ID_SRB2, tmp);
//nas.write_pdu(LCID, tmp);
pool->cleanup();
}

@ -0,0 +1,70 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <assert.h>
#include <iostream>
#include "upper/rlc_am.h"
// Simple status PDU
uint8_t pdu1[] = {0x00, 0x78};
uint32_t PDU1_LEN = 2;
// Status PDU with 4 NACKs
uint8_t pdu2[] = {0x00 ,0x22 ,0x00 ,0x40 ,0x0C ,0x01 ,0xC0 ,0x20};
uint32_t PDU2_LEN = 8;
int main(int argc, char **argv) {
srsue::rlc_status_pdu_t s;
srslte::byte_buffer_t b1,b2;
memcpy(b1.msg, &pdu1[0], PDU1_LEN);
b1.N_bytes = PDU1_LEN;
rlc_am_read_status_pdu(&b1, &s);
assert(s.ack_sn == 30);
assert(s.N_nack == 0);
rlc_am_write_status_pdu(&s, &b2);
assert(b2.N_bytes == PDU1_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
b1.reset();
b2.reset();
memset(&s, 0, sizeof(srsue::rlc_status_pdu_t));
memcpy(b1.msg, &pdu2[0], PDU2_LEN);
b1.N_bytes = PDU2_LEN;
rlc_am_read_status_pdu(&b1, &s);
assert(s.ack_sn == 8);
assert(s.N_nack == 4);
assert(s.nacks[0].nack_sn == 0);
assert(s.nacks[1].nack_sn == 1);
assert(s.nacks[2].nack_sn == 3);
assert(s.nacks[3].nack_sn == 4);
rlc_am_write_status_pdu(&s, &b2);
assert(b2.N_bytes == PDU2_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
}

@ -0,0 +1,109 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <assert.h>
#include <iostream>
#include "upper/rlc_am.h"
// Fixed header only
uint8_t pdu1[] = {0x88, 0x06};
uint32_t PDU1_LEN = 2;
// Fixed + 2 LI fields (each 1500)
uint8_t pdu2[] = {0x8C, 0x00, 0xDD, 0xC5, 0xDC};
uint32_t PDU2_LEN = 5;
// Fixed + 3 LI fields (each 1500)
uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0};
uint32_t PDU3_LEN = 7;
using namespace srsue;
int main(int argc, char **argv) {
rlc_amd_pdu_header_t h;
byte_buffer_t b1,b2;
memcpy(b1.msg, &pdu1[0], PDU1_LEN);
b1.N_bytes = PDU1_LEN;
rlc_am_read_data_pdu_header(&b1, &h);
assert(RLC_DC_FIELD_DATA_PDU == h.dc);
assert(0x01 == h.fi);
assert(0 == h.N_li);
assert(0 == h.lsf);
assert(0 == h.p);
assert(0 == h.rf);
assert(0 == h.so);
assert(6 == h.sn);
rlc_am_write_data_pdu_header(&h, &b2);
assert(b2.N_bytes == PDU1_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
b1.reset();
b2.reset();
memset(&h, 0, sizeof(rlc_amd_pdu_header_t));
memcpy(b1.msg, &pdu2[0], PDU2_LEN);
b1.N_bytes = PDU2_LEN;
rlc_am_read_data_pdu_header(&b1, &h);
assert(RLC_DC_FIELD_DATA_PDU == h.dc);
assert(0x01 == h.fi);
assert(2 == h.N_li);
assert(1500 == h.li[0]);
assert(1500 == h.li[1]);
assert(0 == h.lsf);
assert(0 == h.p);
assert(0 == h.rf);
assert(0 == h.so);
assert(0 == h.sn);
rlc_am_write_data_pdu_header(&h, &b2);
assert(b2.N_bytes == PDU2_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
b1.reset();
b2.reset();
memset(&h, 0, sizeof(rlc_amd_pdu_header_t));
memcpy(b1.msg, &pdu3[0], PDU3_LEN);
b1.N_bytes = PDU3_LEN;
rlc_am_read_data_pdu_header(&b1, &h);
assert(RLC_DC_FIELD_DATA_PDU == h.dc);
assert(0x01 == h.fi);
assert(3 == h.N_li);
assert(1500 == h.li[0]);
assert(1500 == h.li[1]);
assert(1500 == h.li[2]);
assert(0 == h.lsf);
assert(0 == h.p);
assert(0 == h.rf);
assert(0 == h.so);
assert(0 == h.sn);
rlc_am_write_data_pdu_header(&h, &b2);
assert(b2.N_bytes == PDU3_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,71 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include "upper/rlc_um.h"
#include <assert.h>
// Fixed header only
uint8_t pdu1[] = {0x18 ,0xE2};
uint32_t PDU1_LEN = 2;
// Fixed + 1 LI field (value 104)
uint8_t pdu2[] = {0x1C ,0xE1 ,0x06 ,0x80};
uint32_t PDU2_LEN = 4;
using namespace srsue;
int main(int argc, char **argv) {
rlc_umd_pdu_header_t h;
srslte::byte_buffer_t b1,b2;
memcpy(b1.msg, &pdu1[0], PDU1_LEN);
b1.N_bytes = PDU1_LEN;
rlc_um_read_data_pdu_header(&b1, RLC_UMD_SN_SIZE_10_BITS, &h);
assert(0x03 == h.fi);
assert(0 == h.N_li);
assert(226 == h.sn);
rlc_um_write_data_pdu_header(&h, &b2);
assert(b2.N_bytes == PDU1_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
b1.reset();
b2.reset();
memset(&h, 0, sizeof(rlc_umd_pdu_header_t));
memcpy(b1.msg, &pdu2[0], PDU2_LEN);
b1.N_bytes = PDU2_LEN;
rlc_um_read_data_pdu_header(&b1, RLC_UMD_SN_SIZE_10_BITS, &h);
assert(0x03 == h.fi);
assert(225 == h.sn);
assert(1 == h.N_li);
assert(104 == h.li[0]);
rlc_um_write_data_pdu_header(&h, &b2);
assert(b2.N_bytes == PDU2_LEN);
for(int i=0;i<b2.N_bytes;i++)
assert(b2.msg[i] == b1.msg[i]);
}

@ -0,0 +1,218 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include "common/log_stdout.h"
#include "upper/rlc_um.h"
#include <assert.h>
#define NBUFS 5
using namespace srslte;
using namespace srsue;
class mac_dummy_timers
:public srslte::mac_interface_timers
{
public:
srslte::timers::timer* get(uint32_t timer_id)
{
return &t;
}
uint32_t get_unique_id(){return 0;}
void step()
{
t.step();
}
private:
srslte::timers::timer t;
};
class rlc_um_tester
:public pdcp_interface_rlc
,public rrc_interface_rlc
{
public:
rlc_um_tester(){n_sdus = 0;}
// PDCP interface
void write_pdu(uint32_t lcid, byte_buffer_t *sdu)
{
assert(lcid == 3);
sdus[n_sdus++] = sdu;
}
void write_pdu_bcch_bch(byte_buffer_t *sdu) {}
void write_pdu_bcch_dlsch(byte_buffer_t *sdu) {}
void write_pdu_pcch(byte_buffer_t *sdu) {}
// RRC interface
void max_retx_attempted(){}
byte_buffer_t *sdus[5];
int n_sdus;
};
void basic_test()
{
srslte::log_stdout log1("RLC_UM_1");
srslte::log_stdout log2("RLC_UM_2");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log2.set_level(srslte::LOG_LEVEL_DEBUG);
log1.set_hex_limit(-1);
log2.set_hex_limit(-1);
rlc_um_tester tester;
mac_dummy_timers timers;
rlc_um rlc1;
rlc_um rlc2;
int len;
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log2.set_level(srslte::LOG_LEVEL_DEBUG);
rlc1.init(&log1, 3, &tester, &tester, &timers);
rlc2.init(&log2, 3, &tester, &tester, &timers);
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
rlc1.configure(&cnfg);
rlc2.configure(&cnfg);
// Push 5 SDUs into RLC1
byte_buffer_t sdu_bufs[NBUFS];
for(int i=0;i<NBUFS;i++)
{
*sdu_bufs[i].msg = i; // Write the index into the buffer
sdu_bufs[i].N_bytes = 1; // Give each buffer a size of 1 byte
rlc1.write_sdu(&sdu_bufs[i]);
}
assert(13 == rlc1.get_buffer_state());
// Read 5 PDUs from RLC1 (1 byte each)
byte_buffer_t pdu_bufs[NBUFS];
for(int i=0;i<NBUFS;i++)
{
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 3 bytes for header + payload
pdu_bufs[i].N_bytes = len;
}
assert(0 == rlc1.get_buffer_state());
// Write 5 PDUs into RLC2
for(int i=0;i<NBUFS;i++)
{
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
}
assert(0 == rlc2.get_buffer_state());
assert(NBUFS == tester.n_sdus);
for(int i=0; i<tester.n_sdus; i++)
{
assert(tester.sdus[i]->N_bytes == 1);
assert(*(tester.sdus[i]->msg) == i);
}
}
void loss_test()
{
srslte::log_stdout log1("RLC_UM_1");
srslte::log_stdout log2("RLC_UM_2");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log2.set_level(srslte::LOG_LEVEL_DEBUG);
log1.set_hex_limit(-1);
log2.set_hex_limit(-1);
rlc_um_tester tester;
mac_dummy_timers timers;
rlc_um rlc1;
rlc_um rlc2;
int len;
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log2.set_level(srslte::LOG_LEVEL_DEBUG);
rlc1.init(&log1, 3, &tester, &tester, &timers);
rlc2.init(&log2, 3, &tester, &tester, &timers);
LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg;
cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI;
cnfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5;
cnfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
cnfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10;
rlc1.configure(&cnfg);
rlc2.configure(&cnfg);
// Push 5 SDUs into RLC1
byte_buffer_t sdu_bufs[NBUFS];
for(int i=0;i<NBUFS;i++)
{
*sdu_bufs[i].msg = i; // Write the index into the buffer
sdu_bufs[i].N_bytes = 1; // Give each buffer a size of 1 byte
rlc1.write_sdu(&sdu_bufs[i]);
}
assert(13 == rlc1.get_buffer_state());
// Read 5 PDUs from RLC1 (1 byte each)
byte_buffer_t pdu_bufs[NBUFS];
for(int i=0;i<NBUFS;i++)
{
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 3 bytes for header + payload
pdu_bufs[i].N_bytes = len;
}
assert(0 == rlc1.get_buffer_state());
// Write 5 PDUs into RLC2 (skip SN 1)
for(int i=0;i<NBUFS;i++)
{
if(i != 1)
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
}
// Step the reordering timer until expiry
while(!timers.get(1)->is_expired())
timers.get(1)->step();
assert(NBUFS-1 == tester.n_sdus);
}
int main(int argc, char **argv) {
basic_test();
byte_buffer_pool::get_instance()->cleanup();
loss_test();
byte_buffer_pool::get_instance()->cleanup();
}

@ -0,0 +1,133 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include <srslte/srslte.h>
#include "common/log_stdout.h"
#include "liblte_rrc.h"
#include "liblte_mme.h"
void nas_test() {
srslte::log_stdout log1("NAS");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log1.set_hex_limit(-1);
uint32_t nas_message_len = 73;
uint8_t nas_message[128] = {0x27, 0x4f, 0xab, 0xef, 0x59, 0x01, 0x07, 0x42,
0x01, 0x49, 0x06, 0x40, 0x00, 0xf1, 0x10, 0x31,
0x32, 0x00, 0x22, 0x52, 0x01, 0xc1, 0x05, 0x07,
0xff, 0xff, 0xff, 0xff, 0x0c, 0x0b, 0x76, 0x7a,
0x77, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x05, 0x01, 0x0e, 0x0e, 0x0e, 0x01, 0x5e,
0x04, 0xfe, 0xfe, 0x81, 0x4e, 0x50, 0x0b, 0xf6,
0x00, 0xf1, 0x10, 0x00, 0x02, 0x01, 0x01, 0x00,
0x00, 0x62, 0x17, 0x2c, 0x59, 0x49, 0x64, 0x01,
0x03};
uint8 pd;
uint8 msg_type;
LIBLTE_BYTE_MSG_STRUCT buf;
LIBLTE_MME_ATTACH_ACCEPT_MSG_STRUCT attach_accept;
LIBLTE_MME_ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT act_def_eps_bearer_context_req;
memcpy(buf.msg, nas_message, nas_message_len);
buf.N_bytes = nas_message_len;
liblte_mme_parse_msg_header(&buf, &pd, &msg_type);
switch(msg_type)
{
case LIBLTE_MME_MSG_TYPE_ATTACH_ACCEPT:
liblte_mme_unpack_attach_accept_msg(&buf, &attach_accept);
liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, &act_def_eps_bearer_context_req);
break;
case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT:
break;
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST:
break;
case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT:
break;
case LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST:
break;
case LIBLTE_MME_MSG_TYPE_SECURITY_MODE_COMMAND:
break;
case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT:
break;
case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST:
break;
case LIBLTE_MME_MSG_TYPE_EMM_INFORMATION:
break;
default:
break;
}
}
void basic_test() {
srslte::log_stdout log1("RRC");
log1.set_level(srslte::LOG_LEVEL_DEBUG);
log1.set_hex_limit(-1);
LIBLTE_BIT_MSG_STRUCT bit_buf;
LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg;
uint32_t rrc_message_len = 147;
uint8_t rrc_message[256] = {0x22, 0x16, 0x95, 0xa0, 0x18, 0x00, 0x05, 0xaa,
0x50, 0x36, 0x00, 0x61, 0x08, 0x9c, 0xe3, 0x40,
0xb0, 0x84, 0x4e, 0x71, 0xc0, 0x30, 0x84, 0x6e,
0x71, 0xe0, 0x70, 0x84, 0x6e, 0x70, 0x6c, 0x63,
0x1a, 0xc6, 0xb9, 0x8e, 0x7b, 0x1e, 0x84, 0xc0,
0x01, 0x24, 0x9d, 0x3e, 0xaf, 0xbd, 0x64, 0x04,
0x1d, 0x08, 0x05, 0x24, 0x19, 0x00, 0x03, 0xc4,
0x40, 0xc4, 0xc8, 0x00, 0x89, 0x48, 0x07, 0x04,
0x14, 0x1f, 0xff, 0xff, 0xff, 0xfc, 0x30, 0x2d,
0xd9, 0xe9, 0xdd, 0xa5, 0xb9, 0xd1, 0x95, 0xc9,
0xb9, 0x95, 0xd0, 0x14, 0x04, 0x38, 0x38, 0x38,
0x05, 0x78, 0x13, 0xfb, 0xfa, 0x05, 0x39, 0x40,
0x2f, 0xd8, 0x03, 0xc4, 0x40, 0x00, 0x08, 0x04,
0x04, 0x00, 0x01, 0x88, 0x5c, 0xb1, 0x65, 0x25,
0x90, 0x04, 0x0d, 0xa9, 0xc0, 0x2a, 0x9a, 0x01,
0x99, 0x3b, 0x01, 0xf5, 0x12, 0xf0, 0x85, 0x0d,
0x85, 0xef, 0xc0, 0x01, 0xf2, 0x20, 0x60, 0x18,
0x07, 0x97, 0x09, 0x1f, 0xc3, 0x06, 0x00, 0x81,
0x00, 0x00, 0x11};
srslte_bit_unpack_vector(rrc_message, bit_buf.msg, rrc_message_len*8);
bit_buf.N_bits = rrc_message_len*8;
liblte_rrc_unpack_dl_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dl_dcch_msg);
printf("done\n");
}
int main(int argc, char **argv) {
basic_test();
nas_test();
}

@ -0,0 +1,87 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2015 Software Radio Systems Limited
*
* \section LICENSE
*
* This file is part of the srsUE library.
*
* srsUE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsUE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#include <iostream>
#include "upper/usim.h"
#include "common/log_stdout.h"
#include <assert.h>
using namespace srsue;
/*
Debug output generated from the OpenAirInterface HSS:
Converted 02f839 to plmn 208.93
Query: SELECT `key`,`sqn`,`rand`,`OPc` FROM `users` WHERE `users`.`imsi`='208930000000001'
Key: 8b.af.47.3f.2f.8f.d0.94.87.cc.cb.d7.09.7c.68.62.
Received SQN 00000000000000006999 converted to 6999
SQN: 00.00.00.00.1b.57.
RAND: 7c.f6.e2.6b.20.0a.ca.27.a1.a0.91.40.f5.cf.9d.62.
OPc: 8e.27.b6.af.0e.69.2e.75.0f.32.66.7a.3b.14.60.5d.
Generated random
RijndaelKeySchedule: K 8BAF473F2F8FD09487CCCBD7097C6862
MAC_A : 84.ba.37.b0.f6.73.4d.d1.
SQN : 00.00.00.00.1b.57.
RAND : 88.38.c3.55.c8.78.aa.57.21.49.fe.69.db.68.6b.5a.
RijndaelKeySchedule: K 8BAF473F2F8FD09487CCCBD7097C6862
AK : d7.44.51.9b.3e.fd.
CK : 05.d3.53.3d.fe.7b.e7.2d.42.c7.bb.02.f2.8e.da.7f.
IK : 26.33.a2.0b.dc.a8.9d.78.58.ba.42.47.8b.e4.d2.4d.
XRES : e5.5d.88.27.91.8d.ac.c6.
AUTN : d7.44.51.9b.25.aa.80.00.84.ba.37.b0.f6.73.4d.d1.
0x05 0xd3 0x53 0x3d 0xfe 0x7b 0xe7 0x2d 0x42 0xc7 0xbb 0x02 0xf2 0x8e
0xda 0x7f 0x26 0x33 0xa2 0x0b 0xdc 0xa8 0x9d 0x78 0x58 0xba 0x42 0x47
0x8b 0xe4 0xd2 0x4d 0x10 0x02 0xf8 0x39 0x00 0x03 0xd7 0x44 0x51 0x9b
0x25 0xaa 0x00 0x06
KASME : a8.27.57.5e.ea.1a.10.17.3a.a1.bf.ce.4b.0c.21.85.e0.51.ef.bd.91.7f.fe.f5.1f.74.29.61.f9.03.7a.35.
*/
uint8_t rand_enb[] = {0x88, 0x38, 0xc3, 0x55, 0xc8, 0x78, 0xaa, 0x57, 0x21, 0x49, 0xfe, 0x69, 0xdb, 0x68, 0x6b, 0x5a};
uint8_t autn_enb[] = {0xd7, 0x44, 0x51, 0x9b, 0x25, 0xaa, 0x80, 0x00, 0x84, 0xba, 0x37, 0xb0, 0xf6, 0x73, 0x4d, 0xd1};
uint16 mcc = 208;
uint16 mnc = 93;
int main(int argc, char **argv)
{
srslte::log_stdout usim_log("USIM");
bool net_valid;
uint8_t res[16];
usim_args_t args;
args.algo = "milenage";
args.amf = "8000";
args.imei = "35609204079301";
args.imsi = "208930000000001";
args.k = "8BAF473F2F8FD09487CCCBD7097C6862";
args.op = "11111111111111111111111111111111";
srsue::usim usim;
usim.init(&args, &usim_log);
usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, &net_valid, res);
assert(net_valid == true);
}
Loading…
Cancel
Save