diff --git a/common/utils/telnetsrv/CMakeLists.txt b/common/utils/telnetsrv/CMakeLists.txt index d310e1162c..d7b9b3cf9d 100644 --- a/common/utils/telnetsrv/CMakeLists.txt +++ b/common/utils/telnetsrv/CMakeLists.txt @@ -59,8 +59,13 @@ add_library(telnetsrv_ci MODULE telnetsrv_ci.c) target_link_libraries(telnetsrv_ci PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_dependencies(telnetsrv telnetsrv_ci) +message(STATUS "Add bearer specific telnet functions in libtelnetsrv_bearer.so") +add_library(telnetsrv_bearer MODULE telnetsrv_bearer.c) +target_link_libraries(telnetsrv_bearer PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) +add_dependencies(telnetsrv telnetsrv_bearer) + # all libraries should be written to root build dir -set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci +set_target_properties(telnetsrv telnetsrv_enb telnetsrv_5Gue telnetsrv_ci telnetsrv_bearer PROPERTIES LIBRARY_OUTPUT_DIRECTORY ../../.. ) diff --git a/common/utils/telnetsrv/telnetsrv_bearer.c b/common/utils/telnetsrv/telnetsrv_bearer.c new file mode 100644 index 0000000000..f019d3d48b --- /dev/null +++ b/common/utils/telnetsrv/telnetsrv_bearer.c @@ -0,0 +1,121 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "openair2/RRC/NR/rrc_gNB_UE_context.h" + +#define TELNETSERVERCODE +#include "telnetsrv.h" + +#define ERROR_MSG_RET(mSG, aRGS...) do { prnt(mSG, ##aRGS); return 1; } while (0) + +static int get_single_ue_rnti(void) +{ + rrc_gNB_ue_context_t *ue_context_p = NULL; + RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &(RC.nrrrc[0]->rrc_ue_head)) { + return ue_context_p->ue_context.rnti; + } + return -1; +} + +int get_single_rnti(char *buf, int debug, telnet_printfunc_t prnt) +{ + if (buf) + ERROR_MSG_RET("no parameter allowed\n"); + + int rnti = get_single_ue_rnti(); + if (rnti < 1) + ERROR_MSG_RET("different number of UEs\n"); + + prnt("single UE RNTI %04x\n", rnti); + return 0; +} + +void rrc_gNB_trigger_new_bearer(int rnti); +int add_bearer(char *buf, int debug, telnet_printfunc_t prnt) +{ + int rnti = -1; + if (!buf) { + rnti = get_single_ue_rnti(); + if (rnti < 1) + ERROR_MSG_RET("no UE found\n"); + } else { + rnti = strtol(buf, NULL, 16); + if (rnti < 1 || rnti >= 0xfffe) + ERROR_MSG_RET("RNTI needs to be [1,0xfffe]\n"); + } + + // verify it exists in RRC as well + rrc_gNB_ue_context_t *rrcue = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], rnti); + if (!rrcue) + ERROR_MSG_RET("could not find UE with RNTI %04x\n", rnti); + + rrc_gNB_trigger_new_bearer(rnti); + prnt("called rrc_gNB_trigger_new_bearer(%04x)\n", rnti); + return 0; +} + +void rrc_gNB_trigger_release_bearer(int rnti); +int release_bearer(char *buf, int debug, telnet_printfunc_t prnt) +{ + int rnti = -1; + if (!buf) { + rnti = get_single_ue_rnti(); + if (rnti < 1) + ERROR_MSG_RET("no UE found\n"); + } else { + rnti = strtol(buf, NULL, 16); + if (rnti < 1 || rnti >= 0xfffe) + ERROR_MSG_RET("RNTI needs to be [1,0xfffe]\n"); + } + + // verify it exists in RRC as well + rrc_gNB_ue_context_t *rrcue = rrc_gNB_get_ue_context_by_rnti(RC.nrrrc[0], rnti); + if (!rrcue) + ERROR_MSG_RET("could not find UE with RNTI %04x\n", rnti); + + rrc_gNB_trigger_release_bearer(rnti); + prnt("called rrc_gNB_trigger_release_bearer(%04x)\n", rnti); + return 0; +} + +static telnetshell_cmddef_t bearercmds[] = { + {"get_single_rnti", "", get_single_rnti}, + {"add_bearer", "[rnti(hex,opt)]", add_bearer}, + {"release_bearer", "[rnti(hex,opt)]", release_bearer}, + {"", "", NULL}, +}; + +static telnetshell_vardef_t bearervars[] = { + + {"", 0, 0, NULL} +}; + +void add_bearer_cmds(void) { + add_telnetcmd("bearer", bearervars, bearercmds); +} diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index df7f71b23b..a4809f3852 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -46,6 +46,7 @@ #include "LAYER2/NR_MAC_gNB/mac_proto.h" #include "common/utils/LOG/log.h" #include "RRC/NR/MESSAGES/asn1_msg.h" +#include "openair2/E1AP/e1ap_asnc.h" #include "NR_BCCH-BCH-Message.h" #include "NR_UL-DCCH-Message.h" @@ -2858,6 +2859,147 @@ rrc_gNB_generate_RRCRelease( /* UE will be freed after UE context release complete */ } +void rrc_gNB_trigger_new_bearer(int rnti) +{ + /* get RRC and UE */ + gNB_RRC_INST *rrc = RC.nrrrc[0]; + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, rnti); + if (ue_context_p == NULL) { + LOG_E(RRC, "unknown UE RNTI %04x\n", rnti); + return; + } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; + + /* get the existing PDU sessoin */ + if (ue->nb_of_pdusessions < 1) { + LOG_E(RRC, "no PDU session set up yet, cannot create additional bearer\n"); + return; + } + + if (ue->established_drbs[0].status != DRB_INACTIVE + && ue->established_drbs[1].status != DRB_INACTIVE) { + LOG_E(RRC, "already have two established bearers, aborting\n"); + return; + } + + e1ap_bearer_setup_req_t bearer_req = {0}; + bearer_req.gNB_cu_cp_ue_id = ue->rrc_ue_id; + bearer_req.cipheringAlgorithm = ue->ciphering_algorithm; + memcpy(bearer_req.encryptionKey, ue->kgnb, sizeof(ue->kgnb)); + bearer_req.integrityProtectionAlgorithm = ue->integrity_algorithm; + memcpy(bearer_req.integrityProtectionKey, ue->kgnb, sizeof(ue->kgnb)); + bearer_req.ueDlAggMaxBitRate = 10000; /* probably does not matter */ + + pdu_session_to_setup_t *pdu = &bearer_req.pduSession[0]; + //bearer_req.numPDUSessions++; + bearer_req.numPDUSessions = 1; + //pdu->sessionId = session->pdusession_id; + //pdu->sst = msg->allowed_nssai[i].sST; + //pdu->integrityProtectionIndication = rrc->security.do_drb_integrity ? E1AP_IntegrityProtectionIndication_required : E1AP_IntegrityProtectionIndication_not_needed; + + //pdu->confidentialityProtectionIndication = rrc->security.do_drb_ciphering ? E1AP_ConfidentialityProtectionIndication_required : E1AP_ConfidentialityProtectionIndication_not_needed; + //pdu->teId = session->gtp_teid; + pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding + DRB_nGRAN_to_setup_t *drb = &pdu->DRBnGRanList[0]; + int drb_id = 2; + drb->id = drb_id; + + drb->defaultDRB = E1AP_DefaultDRB_false; + drb->sDAP_Header_UL = !(rrc->configuration.enable_sdap); + drb->sDAP_Header_DL = !(rrc->configuration.enable_sdap); + + drb->pDCP_SN_Size_UL = E1AP_PDCP_SN_Size_s_18; + drb->pDCP_SN_Size_DL = E1AP_PDCP_SN_Size_s_18; + + drb->discardTimer = E1AP_DiscardTimer_infinity; + drb->reorderingTimer = E1AP_T_Reordering_ms0; + + drb->rLC_Mode = E1AP_RLC_Mode_rlc_am; + + drb->numCellGroups = 1; // assume one cell group associated with a DRB + + for (int k=0; k < drb->numCellGroups; k++) { + cell_group_t *cellGroup = drb->cellGroupList + k; + cellGroup->id = 0; // MCG + } + + int xid = rrc_gNB_get_next_transaction_identifier(0); + /* generate a new bearer, it will be put into internal RRC state and picked + * up later */ + generateDRB(ue, + drb_id, + &ue->pduSession[0], + rrc->configuration.enable_sdap, + rrc->security.do_drb_integrity, + rrc->security.do_drb_ciphering); + + /* associate the new bearer to it */ + ue->xids[xid] = RRC_PDUSESSION_MODIFY; + ue->pduSession[0].xid = xid; // hack: fake xid for ongoing PDU session + LOG_W(RRC, "trigger new bearer %ld for UE %04x xid %d\n", drb->id, ue->rnti, xid); + rrc->cucp_cuup.bearer_context_setup(&bearer_req, 0); +} + +void rrc_gNB_trigger_release_bearer(int rnti) +{ + /* get RRC and UE */ + gNB_RRC_INST *rrc = RC.nrrrc[0]; + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, rnti); + if (ue_context_p == NULL) { + LOG_E(RRC, "unknown UE RNTI %04x\n", rnti); + return; + } + gNB_RRC_UE_t *ue = &ue_context_p->ue_context; + + if (ue->established_drbs[1].status == DRB_INACTIVE) { + LOG_E(RRC, "no second bearer, aborting\n"); + return; + } + + // don't use E1: bearer release is not implemented, call directly + // into PDCP/SDAP and then send corresponding message via F1 + + int drb_id = 2; + ue->established_drbs[1].status = DRB_INACTIVE; + ue->DRB_ReleaseList = calloc(1, sizeof(*ue->DRB_ReleaseList)); + AssertFatal(ue->DRB_ReleaseList != NULL, "out of memory\n"); + NR_DRB_Identity_t *asn1_drb = malloc(sizeof(*asn1_drb)); + AssertFatal(asn1_drb != NULL, "out of memory\n"); + int idx = 0; + NR_DRB_ToAddModList_t *drb_list = createDRBlist(ue, false); + while (idx < drb_list->list.count) { + const NR_DRB_ToAddMod_t *drbc = drb_list->list.array[idx]; + if (drbc->drb_Identity == drb_id) + break; + ++idx; + } + if (idx < drb_list->list.count) { + nr_pdcp_release_drb(rnti, drb_id); + asn_sequence_del(&drb_list->list, idx, 1); + } + *asn1_drb = drb_id; + asn1cSeqAdd(&ue->DRB_ReleaseList->list, asn1_drb); + + f1ap_drb_to_be_released_t drbs_to_be_released[1] = {{.rb_id = drb_id}}; + f1_ue_data_t ue_data = cu_get_f1_ue_data(ue->rrc_ue_id); + f1ap_ue_context_modif_req_t ue_context_modif_req = { + .gNB_CU_ue_id = ue->rrc_ue_id, + .gNB_DU_ue_id = ue_data.secondary_ue, + .plmn.mcc = rrc->configuration.mcc[0], + .plmn.mnc = rrc->configuration.mnc[0], + .plmn.mnc_digit_length = rrc->configuration.mnc_digit_length[0], + .nr_cellid = rrc->nr_cellid, + .servCellId = 0, /* TODO: correct value? */ + .srbs_to_be_setup_length = 0, + .srbs_to_be_setup = NULL, + .drbs_to_be_setup_length = 0, + .drbs_to_be_setup = NULL, + .drbs_to_be_released_length = 1, + .drbs_to_be_released = drbs_to_be_released, + }; + rrc->mac_rrc.ue_context_modification_request(&ue_context_modif_req); +} + int rrc_gNB_generate_pcch_msg(uint32_t tmsi, uint8_t paging_drx, instance_t instance, uint8_t CC_id){ const unsigned int Ttab[4] = {32,64,128,256}; uint8_t Tc;