diff --git a/srslte/include/srslte/upper/gw.h b/srslte/include/srslte/upper/gw.h new file mode 100644 index 000000000..da408fa1b --- /dev/null +++ b/srslte/include/srslte/upper/gw.h @@ -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 + +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 diff --git a/srslte/include/srslte/upper/gw_metrics.h b/srslte/include/srslte/upper/gw_metrics.h new file mode 100644 index 000000000..e596046c9 --- /dev/null +++ b/srslte/include/srslte/upper/gw_metrics.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 diff --git a/srslte/include/srslte/upper/nas.h b/srslte/include/srslte/upper/nas.h new file mode 100644 index 000000000..6ad957c28 --- /dev/null +++ b/srslte/include/srslte/upper/nas.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 diff --git a/srslte/include/srslte/upper/pdcp.h b/srslte/include/srslte/upper/pdcp.h new file mode 100644 index 000000000..09741c653 --- /dev/null +++ b/srslte/include/srslte/upper/pdcp.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 diff --git a/srslte/include/srslte/upper/pdcp_entity.h b/srslte/include/srslte/upper/pdcp_entity.h new file mode 100644 index 000000000..01d1e9067 --- /dev/null +++ b/srslte/include/srslte/upper/pdcp_entity.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 diff --git a/srslte/include/srslte/upper/rlc.h b/srslte/include/srslte/upper/rlc.h new file mode 100644 index 000000000..62c9be5f8 --- /dev/null +++ b/srslte/include/srslte/upper/rlc.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 diff --git a/srslte/include/srslte/upper/rlc_am.h b/srslte/include/srslte/upper/rlc_am.h new file mode 100644 index 000000000..dcb6e2734 --- /dev/null +++ b/srslte/include/srslte/upper/rlc_am.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 +#include +#include + +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 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 tx_window; + std::deque retx_queue; + std::map rx_window; + std::map 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 diff --git a/srslte/include/srslte/upper/rlc_common.h b/srslte/include/srslte/upper/rlc_common.h new file mode 100644 index 000000000..bb353a6ae --- /dev/null +++ b/srslte/include/srslte/upper/rlc_common.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 +#include +#include + +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 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 diff --git a/srslte/include/srslte/upper/rrc.h b/srslte/include/srslte/upper/rrc.h new file mode 100644 index 000000000..1e1844b72 --- /dev/null +++ b/srslte/include/srslte/upper/rrc.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 + +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 srbs; + std::map 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 diff --git a/srslte/include/srslte/upper/usim.h b/srslte/include/srslte/upper/usim.h new file mode 100644 index 000000000..2a90039a5 --- /dev/null +++ b/srslte/include/srslte/upper/usim.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 +#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 diff --git a/srslte/lib/upper/CMakeLists.txt b/srslte/lib/upper/CMakeLists.txt new file mode 100644 index 000000000..2a4e592cb --- /dev/null +++ b/srslte/lib/upper/CMakeLists.txt @@ -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) diff --git a/srslte/lib/upper/gw.cc b/srslte/lib/upper/gw.cc new file mode 100644 index 000000000..c35cbb549 --- /dev/null +++ b/srslte/lib/upper/gw.cc @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + + +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 diff --git a/srslte/lib/upper/nas.cc b/srslte/lib/upper/nas.cc new file mode 100644 index 000000000..6e1407ef4 --- /dev/null +++ b/srslte/lib/upper/nas.cc @@ -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 diff --git a/srslte/lib/upper/pdcp.cc b/srslte/lib/upper/pdcp.cc new file mode 100644 index 000000000..1ae51ee7b --- /dev/null +++ b/srslte/lib/upper/pdcp.cc @@ -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) { + 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 diff --git a/srslte/lib/upper/pdcp_entity.cc b/srslte/lib/upper/pdcp_entity.cc new file mode 100644 index 000000000..505f067fd --- /dev/null +++ b/srslte/lib/upper/pdcp_entity.cc @@ -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; +} + +} diff --git a/srslte/lib/upper/rlc.cc b/srslte/lib/upper/rlc.cc new file mode 100644 index 000000000..490dc5589 --- /dev/null +++ b/srslte/lib/upper/rlc.cc @@ -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;iinfo("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; iinfo_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 diff --git a/srslte/lib/upper/rlc_am.cc b/srslte/lib/upper/rlc_am.cc new file mode 100644 index 000000000..5a2106271 --- /dev/null +++ b/srslte/lib/upper/rlc_am.cc @@ -0,0 +1,1474 @@ +/** + * + * \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_am.h" + +#include +#include + +#define MOD 1024 +#define RX_MOD_BASE(x) (x-vr_r)%1024 +#define TX_MOD_BASE(x) (x-vt_a)%1024 + +using namespace srslte; + +namespace srsue{ + +rlc_am::rlc_am() : tx_sdu_queue(16) +{ + tx_sdu = NULL; + rx_sdu = NULL; + pool = byte_buffer_pool::get_instance(); + + pthread_mutex_init(&mutex, NULL); + + vt_a = 0; + vt_ms = RLC_AM_WINDOW_SIZE; + vt_s = 0; + poll_sn = 0; + + vr_r = 0; + vr_mr = RLC_AM_WINDOW_SIZE; + vr_x = 0; + vr_ms = 0; + vr_h = 0; + + pdu_without_poll = 0; + byte_without_poll = 0; + + poll_received = false; + do_status = false; +} + +void rlc_am::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_am::configure(LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg) +{ + t_poll_retx = liblte_rrc_t_poll_retransmit_num[cnfg->ul_am_rlc.t_poll_retx]; + poll_pdu = liblte_rrc_poll_pdu_num[cnfg->ul_am_rlc.poll_pdu]; + poll_byte = liblte_rrc_poll_byte_num[cnfg->ul_am_rlc.poll_byte]*1000; // KB + max_retx_thresh = liblte_rrc_max_retx_threshold_num[cnfg->ul_am_rlc.max_retx_thresh]; + + t_reordering = liblte_rrc_t_reordering_num[cnfg->dl_am_rlc.t_reordering]; + t_status_prohibit = liblte_rrc_t_status_prohibit_num[cnfg->dl_am_rlc.t_status_prohibit]; + + log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " + "t_reordering=%d, t_status_prohibit=%d\n", + rb_id_text[lcid], t_poll_retx, poll_pdu, poll_byte, max_retx_thresh, + t_reordering, t_status_prohibit); +} + + +void rlc_am::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_am::reset() +{ + // Empty tx_sdu_queue before locking the mutex + empty_queue(); + + pthread_mutex_lock(&mutex); + reordering_timeout.reset(); + if(tx_sdu) + tx_sdu->reset(); + if(rx_sdu) + rx_sdu->reset(); + + vt_a = 0; + vt_ms = RLC_AM_WINDOW_SIZE; + vt_s = 0; + poll_sn = 0; + + vr_r = 0; + vr_mr = RLC_AM_WINDOW_SIZE; + vr_x = 0; + vr_ms = 0; + vr_h = 0; + + pdu_without_poll = 0; + byte_without_poll = 0; + + poll_received = false; + do_status = false; + + // Drop all messages in RX segments + std::map::iterator rxsegsit; + std::list::iterator segit; + for(rxsegsit = rx_segments.begin(); rxsegsit != rx_segments.end(); rxsegsit++) { + std::list l = rxsegsit->second.segments; + for(segit = l.begin(); segit != l.end(); segit++) { + pool->deallocate(segit->buf); + } + l.clear(); + } + rx_segments.clear(); + + // Drop all messages in RX window + std::map::iterator rxit; + for(rxit = rx_window.begin(); rxit != rx_window.end(); rxit++) { + pool->deallocate(rxit->second.buf); + } + rx_window.clear(); + + // Drop all messages in TX window + std::map::iterator txit; + for(txit = tx_window.begin(); txit != tx_window.end(); txit++) { + pool->deallocate(txit->second.buf); + } + tx_window.clear(); + + // Drop all messages in RETX queue + retx_queue.clear(); + pthread_mutex_unlock(&mutex); +} + +rlc_mode_t rlc_am::get_mode() +{ + return RLC_MODE_AM; +} + +uint32_t rlc_am::get_bearer() +{ + return lcid; +} + +/**************************************************************************** + * PDCP interface + ***************************************************************************/ + +void rlc_am::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_am::get_total_buffer_state() +{ + pthread_mutex_lock(&mutex); + uint32_t n_bytes = 0; + uint32_t n_sdus = 0; + + // Bytes needed for status report + check_reordering_timeout(); + if(do_status && !status_prohibited()) { + n_bytes += prepare_status(); + log->debug("Buffer state - status report: %d bytes\n", n_bytes); + } + + // Bytes needed for retx + if(retx_queue.size() > 0) { + rlc_amd_retx_t retx = retx_queue.front(); + log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end); + if(tx_window.end() != tx_window.find(retx.sn)) { + n_bytes += required_buffer_size(retx); + log->debug("Buffer state - retx: %d bytes\n", n_bytes); + } + } + + // Bytes needed for tx SDUs + n_sdus = tx_sdu_queue.size(); + 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; + log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes); + } + + pthread_mutex_unlock(&mutex); + return n_bytes; +} + +uint32_t rlc_am::get_buffer_state() +{ + pthread_mutex_lock(&mutex); + uint32_t n_bytes = 0; + uint32_t n_sdus = 0; + + // Bytes needed for status report + check_reordering_timeout(); + if(do_status && !status_prohibited()) { + n_bytes = prepare_status(); + log->debug("Buffer state - status report: %d bytes\n", n_bytes); + goto unlock_and_return; + } + + // Bytes needed for retx + if(retx_queue.size() > 0) { + rlc_amd_retx_t retx = retx_queue.front(); + log->debug("Buffer state - retx - SN: %d, Segment: %s, %d:%d\n", retx.sn, retx.is_segment ? "true" : "false", retx.so_start, retx.so_end); + if(tx_window.end() != tx_window.find(retx.sn)) { + n_bytes = required_buffer_size(retx); + log->debug("Buffer state - retx: %d bytes\n", n_bytes); + goto unlock_and_return; + } + } + + // Bytes needed for tx SDUs + n_sdus = tx_sdu_queue.size(); + 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; + log->debug("Buffer state - tx SDUs: %d bytes\n", n_bytes); + } + +unlock_and_return: + pthread_mutex_unlock(&mutex); + return n_bytes; +} + +int rlc_am::read_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + pthread_mutex_lock(&mutex); + + log->debug("MAC opportunity - %d bytes\n", nof_bytes); + + // Tx STATUS if requested + if(do_status && !status_prohibited()) { + pthread_mutex_unlock(&mutex); + return build_status_pdu(payload, nof_bytes); + } + // RETX if required + if(retx_queue.size() > 0) { + pthread_mutex_unlock(&mutex); + return build_retx_pdu(payload, nof_bytes); + } + + pthread_mutex_unlock(&mutex); + + // Build a PDU from SDUs + return build_data_pdu(payload, nof_bytes); +} + +void rlc_am::write_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + if(nof_bytes < 1) + return; + pthread_mutex_lock(&mutex); + + if(rlc_am_is_control_pdu(payload)) { + handle_control_pdu(payload, nof_bytes); + } else { + rlc_amd_pdu_header_t header; + rlc_am_read_data_pdu_header(&payload, &nof_bytes, &header); + if(header.rf) { + handle_data_pdu_segment(payload, nof_bytes, header); + }else{ + handle_data_pdu(payload, nof_bytes, header); + } + } + pthread_mutex_unlock(&mutex); +} + +/**************************************************************************** + * Timer checks + ***************************************************************************/ + +bool rlc_am::status_prohibited() +{ + return (status_prohibit_timeout.is_running() && !status_prohibit_timeout.expired()); +} + +bool rlc_am::poll_retx() +{ + return (poll_retx_timeout.is_running() && poll_retx_timeout.expired()); +} + +void rlc_am::check_reordering_timeout() +{ + if(reordering_timeout.is_running() && reordering_timeout.expired()) + { + reordering_timeout.reset(); + log->debug("%s reordering timeout expiry - updating vr_ms\n", rb_id_text[lcid]); + + // 36.322 v10 Section 5.1.3.2.4 + vr_ms = vr_x; + std::map::iterator it = rx_window.find(vr_ms); + while(rx_window.end() != it) + { + vr_ms = (vr_ms + 1)%MOD; + it = rx_window.find(vr_ms); + } + if(poll_received) + do_status = true; + + if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_ms)) + { + reordering_timeout.start(t_reordering); + vr_x = vr_h; + } + + debug_state(); + } +} + +/**************************************************************************** + * Helpers + ***************************************************************************/ + +bool rlc_am::poll_required() +{ + if(poll_pdu > 0 && pdu_without_poll > poll_pdu) + return true; + if(poll_byte > 0 && byte_without_poll > poll_byte) + return true; + if(poll_retx()) + return true; + return false; +} + +int rlc_am::prepare_status() +{ + status.N_nack = 0; + status.ack_sn = vr_ms; + + // We don't use segment NACKs - just NACK the full PDU + + uint32_t i = vr_r; + while(RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms)) + { + if(rx_window.find(i) == rx_window.end()) + status.nacks[status.N_nack++].nack_sn = i; + i = (i + 1)%MOD; + } + + return rlc_am_packed_length(&status); +} + +int rlc_am::build_status_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + int pdu_len = rlc_am_packed_length(&status); + if(nof_bytes >= pdu_len) + { + log->info("%s Tx status PDU - %s\n", + rb_id_text[lcid], rlc_am_to_string(&status).c_str()); + + do_status = false; + poll_received = false; + + if(t_status_prohibit > 0) + status_prohibit_timeout.start(t_status_prohibit); + debug_state(); + return rlc_am_write_status_pdu(&status, payload); + }else{ + log->warning("%s Cannot tx status PDU - %d bytes available, %d bytes required\n", + rb_id_text[lcid], nof_bytes, pdu_len); + return 0; + } +} + +int rlc_am::build_retx_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + rlc_amd_retx_t retx = retx_queue.front(); + + // Sanity check - drop any retx SNs not present in tx_window + while(tx_window.end() == tx_window.find(retx.sn)) { + retx_queue.pop_front(); + retx = retx_queue.front(); + } + + // Is resegmentation needed? + if(retx.is_segment || required_buffer_size(retx) > nof_bytes) { + log->debug("%s build_retx_pdu - resegmentation required\n", rb_id_text[lcid]); + return build_segment(payload, nof_bytes, retx); + } + + // Update & write header + rlc_amd_pdu_header_t new_header = tx_window[retx.sn].header; + new_header.p = 0; + if(poll_required()) + { + new_header.p = 1; + poll_sn = vt_s; + pdu_without_poll = 0; + byte_without_poll = 0; + poll_retx_timeout.start(t_poll_retx); + } + + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&new_header, &ptr); + memcpy(ptr, tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes); + + retx_queue.pop_front(); + tx_window[retx.sn].retx_count++; + if(tx_window[retx.sn].retx_count >= max_retx_thresh) + rrc->max_retx_attempted(); + log->info("%s Retx PDU scheduled for tx. SN: %d, retx count: %d\n", + rb_id_text[lcid], retx.sn, tx_window[retx.sn].retx_count); + + debug_state(); + return (ptr-payload) + tx_window[retx.sn].buf->N_bytes; +} + +int rlc_am::build_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_retx_t retx) +{ + if(!retx.is_segment){ + retx.so_start = 0; + retx.so_end = tx_window[retx.sn].buf->N_bytes; + } + + // Construct new header + rlc_amd_pdu_header_t new_header; + rlc_amd_pdu_header_t old_header = tx_window[retx.sn].header; + + new_header.dc = RLC_DC_FIELD_DATA_PDU; + new_header.rf = 1; + new_header.p = 0; + new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED; + new_header.sn = old_header.sn; + new_header.lsf = 0; + new_header.so = retx.so_start; + new_header.N_li = 0; + + uint32_t head_len = 0; + uint32_t pdu_space = 0; + + head_len = rlc_am_packed_length(&new_header); + if(nof_bytes <= head_len) + { + log->warning("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header\n", + rb_id_text[lcid], nof_bytes, head_len); + return 0; + } + pdu_space = nof_bytes-head_len; + if(pdu_space < (retx.so_end-retx.so_start)) + retx.so_end = retx.so_start+pdu_space; + + // Need to rebuild the li table & update fi based on so_start and so_end + if(retx.so_start == 0 && rlc_am_start_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned + + uint32_t lower = 0; + uint32_t upper = 0; + uint32_t li = 0; + + for(int i=0; i= retx.so_end) + break; + + upper += old_header.li[i]; + + head_len = rlc_am_packed_length(&new_header); + pdu_space = nof_bytes-head_len; + if(pdu_space < (retx.so_end-retx.so_start)) + retx.so_end = retx.so_start+pdu_space; + + if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed + li = upper - lower; + if(upper > retx.so_end) + li -= upper - retx.so_end; + if(lower < retx.so_start) + li -= retx.so_start - lower; + if(lower > 0 && lower == retx.so_start) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU + if(upper == retx.so_end) { + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU + } + new_header.li[new_header.N_li++] = li; + } + + lower += old_header.li[i]; + } + + // Update retx_queue + if(tx_window[retx.sn].buf->N_bytes == retx.so_end) { + retx_queue.pop_front(); + new_header.lsf = 1; + if(rlc_am_end_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment is end aligned + } else if(retx_queue.front().so_end == retx.so_end) { + retx_queue.pop_front(); + } else { + retx_queue.front().is_segment = true; + retx_queue.front().so_start = retx.so_end; + if(new_header.N_li > 0) + new_header.N_li--; + } + + // Write header and pdu + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&new_header, &ptr); + uint8_t* data = &tx_window[retx.sn].buf->msg[retx.so_start]; + uint32_t len = retx.so_end - retx.so_start; + memcpy(ptr, data, len); + + log->info("%s Retx PDU segment scheduled for tx. SN: %d, SO: %d\n", + rb_id_text[lcid], retx.sn, retx.so_start); + + debug_state(); + int pdu_len = (ptr-payload) + len; + if(pdu_len > nof_bytes) { + log->error("%s Retx PDU segment length error. Available: %d, Used: %d\n", + rb_id_text[lcid], nof_bytes, pdu_len); + log->debug("%s Retx PDU segment length error. Header len: %d, Payload len: %d, N_li: %d\n", + rb_id_text[lcid], (ptr-payload), len, new_header.N_li); + } + return pdu_len; + +} + +int rlc_am::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) { + log->console("Fatal Error: Could not allocate PDU in build_data_pdu()\n"); + exit(-1); + } + rlc_amd_pdu_header_t header; + header.dc = RLC_DC_FIELD_DATA_PDU; + header.rf = 0; + header.p = 0; + header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; + header.sn = vt_s; + header.lsf = 0; + header.so = 0; + header.N_li = 0; + + uint32_t head_len = rlc_am_packed_length(&header); + uint32_t to_move = 0; + uint32_t last_li = 0; + uint32_t pdu_space = nof_bytes; + uint8_t *pdu_ptr = pdu->msg; + + 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); + pool->deallocate(pdu); + return 0; + } + + log->debug("%s Building PDU - pdu_space: %d, head_len: %d \n", + rb_id_text[lcid], pdu_space, head_len); + + // 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; + 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; + } + if(pdu_space > to_move) + pdu_space -= to_move; + else + pdu_space = 0; + header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU + + log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n", + rb_id_text[lcid], to_move, pdu_space, head_len); + } + + // Pull SDUs from queue + while(pdu_space > head_len && tx_sdu_queue.size() > 0) + { + if(last_li > 0) + header.li[header.N_li++] = last_li; + head_len = rlc_am_packed_length(&header); + if(head_len >= pdu_space) { + header.N_li--; + break; + } + tx_sdu_queue.read(&tx_sdu); + to_move = ((pdu_space-head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space-head_len; + 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; + } + if(pdu_space > to_move) + pdu_space -= to_move; + else + pdu_space = 0; + + log->debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d \n", + rb_id_text[lcid], to_move, pdu_space, head_len); + } + + if(tx_sdu) + header.fi |= RLC_FI_FIELD_NOT_END_ALIGNED; // Last byte does not correspond to last byte of SDU + + // Set Poll bit + pdu_without_poll++; + byte_without_poll += (pdu->N_bytes + head_len); + if(poll_required()) + { + header.p = 1; + poll_sn = vt_s; + pdu_without_poll = 0; + byte_without_poll = 0; + poll_retx_timeout.start(t_poll_retx); + } + + // Set SN + header.sn = vt_s; + vt_s = (vt_s + 1)%MOD; + log->info("%s PDU scheduled for tx. SN: %d\n", rb_id_text[lcid], header.sn); + + // Place PDU in tx_window, write header and TX + tx_window[header.sn].buf = pdu; + tx_window[header.sn].header = header; + tx_window[header.sn].is_acked = false; + tx_window[header.sn].retx_count = 0; + + uint8_t *ptr = payload; + rlc_am_write_data_pdu_header(&header, &ptr); + memcpy(ptr, pdu->msg, pdu->N_bytes); + + debug_state(); + return (ptr-payload) + pdu->N_bytes; +} + +void rlc_am::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t header) +{ + std::map::iterator it; + + log->info_hex(payload, nof_bytes, "%s Rx data PDU SN: %d", + rb_id_text[lcid], header.sn); + + if(!inside_rx_window(header.sn)) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + do_status = true; + } + log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", + rb_id_text[lcid], header.sn, vr_r, vr_mr); + return; + } + + it = rx_window.find(header.sn); + if(rx_window.end() != it) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + do_status = true; + } + log->info("%s Discarding duplicate SN: %d\n", + rb_id_text[lcid], header.sn); + return; + } + + // Write to rx window + rlc_amd_rx_pdu_t pdu; + pdu.buf = pool->allocate(); + if (!pdu.buf) { + log->console("Fatal Error: Could not allocate PDU in handle_data_pdu()\n"); + exit(-1); + } + + memcpy(pdu.buf->msg, payload, nof_bytes); + pdu.buf->N_bytes = nof_bytes; + pdu.header = header; + + rx_window[header.sn] = pdu; + + // Update vr_h + if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h)) + vr_h = (header.sn + 1)%MOD; + + // Update vr_ms + it = rx_window.find(vr_ms); + while(rx_window.end() != it) + { + vr_ms = (vr_ms + 1)%MOD; + it = rx_window.find(vr_ms); + } + + // Check poll bit + if(header.p) + { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + poll_received = true; + + // 36.322 v10 Section 5.2.3 + if(RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || + RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr)) + { + do_status = true; + } + // else delay for reordering timer + } + + // Reassemble and deliver SDUs + reassemble_rx_sdus(); + + // Update reordering variables and timers (36.322 v10.0.0 Section 5.1.3.2.3) + if(reordering_timeout.is_running()) + { + if( + vr_x == vr_r || + (RX_MOD_BASE(vr_x) < RX_MOD_BASE(vr_r) || + RX_MOD_BASE(vr_x) > RX_MOD_BASE(vr_mr) && + vr_x != vr_mr) + ) + { + reordering_timeout.reset(); + } + } + if(!reordering_timeout.is_running()) + { + if(RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r)) + { + reordering_timeout.start(t_reordering); + vr_x = vr_h; + } + } + + debug_state(); +} + +void rlc_am::handle_data_pdu_segment(uint8_t *payload, uint32_t nof_bytes, rlc_amd_pdu_header_t header) +{ + std::map::iterator it; + + log->info_hex(payload, nof_bytes, "%s Rx data PDU segment. SN: %d, SO: %d", + rb_id_text[lcid], header.sn, header.so); + + // Check inside rx window + if(!inside_rx_window(header.sn)) { + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + do_status = true; + } + log->info("%s SN: %d outside rx window [%d:%d] - discarding\n", + rb_id_text[lcid], header.sn, vr_r, vr_mr); + return; + } + + rlc_amd_rx_pdu_t segment; + segment.buf = pool->allocate(); + if (!segment.buf) { + log->console("Fatal Error: Could not allocate PDU in handle_data_pdu_segment()\n"); + exit(-1); + } + memcpy(segment.buf->msg, payload, nof_bytes); + segment.buf->N_bytes = nof_bytes; + segment.header = header; + + // Check if we already have a segment from the same PDU + it = rx_segments.find(header.sn); + if(rx_segments.end() != it) { + + if(header.p) { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + do_status = true; + } + + // Add segment to PDU list and check for complete + if(add_segment_and_check(&it->second, &segment)) { + std::list::iterator segit; + std::list seglist = it->second.segments; + for(segit = seglist.begin(); segit != seglist.end(); segit++) { + pool->deallocate(segit->buf); + } + seglist.clear(); + rx_segments.erase(it); + } + + } else { + + // Create new PDU segment list and write to rx_segments + rlc_amd_rx_pdu_segments_t pdu; + pdu.segments.push_back(segment); + rx_segments[header.sn] = pdu; + + + // Update vr_h + if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_h)) + vr_h = (header.sn + 1)%MOD; + + // Check poll bit + if(header.p) + { + log->info("%s Status packet requested through polling bit\n", rb_id_text[lcid]); + poll_received = true; + + // 36.322 v10 Section 5.2.3 + if(RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ms) || + RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_mr)) + { + do_status = true; + } + // else delay for reordering timer + } + } + + debug_state(); +} + +void rlc_am::handle_control_pdu(uint8_t *payload, uint32_t nof_bytes) +{ + log->info_hex(payload, nof_bytes, "%s Rx control PDU", rb_id_text[lcid]); + + rlc_status_pdu_t status; + rlc_am_read_status_pdu(payload, nof_bytes, &status); + + log->info("%s Rx Status PDU: %s\n", rb_id_text[lcid], rlc_am_to_string(&status).c_str()); + + poll_retx_timeout.reset(); + + // Handle ACKs and NACKs + bool update_vt_a = true; + uint32_t i = vt_a; + while(TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && + TX_MOD_BASE(i) < TX_MOD_BASE(vt_s)) + { + std::map::iterator it; + + bool nack = false; + for(int j=0;jsecond.buf->N_bytes; + }else{ + retx.so_end = status.nacks[j].so_end + 1; + } + } else { + retx.so_start = 0; + retx.so_end = tx_window.find(i)->second.buf->N_bytes; + } + retx.sn = i; + retx_queue.push_back(retx); + } + } + } + } + + if(!nack) { + //ACKed SNs get marked and removed from tx_window if possible + it = tx_window.find(i); + if(tx_window.end() != it) + { + tx_window[i].is_acked = true; + if(update_vt_a) + { + pool->deallocate(tx_window[i].buf); + tx_window.erase(i); + vt_a = (vt_a + 1)%MOD; + vt_ms = (vt_ms + 1)%MOD; + } + } + } + i = (i+1)%MOD; + } + + debug_state(); +} + +void rlc_am::reassemble_rx_sdus() +{ + if(!rx_sdu) { + rx_sdu = pool->allocate(); + if (!rx_sdu) { + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n"); + exit(-1); + } + } + // Iterate through rx_window, assembling and delivering SDUs + while(rx_window.end() != rx_window.find(vr_r)) + { + // Handle any SDU segments + for(int i=0; imsg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, len); + rx_sdu->N_bytes += len; + rx_window[vr_r].buf->msg += len; + rx_window[vr_r].buf->N_bytes -= len; + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rb_id_text[lcid]); + rx_sdu->set_timestamp(); + pdcp->write_pdu(lcid, rx_sdu); + rx_sdu = pool->allocate(); + if (!rx_sdu) { + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n"); + exit(-1); + } + + } + + // Handle last segment + memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_r].buf->msg, rx_window[vr_r].buf->N_bytes); + rx_sdu->N_bytes += rx_window[vr_r].buf->N_bytes; + if(rlc_am_end_aligned(rx_window[vr_r].header.fi)) + { + log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU", rb_id_text[lcid]); + rx_sdu->set_timestamp(); + pdcp->write_pdu(lcid, rx_sdu); + rx_sdu = pool->allocate(); + if (!rx_sdu) { + log->console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n"); + exit(-1); + } + } + + // Move the rx_window + pool->deallocate(rx_window[vr_r].buf); + rx_window.erase(vr_r); + vr_r = (vr_r + 1)%MOD; + vr_mr = (vr_mr + 1)%MOD; + } +} + +bool rlc_am::inside_tx_window(uint16_t sn) +{ + if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vt_a) && + RX_MOD_BASE(sn) < RX_MOD_BASE(vt_ms)) + { + return true; + }else{ + return false; + } +} + +bool rlc_am::inside_rx_window(uint16_t sn) +{ + if(RX_MOD_BASE(sn) >= RX_MOD_BASE(vr_r) && + RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) + { + return true; + }else{ + return false; + } +} + +void rlc_am::debug_state() +{ + log->debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d " + "vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d\n", + rb_id_text[lcid], vt_a, vt_ms, vt_s, poll_sn, + vr_r, vr_mr, vr_x, vr_ms, vr_h); + +} + +bool rlc_am::add_segment_and_check(rlc_amd_rx_pdu_segments_t *pdu, rlc_amd_rx_pdu_t *segment) +{ + // Ordered insert + std::list::iterator tmpit; + std::list::iterator it = pdu->segments.begin(); + while(it != pdu->segments.end() && it->header.so < segment->header.so) + it++; + pdu->segments.insert(it, *segment); + + // Check for complete + uint32_t so = 0; + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + if(so != it->header.so) + return false; + so += it->buf->N_bytes; + } + if(!pdu->segments.back().header.lsf) + return false; + + // We have all segments of the PDU - reconstruct and handle + rlc_amd_pdu_header_t header; + header.dc = RLC_DC_FIELD_DATA_PDU; + header.rf = 0; + header.p = 0; + header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; + header.sn = pdu->segments.front().header.sn; + header.lsf = 0; + header.so = 0; + header.N_li = 0; + + // Reconstruct fi field + header.fi |= (pdu->segments.front().header.fi & RLC_FI_FIELD_NOT_START_ALIGNED); + header.fi |= (pdu->segments.back().header.fi & RLC_FI_FIELD_NOT_END_ALIGNED); + + // Reconstruct li fields + uint16_t count = 0; + uint16_t carryover = 0; + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + if(it->header.N_li > 0) { + header.li[header.N_li++] = it->header.li[0] + carryover; + count += it->header.li[0]; + for(int i=1; iheader.N_li; i++) { + header.li[header.N_li++] = it->header.li[i]; + count += it->header.li[i]; + } + } + carryover = it->buf->N_bytes - count; + tmpit = it; + if(rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) { + header.li[header.N_li++] = carryover; + carryover = 0; + } + count = 0; + } + + // Copy data + byte_buffer_t *full_pdu = pool->allocate(); + if (!full_pdu) { + log->console("Fatal Error: Could not allocate PDU in add_segment_and_check()\n"); + exit(-1); + } + for(it = pdu->segments.begin(); it != pdu->segments.end(); it++) { + memcpy(&full_pdu->msg[full_pdu->N_bytes], it->buf->msg, it->buf->N_bytes); + full_pdu->N_bytes += it->buf->N_bytes; + } + + handle_data_pdu(full_pdu->msg, full_pdu->N_bytes, header); + return true; +} + +int rlc_am::required_buffer_size(rlc_amd_retx_t retx) +{ + if(!retx.is_segment){ + return rlc_am_packed_length(&tx_window[retx.sn].header) + tx_window[retx.sn].buf->N_bytes; + } + + // Construct new header + rlc_amd_pdu_header_t new_header; + rlc_amd_pdu_header_t old_header = tx_window[retx.sn].header; + + new_header.dc = RLC_DC_FIELD_DATA_PDU; + new_header.rf = 1; + new_header.p = 0; + new_header.fi = RLC_FI_FIELD_NOT_START_OR_END_ALIGNED; + new_header.sn = old_header.sn; + new_header.lsf = 0; + new_header.so = retx.so_start; + new_header.N_li = 0; + + uint32_t head_len = 0; + + // Need to rebuild the li table & update fi based on so_start and so_end + if(retx.so_start != 0 && rlc_am_start_aligned(old_header.fi)) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment is start aligned + + uint32_t lower = 0; + uint32_t upper = 0; + uint32_t li = 0; + + for(int i=0; i= retx.so_end) + break; + + upper += old_header.li[i]; + + head_len = rlc_am_packed_length(&new_header); + + if(upper > retx.so_start && lower < retx.so_end) { // Current SDU is needed + li = upper - lower; + if(upper > retx.so_end) + li -= upper - retx.so_end; + if(lower < retx.so_start) + li -= retx.so_start - lower; + if(lower > 0 && lower == retx.so_start) + new_header.fi &= RLC_FI_FIELD_NOT_END_ALIGNED; // segment start is aligned with this SDU + if(upper == retx.so_end) { + new_header.fi &= RLC_FI_FIELD_NOT_START_ALIGNED; // segment end is aligned with this SDU + } + new_header.li[new_header.N_li++] = li; + } + + lower += old_header.li[i]; + } + +// if(tx_window[retx.sn].buf->N_bytes != retx.so_end) { +// if(new_header.N_li > 0) +// new_header.N_li--; // No li for last segment +// } + + return rlc_am_packed_length(&new_header) + (retx.so_end-retx.so_start); +} + +bool rlc_am::retx_queue_has_sn(uint32_t sn) +{ + std::deque::iterator q_it; + for(q_it = retx_queue.begin(); q_it != retx_queue.end(); q_it++) { + if(q_it->sn == sn) + return true; + } + return false; +} + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ + +// Read header from pdu struct, don't strip header +void rlc_am_read_data_pdu_header(byte_buffer_t *pdu, rlc_amd_pdu_header_t *header) +{ + uint8_t *ptr = pdu->msg; + uint32_t n = 0; + rlc_am_read_data_pdu_header(&ptr, &n, header); +} + +// Read header from raw pointer, strip header +void rlc_am_read_data_pdu_header(uint8_t **payload, uint32_t *nof_bytes, rlc_amd_pdu_header_t *header) +{ + uint8_t ext; + uint8_t *ptr = *payload; + + header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); + + if(RLC_DC_FIELD_DATA_PDU == header->dc) + { + // Fixed part + header->rf = ((*ptr >> 6) & 0x01); + header->p = ((*ptr >> 5) & 0x01); + header->fi = (rlc_fi_field_t)((*ptr >> 3) & 0x03); + ext = ((*ptr >> 2) & 0x01); + header->sn = (*ptr & 0x03) << 8; // 2 bits SN + ptr++; + header->sn |= (*ptr & 0xFF); // 8 bits SN + ptr++; + + if(header->rf) + { + header->lsf = ((*ptr >> 7) & 0x01); + header->so = (*ptr & 0x7F) << 8; // 7 bits of SO + ptr++; + header->so |= (*ptr & 0xFF); // 8 bits of SO + ptr++; + } + + // 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++; + } + } + + // Account for padding if N_li is odd + if(header->N_li%2 == 1) + ptr++; + + *nof_bytes -= ptr-*payload; + *payload = ptr; + } +} + +// Write header to pdu struct +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, byte_buffer_t *pdu) +{ + uint8_t *ptr = pdu->msg; + rlc_am_write_data_pdu_header(header, &ptr); + pdu->N_bytes += ptr - pdu->msg; +} + +// Write header to pointer & move pointer +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t *header, uint8_t **payload) +{ + uint32_t i; + uint8_t ext = (header->N_li > 0) ? 1 : 0; + + uint8_t *ptr = *payload; + + // Fixed part + *ptr = (header->dc & 0x01) << 7; + *ptr |= (header->rf & 0x01) << 6; + *ptr |= (header->p & 0x01) << 5; + *ptr |= (header->fi & 0x03) << 3; + *ptr |= (ext & 0x01) << 2; + + *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN + ptr++; + *ptr = (header->sn & 0xFF); // 8 bits SN + ptr++; + + // Segment part + if(header->rf) + { + *ptr = (header->lsf & 0x01) << 7; + *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO + ptr++; + *ptr = (header->so & 0x00FF); // 8 bits of SO + 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++; + + *payload = ptr; +} + +void rlc_am_read_status_pdu(byte_buffer_t *pdu, rlc_status_pdu_t *status) +{ + rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); +} + +void rlc_am_read_status_pdu(uint8_t *payload, uint32_t nof_bytes, rlc_status_pdu_t *status) +{ + uint32_t i; + uint8_t ext1, ext2; + bit_buffer_t tmp; + uint8_t *ptr = tmp.msg; + + srslte_bit_unpack_vector(payload, tmp.msg, nof_bytes*8); + tmp.N_bits = nof_bytes*8; + + rlc_dc_field_t dc = (rlc_dc_field_t)srslte_bit_pack(&ptr, 1); + + if(RLC_DC_FIELD_CONTROL_PDU == dc) + { + uint8_t cpt = srslte_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) + if(0 == cpt) + { + status->ack_sn = srslte_bit_pack(&ptr, 10); // 10 bits ACK_SN + ext1 = srslte_bit_pack(&ptr, 1); // 1 bits E1 + status->N_nack = 0; + while(ext1) + { + status->nacks[status->N_nack].nack_sn = srslte_bit_pack(&ptr, 10); + ext1 = srslte_bit_pack(&ptr, 1); // 1 bits E1 + ext2 = srslte_bit_pack(&ptr, 1); // 1 bits E2 + if(ext2) + { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = srslte_bit_pack(&ptr, 15); + status->nacks[status->N_nack].so_end = srslte_bit_pack(&ptr, 15); + } + status->N_nack++; + } + } + } +} + +void rlc_am_write_status_pdu(rlc_status_pdu_t *status, byte_buffer_t *pdu ) +{ + pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); +} + +int rlc_am_write_status_pdu(rlc_status_pdu_t *status, uint8_t *payload) +{ + uint32_t i; + uint8_t ext1; + bit_buffer_t tmp; + uint8_t *ptr = tmp.msg; + + srslte_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C + srslte_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) + srslte_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN + ext1 = (status->N_nack == 0) ? 0 : 1; + srslte_bit_unpack(ext1, &ptr, 1); // E1 + for(i=0;iN_nack;i++) + { + srslte_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN + ext1 = ((status->N_nack-1) == i) ? 0 : 1; + srslte_bit_unpack(ext1, &ptr, 1); // E1 + if(status->nacks[i].has_so) { + srslte_bit_unpack(1 , &ptr, 1); // E2 + srslte_bit_unpack(status->nacks[i].so_start , &ptr, 15); + srslte_bit_unpack(status->nacks[i].so_end , &ptr, 15); + }else{ + srslte_bit_unpack(0 , &ptr, 1); // E2 + } + } + + // Pad + tmp.N_bits = ptr - tmp.msg; + uint8_t n_pad = 8 - (tmp.N_bits%8); + srslte_bit_unpack(0, &ptr, n_pad); + tmp.N_bits = ptr - tmp.msg; + + // Pack bits + srslte_bit_pack_vector(tmp.msg, payload, tmp.N_bits); + return tmp.N_bits/8; +} + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t *header) +{ + uint32_t len = 2; // Fixed part is 2 bytes + if(header->rf) len += 2; // Segment header is 2 bytes + len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up + return len; +} + +uint32_t rlc_am_packed_length(rlc_status_pdu_t *status) +{ + uint32_t i; + uint32_t len_bits = 15; // Fixed part is 15 bits + for(i=0;iN_nack;i++) + { + if(status->nacks[i].has_so) { + len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end + }else{ + len_bits += 12; // 10 bits SN, 2 bits ext + } + } + + return (len_bits+7)/8; // Convert to bytes - integer rounding up +} + +bool rlc_am_is_control_pdu(byte_buffer_t *pdu) +{ + return rlc_am_is_control_pdu(pdu->msg); +} + +bool rlc_am_is_control_pdu(uint8_t *payload) +{ + return ((*(payload) >> 7) & 0x01) == RLC_DC_FIELD_CONTROL_PDU; +} + +bool rlc_am_is_pdu_segment(uint8_t *payload) +{ + return ((*(payload) >> 6) & 0x01) == 1; +} + +std::string rlc_am_to_string(rlc_status_pdu_t *status) +{ + std::stringstream ss; + ss << "ACK_SN = " << status->ack_sn; + ss << ", N_nack = " << status->N_nack; + if(status->N_nack > 0) + { + ss << ", NACK_SN = "; + for(int i=0; iN_nack; i++) + { + if(status->nacks[i].has_so) { + ss << "[" << status->nacks[i].nack_sn << " " << status->nacks[i].so_start \ + << ":" << status->nacks[i].so_end << "]"; + }else{ + ss << "[" << status->nacks[i].nack_sn << "]"; + } + } + } + return ss.str(); +} + +bool rlc_am_start_aligned(uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); +} + +bool rlc_am_end_aligned(uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); +} + +} // namespace srsue diff --git a/srslte/lib/upper/rlc_entity.cc b/srslte/lib/upper/rlc_entity.cc new file mode 100644 index 000000000..8313f9353 --- /dev/null +++ b/srslte/lib/upper/rlc_entity.cc @@ -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 diff --git a/srslte/lib/upper/rlc_tm.cc b/srslte/lib/upper/rlc_tm.cc new file mode 100644 index 000000000..a3d977dc9 --- /dev/null +++ b/srslte/lib/upper/rlc_tm.cc @@ -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 diff --git a/srslte/lib/upper/rlc_um.cc b/srslte/lib/upper/rlc_um.cc new file mode 100644 index 000000000..08b8a492e --- /dev/null +++ b/srslte/lib/upper/rlc_um.cc @@ -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::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::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; imsg[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; imsg[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 diff --git a/srslte/lib/upper/rrc.cc b/srslte/lib/upper/rrc.cc new file mode 100644 index 000000000..141b54a4a --- /dev/null +++ b/srslte/lib/upper/rrc.cc @@ -0,0 +1,1482 @@ +/** + * + * \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 +#include + +#include "upper/rrc.h" +#include +#include "common/security.h" +#include "common/bcd_helpers.h" + +#define TIMEOUT_RESYNC_REESTABLISH 100 + +using namespace srslte; + +namespace srsue{ + +rrc::rrc() + :state(RRC_STATE_IDLE) + ,drb_up(false) +{} + +static void liblte_rrc_handler(void *ctx, char *str) { + rrc *r = (rrc*) ctx; + r->liblte_rrc_log(str); +} + +void rrc::liblte_rrc_log(char* str) +{ + if (rrc_log) { + rrc_log->warning("[ASN]: %s\n", str); + } else { + printf("[ASN]: %s\n", str); + } +} + +void rrc::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_, + mac_interface_timers *mac_timers_, + srslte::log *rrc_log_) +{ + pool = byte_buffer_pool::get_instance(); + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + nas = nas_; + usim = usim_; + rrc_log = rrc_log_; + mac_timers = mac_timers_; + + pthread_mutex_init(&mutex, NULL); + + ue_category = SRSUE_UE_CATEGORY; + + transaction_id = 0; + + // Register logging handler with liblte_rrc + liblte_rrc_log_register_handler(this, liblte_rrc_handler); + + // Set default values for all layers + set_rrc_default(); + set_phy_default(); + set_mac_default(); +} + +void rrc::stop() +{} + +rrc_state_t rrc::get_state() +{ + return state; +} + +void rrc::set_ue_category(int category) +{ + if(category >= 1 && category <= 5) { + ue_category = category; + } else { + rrc_log->error("Unsupported UE category %d\n", category); + } +} + + +/******************************************************************************* + NAS interface +*******************************************************************************/ + +void rrc::write_sdu(uint32_t lcid, byte_buffer_t *sdu) +{ + rrc_log->info_hex(sdu->msg, sdu->N_bytes, "RX %s SDU", rb_id_text[lcid]); + + switch(state) + { + case RRC_STATE_COMPLETING_SETUP: + send_con_setup_complete(sdu); + break; + case RRC_STATE_RRC_CONNECTED: + send_ul_info_transfer(lcid, sdu); + break; + default: + rrc_log->error("SDU received from NAS while RRC state = %s", rrc_state_text[state]); + break; + } +} + +uint16_t rrc::get_mcc() +{ + if(sib1.N_plmn_ids > 0) + return sib1.plmn_id[0].id.mcc; + else + return 0; +} + +uint16_t rrc::get_mnc() +{ + if(sib1.N_plmn_ids > 0) + return sib1.plmn_id[0].id.mnc; + else + return 0; +} + +/******************************************************************************* + MAC interface +*******************************************************************************/ +/* Reception of PUCCH/SRS release procedure (Section 5.3.13) */ +void rrc::release_pucch_srs() +{ + // Apply default configuration for PUCCH (CQI and SR) and SRS (release) + set_phy_default_pucch_srs(); + + // Configure RX signals without pregeneration because default option is release + phy->configure_ul_params(true); + +} + +void rrc::ra_problem() { + radio_link_failure(); +} + +/******************************************************************************* + PHY interface +*******************************************************************************/ + +// Detection of physical layer problems (5.3.11.1) +void rrc::out_of_sync() +{ + if (!mac_timers->get(t311)->is_running() && !mac_timers->get(t310)->is_running()) { + n310_cnt++; + if (n310_cnt == N310) { + mac_timers->get(t310)->reset(); + mac_timers->get(t310)->run(); + n310_cnt = 0; + rrc_log->info("Detected %d out-of-sync from PHY. Starting T310 timer\n", N310); + } + } +} + +// Recovery of physical layer problems (5.3.11.2) +void rrc::in_sync() +{ + if (mac_timers->get(t310)->is_running()) { + n311_cnt++; + if (n311_cnt == N311) { + mac_timers->get(t310)->stop(); + n311_cnt = 0; + rrc_log->info("Detected %d in-sync from PHY. Stopping T310 timer\n", N311); + } + } +} + +/******************************************************************************* + GW interface +*******************************************************************************/ + +bool rrc::rrc_connected() +{ + return (RRC_STATE_RRC_CONNECTED == state); +} + +void rrc::rrc_connect() { + pthread_mutex_lock(&mutex); + if(RRC_STATE_IDLE == state) { + rrc_log->info("RRC in IDLE state - sending connection request.\n"); + state = RRC_STATE_WAIT_FOR_CON_SETUP; + send_con_request(); + } + pthread_mutex_unlock(&mutex); +} + +bool rrc::have_drb() +{ + return drb_up; +} + +/******************************************************************************* + PDCP interface +*******************************************************************************/ + +void rrc::write_pdu(uint32_t lcid, byte_buffer_t *pdu) +{ + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "TX %s PDU", rb_id_text[lcid]); + rrc_log->info("TX PDU Stack latency: %ld us\n", pdu->get_latency_us()); + + switch(lcid) + { + case RB_ID_SRB0: + parse_dl_ccch(pdu); + break; + case RB_ID_SRB1: + case RB_ID_SRB2: + parse_dl_dcch(lcid, pdu); + break; + default: + rrc_log->error("TX PDU with invalid bearer id: %s", lcid); + break; + } + +} + +void rrc::write_pdu_bcch_bch(byte_buffer_t *pdu) +{ + // Unpack the MIB + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "BCCH BCH message received."); + rrc_log->info("BCCH BCH message Stack latency: %ld us\n", pdu->get_latency_us()); + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + liblte_rrc_unpack_bcch_bch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &mib); + rrc_log->info("MIB received BW=%s MHz\n", liblte_rrc_dl_bandwidth_text[mib.dl_bw]); + rrc_log->console("MIB received BW=%s MHz\n", liblte_rrc_dl_bandwidth_text[mib.dl_bw]); + + // Start the SIB search state machine + state = RRC_STATE_SIB1_SEARCH; + pthread_create(&sib_search_thread, NULL, &rrc::start_sib_thread, this); +} + +void rrc::write_pdu_bcch_dlsch(byte_buffer_t *pdu) +{ + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "BCCH DLSCH message received."); + rrc_log->info("BCCH DLSCH message Stack latency: %ld us\n", pdu->get_latency_us()); + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT dlsch_msg; + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + liblte_rrc_unpack_bcch_dlsch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dlsch_msg); + + if (dlsch_msg.N_sibs > 0) { + if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1 == dlsch_msg.sibs[0].sib_type && RRC_STATE_SIB1_SEARCH == state) { + // Handle SIB1 + memcpy(&sib1, &dlsch_msg.sibs[0].sib.sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + rrc_log->info("SIB1 received, CellID=%d, si_window=%d, sib2_period=%d\n", + sib1.cell_id&0xfff, + liblte_rrc_si_window_length_num[sib1.si_window_length], + liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]); + std::stringstream ss; + for(int i=0;iset_config_tdd(&dlsch_msg.sibs[0].sib.sib1.tdd_cnfg); + } + + rrc_log->console("SIB1 received, CellID=%d, %s\n", + sib1.cell_id&0xfff, + ss.str().c_str()); + + state = RRC_STATE_SIB2_SEARCH; + mac->bcch_stop_rx(); + //TODO: Use all SIB1 info + + } else if (LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2 == dlsch_msg.sibs[0].sib_type && RRC_STATE_SIB2_SEARCH == state) { + // Handle SIB2 + memcpy(&sib2, &dlsch_msg.sibs[0].sib.sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + rrc_log->console("SIB2 received\n"); + rrc_log->info("SIB2 received\n"); + state = RRC_STATE_WAIT_FOR_CON_SETUP; + mac->bcch_stop_rx(); + apply_sib2_configs(); + send_con_request(); + } + } +} + +void rrc::write_pdu_pcch(byte_buffer_t *pdu) +{ + if (pdu->N_bytes > 0 && pdu->N_bytes < SRSUE_MAX_BUFFER_SIZE_BITS) { + rrc_log->info_hex(pdu->msg, pdu->N_bytes, "PCCH message received %d bytes\n", pdu->N_bytes); + rrc_log->info("PCCH message Stack latency: %ld us\n", pdu->get_latency_us()); + rrc_log->console("PCCH message received %d bytes\n", pdu->N_bytes); + + LIBLTE_RRC_PCCH_MSG_STRUCT pcch_msg; + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + liblte_rrc_unpack_pcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &pcch_msg); + + if (pcch_msg.paging_record_list_size > LIBLTE_RRC_MAX_PAGE_REC) { + pcch_msg.paging_record_list_size = LIBLTE_RRC_MAX_PAGE_REC; + } + + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + if(!nas->get_s_tmsi(&s_tmsi)) { + rrc_log->info("No S-TMSI present in NAS\n"); + return; + } + + LIBLTE_RRC_S_TMSI_STRUCT *s_tmsi_paged; + for (int i=0;iinfo("Received paging (%d/%d) for UE 0x%x\n", i+1, pcch_msg.paging_record_list_size, + pcch_msg.paging_record_list[i].ue_identity.s_tmsi); + rrc_log->console("Received paging (%d/%d) for UE 0x%x\n", i+1, pcch_msg.paging_record_list_size, + pcch_msg.paging_record_list[i].ue_identity.s_tmsi); + if(s_tmsi.mmec == s_tmsi_paged->mmec && s_tmsi.m_tmsi == s_tmsi_paged->m_tmsi) { + rrc_log->info("S-TMSI match in paging message\n"); + rrc_log->console("S-TMSI match in paging message\n"); + mac->pcch_stop_rx(); + if(RRC_STATE_IDLE == state) { + rrc_log->info("RRC in IDLE state - sending connection request.\n"); + state = RRC_STATE_WAIT_FOR_CON_SETUP; + send_con_request(); + } + } + } + } +} + +/******************************************************************************* + RLC interface +*******************************************************************************/ + +void rrc::max_retx_attempted() +{ + //TODO: Handle the radio link failure + rrc_log->warning("Max RLC reTx attempted\n"); + //radio_link_failure(); +} + +/******************************************************************************* + Senders +*******************************************************************************/ + +void rrc::send_con_request() +{ + rrc_log->debug("Preparing RRC Connection Request\n"); + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + + // Prepare ConnectionRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ; + if(nas->get_s_tmsi(&s_tmsi)) { + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI; + ul_ccch_msg.msg.rrc_con_req.ue_id.s_tmsi = s_tmsi; + } else { + ul_ccch_msg.msg.rrc_con_req.ue_id_type = LIBLTE_RRC_CON_REQ_UE_ID_TYPE_RANDOM_VALUE; + ul_ccch_msg.msg.rrc_con_req.ue_id.random = 1000; + } + ul_ccch_msg.msg.rrc_con_req.cause = LIBLTE_RRC_CON_REQ_EST_CAUSE_MO_SIGNALLING; + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + byte_buffer_t *pdcp_buf = pool->allocate(); + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits/8; + pdcp_buf->set_timestamp(); + + // Set UE contention resolution ID in MAC + uint64_t uecri=0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = 6; + for (int i=0;imsg[i]; + } + rrc_log->debug("Setting UE contention resolution ID: %d\n", uecri); + + mac->set_contention_id(uecri); + + rrc_log->info("Sending RRC Connection Request on SRB0\n"); + state = RRC_STATE_WAIT_FOR_CON_SETUP; + pdcp->write_sdu(RB_ID_SRB0, pdcp_buf); +} + + +/* RRC connection re-establishment procedure (5.3.7) */ +void rrc::send_con_restablish_request() +{ + + srslte_cell_t cell; + phy->get_current_cell(&cell); + + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + LIBLTE_RRC_S_TMSI_STRUCT s_tmsi; + + // Compute shortMAC-I + uint8_t varShortMAC[128], varShortMAC_packed[16]; + bzero(varShortMAC, 128); + bzero(varShortMAC_packed, 16); + uint8_t *msg_ptr = varShortMAC; + liblte_rrc_pack_cell_identity_ie(0x1a2d0, &msg_ptr); + liblte_rrc_pack_phys_cell_id_ie(cell.id, &msg_ptr); + mac_interface_rrc::ue_rnti_t ue_rnti; + mac->get_rntis(&ue_rnti); + liblte_rrc_pack_c_rnti_ie(ue_rnti.crnti, &msg_ptr); + srslte_bit_pack_vector(varShortMAC, varShortMAC_packed, msg_ptr - varShortMAC); + + uint8_t mac_key[4]; + security_128_eia2(&k_rrc_int[16], + 1, + 1, + 1, + varShortMAC_packed, + 7, + mac_key); + + mac_interface_rrc::ue_rnti_t uernti; + mac->get_rntis(&uernti); + + // Prepare ConnectionRestalishmentRequest packet + ul_ccch_msg.msg_type = LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti = uernti.crnti; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.phys_cell_id = cell.id; + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.short_mac_i = mac_key[2]<<8 | mac_key[3]; + ul_ccch_msg.msg.rrc_con_reest_req.cause = LIBLTE_RRC_CON_REEST_REQ_CAUSE_OTHER_FAILURE; + liblte_rrc_pack_ul_ccch_msg(&ul_ccch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + rrc_log->info("Initiating RRC Connection Reestablishment Procedure\n"); + rrc_log->console("RRC Connection Reestablishment\n"); + mac_timers->get(t310)->stop(); + mac_timers->get(t311)->reset(); + mac_timers->get(t311)->run(); + + set_phy_default(); + mac->reset(); + + // FIXME: Cell selection should be different?? + phy->resync_sfn(); + + // Wait for cell re-synchronization + uint32_t timeout_cnt = 0; + while(!phy->status_is_sync() && timeout_cnt < TIMEOUT_RESYNC_REESTABLISH){ + usleep(10000); + timeout_cnt++; + } + mac_timers->get(t301)->reset(); + mac_timers->get(t301)->run(); + mac_timers->get(t311)->stop(); + rrc_log->info("Cell Selection finished. Initiating transmission of RRC Connection Reestablishment Request\n"); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + byte_buffer_t *pdcp_buf = pool->allocate(); + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits/8; + + // Set UE contention resolution ID in MAC + uint64_t uecri=0; + uint8_t *ue_cri_ptr = (uint8_t*) &uecri; + uint32_t nbytes = 6; + for (int i=0;imsg[i]; + } + rrc_log->debug("Setting UE contention resolution ID: %d\n", uecri); + mac->set_contention_id(uecri); + + rrc_log->info("Sending RRC Connection Resetablishment Request on SRB0\n"); + state = RRC_STATE_WAIT_FOR_CON_SETUP; + pdcp->write_sdu(RB_ID_SRB0, pdcp_buf); +} + + +void rrc::send_con_restablish_complete() +{ + rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + // Prepare ConnectionSetupComplete packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_REEST_COMPLETE; + ul_dcch_msg.msg.rrc_con_reest_complete.rrc_transaction_id = transaction_id; + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + byte_buffer_t *pdcp_buf = pool->allocate(); + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits/8; + + state = RRC_STATE_RRC_CONNECTED; + rrc_log->console("RRC Connected\n"); + rrc_log->info("Sending RRC Connection Reestablishment Complete\n"); + pdcp->write_sdu(RB_ID_SRB1, pdcp_buf); +} + +void rrc::send_con_setup_complete(byte_buffer_t *nas_msg) +{ + rrc_log->debug("Preparing RRC Connection Setup Complete\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + // Prepare ConnectionSetupComplete packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE; + ul_dcch_msg.msg.rrc_con_setup_complete.registered_mme_present = false; + ul_dcch_msg.msg.rrc_con_setup_complete.rrc_transaction_id = transaction_id; + ul_dcch_msg.msg.rrc_con_setup_complete.selected_plmn_id = 1; + memcpy(ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.msg, nas_msg->msg, nas_msg->N_bytes); + ul_dcch_msg.msg.rrc_con_setup_complete.dedicated_info_nas.N_bytes = nas_msg->N_bytes; + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + byte_buffer_t *pdcp_buf = pool->allocate(); + srslte_bit_pack_vector(bit_buf.msg, pdcp_buf->msg, bit_buf.N_bits); + pdcp_buf->N_bytes = bit_buf.N_bits/8; + pdcp_buf->set_timestamp(); + + state = RRC_STATE_RRC_CONNECTED; + rrc_log->console("RRC Connected\n"); + rrc_log->info("Sending RRC Connection Setup Complete\n"); + pdcp->write_sdu(RB_ID_SRB1, pdcp_buf); +} + +void rrc::send_ul_info_transfer(uint32_t lcid, byte_buffer_t *sdu) +{ + rrc_log->debug("Preparing RX Info Transfer\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + // Prepare RX INFO packet + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER; + ul_dcch_msg.msg.ul_info_transfer.dedicated_info_type = LIBLTE_RRC_UL_INFORMATION_TRANSFER_TYPE_NAS; + memcpy(ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, sdu->msg, sdu->N_bytes); + ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes = sdu->N_bytes; + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Reset and reuse sdu buffer + byte_buffer_t *pdu = sdu; + pdu->reset(); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); + pdu->N_bytes = bit_buf.N_bits/8; + pdu->set_timestamp(); + pdu->set_timestamp(); + + rrc_log->info("Sending RX Info Transfer\n"); + pdcp->write_sdu(lcid, pdu); +} + +void rrc::send_security_mode_complete(uint32_t lcid, byte_buffer_t *pdu) +{ + rrc_log->debug("Preparing Security Mode Complete\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE; + ul_dcch_msg.msg.security_mode_complete.rrc_transaction_id = transaction_id; + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); + pdu->N_bytes = bit_buf.N_bits/8; + pdu->set_timestamp(); + + rrc_log->info("Sending Security Mode Complete\n"); + pdcp->write_sdu(lcid, pdu); +} + +void rrc::send_rrc_con_reconfig_complete(uint32_t lcid, byte_buffer_t *pdu) +{ + rrc_log->debug("Preparing RRC Connection Reconfig Complete\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE; + ul_dcch_msg.msg.rrc_con_reconfig_complete.rrc_transaction_id = transaction_id; + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); + pdu->N_bytes = bit_buf.N_bits/8; + pdu->set_timestamp(); + + rrc_log->info("Sending RRC Connection Reconfig Complete\n"); + pdcp->write_sdu(lcid, pdu); +} + +void rrc::enable_capabilities() +{ + bool enable_ul_64 = ue_category>=5 && sib2.rr_config_common_sib.pusch_cnfg.enable_64_qam; + rrc_log->info("%s 64QAM PUSCH\n", enable_ul_64?"Enabling":"Disabling"); + phy->set_config_64qam_en(enable_ul_64); +} + +void rrc::send_rrc_ue_cap_info(uint32_t lcid, byte_buffer_t *pdu) +{ + rrc_log->debug("Preparing UE Capability Info\n"); + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + + ul_dcch_msg.msg_type = LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO; + ul_dcch_msg.msg.ue_capability_info.rrc_transaction_id = transaction_id; + + LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *info = &ul_dcch_msg.msg.ue_capability_info; + info->N_ue_caps = 1; + info->ue_capability_rat[0].rat_type = LIBLTE_RRC_RAT_TYPE_EUTRA; + + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *cap = &info->ue_capability_rat[0].eutra_capability; + cap->access_stratum_release = LIBLTE_RRC_ACCESS_STRATUM_RELEASE_REL8; + cap->ue_category = ue_category; + + cap->pdcp_params.max_rohc_ctxts_present = false; + cap->pdcp_params.supported_rohc_profiles[0] = false; + cap->pdcp_params.supported_rohc_profiles[1] = false; + cap->pdcp_params.supported_rohc_profiles[2] = false; + cap->pdcp_params.supported_rohc_profiles[3] = false; + cap->pdcp_params.supported_rohc_profiles[4] = false; + cap->pdcp_params.supported_rohc_profiles[5] = false; + cap->pdcp_params.supported_rohc_profiles[6] = false; + cap->pdcp_params.supported_rohc_profiles[7] = false; + cap->pdcp_params.supported_rohc_profiles[8] = false; + + cap->phy_params.specific_ref_sigs_supported = false; + cap->phy_params.tx_antenna_selection_supported = false; + + //TODO: Generate this from user input? + cap->rf_params.N_supported_band_eutras = 3; + cap->rf_params.supported_band_eutra[0].band_eutra = 3; + cap->rf_params.supported_band_eutra[0].half_duplex = false; + cap->rf_params.supported_band_eutra[1].band_eutra = 7; + cap->rf_params.supported_band_eutra[1].half_duplex = false; + cap->rf_params.supported_band_eutra[2].band_eutra = 20; + cap->rf_params.supported_band_eutra[2].half_duplex = false; + + cap->meas_params.N_band_list_eutra = 3; + cap->meas_params.band_list_eutra[0].N_inter_freq_need_for_gaps = 3; + cap->meas_params.band_list_eutra[0].inter_freq_need_for_gaps[0] = true; + cap->meas_params.band_list_eutra[0].inter_freq_need_for_gaps[1] = true; + cap->meas_params.band_list_eutra[0].inter_freq_need_for_gaps[2] = true; + cap->meas_params.band_list_eutra[1].N_inter_freq_need_for_gaps = 3; + cap->meas_params.band_list_eutra[1].inter_freq_need_for_gaps[0] = true; + cap->meas_params.band_list_eutra[1].inter_freq_need_for_gaps[1] = true; + cap->meas_params.band_list_eutra[1].inter_freq_need_for_gaps[2] = true; + cap->meas_params.band_list_eutra[2].N_inter_freq_need_for_gaps = 3; + cap->meas_params.band_list_eutra[2].inter_freq_need_for_gaps[0] = true; + cap->meas_params.band_list_eutra[2].inter_freq_need_for_gaps[1] = true; + cap->meas_params.band_list_eutra[2].inter_freq_need_for_gaps[2] = true; + + cap->feature_group_indicator_present = true; + cap->feature_group_indicator = 0x62001000; + cap->inter_rat_params.utra_fdd_present = false; + cap->inter_rat_params.utra_tdd128_present = false; + cap->inter_rat_params.utra_tdd384_present = false; + cap->inter_rat_params.utra_tdd768_present = false; + cap->inter_rat_params.geran_present = false; + cap->inter_rat_params.cdma2000_hrpd_present = false; + cap->inter_rat_params.cdma2000_1xrtt_present = false; + + liblte_rrc_pack_ul_dcch_msg(&ul_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf); + + // Byte align and pack the message bits for PDCP + if((bit_buf.N_bits % 8) != 0) + { + for(int i=0; i<8-(bit_buf.N_bits % 8); i++) + bit_buf.msg[bit_buf.N_bits + i] = 0; + bit_buf.N_bits += 8 - (bit_buf.N_bits % 8); + } + srslte_bit_pack_vector(bit_buf.msg, pdu->msg, bit_buf.N_bits); + pdu->N_bytes = bit_buf.N_bits/8; + pdu->set_timestamp(); + + rrc_log->info("Sending UE Capability Info\n"); + pdcp->write_sdu(lcid, pdu); +} + +/******************************************************************************* + Parsers +*******************************************************************************/ + +void rrc::parse_dl_ccch(byte_buffer_t *pdu) +{ + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + pool->deallocate(pdu); + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + liblte_rrc_unpack_dl_ccch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dl_ccch_msg); + + rrc_log->info("SRB0 - Received %s\n", + liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg.msg_type]); + + switch(dl_ccch_msg.msg_type) + { + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REJ: + rrc_log->info("Connection Reject received. Wait time: %d\n", + dl_ccch_msg.msg.rrc_con_rej.wait_time); + state = RRC_STATE_IDLE; + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP: + rrc_log->info("Connection Setup received\n"); + transaction_id = dl_ccch_msg.msg.rrc_con_setup.rrc_transaction_id; + handle_con_setup(&dl_ccch_msg.msg.rrc_con_setup); + rrc_log->info("Notifying NAS of connection setup\n"); + state = RRC_STATE_COMPLETING_SETUP; + nas->notify_connection_setup(); + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST: + rrc_log->info("Connection Reestablishment received\n"); + rrc_log->console("Reestablishment OK\n"); + transaction_id = dl_ccch_msg.msg.rrc_con_reest.rrc_transaction_id; + handle_con_reest(&dl_ccch_msg.msg.rrc_con_reest); + break; + case LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ: + rrc_log->info("Connection Reestablishment Reject received\n"); + rrc_log->console("Reestablishment Reject\n"); + usleep(50000); + rrc_connection_release(); + break; + default: + break; + } +} + +void rrc::parse_dl_dcch(uint32_t lcid, byte_buffer_t *pdu) +{ + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + liblte_rrc_unpack_dl_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &dl_dcch_msg); + + rrc_log->info("%s - Received %s\n", + rb_id_text[lcid], + liblte_rrc_dl_dcch_msg_type_text[dl_dcch_msg.msg_type]); + + // Reset and reuse pdu buffer if possible + pdu->reset(); + + switch(dl_dcch_msg.msg_type) + { + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER: + memcpy(pdu->msg, dl_dcch_msg.msg.dl_info_transfer.dedicated_info.msg, dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes); + pdu->N_bytes = dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes; + nas->write_pdu(lcid, pdu); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND: + transaction_id = dl_dcch_msg.msg.security_mode_cmd.rrc_transaction_id; + + cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)dl_dcch_msg.msg.security_mode_cmd.sec_algs.cipher_alg; + integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)dl_dcch_msg.msg.security_mode_cmd.sec_algs.int_alg; + + // Configure PDCP for security + usim->generate_as_keys(nas->get_ul_count(), k_rrc_enc, k_rrc_int, k_up_enc, k_up_int, cipher_algo, integ_algo); + pdcp->config_security(lcid, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); + send_security_mode_complete(lcid, pdu); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG: + transaction_id = dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id; + handle_rrc_con_reconfig(lcid, &dl_dcch_msg.msg.rrc_con_reconfig, pdu); + break; + case LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY: + transaction_id = dl_dcch_msg.msg.ue_cap_enquiry.rrc_transaction_id; + for(int i=0; iinfo("Timer T310 expired: Radio Link Failure\n"); + radio_link_failure(); + } else if (timeout_id == t311) { + rrc_log->info("Timer T311 expired: Going to RRC IDLE\n"); + rrc_connection_release(); + } else if (timeout_id == t301) { + rrc_log->info("Timer T301 expired: Going to RRC IDLE\n"); + rrc_connection_release(); + } else if (timeout_id == safe_reset_timer) { + reset_ue(); + } else { + rrc_log->error("Timeout from unknown timer id %d\n", timeout_id); + } +} + +/******************************************************************************* + Helpers +*******************************************************************************/ + +void rrc::reset_ue() { + phy->reset(); + mac->reset(); + pdcp->reset(); + rlc->reset(); + mac->pcch_start_rx(); + mac_timers->get(safe_reset_timer)->stop(); + mac_timers->get(safe_reset_timer)->reset(); + rrc_log->console("RRC Connection released.\n"); +} + +void rrc::rrc_connection_release() { + pthread_mutex_lock(&mutex); + drb_up = false; + state = RRC_STATE_IDLE; + set_phy_default(); + set_mac_default(); + mac_timers->get(t311)->run(); + mac_timers->get(t310)->stop(); + mac_timers->get(t311)->stop(); + mac_timers->get(safe_reset_timer)->stop(); + mac_timers->get(safe_reset_timer)->reset(); + mac_timers->get(safe_reset_timer)->run(); + pthread_mutex_unlock(&mutex); +} + +void rrc::test_con_restablishment() +{ + printf("Testing connection Reestablishment\n"); + send_con_restablish_request(); +} + +/* Detection of radio link failure (5.3.11.3) */ +void rrc::radio_link_failure() { + // TODO: Generate and store failure report + + rrc_log->warning("Detected Radio-Link Failure\n"); + rrc_log->console("Warning: Detected Radio-Link Failure\n"); + if (state != RRC_STATE_RRC_CONNECTED) { + rrc_connection_release(); + } else { + send_con_restablish_request(); + } +} + +void* rrc::start_sib_thread(void *rrc_) +{ + rrc *r = (rrc*)rrc_; + r->sib_search(); + return NULL; +} + +void rrc::sib_search() +{ + bool searching = true; + uint32_t tti ; + uint32_t si_win_start, si_win_len; + uint16_t period; + uint32_t nof_sib1_trials = 0; + const int SIB1_SEARCH_TIMEOUT = 30; + + while(searching) + { + switch(state) + { + case RRC_STATE_SIB1_SEARCH: + // Instruct MAC to look for SIB1 + while(!phy->status_is_sync()){ + usleep(50000); + } + usleep(10000); + tti = mac->get_current_tti(); + si_win_start = sib_start_tti(tti, 2, 5); + mac->bcch_start_rx(si_win_start, 1); + rrc_log->debug("Instructed MAC to search for SIB1, win_start=%d, win_len=%d\n", + si_win_start, 1); + nof_sib1_trials++; + if (nof_sib1_trials >= SIB1_SEARCH_TIMEOUT) { + rrc_log->info("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + rrc_log->console("Timeout while searching for SIB1. Resynchronizing SFN...\n"); + phy->resync_sfn(); + nof_sib1_trials = 0; + } + break; + case RRC_STATE_SIB2_SEARCH: + // Instruct MAC to look for SIB2 + usleep(10000); + tti = mac->get_current_tti(); + period = liblte_rrc_si_periodicity_num[sib1.sched_info[0].si_periodicity]; + si_win_start = sib_start_tti(tti, period, 0); + si_win_len = liblte_rrc_si_window_length_num[sib1.si_window_length]; + + mac->bcch_start_rx(si_win_start, si_win_len); + rrc_log->debug("Instructed MAC to search for SIB2, win_start=%d, win_len=%d\n", + si_win_start, si_win_len); + + break; + default: + searching = false; + break; + } + usleep(100000); + } +} + +// Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message +uint32_t rrc::sib_start_tti(uint32_t tti, uint32_t period, uint32_t x) { + return (period*10*(1+tti/(period*10))+x)%10240; // the 1 means next opportunity +} + +void rrc::apply_sib2_configs() +{ + if(RRC_STATE_WAIT_FOR_CON_SETUP != state){ + rrc_log->error("State must be RRC_STATE_WAIT_FOR_CON_SETUP to handle SIB2. Actual state: %s\n", + rrc_state_text[state]); + return; + } + + // Apply RACH timeAlginmentTimer configuration + mac_interface_rrc::mac_cfg_t cfg; + mac->get_config(&cfg); + cfg.main.time_alignment_timer = sib2.time_alignment_timer; + memcpy(&cfg.rach, &sib2.rr_config_common_sib.rach_cnfg, sizeof(LIBLTE_RRC_RACH_CONFIG_COMMON_STRUCT)); + cfg.prach_config_index = sib2.rr_config_common_sib.prach_cnfg.root_sequence_index; + mac->set_config(&cfg); + + rrc_log->info("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms\n", + liblte_rrc_number_of_ra_preambles_num[sib2.rr_config_common_sib.rach_cnfg.num_ra_preambles], + liblte_rrc_ra_response_window_size_num[sib2.rr_config_common_sib.rach_cnfg.ra_resp_win_size], + liblte_rrc_mac_contention_resolution_timer_num[sib2.rr_config_common_sib.rach_cnfg.mac_con_res_timer]); + + // Apply PHY RR Config Common + phy_interface_rrc::phy_cfg_common_t common; + memcpy(&common.pdsch_cnfg, &sib2.rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pusch_cnfg, &sib2.rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.pucch_cnfg, &sib2.rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&common.ul_pwr_ctrl, &sib2.rr_config_common_sib.ul_pwr_ctrl, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_COMMON_STRUCT)); + memcpy(&common.prach_cnfg, &sib2.rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + if (sib2.rr_config_common_sib.srs_ul_cnfg.present) { + memcpy(&common.srs_ul_cnfg, &sib2.rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + } else { + // default is release + common.srs_ul_cnfg.present = false; + } + phy->set_config_common(&common); + + phy->configure_ul_params(); + + rrc_log->info("Set PUSCH ConfigCommon: HopOffset=%d, RSGroup=%d, RSNcs=%d, N_sb=%d\n", + sib2.rr_config_common_sib.pusch_cnfg.pusch_hopping_offset, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch, + sib2.rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift, + sib2.rr_config_common_sib.pusch_cnfg.n_sb); + + rrc_log->info("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n", + liblte_rrc_delta_pucch_shift_num[sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift], + sib2.rr_config_common_sib.pucch_cnfg.n_cs_an, + sib2.rr_config_common_sib.pucch_cnfg.n1_pucch_an, + sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi); + + rrc_log->info("Set PRACH ConfigCommon: SeqIdx=%d, HS=%s, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n", + sib2.rr_config_common_sib.prach_cnfg.root_sequence_index, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag?"yes":"no", + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config, + sib2.rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index); + + rrc_log->info("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, ACKNACK=%s\n", + liblte_rrc_srs_bw_config_num[sib2.rr_config_common_sib.srs_ul_cnfg.bw_cnfg], + liblte_rrc_srs_subfr_config_num[sib2.rr_config_common_sib.srs_ul_cnfg.subfr_cnfg], + sib2.rr_config_common_sib.srs_ul_cnfg.ack_nack_simul_tx?"yes":"no"); + + mac_timers->get(t301)->set(this, liblte_rrc_t301_num[sib2.ue_timers_and_constants.t301]); + mac_timers->get(t310)->set(this, liblte_rrc_t310_num[sib2.ue_timers_and_constants.t310]); + mac_timers->get(t311)->set(this, liblte_rrc_t311_num[sib2.ue_timers_and_constants.t311]); + N310 = liblte_rrc_n310_num[sib2.ue_timers_and_constants.n310]; + N311 = liblte_rrc_n311_num[sib2.ue_timers_and_constants.n311]; + + rrc_log->info("Set Constants and Timers: N310=%d, N311=%d, t301=%d, t310=%d, t311=%d\n", + N310, N311, mac_timers->get(t301)->get_timeout(), + mac_timers->get(t310)->get_timeout(), mac_timers->get(t311)->get_timeout()); + +} + + // Go through all information elements and apply defaults (9.2.4) if not defined +void rrc::apply_phy_config_dedicated(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cnfg, bool apply_defaults) +{ + // Get current configuration + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *current_cfg; + phy_interface_rrc::phy_cfg_t c; + phy->get_config(&c); + current_cfg = &c.dedicated; + + if(phy_cnfg->pucch_cnfg_ded_present) { + memcpy(¤t_cfg->pucch_cnfg_ded, &phy_cnfg->pucch_cnfg_ded, sizeof(LIBLTE_RRC_PUCCH_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->pucch_cnfg_ded.tdd_ack_nack_feedback_mode = LIBLTE_RRC_TDD_ACK_NACK_FEEDBACK_MODE_BUNDLING; + current_cfg->pucch_cnfg_ded.ack_nack_repetition_setup_present = false; + } + if(phy_cnfg->pusch_cnfg_ded_present) { + memcpy(¤t_cfg->pusch_cnfg_ded, &phy_cnfg->pusch_cnfg_ded, sizeof(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->pusch_cnfg_ded.beta_offset_ack_idx = 10; + current_cfg->pusch_cnfg_ded.beta_offset_ri_idx = 12; + current_cfg->pusch_cnfg_ded.beta_offset_cqi_idx = 15; + } + if(phy_cnfg->ul_pwr_ctrl_ded_present) { + memcpy(¤t_cfg->ul_pwr_ctrl_ded, &phy_cnfg->ul_pwr_ctrl_ded, sizeof(LIBLTE_RRC_UL_POWER_CONTROL_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->ul_pwr_ctrl_ded.p0_ue_pusch = 0; + current_cfg->ul_pwr_ctrl_ded.delta_mcs_en = LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + current_cfg->ul_pwr_ctrl_ded.accumulation_en = true; + current_cfg->ul_pwr_ctrl_ded.p0_ue_pucch = 0; + current_cfg->ul_pwr_ctrl_ded.p_srs_offset = 7; + current_cfg->ul_pwr_ctrl_ded.filter_coeff = LIBLTE_RRC_FILTER_COEFFICIENT_FC4; + current_cfg->ul_pwr_ctrl_ded.filter_coeff_present = true; + } + if(phy_cnfg->tpc_pdcch_cnfg_pucch_present) { + memcpy(¤t_cfg->tpc_pdcch_cnfg_pucch, &phy_cnfg->tpc_pdcch_cnfg_pucch, sizeof(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT)); + } else if (apply_defaults) { + current_cfg->tpc_pdcch_cnfg_pucch.setup_present = false; + } + if(phy_cnfg->tpc_pdcch_cnfg_pusch_present) { + memcpy(¤t_cfg->tpc_pdcch_cnfg_pusch, &phy_cnfg->tpc_pdcch_cnfg_pusch, sizeof(LIBLTE_RRC_TPC_PDCCH_CONFIG_STRUCT)); + } else { + current_cfg->tpc_pdcch_cnfg_pusch.setup_present = false; + } + if(phy_cnfg->cqi_report_cnfg_present) { + if (phy_cnfg->cqi_report_cnfg.report_periodic_present) { + memcpy(¤t_cfg->cqi_report_cnfg.report_periodic, &phy_cnfg->cqi_report_cnfg.report_periodic, sizeof(LIBLTE_RRC_CQI_REPORT_PERIODIC_STRUCT)); + current_cfg->cqi_report_cnfg.report_periodic_setup_present = phy_cnfg->cqi_report_cnfg.report_periodic_setup_present; + } else if (apply_defaults) { + current_cfg->cqi_report_cnfg.report_periodic_setup_present = false; + } + if (phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present) { + current_cfg->cqi_report_cnfg.report_mode_aperiodic = phy_cnfg->cqi_report_cnfg.report_mode_aperiodic; + current_cfg->cqi_report_cnfg.report_mode_aperiodic_present = phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present; + } else if (apply_defaults) { + current_cfg->cqi_report_cnfg.report_mode_aperiodic_present = false; + } + current_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = phy_cnfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset; + } + if(phy_cnfg->srs_ul_cnfg_ded_present && phy_cnfg->srs_ul_cnfg_ded.setup_present) { + memcpy(¤t_cfg->srs_ul_cnfg_ded, &phy_cnfg->srs_ul_cnfg_ded, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->srs_ul_cnfg_ded.setup_present = false; + } + if(phy_cnfg->antenna_info_present) { + if (!phy_cnfg->antenna_info_default_value) { + if(phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_1 && + phy_cnfg->antenna_info_explicit_value.tx_mode != LIBLTE_RRC_TRANSMISSION_MODE_2) { + rrc_log->error("Transmission mode TM%s not currently supported by srsUE\n", liblte_rrc_transmission_mode_text[phy_cnfg->antenna_info_explicit_value.tx_mode]); + } + memcpy(¤t_cfg->antenna_info_explicit_value, &phy_cnfg->antenna_info_explicit_value, sizeof(LIBLTE_RRC_ANTENNA_INFO_DEDICATED_STRUCT)); + } else if (apply_defaults) { + current_cfg->antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_2; + current_cfg->antenna_info_explicit_value.codebook_subset_restriction_present = false; + current_cfg->antenna_info_explicit_value.ue_tx_antenna_selection_setup_present = false; + } + } else if (apply_defaults) { + current_cfg->antenna_info_explicit_value.tx_mode = LIBLTE_RRC_TRANSMISSION_MODE_2; + current_cfg->antenna_info_explicit_value.codebook_subset_restriction_present = false; + current_cfg->antenna_info_explicit_value.ue_tx_antenna_selection_setup_present = false; + } + if(phy_cnfg->sched_request_cnfg_present) { + memcpy(¤t_cfg->sched_request_cnfg, &phy_cnfg->sched_request_cnfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + } else if (apply_defaults) { + current_cfg->sched_request_cnfg.setup_present = false; + } + if(phy_cnfg->pdsch_cnfg_ded_present) { + current_cfg->pdsch_cnfg_ded = phy_cnfg->pdsch_cnfg_ded; + } else if (apply_defaults) { + current_cfg->pdsch_cnfg_ded = LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_0; + } + + if (phy_cnfg->cqi_report_cnfg_present) { + if (phy_cnfg->cqi_report_cnfg.report_periodic_present) { + rrc_log->info("Set cqi-PUCCH-ResourceIndex=%d, cqi-pmi-ConfigIndex=%d, cqi-FormatIndicatorPeriodic=%s\n", + current_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx, + current_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + liblte_rrc_cqi_format_indicator_periodic_text[current_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic]); + } + if (phy_cnfg->cqi_report_cnfg.report_mode_aperiodic_present) { + rrc_log->info("Set cqi-ReportModeAperiodic=%s\n", + liblte_rrc_cqi_report_mode_aperiodic_text[current_cfg->cqi_report_cnfg.report_mode_aperiodic]); + } + + } + + if (phy_cnfg->sched_request_cnfg_present) { + rrc_log->info("Set PHY config ded: SR-n_pucch=%d, SR-ConfigIndex=%d, SR-TransMax=%d\n", + current_cfg->sched_request_cnfg.sr_pucch_resource_idx, + current_cfg->sched_request_cnfg.sr_cnfg_idx, + liblte_rrc_dsr_trans_max_num[current_cfg->sched_request_cnfg.dsr_trans_max]); + } + + if (current_cfg->srs_ul_cnfg_ded_present) { + rrc_log->info("Set PHY config ded: SRS-ConfigIndex=%d, SRS-bw=%s, SRS-Nrcc=%d, SRS-hop=%s, SRS-Ncs=%s\n", + current_cfg->srs_ul_cnfg_ded.srs_cnfg_idx, + liblte_rrc_srs_bandwidth_text[current_cfg->srs_ul_cnfg_ded.srs_bandwidth], + current_cfg->srs_ul_cnfg_ded.freq_domain_pos, + liblte_rrc_srs_hopping_bandwidth_text[current_cfg->srs_ul_cnfg_ded.srs_hopping_bandwidth], + liblte_rrc_cyclic_shift_text[current_cfg->srs_ul_cnfg_ded.cyclic_shift]); + } + + phy->set_config_dedicated(current_cfg); + + // Apply changes to PHY + phy->configure_ul_params(); + +} + +void rrc::apply_mac_config_dedicated(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cnfg, bool apply_defaults) +{ + // Set Default MAC main configuration (9.2.2) + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT default_cfg; + bzero(&default_cfg, sizeof(LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT)); + default_cfg.ulsch_cnfg.max_harq_tx = LIBLTE_RRC_MAX_HARQ_TX_N5; + default_cfg.ulsch_cnfg.periodic_bsr_timer = LIBLTE_RRC_PERIODIC_BSR_TIMER_INFINITY; + default_cfg.ulsch_cnfg.retx_bsr_timer = LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_SF2560; + default_cfg.ulsch_cnfg.tti_bundling = false; + default_cfg.drx_cnfg.setup_present = false; + default_cfg.phr_cnfg.setup_present = false; + default_cfg.time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + + + if (!apply_defaults) { + if(mac_cnfg->ulsch_cnfg_present) + { + if(mac_cnfg->ulsch_cnfg.max_harq_tx_present) { + default_cfg.ulsch_cnfg.max_harq_tx = mac_cnfg->ulsch_cnfg.max_harq_tx; + default_cfg.ulsch_cnfg.max_harq_tx_present = true; + } + if(mac_cnfg->ulsch_cnfg.periodic_bsr_timer_present) { + default_cfg.ulsch_cnfg.periodic_bsr_timer = mac_cnfg->ulsch_cnfg.periodic_bsr_timer; + default_cfg.ulsch_cnfg.periodic_bsr_timer_present = true; + } + default_cfg.ulsch_cnfg.retx_bsr_timer = mac_cnfg->ulsch_cnfg.retx_bsr_timer; + default_cfg.ulsch_cnfg.tti_bundling = mac_cnfg->ulsch_cnfg.tti_bundling; + } + if(mac_cnfg->drx_cnfg_present) { + memcpy(&default_cfg.drx_cnfg, &mac_cnfg->drx_cnfg, sizeof(LIBLTE_RRC_DRX_CONFIG_STRUCT)); + default_cfg.drx_cnfg_present = true; + } + if(mac_cnfg->phr_cnfg_present) { + memcpy(&default_cfg.phr_cnfg, &mac_cnfg->phr_cnfg, sizeof(LIBLTE_RRC_PHR_CONFIG_STRUCT)); + default_cfg.phr_cnfg_present = true; + } + default_cfg.time_alignment_timer = mac_cnfg->time_alignment_timer; + } + + // Setup MAC configuration + mac->set_config_main(&default_cfg); + + rrc_log->info("Set MAC main config: harq-MaxReTX=%d, bsr-TimerReTX=%d, bsr-TimerPeriodic=%d\n", + liblte_rrc_max_harq_tx_num[default_cfg.ulsch_cnfg.max_harq_tx], + liblte_rrc_retransmission_bsr_timer_num[default_cfg.ulsch_cnfg.retx_bsr_timer], + liblte_rrc_periodic_bsr_timer_num[default_cfg.ulsch_cnfg.periodic_bsr_timer]); + if (default_cfg.phr_cnfg_present) { + rrc_log->info("Set MAC PHR config: periodicPHR-Timer=%d, prohibitPHR-Timer=%d, dl-PathlossChange=%d\n", + liblte_rrc_periodic_phr_timer_num[default_cfg.phr_cnfg.periodic_phr_timer], + liblte_rrc_prohibit_phr_timer_num[default_cfg.phr_cnfg.prohibit_phr_timer], + liblte_rrc_dl_pathloss_change_num[default_cfg.phr_cnfg.dl_pathloss_change]); + } +} + +void rrc::apply_rr_config_dedicated(LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT *cnfg) { + if(cnfg->phy_cnfg_ded_present) { + apply_phy_config_dedicated(&cnfg->phy_cnfg_ded, false); + // Apply SR configuration to MAC + if (cnfg->phy_cnfg_ded.sched_request_cnfg_present) { + mac->set_config_sr(&cnfg->phy_cnfg_ded.sched_request_cnfg); + } + } + + if(cnfg->mac_main_cnfg_present) { + apply_mac_config_dedicated(&cnfg->mac_main_cnfg.explicit_value, cnfg->mac_main_cnfg.default_value); + } + + if(cnfg->sps_cnfg_present) { + //TODO + } + if(cnfg->rlf_timers_and_constants_present) { + //TODO + } + for(int i=0; isrb_to_add_mod_list_size; i++) { + // TODO: handle SRB modification + add_srb(&cnfg->srb_to_add_mod_list[i]); + } + for(int i=0; idrb_to_release_list_size; i++) { + release_drb(cnfg->drb_to_release_list[i]); + } + for(int i=0; idrb_to_add_mod_list_size; i++) { + // TODO: handle DRB modification + add_drb(&cnfg->drb_to_add_mod_list[i]); + } +} + +void rrc::handle_con_setup(LIBLTE_RRC_CONNECTION_SETUP_STRUCT *setup) +{ + // Apply the Radio Resource configuration + apply_rr_config_dedicated(&setup->rr_cnfg); +} + +/* Reception of RRCConnectionReestablishment by the UE 5.3.7.5 */ +void rrc::handle_con_reest(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_STRUCT *setup) +{ + mac_timers->get(t301)->stop(); + + // TODO: Restablish DRB1. Not done because never was suspended + + // Apply the Radio Resource configuration + apply_rr_config_dedicated(&setup->rr_cnfg); + + // TODO: Some security stuff here... is it necessary? + + send_con_restablish_complete(); +} + + +void rrc::handle_rrc_con_reconfig(uint32_t lcid, LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT *reconfig, byte_buffer_t *pdu) +{ + uint32_t i; + + if (reconfig->rr_cnfg_ded_present) { + apply_rr_config_dedicated(&reconfig->rr_cnfg_ded); + } else { + printf("received con reconfig no rr confg present\n"); + } + if(reconfig->meas_cnfg_present) + { + //TODO: handle meas_cnfg + } + if(reconfig->mob_ctrl_info_present) + { + //TODO: handle mob_ctrl_info + } + + send_rrc_con_reconfig_complete(lcid, pdu); + + byte_buffer_t *nas_sdu; + for(i=0;iN_ded_info_nas;i++) + { + nas_sdu = pool->allocate(); + memcpy(nas_sdu->msg, &reconfig->ded_info_nas_list[i].msg, reconfig->ded_info_nas_list[i].N_bytes); + nas_sdu->N_bytes = reconfig->ded_info_nas_list[i].N_bytes; + nas->write_pdu(lcid, nas_sdu); + } +} + +void rrc::add_srb(LIBLTE_RRC_SRB_TO_ADD_MOD_STRUCT *srb_cnfg) +{ + // Setup PDCP + pdcp->add_bearer(srb_cnfg->srb_id); + if(RB_ID_SRB2 == srb_cnfg->srb_id) + pdcp->config_security(srb_cnfg->srb_id, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); + + // Setup RLC + if(srb_cnfg->rlc_cnfg_present) + { + if(srb_cnfg->rlc_default_cnfg_present) + { + rlc->add_bearer(srb_cnfg->srb_id); + }else{ + rlc->add_bearer(srb_cnfg->srb_id, &srb_cnfg->rlc_explicit_cnfg); + } + } + + // Setup MAC + uint8_t log_chan_group = 0; + uint8_t priority = 1; + int prioritized_bit_rate = -1; + int bucket_size_duration = -1; + + if(srb_cnfg->lc_cnfg_present) + { + if(srb_cnfg->lc_default_cnfg_present) + { + if(RB_ID_SRB2 == srb_cnfg->srb_id) + priority = 3; + }else{ + if(srb_cnfg->lc_explicit_cnfg.log_chan_sr_mask_present) + { + //TODO + } + if(srb_cnfg->lc_explicit_cnfg.ul_specific_params_present) + { + if(srb_cnfg->lc_explicit_cnfg.ul_specific_params.log_chan_group_present) + log_chan_group = srb_cnfg->lc_explicit_cnfg.ul_specific_params.log_chan_group; + + priority = srb_cnfg->lc_explicit_cnfg.ul_specific_params.priority; + prioritized_bit_rate = liblte_rrc_prioritized_bit_rate_num[srb_cnfg->lc_explicit_cnfg.ul_specific_params.prioritized_bit_rate]; + bucket_size_duration = liblte_rrc_bucket_size_duration_num[srb_cnfg->lc_explicit_cnfg.ul_specific_params.bucket_size_duration]; + } + } + mac->setup_lcid(srb_cnfg->srb_id, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration); + } + + srbs[srb_cnfg->srb_id] = *srb_cnfg; + rrc_log->info("Added radio bearer %s\n", rb_id_text[srb_cnfg->srb_id]); +} + +void rrc::add_drb(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb_cnfg) +{ + + if(!drb_cnfg->pdcp_cnfg_present || + !drb_cnfg->rlc_cnfg_present || + !drb_cnfg->lc_cnfg_present) + { + rrc_log->error("Cannot add DRB - incomplete configuration\n"); + return; + } + uint32_t lcid = 0; + if (drb_cnfg->lc_id_present) { + lcid = drb_cnfg->lc_id; + } else { + lcid = RB_ID_SRB2 + drb_cnfg->drb_id; + rrc_log->warning("LCID not present, using %d\n", lcid); + } + + // Setup PDCP + pdcp->add_bearer(lcid, &drb_cnfg->pdcp_cnfg); + // TODO: setup PDCP security (using k_up_enc) + + // Setup RLC + rlc->add_bearer(lcid, &drb_cnfg->rlc_cnfg); + + // Setup MAC + uint8_t log_chan_group = 0; + uint8_t priority = 1; + int prioritized_bit_rate = -1; + int bucket_size_duration = -1; + if(drb_cnfg->lc_cnfg.ul_specific_params_present) + { + if(drb_cnfg->lc_cnfg.ul_specific_params.log_chan_group_present) { + log_chan_group = drb_cnfg->lc_cnfg.ul_specific_params.log_chan_group; + } else { + rrc_log->warning("LCG not present, setting to 0\n"); + } + priority = drb_cnfg->lc_cnfg.ul_specific_params.priority; + prioritized_bit_rate = liblte_rrc_prioritized_bit_rate_num[drb_cnfg->lc_cnfg.ul_specific_params.prioritized_bit_rate]; + + if (prioritized_bit_rate > 0) { + rrc_log->warning("PBR>0 currently not supported. Setting it to Inifinty\n"); + prioritized_bit_rate = -1; + } + + bucket_size_duration = liblte_rrc_bucket_size_duration_num[drb_cnfg->lc_cnfg.ul_specific_params.bucket_size_duration]; + } + mac->setup_lcid(lcid, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration); + + drbs[lcid] = *drb_cnfg; + drb_up = true; + rrc_log->info("Added radio bearer %s\n", rb_id_text[lcid]); +} + +void rrc::release_drb(uint8_t lcid) +{ + // TODO +} + +/************************** + * DEFAULT VALUES Section 9 +****************************/ + +// PHY CONFIG DEDICATED Defaults (3GPP 36.331 v10 9.2.4) +void rrc::set_phy_default_pucch_srs() +{ + + phy_interface_rrc::phy_cfg_t current_cfg; + phy->get_config(¤t_cfg); + + // Set defaults to CQI, SRS and SR + current_cfg.dedicated.cqi_report_cnfg_present = false; + current_cfg.dedicated.srs_ul_cnfg_ded_present = false; + current_cfg.dedicated.sched_request_cnfg_present = false; + + apply_phy_config_dedicated(¤t_cfg.dedicated, true); + + // Release SR configuration from MAC + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + mac->set_config_sr(&cfg); +} + +void rrc::set_phy_default() +{ + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT defaults; + bzero(&defaults, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + apply_phy_config_dedicated(&defaults, true); +} + +void rrc::set_mac_default() +{ + apply_mac_config_dedicated(NULL, true); + LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT sr_cfg; + bzero(&sr_cfg, sizeof(LIBLTE_RRC_SCHEDULING_REQUEST_CONFIG_STRUCT)); + sr_cfg.setup_present = false; + mac->set_config_sr(&sr_cfg); +} + +void rrc::set_rrc_default() { + N310 = 1; + N311 = 1; + t301 = mac_timers->get_unique_id(); + t310 = mac_timers->get_unique_id(); + t311 = mac_timers->get_unique_id(); + safe_reset_timer = mac_timers->get_unique_id(); + mac_timers->get(t310)->set(this, 1000); + mac_timers->get(t311)->set(this, 1000); + mac_timers->get(safe_reset_timer)->set(this, 10); +} + +} // namespace srsue diff --git a/srslte/lib/upper/usim.cc b/srslte/lib/upper/usim.cc new file mode 100644 index 000000000..7b1f92896 --- /dev/null +++ b/srslte/lib/upper/usim.cc @@ -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= '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 diff --git a/srslte/test/upper/CMakeLists.txt b/srslte/test/upper/CMakeLists.txt new file mode 100644 index 000000000..12243c259 --- /dev/null +++ b/srslte/test/upper/CMakeLists.txt @@ -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 "") diff --git a/srslte/test/upper/nas_test.cc b/srslte/test/upper/nas_test.cc new file mode 100644 index 000000000..a34603afe --- /dev/null +++ b/srslte/test/upper/nas_test.cc @@ -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 +#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(); + +} diff --git a/srslte/test/upper/rlc_am_control_test.cc b/srslte/test/upper/rlc_am_control_test.cc new file mode 100644 index 000000000..7f400ef28 --- /dev/null +++ b/srslte/test/upper/rlc_am_control_test.cc @@ -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 +#include +#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 +#include +#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 +#include "common/log_stdout.h" +#include "upper/rlc_am.h" +#include +#define NBUFS 5 + +using namespace srsue; +using namespace srslte; + +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;} + +private: + srslte::timers::timer t; +}; + +class rlc_am_tester + :public pdcp_interface_rlc + ,public rrc_interface_rlc +{ +public: + rlc_am_tester(){n_sdus = 0;} + + // PDCP interface + void write_pdu(uint32_t lcid, byte_buffer_t *sdu) + { + assert(lcid == 1); + 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[10]; + int n_sdus; +}; + +void basic_test() +{ + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void concat_test() +{ + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void segment_test() +{ + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;i 0){ + len = rlc1.read_pdu(pdu_bufs[n_pdus].msg, 10); // 2 header + payload + pdu_bufs[n_pdus++].N_bytes = len; + } + + assert(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void retx_test() +{ + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 1); + assert(*(tester.sdus[i]->msg) == i); + } +} + +void resegment_test_1() +{ + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 10 | 10 | 10 | 10 | 10 | + // Retx PDU segments: | 5 | 5| + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_2() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 10 | 20 | 10 | 5 | + // Retx PDU segments: | 10 | 10 | + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_3() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 5| 20 | 10 | 10 | + // Retx PDU segments: | 10 | 10 | + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_4() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: | 5 | 5| 30 | 5 | 5| + // Retx PDU segments: | 15 | 15 | + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_5() +{ + + // SDUs: | 10 | 10 | 10 | 10 | 10 | + // PDUs: |2|3| 40 |3|2| + // Retx PDU segments: | 20 | 20 | + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push 5 SDUs into RLC1 + byte_buffer_t sdu_bufs[NBUFS]; + for(int i=0;iN_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +void resegment_test_6() +{ + // SDUs: |10|10|10| 54 | 54 | 54 | 54 | 54 | 54 | + // PDUs: |10|10|10| 270 | 54 | + // Retx PDU segments: | 120 | 150 | + + srslte::log_stdout log1("RLC_AM_1"); + srslte::log_stdout log2("RLC_AM_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_am_tester tester; + mac_dummy_timers timers; + + rlc_am rlc1; + rlc_am rlc2; + + int len; + + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + + rlc1.init(&log1, 1, &tester, &tester, &timers); + rlc2.init(&log2, 1, &tester, &tester, &timers); + + LIBLTE_RRC_RLC_CONFIG_STRUCT cnfg; + cnfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + cnfg.dl_am_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS5; + cnfg.dl_am_rlc.t_status_prohibit = LIBLTE_RRC_T_STATUS_PROHIBIT_MS5; + cnfg.ul_am_rlc.max_retx_thresh = LIBLTE_RRC_MAX_RETX_THRESHOLD_T4; + cnfg.ul_am_rlc.poll_byte = LIBLTE_RRC_POLL_BYTE_KB25; + cnfg.ul_am_rlc.poll_pdu = LIBLTE_RRC_POLL_PDU_P4; + + rlc1.configure(&cnfg); + rlc2.configure(&cnfg); + + // Push SDUs into RLC1 + byte_buffer_t sdu_bufs[9]; + for(int i=0;i<3;i++) + { + for(int j=0;j<10;j++) + sdu_bufs[i].msg[j] = j; + sdu_bufs[i].N_bytes = 10; + rlc1.write_sdu(&sdu_bufs[i]); + } + for(int i=3;i<9;i++) + { + for(int j=0;j<54;j++) + sdu_bufs[i].msg[j] = j; + sdu_bufs[i].N_bytes = 54; + rlc1.write_sdu(&sdu_bufs[i]); + } + + assert(368 == rlc1.get_buffer_state()); + + // Read PDUs from RLC1 (10, 10, 10, 270, 54) + byte_buffer_t pdu_bufs[5]; + for(int i=0;i<3;i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 12); + pdu_bufs[i].N_bytes = len; + } + len = rlc1.read_pdu(pdu_bufs[3].msg, 278); + pdu_bufs[3].N_bytes = len; + len = rlc1.read_pdu(pdu_bufs[4].msg, 56); + pdu_bufs[4].N_bytes = len; + + assert(0 == rlc1.get_buffer_state()); + + // Write PDUs into RLC2 (skip SN 3) + for(int i=0;i<5;i++) + { + if(i != 3) + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + // Sleep to let reordering timeout expire + usleep(10000); + + assert(4 == rlc2.get_buffer_state()); + + // Read status PDU from RLC2 + byte_buffer_t status_buf; + len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status + status_buf.N_bytes = len; + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + assert(278 == rlc1.get_buffer_state()); + + // Read the retx PDU from RLC1 and force resegmentation + byte_buffer_t retx1; + len = rlc1.read_pdu(retx1.msg, 127); + retx1.N_bytes = len; + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx1.msg, retx1.N_bytes); + + assert(157 == rlc1.get_buffer_state()); + + // Read the remaining segment + byte_buffer_t retx2; + len = rlc1.read_pdu(retx2.msg, 157); + retx2.N_bytes = len; + + // Write the retx PDU to RLC2 + rlc2.write_pdu(retx2.msg, retx2.N_bytes); + + assert(tester.n_sdus == 9); + for(int i=0;i<3;i++) + { + assert(tester.sdus[i]->N_bytes == 10); + for(int j=0;j<10;j++) + assert(tester.sdus[i]->msg[j] == j); + } + for(int i=3;i<9;i++) + { + assert(tester.sdus[i]->N_bytes == 54); + for(int j=0;j<54;j++) + assert(tester.sdus[i]->msg[j] == j); + } +} + +int main(int argc, char **argv) { + basic_test(); + byte_buffer_pool::get_instance()->cleanup(); + concat_test(); + byte_buffer_pool::get_instance()->cleanup(); + segment_test(); + byte_buffer_pool::get_instance()->cleanup(); + retx_test(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_1(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_2(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_3(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_4(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_5(); + byte_buffer_pool::get_instance()->cleanup(); + resegment_test_6(); +} diff --git a/srslte/test/upper/rlc_um_data_test.cc b/srslte/test/upper/rlc_um_data_test.cc new file mode 100644 index 000000000..c38363fc6 --- /dev/null +++ b/srslte/test/upper/rlc_um_data_test.cc @@ -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 +#include "upper/rlc_um.h" +#include + +// 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 +#include "common/log_stdout.h" +#include "upper/rlc_um.h" +#include + +#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;iN_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;iis_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(); +} diff --git a/srslte/test/upper/rrc_reconfig_test.cc b/srslte/test/upper/rrc_reconfig_test.cc new file mode 100644 index 000000000..99844176a --- /dev/null +++ b/srslte/test/upper/rrc_reconfig_test.cc @@ -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 +#include +#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(); +} diff --git a/srslte/test/upper/usim_test.cc b/srslte/test/upper/usim_test.cc new file mode 100644 index 000000000..550af622a --- /dev/null +++ b/srslte/test/upper/usim_test.cc @@ -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 +#include "upper/usim.h" +#include "common/log_stdout.h" +#include + +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); +}