From 7b38286864d90514030cb3b60cae25480e417216 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Wed, 18 Mar 2020 15:04:17 +0700 Subject: [PATCH] srsue/extnas: implement RRCTL codec and message handling RRCTL is a simple protocol (inspired by Osmocom's L1CTL) that allows an external NAS entity to control the RRC layer of srsUE. The most notable primitives are PLMN search, selection, and PDU transfer. The protocol assumes traditional master-slave communication, where one side (an external NAS entity) initiates various processes, while the other (srsUE) executes them and indicates the outcome. Each RRCTL message starts with a header that can be defined as follows: +-------------------------------+--------------------------+ | Message type | 6 bits | +-------------------------------+--------------------------+ | Message sub-type | 2 bits | +-------------------------------+--------------------------+ | Spare (RFU) | 8 bits | +-------------------------------+--------------------------+ | Payload length | 2 octets (big endian) | +-------------------------------+--------------------------+ | Payload (optional) | (see payload length) | +-------------------------------+--------------------------+ The following message types are defined at the moment: - RRCTL_RESET - reset internal state of the external NAS interface (does nothing for now, may be useful in the future); - RRCTL_PLMN_SEARCH - initiates PLMN (carrier) search on pre-configured EARFCN (Absolute Radio Freqency Number); - RRCTL_PLMN_SELECT - binds the UE to one of the previously detected carriers (see RRCTL_PLMN_SEARCH) defined by a given pair of MCC and MNC; - RRCTL_CONN_ESTABLISH - establishes connection to the serving cell (previously selected using RRCTL_PLMN_SELECT) with a given cause and NAS PDU; - RRCTL_CONN_RELEASE - releases previously established dedicated connection (currently does nothing because the RRC layer does not expose any API for that); - RRCTL_DATA - encapsulates a received (Downlink) or to be transmitted (Uplink) NAS PDU (the former also contains LCID). Each message type has at least two of the following sub-types: - RRCTL_REQ - request (usually comes from an external NAS entity), used to initiate some process (e.g. PLMN search); - RRCTL_IND - indication that something has happened without a prior request (for example, a Downlink NAS PDU was received); - RRCTL_CNF - confirmation (positive conslusion) of the requested task; - RRCTL_ERR - negative conslusion of the requested task (error). The protocol definition (enums ans structs) and codec functions are defined in a separate namespaces: 'rrctl::proto' and 'rrctl::codec' respectively. The codec functions may throw exceptions of type 'rrctl::codec::error' if something goes wrong. --- .../srslte/interfaces/rrc_interface_types.h | 28 +++ srsue/hdr/stack/upper/nas_ext.h | 13 ++ srsue/hdr/stack/upper/rrctl.h | 162 +++++++++++++ srsue/src/stack/upper/CMakeLists.txt | 2 +- srsue/src/stack/upper/nas_ext.cc | 199 +++++++++++++++- srsue/src/stack/upper/rrctl.cc | 218 ++++++++++++++++++ 6 files changed, 613 insertions(+), 9 deletions(-) create mode 100644 srsue/hdr/stack/upper/rrctl.h create mode 100644 srsue/src/stack/upper/rrctl.cc diff --git a/lib/include/srslte/interfaces/rrc_interface_types.h b/lib/include/srslte/interfaces/rrc_interface_types.h index 143873140..ce25f0b1c 100644 --- a/lib/include/srslte/interfaces/rrc_interface_types.h +++ b/lib/include/srslte/interfaces/rrc_interface_types.h @@ -87,6 +87,34 @@ struct plmn_id_t { uint8_t* plmn_ptr = (uint8_t*)&s1ap_plmn; memcpy(&plmn_bytes[0], plmn_ptr + 1, 3); } + void to_rrctl_bytes(uint8_t *mcc_buf, uint8_t *mnc_buf) const + { + mcc_buf[0] = ((mcc[1] & 0x0f) << 4) | (mcc[0] & 0x0f); + mcc_buf[1] = (0x0f << 4) | (mcc[2] & 0x0f); + + mnc_buf[0] = ((mnc[1] & 0x0f) << 4) | (mnc[0] & 0x0f); + if (nof_mnc_digits > 2) + mnc_buf[1] = (0x0f << 4) | (mnc[2] & 0x0f); + else + mnc_buf[1] = 0xff; + } + void from_rrctl_bytes(const uint8_t *mcc_buf, const uint8_t *mnc_buf) + { + mcc[0] = mcc_buf[0] & 0x0f; + mcc[1] = mcc_buf[0] >> 4; + mcc[2] = mcc_buf[1] & 0x0f; + + mnc[0] = mnc_buf[0] & 0x0f; + mnc[1] = mnc_buf[0] >> 4; + + if (mnc_buf[1] != 0xff) { + nof_mnc_digits = 3; + mnc[2] = mnc_buf[1] & 0x0f; + } else { + nof_mnc_digits = 2; + mnc[2] = 0x00; + } + } int from_string(const std::string& plmn_str) { if (plmn_str.size() < 5 or plmn_str.size() > 6) { diff --git a/srsue/hdr/stack/upper/nas_ext.h b/srsue/hdr/stack/upper/nas_ext.h index 471ff709a..5802f130e 100644 --- a/srsue/hdr/stack/upper/nas_ext.h +++ b/srsue/hdr/stack/upper/nas_ext.h @@ -37,6 +37,7 @@ #include "srsue/hdr/stack/upper/nas_ext.h" #include "srsue/hdr/stack/upper/nas_extif.h" #include "srsue/hdr/stack/upper/nas_metrics.h" +#include "srsue/hdr/stack/upper/rrctl.h" using srslte::byte_buffer_t; @@ -79,6 +80,18 @@ private: // Interface to an external NAS entity std::unique_ptr iface; + + // RRCTL message handlers + void handle_rrctl_reset(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_plmn_search(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_plmn_select(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_conn_establish(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_conn_release(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + + void rrctl_send_confirm(rrctl::proto::msg_type type); + void rrctl_send_error(rrctl::proto::msg_type type); }; } // namespace srsue diff --git a/srsue/hdr/stack/upper/rrctl.h b/srsue/hdr/stack/upper/rrctl.h new file mode 100644 index 000000000..e17db4534 --- /dev/null +++ b/srsue/hdr/stack/upper/rrctl.h @@ -0,0 +1,162 @@ +/* + * Copyright 2020 Software Radio Systems Limited + * Author: Vadim Yanitskiy + * Sponsored by Positive Technologies + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "srslte/common/common.h" +#include "srslte/interfaces/ue_interfaces.h" + +namespace rrctl { + +namespace proto { + + enum msg_type { + RRCTL_RESET = 0x00, + RRCTL_DATA, + RRCTL_PLMN_SEARCH, + RRCTL_PLMN_SELECT, + RRCTL_CONN_ESTABLISH, + RRCTL_CONN_RELEASE, + RRCTL_PAGING, + RRCTL_PARAM, + }; + + enum msg_disc { + RRCTL_REQ = 0x00, + RRCTL_IND = 0x01, + RRCTL_CNF = 0x02, + RRCTL_ERR = 0x03, + }; + + struct msg_hdr { +#if defined(__LITTLE_ENDIAN_BITFIELD) + uint8_t disc:2, type:6; +#elif defined(__BIG_ENDIAN_BITFIELD) + uint8_t type:6, disc:2; +#else + #error "Please fix " +#endif + uint8_t rfu; // Reserved + uint16_t len; + uint8_t data[0]; + } __attribute__((packed)); + + struct msg_plmn_search_res { + uint8_t nof_plmns; + struct plmn { + uint8_t mcc[2]; + uint8_t mnc[2]; + uint16_t tac; + } plmns[16]; + } __attribute__((packed)); + + struct msg_plmn_select_req { + uint8_t mcc[2]; + uint8_t mnc[2]; + } __attribute__((packed)); + + struct msg_conn_establish_req { + uint8_t cause; + uint8_t pdu[0]; + } __attribute__((packed)); + + struct msg_data { + uint32_t lcid; + uint8_t pdu[0]; + } __attribute__((packed)); + + struct __mmec_m_tmsi { + uint8_t mmec; + uint32_t m_tmsi; + } __attribute__((packed)); + + struct msg_paging_ind { + struct __mmec_m_tmsi ueid; + } __attribute__((packed)); + + enum msg_param_type { + RRCTL_PARAM_UEID = 0x00, + }; + + struct msg_param_req { + uint8_t type; + uint8_t len; + union { + struct __mmec_m_tmsi ueid; + } u; + } __attribute__((packed)); + + struct msg { + struct msg_hdr hdr; + union { + struct msg_data data; + struct msg_param_req param_req; + struct msg_paging_ind paging_ind; + struct msg_plmn_search_res plmn_search_res; + struct msg_plmn_select_req plmn_select_req; + struct msg_conn_establish_req conn_establish_req; + } u; + } __attribute__((packed)); + + std::string msg_hdr_desc(proto::msg_type type, proto::msg_disc disc, uint16_t len = 0); + +} // namespace proto + +namespace codec { + +class error : public std::runtime_error { +public: + explicit error(const std::string& msg) : std::runtime_error(msg) {}; +}; + +void enc_hdr(srslte::byte_buffer_t& buf, + proto::msg_type type, + proto::msg_disc disc, + uint16_t len = 0); +const uint8_t* dec_hdr(const srslte::byte_buffer_t& buf, + proto::msg_type& type, + proto::msg_disc& disc, + uint16_t& len); + +void enc_plmn_search_res(srslte::byte_buffer_t& buf, + const srsue::rrc_interface_nas::found_plmn_t* plmns, + size_t nof_plmns); + +void dec_plmn_select_req(std::pair& mcc_mnc, + const uint8_t* payload, size_t len); + +void dec_conn_establish_req(srslte::establishment_cause_t& cause, + const uint8_t*& pdu, size_t& pdu_len, + const uint8_t* payload, size_t len); + +void enc_data_ind(srslte::byte_buffer_t& buf, + const uint8_t *pdu, size_t pdu_len, + uint32_t lcid); + +void enc_paging_ind(srslte::byte_buffer_t& buf, + srslte::s_tmsi_t* ue_identity); + +} // namespace codec + +} // namespace rrctl diff --git a/srsue/src/stack/upper/CMakeLists.txt b/srsue/src/stack/upper/CMakeLists.txt index 8b9ca981e..d7fd4bc1f 100644 --- a/srsue/src/stack/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/CMakeLists.txt @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES gw.cc nas.cc nas_ext.cc nas_extif.cc usim_base.cc usim.cc tft_packet_filter.cc) +set(SOURCES gw.cc nas.cc nas_ext.cc nas_extif.cc rrctl.cc usim_base.cc usim.cc tft_packet_filter.cc) if(HAVE_PCSC) list(APPEND SOURCES "pcsc_usim.cc") diff --git a/srsue/src/stack/upper/nas_ext.cc b/srsue/src/stack/upper/nas_ext.cc index de5147835..e86bcdab1 100644 --- a/srsue/src/stack/upper/nas_ext.cc +++ b/srsue/src/stack/upper/nas_ext.cc @@ -45,14 +45,172 @@ void nas_ext::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interf rrc = rrc_; gw = gw_; - auto rx_cb = [this](const srslte::byte_buffer_t& pdu) { - // TODO: parse received payload + // RRCTL PDU handler + auto rrctl_rx_cb = [this](const srslte::byte_buffer_t& pdu) { + rrctl::proto::msg_type type; + rrctl::proto::msg_disc disc; + const uint8_t* payload; + std::string desc; + uint16_t length; + + // Parse the message header + try { + payload = rrctl::codec::dec_hdr(pdu, type, disc, length); + desc = rrctl::proto::msg_hdr_desc(type, disc, length); + nas_log->info("Got RRCTL message: %s\n", desc.c_str()); + } catch (const rrctl::codec::error& e) { + nas_log->warning("Got malformed RRCTL message: %s\n", e.what()); + return; + } + + // Call the corresponding handler + switch (type) { + case rrctl::proto::RRCTL_RESET: + handle_rrctl_reset(disc, payload, length); + break; + case rrctl::proto::RRCTL_PLMN_SEARCH: + handle_rrctl_plmn_search(disc, payload, length); + break; + case rrctl::proto::RRCTL_PLMN_SELECT: + handle_rrctl_plmn_select(disc, payload, length); + break; + case rrctl::proto::RRCTL_CONN_ESTABLISH: + handle_rrctl_conn_establish(disc, payload, length); + break; + case rrctl::proto::RRCTL_DATA: + handle_rrctl_data(disc, payload, length); + break; + case rrctl::proto::RRCTL_PARAM: + handle_rrctl_param(disc, payload, length); + break; + case rrctl::proto::RRCTL_CONN_RELEASE: + default: + nas_log->warning("%s is not handled\n", desc.c_str()); + } }; - std::unique_ptr iface_(new nas_extif_unix(rx_cb, cfg.sock_path)); + std::unique_ptr iface_(new nas_extif_unix(rrctl_rx_cb, cfg.sock_path)); iface = std::move(iface_); } +void nas_ext::rrctl_send_confirm(rrctl::proto::msg_type type) +{ + srslte::byte_buffer_t pdu; + + rrctl::codec::enc_hdr(pdu, type, rrctl::proto::RRCTL_CNF); + iface->write(pdu); +} + +void nas_ext::rrctl_send_error(rrctl::proto::msg_type type) +{ + srslte::byte_buffer_t pdu; + + rrctl::codec::enc_hdr(pdu, type, rrctl::proto::RRCTL_ERR); + iface->write(pdu); +} + +void nas_ext::handle_rrctl_reset(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + // TODO: do we need to reset anything? + rrctl_send_confirm(rrctl::proto::RRCTL_RESET); +} + +void nas_ext::handle_rrctl_plmn_search(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + // Response to be sent when RRC responds + rrc->plmn_search(); +} + +void nas_ext::handle_rrctl_plmn_select(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + std::pair mcc_mnc; + srslte::plmn_id_t plmn_id; + + // Parse PLMN ID + try { + rrctl::codec::dec_plmn_select_req(mcc_mnc, msg, len); + } catch (const rrctl::codec::error& e) { + nas_log->warning("Failed to parse RRCTL message: %s\n", e.what()); + rrctl_send_error(rrctl::proto::RRCTL_PLMN_SELECT); + return; + } + + if (plmn_id.from_number(mcc_mnc.first, mcc_mnc.second) != SRSLTE_SUCCESS) { + nas_log->warning("Failed to parse PLMN ID from PLMN Select Request\n"); + rrctl_send_error(rrctl::proto::RRCTL_PLMN_SELECT); + } + + rrc->plmn_select(plmn_id); + rrctl_send_confirm(rrctl::proto::RRCTL_PLMN_SELECT); +} + +void nas_ext::handle_rrctl_conn_establish(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + srslte::establishment_cause_t cause; + const uint8_t* pdu; + size_t pdu_len; + + try { + rrctl::codec::dec_conn_establish_req(cause, pdu, pdu_len, msg, len); + } catch (const rrctl::codec::error& e) { + nas_log->warning("Failed to parse RRCTL message: %s\n", e.what()); + rrctl_send_error(rrctl::proto::RRCTL_CONN_ESTABLISH); + return; + } + + // Allocate a new NAS PDU on heap + unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true); + nas_pdu->append_bytes(pdu, pdu_len); + + rrc->connection_request(cause, std::move(nas_pdu)); +} + +void nas_ext::handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + if (not rrc->is_connected()) { + nas_log->warning("Received DATA.req, but there is no active connection\n"); + rrctl_send_error(rrctl::proto::RRCTL_DATA); + return; + } + + // Allocate a new NAS PDU on heap + unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true); + nas_pdu->append_bytes(msg, len); + + rrc->write_sdu(std::move(nas_pdu)); + rrctl_send_confirm(rrctl::proto::RRCTL_DATA); +} + +void nas_ext::handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + const struct rrctl::proto::msg_param_req* param; + srslte::s_tmsi_t ue_identity; + + param = reinterpret_cast (msg); + nas_log->warning("Rx PARAM.req (type=%02x, len=%u)\n", param->type, param->len); + + if (param->len != (len - 2)) { /* XXX: type + length */ + nas_log->info("Received malformed PARAM.req (len=%u)\n", param->len); + rrctl_send_error(rrctl::proto::RRCTL_PARAM); + return; + } + + switch (param->type) { + case rrctl::proto::RRCTL_PARAM_UEID: + ue_identity.m_tmsi = ntohl(param->u.ueid.m_tmsi); + ue_identity.mmec = param->u.ueid.mmec; + + nas_log->info("Setting UEID: mmec=%x m_tmsi=%x\n", + ue_identity.mmec, ue_identity.m_tmsi); + rrc->set_ue_identity(ue_identity); + rrctl_send_confirm(rrctl::proto::RRCTL_PARAM); + break; + default: + nas_log->warning("Unhandled PARAM.req type (0x%02x)\n", param->type); + rrctl_send_error(rrctl::proto::RRCTL_PARAM); + } +} + void nas_ext::get_metrics(nas_metrics_t* m) { nas_metrics_t metrics = {}; @@ -100,15 +258,25 @@ void nas_ext::left_rrc_connected() {} bool nas_ext::paging(srslte::s_tmsi_t* ue_identity) { + srslte::byte_buffer_t msg; + nas_log->info("Received paging from RRC\n"); - // TODO: send PAGING.ind to the external entity + + rrctl::codec::enc_paging_ind(msg, ue_identity); + iface->write(msg); + + // TODO: what are we supposed to return? return false; } void nas_ext::write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) { + srslte::byte_buffer_t msg; + nas_log->info_hex(pdu->msg, pdu->N_bytes, "Received DL %s PDU from RRC\n", rrc->get_rb_name(lcid).c_str()); - // TODO: send DATA.ind to the external entity + + rrctl::codec::enc_data_ind(msg, pdu->msg, pdu->N_bytes, lcid); + iface->write(msg); } uint32_t nas_ext::get_k_enb_count() @@ -145,15 +313,30 @@ void nas_ext::plmn_search_completed( const rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS], int nof_plmns) { + srslte::byte_buffer_t pdu; + nas_log->info("RRC has completed PLMN search, %d carriers found\n", nof_plmns); - // TODO: send PLMN_SEARCH.res to the external entity + + // Send PLMN_SEARCH.res to an external entity + if (nof_plmns >= 0) { + rrctl::codec::enc_plmn_search_res(pdu, found_plmns, nof_plmns); + iface->write(pdu); + } else { + nas_log->warning("PLMN search completed with an error\n"); + rrctl_send_error(rrctl::proto::RRCTL_PLMN_SEARCH); + } } bool nas_ext::connection_request_completed(bool outcome) { nas_log->info("RRC has %s connection establisment\n", outcome ? "completed" : "failed"); - // TODO: send CONN_ESTABLISH.res to the external entity - return false; + + if (outcome) + rrctl_send_confirm(rrctl::proto::RRCTL_CONN_ESTABLISH); + else + rrctl_send_error(rrctl::proto::RRCTL_CONN_ESTABLISH); + + return false; // FIXME: what should we return here? } } // namespace srsue diff --git a/srsue/src/stack/upper/rrctl.cc b/srsue/src/stack/upper/rrctl.cc new file mode 100644 index 000000000..0d1f78f87 --- /dev/null +++ b/srsue/src/stack/upper/rrctl.cc @@ -0,0 +1,218 @@ +/* + * Copyright 2020 Software Radio Systems Limited + * Author: Vadim Yanitskiy + * Sponsored by Positive Technologies + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "srsue/hdr/stack/upper/rrctl.h" + +namespace rrctl { + +namespace proto { + +std::string msg_hdr_desc(proto::msg_type type, proto::msg_disc disc, uint16_t len) +{ + std::string desc; + + switch (type) { + case RRCTL_RESET: + desc += "Reset"; + break; + case RRCTL_PLMN_SEARCH: + desc += "PLMN Search"; + break; + case RRCTL_PLMN_SELECT: + desc += "PLMN Select"; + break; + case RRCTL_CONN_ESTABLISH: + desc += "Connection Establish"; + break; + case RRCTL_CONN_RELEASE: + desc += "Connection Release"; + break; + case RRCTL_DATA: + desc += "Data (PDU)"; + break; + case RRCTL_PAGING: + desc += "Paging"; + break; + case RRCTL_PARAM: + desc += "Parameter"; + break; + default: + desc += ""; + } + + desc += " "; + + switch (disc) { + case RRCTL_REQ: + desc += "Request"; + break; + case RRCTL_IND: + desc += "Indication"; + break; + case RRCTL_CNF: + desc += "Confirmation"; + break; + case RRCTL_ERR: + desc += "Error"; + break; + } + + if (len > 0) { + desc += " (length "; + desc += std::to_string(len); + desc += ")"; + } + + return desc; +} + +} + +namespace codec { + +void enc_hdr(srslte::byte_buffer_t& buf, + proto::msg_type type, + proto::msg_disc disc, + uint16_t len) +{ + struct proto::msg_hdr hdr = { +#if defined(__LITTLE_ENDIAN_BITFIELD) + .disc = (uint8_t) disc, + .type = (uint8_t) type, +#elif defined(__BIG_ENDIAN_BITFIELD) + .type = (uint8_t) type, + .disc = (uint8_t) disc, +#else + #error "Please fix " +#endif + .len = ntohs(len), + }; + + buf.append_bytes((const uint8_t*) &hdr, sizeof(hdr)); +} + +const uint8_t* dec_hdr(const srslte::byte_buffer_t& buf, + proto::msg_type& type, + proto::msg_disc& disc, + uint16_t& len) +{ + const struct proto::msg_hdr* hdr; + + // Make sure at least header is present + if (buf.N_bytes < sizeof(*hdr)) + throw codec::error("header is too short"); + + hdr = reinterpret_cast (buf.msg); + type = static_cast (hdr->type); + disc = static_cast (hdr->disc); + len = htons(hdr->len); + + // Make sure the whole message fits + if (buf.N_bytes < sizeof(*hdr) + len) + throw codec::error("body is too short"); + + // Return pointer to the payload (if present) + return len ? hdr->data : NULL; +} + +void enc_plmn_search_res(srslte::byte_buffer_t& buf, + const srsue::rrc_interface_nas::found_plmn_t* plmns, + size_t nof_plmns) +{ + struct proto::msg_plmn_search_res msg; + uint16_t msg_len; + + if (nof_plmns > 16) + throw codec::error("too many PLMNS to encode"); + msg.nof_plmns = static_cast (nof_plmns); + + for (size_t i = 0; i < nof_plmns; i++) { + plmns[i].plmn_id.to_rrctl_bytes(msg.plmns[i].mcc, msg.plmns[i].mnc); + msg.plmns[i].tac = htons(plmns[i].tac); + } + + msg_len = sizeof(proto::msg_plmn_search_res::plmn) * nof_plmns + 1; + enc_hdr(buf, proto::RRCTL_PLMN_SEARCH, proto::RRCTL_CNF, msg_len); + buf.append_bytes((uint8_t *) &msg, msg_len); +} + +void dec_plmn_select_req(std::pair& mcc_mnc, + const uint8_t* payload, size_t len) +{ + const struct proto::msg_plmn_select_req* msg; + struct srslte::plmn_id_t plmn_id; + + if (len < sizeof(*msg)) + throw codec::error("body is too short"); + + msg = reinterpret_cast (payload); + plmn_id.from_rrctl_bytes(msg->mcc, msg->mnc); + mcc_mnc = plmn_id.to_number(); +} + +void dec_conn_establish_req(srslte::establishment_cause_t& cause, + const uint8_t*& pdu, size_t& pdu_len, + const uint8_t* payload, size_t len) +{ + const struct proto::msg_conn_establish_req* msg; + + if (len < sizeof(*msg)) + throw codec::error("body is too short"); + + msg = reinterpret_cast (payload); + cause = static_cast (msg->cause); + pdu_len = len - 1; + pdu = msg->pdu; +} + +void enc_data_ind(srslte::byte_buffer_t& buf, + const uint8_t *pdu, size_t pdu_len, + uint32_t lcid) +{ + struct proto::msg_data msg; + + msg.lcid = htonl(lcid); + + enc_hdr(buf, proto::RRCTL_DATA, proto::RRCTL_IND, sizeof(msg) + pdu_len); + buf.append_bytes((const uint8_t*) &msg, sizeof(msg)); + buf.append_bytes(pdu, pdu_len); +} + +void enc_paging_ind(srslte::byte_buffer_t& buf, + srslte::s_tmsi_t* ue_identity) +{ + struct proto::msg_paging_ind msg; + + msg.ueid.m_tmsi = htonl(ue_identity->m_tmsi); + msg.ueid.mmec = ue_identity->mmec; + + enc_hdr(buf, proto::RRCTL_PAGING, proto::RRCTL_IND, sizeof(msg)); + buf.append_bytes((const uint8_t*) &msg, sizeof(msg)); +} + +} // namespace codec + +} // namespace rrctl