|
|
|
/**
|
|
|
|
*
|
|
|
|
* \section COPYRIGHT
|
|
|
|
*
|
|
|
|
* Copyright 2013-2020 Software Radio Systems Limited
|
|
|
|
*
|
|
|
|
* By using this file, you agree to the terms and conditions set
|
|
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
|
|
* the distribution.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef SRSUE_TTCN3_SYS_INTERFACE_H
|
|
|
|
#define SRSUE_TTCN3_SYS_INTERFACE_H
|
|
|
|
|
|
|
|
#include "srslte/asn1/rrc_utils.h"
|
|
|
|
#include "srslte/common/buffer_pool.h"
|
|
|
|
#include "ttcn3_helpers.h"
|
|
|
|
#include "ttcn3_interfaces.h"
|
|
|
|
|
|
|
|
using namespace srslte;
|
|
|
|
|
|
|
|
// The EUTRA.SYS interface
|
|
|
|
class ttcn3_sys_interface : public ttcn3_port_handler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ttcn3_sys_interface(srslog::basic_logger& logger) : ttcn3_port_handler(logger) {}
|
|
|
|
~ttcn3_sys_interface(){};
|
|
|
|
|
|
|
|
int init(ss_sys_interface* syssim_, std::string net_ip_, uint32_t net_port_)
|
|
|
|
{
|
|
|
|
syssim = syssim_;
|
|
|
|
net_ip = net_ip_;
|
|
|
|
net_port = net_port_;
|
|
|
|
initialized = true;
|
|
|
|
logger.debug("Initialized.");
|
|
|
|
pool = byte_buffer_pool::get_instance();
|
|
|
|
return port_listen();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
///< Main message handler
|
|
|
|
int handle_message(const unique_byte_array_t& rx_buf, const uint32_t n)
|
|
|
|
{
|
|
|
|
logger.debug("Received %d B from remote.", n);
|
|
|
|
|
|
|
|
// Chop incoming msg, first two bytes are length of the JSON
|
|
|
|
// (see IPL4_EUTRA_SYSTEM_Definitions.ttcn
|
|
|
|
uint16_t json_len = ((uint16_t)rx_buf->at(0) << 8) | rx_buf->at(1);
|
|
|
|
|
|
|
|
// Copy JSON from received buffer and null-terminate
|
|
|
|
char json[json_len + 1];
|
|
|
|
memcpy(json, &rx_buf->at(2), json_len);
|
|
|
|
json[json_len] = '\0';
|
|
|
|
|
|
|
|
// The data part after the JSON starts right here but handling
|
|
|
|
// is done in the respective functions
|
|
|
|
uint16_t rx_buf_offset = json_len + 2;
|
|
|
|
|
|
|
|
Document document;
|
|
|
|
if (document.Parse(json).HasParseError() || document.IsObject() == false) {
|
|
|
|
logger.error((uint8*)json, json_len, "Error parsing incoming data.");
|
|
|
|
return SRSLTE_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pretty-print
|
|
|
|
StringBuffer buffer;
|
|
|
|
PrettyWriter<StringBuffer> writer(buffer);
|
|
|
|
document.Accept(writer);
|
|
|
|
logger.info("Received %d bytes\n%s", json_len, (char*)buffer.GetString());
|
|
|
|
|
|
|
|
// check for common
|
|
|
|
assert(document.HasMember("Common"));
|
|
|
|
assert(document["Common"].IsObject());
|
|
|
|
|
|
|
|
// Check for request type
|
|
|
|
assert(document.HasMember("Request"));
|
|
|
|
assert(document["Request"].IsObject());
|
|
|
|
|
|
|
|
// Get request type
|
|
|
|
const Value& request = document["Request"];
|
|
|
|
if (request.HasMember("Cell")) {
|
|
|
|
logger.info("Received Cell request.");
|
|
|
|
handle_request_cell(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
|
|
|
|
} else if (request.HasMember("L1MacIndCtrl")) {
|
|
|
|
logger.info("Received L1MacIndCtrl request.");
|
|
|
|
handle_request_l1_mac_ind_ctrl(document);
|
|
|
|
} else if (request.HasMember("RadioBearerList")) {
|
|
|
|
logger.info("Received RadioBearerList request.");
|
|
|
|
handle_request_radio_bearer_list(document);
|
|
|
|
} else if (request.HasMember("CellAttenuationList")) {
|
|
|
|
logger.info("Received CellAttenuationList request.");
|
|
|
|
handle_request_cell_attenuation_list(document);
|
|
|
|
} else if (request.HasMember("PdcpCount")) {
|
|
|
|
logger.info("Received PdcpCount request.");
|
|
|
|
handle_request_pdcp_count(document);
|
|
|
|
} else if (request.HasMember("AS_Security")) {
|
|
|
|
logger.info("Received AS_Security request.");
|
|
|
|
handle_request_as_security(document);
|
|
|
|
} else if (request.HasMember("EnquireTiming")) {
|
|
|
|
logger.info("Received EnquireTiming request.");
|
|
|
|
handle_request_enquire_timing(document);
|
|
|
|
} else if (request.HasMember("Paging")) {
|
|
|
|
logger.info("Received Paging request.");
|
|
|
|
handle_request_paging(document, &rx_buf->at(rx_buf_offset), n - rx_buf_offset);
|
|
|
|
} else if (request.HasMember("PdcpHandoverControl")) {
|
|
|
|
logger.info("Received PdcpHandoverControl.");
|
|
|
|
handle_request_pdcp_handover_control(document);
|
|
|
|
} else {
|
|
|
|
logger.error("Received unknown request.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return SRSLTE_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_cell_basic(Document& document, const uint8_t* payload, const uint16_t len)
|
|
|
|
{
|
|
|
|
// That is the cellId or name that the testsuites uses to refer to a specific cell
|
|
|
|
const Value& cell_name = document["Common"]["CellId"];
|
|
|
|
assert(cell_name.IsString());
|
|
|
|
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"]["Basic"].HasMember("StaticCellInfo")) {
|
|
|
|
// Fill all relevant info from the cell request
|
|
|
|
ss_sys_interface::cell_config_t cell;
|
|
|
|
|
|
|
|
// set name
|
|
|
|
cell.name = cell_name.GetString();
|
|
|
|
|
|
|
|
// Extract EARFCN
|
|
|
|
const Value& earfcn =
|
|
|
|
document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Downlink"]["Earfcn"];
|
|
|
|
assert(earfcn.IsInt());
|
|
|
|
cell.earfcn = earfcn.GetInt();
|
|
|
|
|
|
|
|
// Extract cell config
|
|
|
|
const Value& common_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Common"];
|
|
|
|
const Value& dl_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["StaticCellInfo"]["Downlink"];
|
|
|
|
const Value& phy_dl_config = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["PhysicalLayerConfigDL"];
|
|
|
|
|
|
|
|
cell.phy_cell.id = common_config["PhysicalCellId"].GetInt();
|
|
|
|
cell.phy_cell.cp =
|
|
|
|
(strcmp(dl_config["CyclicPrefix"].GetString(), "normal") == 0) ? SRSLTE_CP_NORM : SRSLTE_CP_EXT;
|
|
|
|
cell.phy_cell.nof_ports =
|
|
|
|
(strcmp(phy_dl_config["AntennaGroup"]["AntennaInfoCommon"]["R8"]["antennaPortsCount"].GetString(), "an1") ==
|
|
|
|
0)
|
|
|
|
? 1
|
|
|
|
: 2;
|
|
|
|
cell.phy_cell.nof_prb = (strcmp(dl_config["Bandwidth"].GetString(), "n25") == 0) ? 25 : 0;
|
|
|
|
cell.phy_cell.phich_length =
|
|
|
|
(strcmp(phy_dl_config["Phich"]["PhichConfig"]["R8"]["phich_Duration"].GetString(), "normal") == 0)
|
|
|
|
? SRSLTE_PHICH_NORM
|
|
|
|
: SRSLTE_PHICH_EXT;
|
|
|
|
cell.phy_cell.phich_resources =
|
|
|
|
(strcmp(phy_dl_config["Phich"]["PhichConfig"]["R8"]["phich_Resource"].GetString(), "one") == 0)
|
|
|
|
? SRSLTE_PHICH_R_1
|
|
|
|
: SRSLTE_PHICH_R_1_6;
|
|
|
|
logger.info("DL EARFCN is %d with n_prb=%d", cell.earfcn, cell.phy_cell.nof_prb);
|
|
|
|
|
|
|
|
const Value& ref_power =
|
|
|
|
document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["InitialCellPower"]["MaxReferencePower"];
|
|
|
|
assert(ref_power.IsInt());
|
|
|
|
|
|
|
|
// set power
|
|
|
|
cell.initial_power = ref_power.GetInt();
|
|
|
|
|
|
|
|
const Value& att = document["Request"]["Cell"]["AddOrReconfigure"]["Basic"]["InitialCellPower"]["Attenuation"];
|
|
|
|
|
|
|
|
cell.attenuation = 0;
|
|
|
|
if (att.HasMember("Value")) {
|
|
|
|
cell.attenuation = att["Value"].GetInt();
|
|
|
|
} else if (att.HasMember("Off")) {
|
|
|
|
// is there other values than Off=True?
|
|
|
|
assert(att["Off"].GetBool() == true);
|
|
|
|
if (att["Off"].GetBool() == true) {
|
|
|
|
// use high attenuation value (-145dB RX power as per TS 36.508 Sec 6.2.2.1-1 is a non-suitable Off cell)
|
|
|
|
cell.attenuation = 90.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse and handle reconfig of active cells
|
|
|
|
handle_active_cell_reconfig_section(document, cell);
|
|
|
|
|
|
|
|
// Now configure cell
|
|
|
|
syssim->set_cell_config(ttcn3_helpers::get_timing_info(document), cell);
|
|
|
|
logger.info("Configuring attenuation of %s to %.2f dB", cell_name.GetString(), cell.attenuation);
|
|
|
|
syssim->set_cell_attenuation(ttcn3_helpers::get_timing_info(document), cell_name.GetString(), cell.attenuation);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pull out SIBs and send to syssim
|
|
|
|
uint16_t consumed_bytes = 0;
|
|
|
|
const uint8_t* payload_ptr = payload;
|
|
|
|
while (consumed_bytes < len) {
|
|
|
|
uint16_t tb_len = ((uint16_t)payload_ptr[0] << 8) | payload_ptr[1];
|
|
|
|
payload_ptr += 2;
|
|
|
|
|
|
|
|
unique_byte_buffer_t sib = pool_allocate_blocking;
|
|
|
|
memcpy(sib->msg, payload_ptr, tb_len);
|
|
|
|
payload_ptr += tb_len;
|
|
|
|
sib->N_bytes = tb_len;
|
|
|
|
|
|
|
|
// Push to main component
|
|
|
|
logger.info(sib->msg, sib->N_bytes, "Received BCCH DL-SCH for %s", cell_name.GetString());
|
|
|
|
syssim->add_bcch_dlsch_pdu(cell_name.GetString(), std::move(sib));
|
|
|
|
|
|
|
|
consumed_bytes = payload_ptr - payload;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"]["Basic"].HasMember("StaticCellInfo")) {
|
|
|
|
// Create response for template car_CellConfig_CNF(CellId_Type p_CellId)
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_name.GetString(), "Cell");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_cell_active(Document& document, const uint8_t* payload, const uint16_t len)
|
|
|
|
{
|
|
|
|
// Create response for template car_CellConfig_CNF(CellId_Type p_CellId)
|
|
|
|
std::string cell_id = document["Common"]["CellId"].GetString();
|
|
|
|
|
|
|
|
// Fill relevant content
|
|
|
|
ss_sys_interface::cell_config_t cell;
|
|
|
|
cell.name = cell_id;
|
|
|
|
|
|
|
|
handle_active_cell_reconfig_section(document, cell);
|
|
|
|
|
|
|
|
// Now configure cell
|
|
|
|
syssim->set_cell_config(ttcn3_helpers::get_timing_info(document), cell);
|
|
|
|
|
|
|
|
if (ttcn3_helpers::requires_confirm(document)) {
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id, "Cell");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
} else {
|
|
|
|
logger.info("Skipping response for request cell active message.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function just pulls out the reconfiguration section but doesn't send response to SS
|
|
|
|
void handle_active_cell_reconfig_section(Document& document, ss_sys_interface::cell_config_t& cell)
|
|
|
|
{
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"].HasMember("Active")) {
|
|
|
|
// Extract CRNTI
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"]["Active"].HasMember("C_RNTI")) {
|
|
|
|
const Value& crnti_string = document["Request"]["Cell"]["AddOrReconfigure"]["Active"]["C_RNTI"];
|
|
|
|
assert(crnti_string.IsString());
|
|
|
|
cell.crnti = std::bitset<16>(crnti_string.GetString()).to_ulong();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extra Cont Resolution scheme
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"]["Active"].HasMember("RachProcedureConfig")) {
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"]["Active"]["RachProcedureConfig"].HasMember(
|
|
|
|
"RachProcedureList")) {
|
|
|
|
const Value& rach_proc_list =
|
|
|
|
document["Request"]["Cell"]["AddOrReconfigure"]["Active"]["RachProcedureConfig"]["RachProcedureList"];
|
|
|
|
assert(rach_proc_list.IsArray());
|
|
|
|
for (Value::ConstValueIterator itr = rach_proc_list.Begin(); itr != rach_proc_list.End(); ++itr) {
|
|
|
|
if (itr->HasMember("ContentionResolutionCtrl")) {
|
|
|
|
const Value& cont_res_type = (*itr)["ContentionResolutionCtrl"];
|
|
|
|
if (cont_res_type.HasMember("CRNTI_Based")) {
|
|
|
|
// TODO: handle CRNTI based contention resolution
|
|
|
|
} else if (cont_res_type.HasMember("TCRNTI_Based")) {
|
|
|
|
// TODO: handle TCRNTI based contention resolution
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (itr->HasMember("RAResponse")) {
|
|
|
|
if ((*itr)["RAResponse"].HasMember("Ctrl")) {
|
|
|
|
if ((*itr)["RAResponse"]["Ctrl"].HasMember("Rar")) {
|
|
|
|
if ((*itr)["RAResponse"]["Ctrl"]["Rar"].HasMember("List")) {
|
|
|
|
const Value& rar_list_list = (*itr)["RAResponse"]["Ctrl"]["Rar"]["List"];
|
|
|
|
assert(rar_list_list.IsArray());
|
|
|
|
for (Value::ConstValueIterator rar_itr = rar_list_list.Begin(); rar_itr != rar_list_list.End();
|
|
|
|
++rar_itr) {
|
|
|
|
if (rar_itr->HasMember("TempC_RNTI")) {
|
|
|
|
if ((*rar_itr)["TempC_RNTI"].HasMember("SameAsC_RNTI")) {
|
|
|
|
const Value& temp_crnti = (*rar_itr)["TempC_RNTI"]["SameAsC_RNTI"];
|
|
|
|
assert(temp_crnti.IsBool() && temp_crnti.GetBool() == true);
|
|
|
|
cell.temp_crnti = cell.crnti;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_cell(Document& document, const uint8_t* payload, const uint16_t len)
|
|
|
|
{
|
|
|
|
// get Cmd and make sure it has MMI
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// Handle cell creation
|
|
|
|
if (document["Request"]["Cell"].HasMember("AddOrReconfigure")) {
|
|
|
|
if (document["Request"]["Cell"]["AddOrReconfigure"].HasMember("Basic")) {
|
|
|
|
// basic information for a cell (e.g. broadcasting)
|
|
|
|
handle_request_cell_basic(document, payload, len);
|
|
|
|
} else if (document["Request"]["Cell"]["AddOrReconfigure"].HasMember("Active")) {
|
|
|
|
// additional configuration for active cell (i.e. cell being capable to receive RACH preamble)
|
|
|
|
handle_request_cell_active(document, payload, len);
|
|
|
|
}
|
|
|
|
} else if (document["Request"]["Cell"].HasMember("Release")) {
|
|
|
|
logger.info("Received cell release command");
|
|
|
|
// do nothing more
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_l1_mac_ind_ctrl(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("L1MacIndCtrl"));
|
|
|
|
|
|
|
|
const Value& mac_ind_ctrl = req["L1MacIndCtrl"];
|
|
|
|
|
|
|
|
if (mac_ind_ctrl.HasMember("HarqError")) {
|
|
|
|
assert(mac_ind_ctrl["HarqError"].IsString());
|
|
|
|
bool harq_error = (strcmp(mac_ind_ctrl["HarqError"].GetString(), "enable") == 0) ? true : false;
|
|
|
|
logger.info("Setting HarqError to %s", harq_error ? "True" : "False");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ttcn3_helpers::requires_confirm(document)) {
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "L1MacIndCtrl");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_radio_bearer_list(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("RadioBearerList"));
|
|
|
|
|
|
|
|
const Value& bearers = req["RadioBearerList"];
|
|
|
|
assert(bearers.IsArray());
|
|
|
|
|
|
|
|
// iterate over all bearers and configure them
|
|
|
|
for (Value::ConstValueIterator itr = bearers.Begin(); itr != bearers.End(); ++itr) {
|
|
|
|
assert(itr->HasMember("Id"));
|
|
|
|
const Value& id = (*itr)["Id"];
|
|
|
|
if (id.HasMember("Srb")) {
|
|
|
|
const Value& config = (*itr)["Config"];
|
|
|
|
if (config.HasMember("AddOrReconfigure")) {
|
|
|
|
uint32_t lcid = id["Srb"].GetInt();
|
|
|
|
const Value& aor = config["AddOrReconfigure"];
|
|
|
|
if (aor.HasMember("Mac") && aor["Mac"].HasMember("TestMode") && aor["Mac"]["TestMode"].HasMember("Info") &&
|
|
|
|
aor["Mac"]["TestMode"]["Info"].HasMember("DiffLogChId")) {
|
|
|
|
uint32_t force_lcid = 0;
|
|
|
|
const Value& dlcid = aor["Mac"]["TestMode"]["Info"]["DiffLogChId"];
|
|
|
|
assert(dlcid.HasMember("LogChId"));
|
|
|
|
force_lcid = dlcid["LogChId"].GetInt();
|
|
|
|
logger.info("TestMode: lcid overridden: %d", force_lcid);
|
|
|
|
syssim->set_forced_lcid(force_lcid);
|
|
|
|
} else {
|
|
|
|
// Unset override function to use different lcid
|
|
|
|
logger.info("TestMode: lcid reset");
|
|
|
|
syssim->set_forced_lcid(-1);
|
|
|
|
}
|
|
|
|
if (lcid > 0) {
|
|
|
|
pdcp_config_t pdcp_cfg = make_srb_pdcp_config_t(static_cast<uint8_t>(lcid), false);
|
|
|
|
syssim->add_srb(
|
|
|
|
ttcn3_helpers::get_timing_info(document), ttcn3_helpers::get_cell_name(document), lcid, pdcp_cfg);
|
|
|
|
}
|
|
|
|
} else if (config.HasMember("Release")) {
|
|
|
|
uint32_t lcid = id["Srb"].GetInt();
|
|
|
|
syssim->del_srb(ttcn3_helpers::get_timing_info(document), ttcn3_helpers::get_cell_name(document), lcid);
|
|
|
|
} else {
|
|
|
|
logger.error("Unknown config.");
|
|
|
|
}
|
|
|
|
} else if (id.HasMember("Drb")) {
|
|
|
|
logger.info("Configure DRB%d", id["Drb"].GetInt());
|
|
|
|
|
|
|
|
const Value& config = (*itr)["Config"];
|
|
|
|
if (config.HasMember("AddOrReconfigure")) {
|
|
|
|
const Value& aor = config["AddOrReconfigure"];
|
|
|
|
if (aor.HasMember("LogicalChannelId")) {
|
|
|
|
uint32_t lcid = aor["LogicalChannelId"].GetInt();
|
|
|
|
if (lcid > 0) {
|
|
|
|
pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(static_cast<uint8_t>(lcid), false);
|
|
|
|
syssim->add_drb(
|
|
|
|
ttcn3_helpers::get_timing_info(document), ttcn3_helpers::get_cell_name(document), lcid, pdcp_cfg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (config.HasMember("Release")) {
|
|
|
|
uint32_t lcid = id["Drb"].GetInt() + 2;
|
|
|
|
syssim->del_drb(ttcn3_helpers::get_timing_info(document), ttcn3_helpers::get_cell_name(document), lcid);
|
|
|
|
} else {
|
|
|
|
logger.error("Unknown config.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ttcn3_helpers::requires_confirm(document)) {
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "RadioBearerList");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
} else {
|
|
|
|
logger.info("Skipping response for radio bearer list message.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_cell_attenuation_list(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("CellAttenuationList"));
|
|
|
|
|
|
|
|
const Value& cells = req["CellAttenuationList"];
|
|
|
|
assert(cells.IsArray());
|
|
|
|
|
|
|
|
// iterate over all bearers and configure them
|
|
|
|
for (Value::ConstValueIterator itr = cells.Begin(); itr != cells.End(); ++itr) {
|
|
|
|
assert(itr->HasMember("CellId"));
|
|
|
|
const Value& id = (*itr)["CellId"];
|
|
|
|
|
|
|
|
assert(itr->HasMember("Attenuation"));
|
|
|
|
const Value& att = (*itr)["Attenuation"];
|
|
|
|
|
|
|
|
float att_value = 0;
|
|
|
|
if (att.HasMember("Value")) {
|
|
|
|
att_value = att["Value"].GetInt();
|
|
|
|
} else if (att.HasMember("Off")) {
|
|
|
|
// is there other values than Off=True?
|
|
|
|
assert(att["Off"].GetBool() == true);
|
|
|
|
if (att["Off"].GetBool() == true) {
|
|
|
|
// use high attenuation value (-145dB RX power as per TS 36.508 Sec 6.2.2.1-1 is a non-suitable Off cell)
|
|
|
|
att_value = 90.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
logger.info("Configuring attenuation of %s to %.2f dB", id.GetString(), att_value);
|
|
|
|
syssim->set_cell_attenuation(ttcn3_helpers::get_timing_info(document), id.GetString(), att_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "CellAttenuationList");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_pdcp_count(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("PdcpCount"));
|
|
|
|
|
|
|
|
const Value& pdcp_count = req["PdcpCount"];
|
|
|
|
assert(pdcp_count.HasMember("Get"));
|
|
|
|
|
|
|
|
const Value& get = pdcp_count["Get"];
|
|
|
|
assert(get.HasMember("AllRBs"));
|
|
|
|
|
|
|
|
std::string resp = ttcn3_helpers::get_pdcp_count_response(
|
|
|
|
cell_id.GetString(), syssim->get_pdcp_count(ttcn3_helpers::get_cell_name(document)));
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_as_security(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
const Value& config_flag = b["CnfFlag"];
|
|
|
|
assert(config_flag.IsBool());
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("AS_Security"));
|
|
|
|
|
|
|
|
// check AS security start
|
|
|
|
const Value& as_sec = req["AS_Security"];
|
|
|
|
if (as_sec.HasMember("StartRestart")) {
|
|
|
|
// get integrity algo
|
|
|
|
srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo = {};
|
|
|
|
std::string int_algo_string = as_sec["StartRestart"]["Integrity"]["Algorithm"].GetString();
|
|
|
|
if (int_algo_string == "eia0") {
|
|
|
|
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_EIA0;
|
|
|
|
} else if (int_algo_string == "eia1") {
|
|
|
|
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA1;
|
|
|
|
} else if (int_algo_string == "eia2") {
|
|
|
|
integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA2;
|
|
|
|
} else {
|
|
|
|
logger.error("Unsupported integrity algorithm %s", int_algo_string.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// get integrity key
|
|
|
|
std::string integ_key_string = as_sec["StartRestart"]["Integrity"]["KRRCint"].GetString();
|
|
|
|
std::array<uint8_t, 32> k_rrc_int = get_key_from_string(integ_key_string);
|
|
|
|
logger.debug(k_rrc_int.data(), k_rrc_int.size(), "K_rrc_int");
|
|
|
|
|
|
|
|
// get enc algo
|
|
|
|
srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo = {};
|
|
|
|
std::string cipher_algo_string = as_sec["StartRestart"]["Ciphering"]["Algorithm"].GetString();
|
|
|
|
if (cipher_algo_string == "eea0") {
|
|
|
|
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0;
|
|
|
|
} else if (cipher_algo_string == "eea1") {
|
|
|
|
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA1;
|
|
|
|
} else if (cipher_algo_string == "eea2") {
|
|
|
|
cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA2;
|
|
|
|
} else {
|
|
|
|
logger.error("Unsupported ciphering algorithm %s", cipher_algo_string.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
// get cipher key
|
|
|
|
std::string cipher_key_string = as_sec["StartRestart"]["Ciphering"]["KRRCenc"].GetString();
|
|
|
|
std::array<uint8_t, 32> k_rrc_enc = get_key_from_string(cipher_key_string);
|
|
|
|
logger.debug(k_rrc_enc.data(), k_rrc_enc.size(), "K_rrc_enc");
|
|
|
|
|
|
|
|
// get UP enc key
|
|
|
|
std::string up_enc_key_string = as_sec["StartRestart"]["Ciphering"]["KUPenc"].GetString();
|
|
|
|
std::array<uint8_t, 32> k_up_enc = get_key_from_string(up_enc_key_string);
|
|
|
|
logger.debug(k_up_enc.data(), k_up_enc.size(), "K_UP_enc");
|
|
|
|
|
|
|
|
// parse ActTimeList
|
|
|
|
ttcn3_helpers::pdcp_count_map_t bearers;
|
|
|
|
if (as_sec["StartRestart"]["Ciphering"].HasMember("ActTimeList")) {
|
|
|
|
const Value& act_time_list = as_sec["StartRestart"]["Ciphering"]["ActTimeList"];
|
|
|
|
if (act_time_list.IsArray()) {
|
|
|
|
for (Value::ConstValueIterator itr = act_time_list.Begin(); itr != act_time_list.End(); ++itr) {
|
|
|
|
ttcn3_helpers::pdcp_count_t bearer = {};
|
|
|
|
|
|
|
|
// obtain LCID and type
|
|
|
|
if (itr->HasMember("RadioBearerId") && (*itr)["RadioBearerId"].HasMember("Srb")) {
|
|
|
|
bearer.rb_is_srb = true;
|
|
|
|
bearer.rb_id = (*itr)["RadioBearerId"]["Srb"].GetInt();
|
|
|
|
} else if (itr->HasMember("RadioBearerId") && (*itr)["RadioBearerId"].HasMember("Drb")) {
|
|
|
|
bearer.rb_is_srb = false;
|
|
|
|
bearer.rb_id = (*itr)["RadioBearerId"]["Drb"].GetInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
// obtain UL count
|
|
|
|
if (itr->HasMember("UL")) {
|
|
|
|
bearer.ul_value = (*itr)["UL"]["SQN"]["Value"].GetInt();
|
|
|
|
bearer.ul_value_valid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// obtain DL count
|
|
|
|
if (itr->HasMember("DL")) {
|
|
|
|
bearer.dl_value = (*itr)["DL"]["SQN"]["Value"].GetInt();
|
|
|
|
bearer.dl_value_valid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// append to list
|
|
|
|
bearers.push_back(bearer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// configure SS to use AS security
|
|
|
|
syssim->set_as_security(ttcn3_helpers::get_timing_info(document),
|
|
|
|
cell_id.GetString(),
|
|
|
|
k_rrc_enc,
|
|
|
|
k_rrc_int,
|
|
|
|
k_up_enc,
|
|
|
|
cipher_algo,
|
|
|
|
integ_algo,
|
|
|
|
bearers);
|
|
|
|
} else if (as_sec.HasMember("Release")) {
|
|
|
|
// release all security configs
|
|
|
|
syssim->release_as_security(ttcn3_helpers::get_timing_info(document), cell_id.GetString());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (config_flag.GetBool() == true) {
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "AS_Security");
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
} else {
|
|
|
|
logger.info("Skipping response for AS_Security message.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::array<uint8_t, 32> get_key_from_string(const std::string& str)
|
|
|
|
{
|
|
|
|
std::array<uint8_t, 32> key = {};
|
|
|
|
if (str.size() == 128) {
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
|
|
std::string byte_string(str, i * 8, 8);
|
|
|
|
key.at(i + 16) = std::stoul(byte_string, 0, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_enquire_timing(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("EnquireTiming"));
|
|
|
|
|
|
|
|
std::string resp =
|
|
|
|
ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "EnquireTiming", syssim->get_tti());
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_paging(Document& document, const uint8_t* payload, const uint16_t len)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("Paging"));
|
|
|
|
|
|
|
|
// Pack payload into
|
|
|
|
// Inform SYSSIM about paging
|
|
|
|
|
|
|
|
uint16_t consumed_bytes = 0;
|
|
|
|
const uint8_t* payload_ptr = payload;
|
|
|
|
|
|
|
|
uint16_t tb_len = ((uint16_t)payload_ptr[0] << 8) | payload_ptr[1];
|
|
|
|
payload_ptr += 2;
|
|
|
|
|
|
|
|
unique_byte_buffer_t pch = pool_allocate_blocking;
|
|
|
|
memcpy(pch->msg, payload_ptr, tb_len);
|
|
|
|
payload_ptr += tb_len;
|
|
|
|
pch->N_bytes = tb_len;
|
|
|
|
|
|
|
|
// Push to main component
|
|
|
|
logger.info(pch->msg, pch->N_bytes, "Received PCH DL-SCH");
|
|
|
|
syssim->add_pch_pdu(std::move(pch));
|
|
|
|
|
|
|
|
if (ttcn3_helpers::requires_confirm(document)) {
|
|
|
|
std::string resp = ttcn3_helpers::get_sys_req_cnf_with_time(cell_id.GetString(), "Paging", syssim->get_tti());
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
} else {
|
|
|
|
logger.info("Skipping response for Paging message.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handle_request_pdcp_handover_control(Document& document)
|
|
|
|
{
|
|
|
|
const Value& a = document["Common"];
|
|
|
|
|
|
|
|
assert(a.HasMember("CellId"));
|
|
|
|
const Value& cell_id = a["CellId"];
|
|
|
|
|
|
|
|
// check cnf flag
|
|
|
|
assert(a.HasMember("ControlInfo"));
|
|
|
|
const Value& b = a["ControlInfo"];
|
|
|
|
assert(b.HasMember("CnfFlag"));
|
|
|
|
|
|
|
|
// check request
|
|
|
|
const Value& req = document["Request"];
|
|
|
|
assert(req.HasMember("PdcpHandoverControl"));
|
|
|
|
|
|
|
|
const Value& ho_control = req["PdcpHandoverControl"];
|
|
|
|
|
|
|
|
if (ho_control.HasMember("HandoverInit")) {
|
|
|
|
const Value& ho_init = ho_control["HandoverInit"];
|
|
|
|
assert(ho_init.HasMember("SourceCellId"));
|
|
|
|
const Value& ho_source_cellid = ho_init["SourceCellId"];
|
|
|
|
}
|
|
|
|
|
|
|
|
// send confirm
|
|
|
|
if (ttcn3_helpers::requires_confirm(document)) {
|
|
|
|
std::string resp = ttcn3_helpers::get_basic_sys_req_cnf(cell_id.GetString(), "PdcpHandoverControl");
|
|
|
|
|
|
|
|
logger.info("Sending %s to tester (%zd B)", resp.c_str(), resp.length());
|
|
|
|
send((const uint8_t*)resp.c_str(), resp.length());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ss_sys_interface* syssim = nullptr;
|
|
|
|
byte_buffer_pool* pool = nullptr;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // SRSUE_TTCN3_SYS_INTERFACE_H
|