384 lines
14 KiB
C++
384 lines
14 KiB
C++
/*
|
||
* Copyright 2013-2020 Software Radio Systems Limited
|
||
*
|
||
* 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 "srsenb/hdr/stack/rrc/rrc_bearer_cfg.h"
|
||
#include "srsenb/hdr/stack/upper/common_enb.h"
|
||
#include "srslte/asn1/rrc_asn1_utils.h"
|
||
#include "srslte/rrc/rrc_cfg_utils.h"
|
||
|
||
namespace srsenb {
|
||
|
||
using namespace asn1::rrc;
|
||
|
||
asn1::rrc::security_algorithm_cfg_s security_cfg_handler::get_security_algorithm_cfg()
|
||
{
|
||
asn1::rrc::security_algorithm_cfg_s ret;
|
||
// TODO: select these based on UE capabilities and preference order
|
||
ret.integrity_prot_algorithm = (security_algorithm_cfg_s::integrity_prot_algorithm_e_::options)sec_cfg.integ_algo;
|
||
ret.ciphering_algorithm = (ciphering_algorithm_r12_e::options)sec_cfg.cipher_algo;
|
||
return ret;
|
||
}
|
||
|
||
bool security_cfg_handler::set_security_capabilities(const asn1::s1ap::ue_security_cap_s& caps)
|
||
{
|
||
security_capabilities = caps;
|
||
|
||
// Selects security algorithms (cipher_algo and integ_algo) based on capabilities and config preferences
|
||
// Each position in the bitmap represents an encryption algorithm:
|
||
// “all bits equal to 0” – UE supports no other algorithm than EEA0,
|
||
// “first bit” – 128-EEA1,
|
||
// “second bit” – 128-EEA2,
|
||
// “third bit” – 128-EEA3,
|
||
// other bits reserved for future use. Value ‘1’ indicates support and value
|
||
// ‘0’ indicates no support of the algorithm.
|
||
// Algorithms are defined in TS 33.401 [15].
|
||
// Note: information missing
|
||
|
||
bool enc_algo_found = false;
|
||
bool integ_algo_found = false;
|
||
|
||
for (auto& cipher_item : cfg->eea_preference_list) {
|
||
auto& v = security_capabilities.encryption_algorithms;
|
||
switch (cipher_item) {
|
||
case srslte::CIPHERING_ALGORITHM_ID_EEA0:
|
||
// “all bits equal to 0” – UE supports no other algorithm than EEA0,
|
||
// specification does not cover the case in which EEA0 is supported with other algorithms
|
||
// just assume that EEA0 is always supported even this can not be explicity signaled by S1AP
|
||
sec_cfg.cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0;
|
||
enc_algo_found = true;
|
||
log_h->info("Selected EEA0 as RRC encryption algorithm\n");
|
||
break;
|
||
case srslte::CIPHERING_ALGORITHM_ID_128_EEA1:
|
||
// “first bit” – 128-EEA1,
|
||
if (v.get(v.length() - srslte::CIPHERING_ALGORITHM_ID_128_EEA1)) {
|
||
sec_cfg.cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA1;
|
||
enc_algo_found = true;
|
||
log_h->info("Selected EEA1 as RRC encryption algorithm\n");
|
||
break;
|
||
} else {
|
||
log_h->info("Failed to selected EEA1 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
case srslte::CIPHERING_ALGORITHM_ID_128_EEA2:
|
||
// “second bit” – 128-EEA2,
|
||
if (v.get(v.length() - srslte::CIPHERING_ALGORITHM_ID_128_EEA2)) {
|
||
sec_cfg.cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA2;
|
||
enc_algo_found = true;
|
||
log_h->info("Selected EEA2 as RRC encryption algorithm\n");
|
||
break;
|
||
} else {
|
||
log_h->info("Failed to selected EEA2 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
case srslte::CIPHERING_ALGORITHM_ID_128_EEA3:
|
||
// “third bit” – 128-EEA3,
|
||
if (v.get(v.length() - srslte::CIPHERING_ALGORITHM_ID_128_EEA3)) {
|
||
sec_cfg.cipher_algo = srslte::CIPHERING_ALGORITHM_ID_128_EEA3;
|
||
enc_algo_found = true;
|
||
log_h->info("Selected EEA3 as RRC encryption algorithm\n");
|
||
break;
|
||
} else {
|
||
log_h->info("Failed to selected EEA2 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
default:
|
||
enc_algo_found = false;
|
||
break;
|
||
}
|
||
if (enc_algo_found) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
for (auto& eia_enum : cfg->eia_preference_list) {
|
||
auto& v = security_capabilities.integrity_protection_algorithms;
|
||
switch (eia_enum) {
|
||
case srslte::INTEGRITY_ALGORITHM_ID_EIA0:
|
||
// Null integrity is not supported
|
||
log_h->info("Skipping EIA0 as RRC integrity algorithm. Null integrity is not supported.\n");
|
||
break;
|
||
case srslte::INTEGRITY_ALGORITHM_ID_128_EIA1:
|
||
// “first bit” – 128-EIA1,
|
||
if (v.get(v.length() - srslte::INTEGRITY_ALGORITHM_ID_128_EIA1)) {
|
||
sec_cfg.integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA1;
|
||
integ_algo_found = true;
|
||
log_h->info("Selected EIA1 as RRC integrity algorithm.\n");
|
||
} else {
|
||
log_h->info("Failed to selected EIA1 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
case srslte::INTEGRITY_ALGORITHM_ID_128_EIA2:
|
||
// “second bit” – 128-EIA2,
|
||
if (v.get(v.length() - srslte::INTEGRITY_ALGORITHM_ID_128_EIA2)) {
|
||
sec_cfg.integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA2;
|
||
integ_algo_found = true;
|
||
log_h->info("Selected EIA2 as RRC integrity algorithm.\n");
|
||
} else {
|
||
log_h->info("Failed to selected EIA2 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
case srslte::INTEGRITY_ALGORITHM_ID_128_EIA3:
|
||
// “third bit” – 128-EIA3,
|
||
if (v.get(v.length() - srslte::INTEGRITY_ALGORITHM_ID_128_EIA3)) {
|
||
sec_cfg.integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA3;
|
||
integ_algo_found = true;
|
||
log_h->info("Selected EIA3 as RRC integrity algorithm.\n");
|
||
} else {
|
||
log_h->info("Failed to selected EIA3 as RRC encryption algorithm, due to unsupported algorithm\n");
|
||
}
|
||
break;
|
||
default:
|
||
integ_algo_found = false;
|
||
break;
|
||
}
|
||
|
||
if (integ_algo_found) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (not integ_algo_found || not enc_algo_found) {
|
||
// TODO: if no security algorithm found abort radio connection and issue
|
||
// encryption-and-or-integrity-protection-algorithms-not-supported message
|
||
log_h->error("Did not find a matching integrity or encryption algorithm with the UE\n");
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
void security_cfg_handler::set_security_key(const asn1::fixed_bitstring<256, false, true>& key)
|
||
{
|
||
k_enb_present = true;
|
||
for (uint32_t i = 0; i < key.nof_octets(); ++i) {
|
||
k_enb[i] = key.data()[key.nof_octets() - 1 - i];
|
||
}
|
||
log_h->info_hex(k_enb, 32, "Key eNodeB (k_enb)");
|
||
|
||
generate_as_keys();
|
||
}
|
||
|
||
void security_cfg_handler::generate_as_keys()
|
||
{
|
||
// Generate K_rrc_enc and K_rrc_int
|
||
srslte::security_generate_k_rrc(
|
||
k_enb, sec_cfg.cipher_algo, sec_cfg.integ_algo, sec_cfg.k_rrc_enc.data(), sec_cfg.k_rrc_int.data());
|
||
|
||
// Generate K_up_enc and K_up_int
|
||
security_generate_k_up(
|
||
k_enb, sec_cfg.cipher_algo, sec_cfg.integ_algo, sec_cfg.k_up_enc.data(), sec_cfg.k_up_int.data());
|
||
|
||
log_h->info_hex(k_enb, 32, "K_eNB (k_enb)");
|
||
log_h->info_hex(sec_cfg.k_rrc_enc.data(), 32, "RRC Encryption Key (k_rrc_enc)");
|
||
log_h->info_hex(sec_cfg.k_rrc_int.data(), 32, "RRC Integrity Key (k_rrc_int)");
|
||
log_h->info_hex(sec_cfg.k_up_enc.data(), 32, "UP Encryption Key (k_up_enc)");
|
||
}
|
||
|
||
void security_cfg_handler::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_earfcn)
|
||
{
|
||
log_h->info("Regenerating KeNB with PCI=0x%02x, DL-EARFCN=%d\n", new_pci, new_dl_earfcn);
|
||
log_h->info_hex(k_enb, 32, "Old K_eNB (k_enb)");
|
||
// Generate K_enb*
|
||
uint8_t k_enb_star[32];
|
||
srslte::security_generate_k_enb_star(k_enb, new_pci, new_dl_earfcn, k_enb_star);
|
||
|
||
// K_enb becomes K_enb*
|
||
memcpy(k_enb, k_enb_star, 32);
|
||
|
||
generate_as_keys();
|
||
}
|
||
|
||
/*****************************
|
||
* Bearer Handler
|
||
****************************/
|
||
|
||
bearer_cfg_handler::bearer_cfg_handler(uint16_t rnti_, const rrc_cfg_t& cfg_) : rnti(rnti_), cfg(&cfg_) {}
|
||
|
||
void bearer_cfg_handler::add_srb(uint8_t srb_id)
|
||
{
|
||
if (srb_id > 2 or srb_id == 0) {
|
||
log_h->error("Invalid SRB id=%d\n", srb_id);
|
||
return;
|
||
}
|
||
|
||
// Set SRBtoAddMod
|
||
auto srb_it = srslte::add_rrc_obj_id(srbs_to_add, srb_id);
|
||
srb_it->lc_ch_cfg_present = true;
|
||
srb_it->lc_ch_cfg.set(srb_to_add_mod_s::lc_ch_cfg_c_::types_opts::default_value);
|
||
srb_it->rlc_cfg_present = true;
|
||
srb_it->rlc_cfg.set(srb_to_add_mod_s::rlc_cfg_c_::types_opts::default_value);
|
||
}
|
||
|
||
int bearer_cfg_handler::add_erab(uint8_t erab_id,
|
||
const asn1::s1ap::erab_level_qos_params_s& qos,
|
||
const asn1::bounded_bitstring<1, 160, true, true>& addr,
|
||
uint32_t teid_out,
|
||
const asn1::unbounded_octstring<true>* nas_pdu)
|
||
{
|
||
if (erab_id < 5) {
|
||
log_h->error("ERAB id=%d is invalid\n", erab_id);
|
||
return SRSLTE_ERROR;
|
||
}
|
||
uint8_t lcid = erab_id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1)
|
||
uint8_t drbid = erab_id - 4;
|
||
|
||
if (qos.qci >= MAX_NOF_QCI) {
|
||
log_h->error("Invalid QCI=%d for ERAB_id=%d, DRB_id=%d\n", qos.qci, erab_id, drbid);
|
||
return SRSLTE_ERROR;
|
||
}
|
||
if (not cfg->qci_cfg[qos.qci].configured) {
|
||
log_h->error("QCI=%d not configured\n", qos.qci);
|
||
return SRSLTE_ERROR;
|
||
}
|
||
|
||
erabs[erab_id].id = erab_id;
|
||
erabs[erab_id].qos_params = qos;
|
||
erabs[erab_id].address = addr;
|
||
erabs[erab_id].teid_out = teid_out;
|
||
|
||
if (addr.length() > 32) {
|
||
log_h->error("Only addresses with length <= 32 are supported\n");
|
||
return SRSLTE_ERROR;
|
||
}
|
||
|
||
if (nas_pdu != nullptr and nas_pdu->size() > 0) {
|
||
erab_info_list[erab_id].assign(nas_pdu->data(), nas_pdu->data() + nas_pdu->size());
|
||
log_h->info_hex(
|
||
&erab_info_list[erab_id][0], erab_info_list[erab_id].size(), "setup_erab nas_pdu -> erab_info rnti 0x%x", rnti);
|
||
}
|
||
|
||
// Set DRBtoAddMod
|
||
auto drb_it = srslte::add_rrc_obj_id(drbs_to_add, drbid);
|
||
drb_it->lc_ch_id_present = true;
|
||
drb_it->lc_ch_id = (uint8_t)lcid;
|
||
drb_it->eps_bearer_id_present = true;
|
||
drb_it->eps_bearer_id = (uint8_t)erab_id;
|
||
drb_it->lc_ch_cfg_present = true;
|
||
drb_it->lc_ch_cfg.ul_specific_params_present = true;
|
||
drb_it->lc_ch_cfg.ul_specific_params.lc_ch_group_present = true;
|
||
drb_it->lc_ch_cfg.ul_specific_params = cfg->qci_cfg[qos.qci].lc_cfg;
|
||
drb_it->pdcp_cfg_present = true;
|
||
drb_it->pdcp_cfg = cfg->qci_cfg[qos.qci].pdcp_cfg;
|
||
drb_it->rlc_cfg_present = true;
|
||
drb_it->rlc_cfg = cfg->qci_cfg[qos.qci].rlc_cfg;
|
||
|
||
return SRSLTE_SUCCESS;
|
||
}
|
||
|
||
void bearer_cfg_handler::release_erab(uint8_t erab_id)
|
||
{
|
||
auto it = erabs.find(erab_id);
|
||
if (it == erabs.end()) {
|
||
log_h->warning("The user rnti=0x%x does not contain ERAB-ID=%d\n", rnti, erab_id);
|
||
return;
|
||
}
|
||
|
||
uint8_t drb_id = erab_id - 4;
|
||
drbs_to_release.push_back(drb_id);
|
||
|
||
erabs.erase(it);
|
||
erab_info_list.erase(erab_id);
|
||
}
|
||
|
||
void bearer_cfg_handler::release_erabs()
|
||
{
|
||
// TODO: notify GTPU layer for each ERAB
|
||
erabs.clear();
|
||
while (not erabs.empty()) {
|
||
release_erab(erabs.begin()->first);
|
||
}
|
||
}
|
||
|
||
void bearer_cfg_handler::reest_bearers()
|
||
{
|
||
// Re-add all SRBs/DRBs
|
||
srbs_to_add = current_srbs;
|
||
drbs_to_add = current_drbs;
|
||
}
|
||
|
||
void bearer_cfg_handler::rr_ded_cfg_complete()
|
||
{
|
||
// Apply changes in internal bearer_handler DRB/SRBtoAddModLists
|
||
srslte::apply_addmodlist_diff(current_srbs, srbs_to_add, current_srbs);
|
||
srslte::apply_addmodremlist_diff(current_drbs, drbs_to_add, drbs_to_release, current_drbs);
|
||
|
||
// Reset DRBs/SRBs to Add/mod/release
|
||
srbs_to_add = {};
|
||
drbs_to_add = {};
|
||
drbs_to_release.resize(0);
|
||
}
|
||
|
||
bool bearer_cfg_handler::fill_rr_cfg_ded(asn1::rrc::rr_cfg_ded_s& msg)
|
||
{
|
||
// Add altered bearers to message
|
||
msg.srb_to_add_mod_list_present = srbs_to_add.size() > 0;
|
||
msg.srb_to_add_mod_list = srbs_to_add;
|
||
msg.drb_to_add_mod_list_present = drbs_to_add.size() > 0;
|
||
msg.drb_to_add_mod_list = drbs_to_add;
|
||
msg.drb_to_release_list_present = drbs_to_release.size() > 0;
|
||
msg.drb_to_release_list = drbs_to_release;
|
||
return msg.srb_to_add_mod_list_present or msg.drb_to_add_mod_list_present or msg.drb_to_release_list_present;
|
||
}
|
||
|
||
void bearer_cfg_handler::add_gtpu_bearer(srsenb::gtpu_interface_rrc* gtpu, uint32_t erab_id)
|
||
{
|
||
auto it = erabs.find(erab_id);
|
||
if (it != erabs.end()) {
|
||
erab_t& erab = it->second;
|
||
// Initialize ERAB in GTPU right-away. DRBs are only created during RRC setup/reconf
|
||
uint32_t addr_ = erab.address.to_number();
|
||
erab.teid_in = gtpu->add_bearer(rnti, erab.id - 2, addr_, erab.teid_out);
|
||
} else {
|
||
log_h->error("Adding erab_id=%d to GTPU\n", erab_id);
|
||
}
|
||
}
|
||
|
||
void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg)
|
||
{
|
||
// Add space for NAS messages
|
||
uint8_t n_nas = erab_info_list.size();
|
||
if (n_nas > 0) {
|
||
msg->ded_info_nas_list_present = true;
|
||
msg->ded_info_nas_list.resize(n_nas);
|
||
}
|
||
|
||
uint32_t idx = 0;
|
||
// DRBs have already been configured in GTPU during bearer setup
|
||
// Add E-RAB info message for the E-RABs
|
||
if (msg->rr_cfg_ded.drb_to_add_mod_list_present) {
|
||
for (const drb_to_add_mod_s& drb : msg->rr_cfg_ded.drb_to_add_mod_list) {
|
||
uint8_t erab_id = drb.drb_id + 4;
|
||
auto it = erab_info_list.find(erab_id);
|
||
if (it != erab_info_list.end()) {
|
||
const std::vector<uint8_t>& erab_info = it->second;
|
||
log_h->info_hex(&erab_info[0], erab_info.size(), "connection_reconf erab_info -> nas_info rnti 0x%x\n", rnti);
|
||
msg->ded_info_nas_list[idx].resize(erab_info.size());
|
||
memcpy(msg->ded_info_nas_list[idx].data(), &erab_info[0], erab_info.size());
|
||
erab_info_list.erase(it);
|
||
} else {
|
||
log_h->debug("Not adding NAS message to connection reconfiguration. E-RAB id %d\n", erab_id);
|
||
}
|
||
idx++;
|
||
}
|
||
}
|
||
}
|
||
|
||
} // namespace srsenb
|