rrc_gNB.c 121 KB
Newer Older
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 * 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
 */

/*! \file rrc_gNB.c
 * \brief rrc procedures for gNB
 * \author Navid Nikaein and  Raymond Knopp , WEI-TAI CHEN
 * \date 2011 - 2014 , 2018
 * \version 1.0
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
27 28
 * \company Eurecom, NTUST
 * \email: navid.nikaein@eurecom.fr and raymond.knopp@eurecom.fr, kroempa@gmail.com
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
29
 */
30
#define RRC_GNB_C
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
31 32
#define RRC_GNB_C

Laurent THOMAS's avatar
Laurent THOMAS committed
33 34 35
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
36
#include <time.h>
Laurent THOMAS's avatar
Laurent THOMAS committed
37

38
#include "nr_rrc_config.h"
39 40
#include "nr_rrc_defs.h"
#include "nr_rrc_extern.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
41 42
#include "assertions.h"
#include "common/ran_context.h"
43
#include "oai_asn1.h"
44
#include "rrc_gNB_radio_bearers.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
45 46

#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
47
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
Raymond Knopp's avatar
Raymond Knopp committed
48
#include "common/utils/LOG/log.h"
49
#include "RRC/NR/MESSAGES/asn1_msg.h"
50
#include "openair2/E1AP/e1ap_asnc.h"
51 52 53 54

#include "NR_BCCH-BCH-Message.h"
#include "NR_UL-DCCH-Message.h"
#include "NR_DL-DCCH-Message.h"
55 56 57 58 59 60
#include "NR_DL-CCCH-Message.h"
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCReject.h"
#include "NR_RejectWaitTime.h"
#include "NR_RRCSetup.h"

61
#include "NR_CellGroupConfig.h"
62
#include "NR_MeasResults.h"
63 64
#include "NR_UL-CCCH-Message.h"
#include "NR_RRCSetupRequest-IEs.h"
Xue Song's avatar
Xue Song committed
65
#include "NR_RRCSetupComplete-IEs.h"
66
#include "NR_RRCReestablishmentRequest-IEs.h"
67
#include "NR_MIB.h"
68 69
#include "uper_encoder.h"
#include "uper_decoder.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
70

71
#include "common/platform_types.h"
Raymond Knopp's avatar
Raymond Knopp committed
72
#include "common/utils/LOG/vcd_signal_dumper.h"
73

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
74 75
#include "T.h"

mir's avatar
mir committed
76
#include "openair3/SECU/secu_defs.h"
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
77

Xue Song's avatar
Xue Song committed
78
#include "rrc_gNB_NGAP.h"
79
#include "rrc_gNB_du.h"
80
#include "rrc_gNB_mobility.h"
Xue Song's avatar
Xue Song committed
81

82
#include "rrc_gNB_GTPV1U.h"
83

84
#include "nr_pdcp/nr_pdcp_entity.h"
85
#include "nr_pdcp/nr_pdcp_oai_api.h"
86

87
#include "intertask_interface.h"
88
#include "SIMULATION/TOOLS/sim.h" // for taus
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
89

90
#include "executables/softmodem-common.h"
laurent's avatar
laurent committed
91
#include <openair2/RRC/NR/rrc_gNB_UE_context.h>
Laurent's avatar
Laurent committed
92
#include <openair2/X2AP/x2ap_eNB.h>
mir's avatar
mir committed
93
#include <openair3/SECU/key_nas_deriver.h>
94
#include <openair3/ocp-gtpu/gtp_itf.h>
95
#include <openair2/RRC/NR/nr_rrc_proto.h>
96
#include "openair2/F1AP/f1ap_common.h"
97
#include "openair2/F1AP/f1ap_ids.h"
98
#include "openair2/F1AP/lib/f1ap_lib_extern.h"
99
#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h"
100
#include "openair2/E1AP/e1ap.h"
101
#include "cucp_cuup_if.h"
102
#include "lib/f1ap_interface_management.h"
103

Xue Song's avatar
Xue Song committed
104 105
#include "BIT_STRING.h"
#include "assertions.h"
106

107 108 109 110
#ifdef E2_AGENT
#include "openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_rc_extern.h"
#endif

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
111 112 113 114
//#define XER_PRINT

extern RAN_CONTEXT_t RC;

115
mui_t rrc_gNB_mui = 0;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
116

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
117 118
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///
luis_pereira87's avatar
luis_pereira87 committed
119

120 121 122 123 124 125 126 127
static void clear_nas_pdu(ngap_pdu_t *pdu)
{
  DevAssert(pdu != NULL);
  free(pdu->buffer); // does nothing if NULL
  pdu->buffer = NULL;
  pdu->length = 0;
}

128 129
static void freeDRBlist(NR_DRB_ToAddModList_t *list)
{
laurent's avatar
laurent committed
130
  //ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToAddModList, list);
131 132
  return;
}
133

134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
const neighbour_cell_configuration_t *get_neighbour_config(int serving_cell_nr_cellid)
{
  const gNB_RRC_INST *rrc = RC.nrrrc[0];
  seq_arr_t *neighbour_cell_configuration = rrc->neighbour_cell_configuration;
  if (!neighbour_cell_configuration)
    return NULL;

  for (int cellIdx = 0; cellIdx < neighbour_cell_configuration->size; cellIdx++) {
    neighbour_cell_configuration_t *neighbour_config =
        (neighbour_cell_configuration_t *)seq_arr_at(neighbour_cell_configuration, cellIdx);
    if (neighbour_config->nr_cell_id == serving_cell_nr_cellid)
      return neighbour_config;
  }
  return NULL;
}

const nr_neighbour_gnb_configuration_t *get_neighbour_cell_information(int serving_cell_nr_cellid, int neighbour_cell_phy_id)
{
  const gNB_RRC_INST *rrc = RC.nrrrc[0];
  seq_arr_t *neighbour_cell_configuration = rrc->neighbour_cell_configuration;
  for (int cellIdx = 0; cellIdx < neighbour_cell_configuration->size; cellIdx++) {
    neighbour_cell_configuration_t *neighbour_config =
        (neighbour_cell_configuration_t *)seq_arr_at(neighbour_cell_configuration, cellIdx);
    if (!neighbour_config)
      continue;

    for (int neighbourIdx = 0; neighbourIdx < neighbour_config->neighbour_cells->size; neighbourIdx++) {
      nr_neighbour_gnb_configuration_t *neighbour =
          (nr_neighbour_gnb_configuration_t *)seq_arr_at(neighbour_config->neighbour_cells, neighbourIdx);
      if (neighbour != NULL && neighbour->physicalCellId == neighbour_cell_phy_id)
        return neighbour;
    }
  }
  return NULL;
}

170 171 172
typedef struct deliver_dl_rrc_message_data_s {
  const gNB_RRC_INST *rrc;
  f1ap_dl_rrc_message_t *dl_rrc;
Robert Schmidt's avatar
Robert Schmidt committed
173
  sctp_assoc_t assoc_id;
174 175 176 177 178 179 180 181
} deliver_dl_rrc_message_data_t;
static void rrc_deliver_dl_rrc_message(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
  DevAssert(deliver_pdu_data != NULL);
  deliver_dl_rrc_message_data_t *data = (deliver_dl_rrc_message_data_t *)deliver_pdu_data;
  data->dl_rrc->rrc_container = (uint8_t *)buf;
  data->dl_rrc->rrc_container_length = size;
  DevAssert(data->dl_rrc->srb_id == srb_id);
Robert Schmidt's avatar
Robert Schmidt committed
182
  data->rrc->mac_rrc.dl_rrc_message_transfer(data->assoc_id, data->dl_rrc);
183 184
}

185 186 187 188 189
void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc,
                                           const gNB_RRC_UE_t *ue_p,
                                           uint8_t srb_id,
                                           const uint8_t *buffer,
                                           int size)
190 191
{
  DevAssert(size > 0);
192
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
193
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
194
  f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, .srb_id = srb_id};
195 196 197 198 199 200 201 202
  deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc, .assoc_id = ue_data.du_assoc_id};
  nr_pdcp_data_req_srb(ue_p->rrc_ue_id,
                       srb_id,
                       rrc_gNB_mui++,
                       size,
                       (unsigned char *const)buffer,
                       rrc_deliver_dl_rrc_message,
                       &data);
203
}
204

WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
205 206 207
///---------------------------------------------------------------------------------------------------------------///
///---------------------------------------------------------------------------------------------------------------///

208
static void init_NR_SI(gNB_RRC_INST *rrc)
209
{
210 211 212
  if (!NODE_IS_DU(rrc->node_type)) {
    rrc->carrier.SIB23 = (uint8_t *) malloc16(100);
    AssertFatal(rrc->carrier.SIB23 != NULL, "cannot allocate memory for SIB");
213
    rrc->carrier.sizeof_SIB23 = do_SIB23_NR(&rrc->carrier);
214
    LOG_I(NR_RRC, "do_SIB23_NR, size %d\n", rrc->carrier.sizeof_SIB23);
215 216
    AssertFatal(rrc->carrier.sizeof_SIB23 != 255,"FATAL, RC.nrrrc[mod].carrier[CC_id].sizeof_SIB23 == 255");
  }
217

218
  if (get_softmodem_params()->phy_test > 0 || get_softmodem_params()->do_ra > 0) {
219
    AssertFatal(NODE_IS_MONOLITHIC(rrc->node_type), "phy_test and do_ra only work in monolithic\n");
220
    rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_allocate_new_ue_context(rrc);
221
    LOG_I(NR_RRC,"Adding new user (%p)\n",ue_context_p);
222
    if (!NODE_IS_CU(rrc->node_type)) {
223
      rrc_add_nsa_user(rrc,ue_context_p,NULL);
224
    }
225
  }
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
226
}
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
227

228
static void rrc_gNB_CU_DU_init(gNB_RRC_INST *rrc)
229 230
{
  switch (rrc->node_type) {
231 232 233 234
    case ngran_gNB_CUCP:
      mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
      cucp_cuup_message_transfer_e1ap_init(rrc);
      break;
235 236
    case ngran_gNB_CU:
      mac_rrc_dl_f1ap_init(&rrc->mac_rrc);
237
      cucp_cuup_message_transfer_direct_init(rrc);
238
      break;
239 240 241 242
    case ngran_gNB:
      mac_rrc_dl_direct_init(&rrc->mac_rrc);
      cucp_cuup_message_transfer_direct_init(rrc);
       break;
243 244 245 246 247 248 249 250 251
    case ngran_gNB_DU:
      /* silently drop this, as we currently still need the RRC at the DU. As
       * soon as this is not the case anymore, we can add the AssertFatal() */
      //AssertFatal(1==0,"nothing to do for DU\n");
      break;
    default:
      AssertFatal(0 == 1, "Unknown node type %d\n", rrc->node_type);
      break;
  }
252
  cu_init_f1_ue_data();
253 254
}

255
void openair_rrc_gNB_configuration(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration)
256
{
257
  AssertFatal(rrc != NULL, "RC.nrrrc not initialized!");
258
  AssertFatal(MAX_MOBILES_PER_GNB < (module_id_t)0xFFFFFFFFFFFFFFFF, " variable overflow");
259
  AssertFatal(configuration!=NULL,"configuration input is null\n");
260
  rrc->module_id = 0;
261
  rrc_gNB_CU_DU_init(rrc);
262
  uid_linear_allocator_init(&rrc->uid_allocator);
263
  RB_INIT(&rrc->rrc_ue_head);
264
  RB_INIT(&rrc->cuups);
Robert Schmidt's avatar
Robert Schmidt committed
265
  RB_INIT(&rrc->dus);
266
  rrc->configuration = *configuration;
267
   /// System Information INIT
268
  init_NR_SI(rrc);
269 270
  return;
} // END openair_rrc_gNB_configuration
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
271

272 273
static void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_addition_req_t *m)
{
274 275 276 277 278 279 280
  struct NR_CG_ConfigInfo *cg_configinfo = NULL;
  asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                            &asn_DEF_NR_CG_ConfigInfo,
                            (void **)&cg_configinfo,
                            (uint8_t *)m->rrc_buffer,
                            (int) m->rrc_buffer_size);//m->rrc_buffer_size);
  gNB_RRC_INST         *rrc=RC.nrrrc[gnb_mod_idP];
281

282 283 284 285 286 287
  if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
    AssertFatal(1==0,"NR_UL_DCCH_MESSAGE decode error\n");
    // free the memory
    SEQUENCE_free(&asn_DEF_NR_CG_ConfigInfo, cg_configinfo, 1);
    return;
  }
288

289 290 291 292 293
  xer_fprint(stdout,&asn_DEF_NR_CG_ConfigInfo, cg_configinfo);
  // recreate enough of X2 EN-DC Container
  AssertFatal(cg_configinfo->criticalExtensions.choice.c1->present == NR_CG_ConfigInfo__criticalExtensions__c1_PR_cg_ConfigInfo,
              "ueCapabilityInformation not present\n");
  parse_CG_ConfigInfo(rrc,cg_configinfo,m);
294
  LOG_I(NR_RRC, "Successfully parsed CG_ConfigInfo of size %zu bits. (%zu bytes)\n",
295
        dec_rval.consumed, (dec_rval.consumed +7/8));
296 297
}

298
//-----------------------------------------------------------------------------
299
unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP)
300 301
//-----------------------------------------------------------------------------
{
302 303 304 305 306 307
  static unsigned int transaction_id[NUMBER_OF_gNB_MAX] = {0};
  // used also in NGAP thread, so need thread safe operation
  unsigned int tmp = __atomic_add_fetch(&transaction_id[gnb_mod_idP], 1, __ATOMIC_SEQ_CST);
  tmp %= NR_RRC_TRANSACTION_IDENTIFIER_NUMBER;
  LOG_T(NR_RRC, "generated xid is %d\n", tmp);
  return tmp;
308 309
}

310 311 312
/**
 * @brief Create srb-ToAddModList for RRCSetup and RRCReconfiguration messages
*/
313 314 315 316 317 318 319
static NR_SRB_ToAddModList_t *createSRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
  if (!ue->Srb[1].Active) {
    LOG_E(NR_RRC, "Call SRB list while SRB1 doesn't exist\n");
    return NULL;
  }
  NR_SRB_ToAddModList_t *list = CALLOC(sizeof(*list), 1);
320
  for (int i = 0; i < NR_NUM_SRB; i++)
321 322 323
    if (ue->Srb[i].Active) {
      asn1cSequenceAdd(list->list, NR_SRB_ToAddMod_t, srb);
      srb->srb_Identity = i;
324
      /* Set reestablishPDCP only for SRB2 */
325 326 327 328 329 330 331 332 333 334 335 336
      if (reestablish && i == 2) {
        asn1cCallocOne(srb->reestablishPDCP, NR_SRB_ToAddMod__reestablishPDCP_true);
      }
    }
  return list;
}

static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish)
{
  NR_DRB_ToAddMod_t *DRB_config = NULL;
  NR_DRB_ToAddModList_t *DRB_configList = CALLOC(sizeof(*DRB_configList), 1);

337
  for (int i = 0; i < MAX_DRBS_PER_UE; i++) {
338 339 340 341 342 343 344 345
    if (ue->established_drbs[i].status != DRB_INACTIVE) {
      DRB_config = generateDRB_ASN1(&ue->established_drbs[i]);
      if (reestablish) {
        asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true);
      }
      asn1cSeqAdd(&DRB_configList->list, DRB_config);
    }
  }
346 347 348 349
  if (DRB_configList->list.count == 0) {
    free(DRB_configList);
    return NULL;
  }
350 351 352
  return DRB_configList;
}

laurent's avatar
laurent committed
353 354 355 356 357 358
static void freeSRBlist(NR_SRB_ToAddModList_t *l)
{
  if (l) {
    for (int i = 0; i < l->list.count; i++)
      free(l->list.array[i]);
    free(l);
laurent's avatar
laurent committed
359 360
  } else
    LOG_E(NR_RRC, "Call free SRB list on NULL pointer\n");
laurent's avatar
laurent committed
361 362
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
static void activate_srb(gNB_RRC_UE_t *UE, int srb_id)
{
  AssertFatal(srb_id == 1 || srb_id == 2, "handling only SRB 1 or 2\n");
  if (UE->Srb[srb_id].Active == 1) {
    LOG_W(RRC, "UE %d SRB %d already activated\n", UE->rrc_ue_id, srb_id);
    return;
  }
  LOG_I(RRC, "activate SRB %d of UE %d\n", srb_id, UE->rrc_ue_id);
  UE->Srb[srb_id].Active = 1;

  NR_SRB_ToAddModList_t *list = CALLOC(sizeof(*list), 1);
  asn1cSequenceAdd(list->list, NR_SRB_ToAddMod_t, srb);
  srb->srb_Identity = srb_id;

  if (srb_id == 1) {
378 379
    nr_pdcp_entity_security_keys_and_algos_t null_security_parameters = {0};
    nr_pdcp_add_srbs(true, UE->rrc_ue_id, list, &null_security_parameters);
380
  } else {
381 382 383 384 385
    nr_pdcp_entity_security_keys_and_algos_t security_parameters;
    security_parameters.ciphering_algorithm = UE->ciphering_algorithm;
    security_parameters.integrity_algorithm = UE->integrity_algorithm;
    nr_derive_key(RRC_ENC_ALG, UE->ciphering_algorithm, UE->kgnb, security_parameters.ciphering_key);
    nr_derive_key(RRC_INT_ALG, UE->integrity_algorithm, UE->kgnb, security_parameters.integrity_key);
386 387 388 389

    nr_pdcp_add_srbs(true,
                     UE->rrc_ue_id,
                     list,
390
                     &security_parameters);
391 392 393 394
  }
  freeSRBlist(list);
}

395
//-----------------------------------------------------------------------------
396 397
static void rrc_gNB_generate_RRCSetup(instance_t instance,
                                      rnti_t rnti,
398 399
                                      rrc_gNB_ue_context_t *const ue_context_pP,
                                      const uint8_t *masterCellGroup,
400
                                      int masterCellGroup_len)
401 402
//-----------------------------------------------------------------------------
{
403
  LOG_UE_DL_EVENT(&ue_context_pP->ue_context, "Send RRC Setup\n");
404 405

  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
406
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
407
  unsigned char buf[1024];
408 409
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(instance);
  ue_p->xids[xid] = RRC_SETUP;
410
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false);
laurent's avatar
laurent committed
411 412

  int size = do_RRCSetup(ue_context_pP, buf, xid, masterCellGroup, masterCellGroup_len, &rrc->configuration, SRBs);
413
  AssertFatal(size > 0, "do_RRCSetup failed\n");
414
  AssertFatal(size <= 1024, "memory corruption\n");
415

416
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Setup\n");
laurent's avatar
laurent committed
417
  freeSRBlist(SRBs);
418
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
419
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
420
  f1ap_dl_rrc_message_t dl_rrc = {
421
    .gNB_CU_ue_id = ue_p->rrc_ue_id,
422
    .gNB_DU_ue_id = ue_data.secondary_ue,
423 424
    .rrc_container = buf,
    .rrc_container_length = size,
425 426
    .srb_id = CCCH
  };
Robert Schmidt's avatar
Robert Schmidt committed
427
  rrc->mac_rrc.dl_rrc_message_transfer(ue_data.du_assoc_id, &dl_rrc);
428 429
}

430
static void rrc_gNB_generate_RRCReject(module_id_t module_id, rrc_gNB_ue_context_t *const ue_context_pP)
431 432
//-----------------------------------------------------------------------------
{
433
  gNB_RRC_INST *rrc = RC.nrrrc[module_id];
434
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
435
  LOG_A(NR_RRC, "Send RRCReject to RNTI %04x\n", ue_p->rnti);
436

437
  unsigned char buf[1024];
438
  int size = do_RRCReject(buf);
439
  AssertFatal(size > 0, "do_RRCReject failed\n");
440
  AssertFatal(size <= 1024, "memory corruption\n");
441

442
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC,
443 444
              (char *)buf,
              size,
445
              "[MSG] RRCReject \n");
446
  LOG_I(NR_RRC, " [RAPROC] ue %04x Logical Channel DL-CCCH, Generating NR_RRCReject (bytes %d)\n", ue_p->rnti, size);
447

448
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
449
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
450
  f1ap_dl_rrc_message_t dl_rrc = {
451
    .gNB_CU_ue_id = ue_p->rrc_ue_id,
452
    .gNB_DU_ue_id = ue_data.secondary_ue,
453 454 455 456 457 458
    .rrc_container = buf,
    .rrc_container_length = size,
    .srb_id = CCCH,
    .execute_duplication  = 1,
    .RAT_frequency_priority_information.en_dc = 0
  };
Robert Schmidt's avatar
Robert Schmidt committed
459
  rrc->mac_rrc.dl_rrc_message_transfer(ue_data.du_assoc_id, &dl_rrc);
460 461
}

Xue Song's avatar
Xue Song committed
462 463 464 465
//-----------------------------------------------------------------------------
/*
* Process the rrc setup complete message from UE (SRB1 Active)
*/
466
static void rrc_gNB_process_RRCSetupComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, NR_RRCSetupComplete_IEs_t *rrcSetupComplete)
Xue Song's avatar
Xue Song committed
467 468
//-----------------------------------------------------------------------------
{
469 470
  UE->Srb[1].Active = 1;
  UE->Srb[2].Active = 0;
Xue Song's avatar
Xue Song committed
471

472
  rrc_gNB_send_NGAP_NAS_FIRST_REQ(rrc, UE, rrcSetupComplete);
Xue Song's avatar
Xue Song committed
473 474
}

475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
static int rrc_gNB_encode_RRCReconfiguration(gNB_RRC_INST *rrc,
                                             gNB_RRC_UE_t *UE,
                                             uint8_t xid,
                                             struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *nas_messages,
                                             uint8_t *buf,
                                             int max_len,
                                             bool reestablish)
{
  NR_CellGroupConfig_t *cellGroupConfig = UE->masterCellGroup;
  nr_rrc_du_container_t *du = get_du_for_ue(rrc, UE->rrc_ue_id);
  DevAssert(du != NULL);
  f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
  NR_MeasConfig_t *measconfig = NULL;
  if (du->mtc != NULL) {
    int scs = get_ssb_scs(cell_info);
    int band = get_dl_band(cell_info);
    const NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming;
    const NR_MeasTiming_t *mt = mtlist->list.array[0];
    const neighbour_cell_configuration_t *neighbour_config = get_neighbour_config(cell_info->nr_cellid);
    seq_arr_t *neighbour_cells = NULL;
    if (neighbour_config)
      neighbour_cells = neighbour_config->neighbour_cells;

    measconfig = get_MeasConfig(mt, band, scs, &rrc->measurementConfiguration, neighbour_cells);
  }

  if (UE->measConfig)
    free_MeasConfig(UE->measConfig);

  UE->measConfig = measconfig;

  NR_SRB_ToAddModList_t *SRBs = createSRBlist(UE, reestablish);
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(UE, reestablish);

  int size = do_RRCReconfiguration(UE,
                                   buf,
                                   max_len,
                                   xid,
                                   SRBs,
                                   DRBs,
                                   UE->DRB_ReleaseList,
                                   NULL,
                                   measconfig,
                                   nas_messages,
                                   cellGroupConfig);
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Reconfiguration\n");
  freeSRBlist(SRBs);
  freeDRBlist(DRBs);
  ASN_STRUCT_FREE(asn_DEF_NR_DRB_ToReleaseList, UE->DRB_ReleaseList);
  UE->DRB_ReleaseList = NULL;

  return size;
}

529
static void rrc_gNB_generate_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
530
{
531
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
532
  ue_p->xids[xid] = RRC_PDUSESSION_ESTABLISH;
533 534 535 536 537 538 539 540 541 542 543 544 545 546
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));

  for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
    if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
      asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
      OCTET_STRING_fromBuf(msg,
                           (char *)ue_p->pduSession[i].param.nas_pdu.buffer,
                           ue_p->pduSession[i].param.nas_pdu.length);

      LOG_D(NR_RRC, "add NAS info with size %d (pdusession idx %d)\n", ue_p->pduSession[i].param.nas_pdu.length, i);
      ue_p->pduSession[i].xid = xid;
    }
    if (ue_p->pduSession[i].status < PDU_SESSION_STATUS_ESTABLISHED) {
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
547
    }
548
  }
549

550 551 552
  if (ue_p->nas_pdu.length) {
    asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, msg);
    OCTET_STRING_fromBuf(msg, (char *)ue_p->nas_pdu.buffer, ue_p->nas_pdu.length);
553 554 555 556 557 558 559 560
  }

  /* If list is empty free the list and reset the address */
  if (dedicatedNAS_MessageList->list.count == 0) {
    free(dedicatedNAS_MessageList);
    dedicatedNAS_MessageList = NULL;
  }

luis_pereira87's avatar
luis_pereira87 committed
561
  /* Free all NAS PDUs */
562 563
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
    clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
luis_pereira87's avatar
luis_pereira87 committed
564

565
  uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
566 567 568
  // TODO refactor dedicatedNAS_MessageList
  int size = rrc_gNB_encode_RRCReconfiguration(rrc, ue_p, xid, dedicatedNAS_MessageList, buffer, sizeof(buffer), false);
  DevAssert(size > 0 && size <= sizeof(buffer));
569

570
  LOG_UE_DL_EVENT(ue_p, "Generate RRCReconfiguration (bytes %d, xid %d)\n", size, xid);
571
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
572 573
}

574
void rrc_gNB_modify_dedicatedRRCReconfiguration(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
575
{
576
  int qos_flow_index = 0;
577
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
578
  ue_p->xids[xid] = RRC_PDUSESSION_MODIFY;
579

580 581
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList =
      CALLOC(1, sizeof(*dedicatedNAS_MessageList));
582

583
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++) {
584
    // bypass the new and already configured pdu sessions
585 586
    if (ue_p->pduSession[i].status >= PDU_SESSION_STATUS_DONE) {
      ue_p->pduSession[i].xid = xid;
587 588 589
      continue;
    }

590
    if (ue_p->pduSession[i].cause != NGAP_CAUSE_NOTHING) {
591
      // set xid of failure pdu session
592 593
      ue_p->pduSession[i].xid = xid;
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
594 595 596 597
      continue;
    }

    // search exist DRB_config
598
    int j;
599
    for (j = 0; i < MAX_DRBS_PER_UE; j++) {
600 601
      if (ue_p->established_drbs[j].status != DRB_INACTIVE
          && ue_p->established_drbs[j].cnAssociation.sdap_config.pdusession_id == ue_p->pduSession[i].param.pdusession_id)
602 603 604
        break;
    }

605
    if (j == MAX_DRBS_PER_UE) {
606 607 608 609
      ue_p->pduSession[i].xid = xid;
      ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
      ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
      ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_unspecified;
610 611 612 613
      continue;
    }

    // Reference TS23501 Table 5.7.4-1: Standardized 5QI to QoS characteristics mapping
614 615
    for (qos_flow_index = 0; qos_flow_index < ue_p->pduSession[i].param.nb_qos; qos_flow_index++) {
      switch (ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI) {
616 617 618 619 620 621 622 623 624 625 626 627 628
        case 1: //100ms
        case 2: //150ms
        case 3: //50ms
        case 4: //300ms
        case 5: //100ms
        case 6: //300ms
        case 7: //100ms
        case 8: //300ms
        case 9: //300ms Video (Buffered Streaming)TCP-based (e.g., www, e-mail, chat, ftp, p2p file sharing, progressive video, etc.)
          // TODO
          break;

        default:
629 630 631 632 633
          LOG_E(NR_RRC, "not supported 5qi %lu\n", ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
          ue_p->pduSession[i].status = PDU_SESSION_STATUS_FAILED;
          ue_p->pduSession[i].xid = xid;
          ue_p->pduSession[i].cause = NGAP_CAUSE_RADIO_NETWORK;
          ue_p->pduSession[i].cause_value = NGAP_CauseRadioNetwork_not_supported_5QI_value;
634 635
          continue;
      }
francescomani's avatar
francescomani committed
636 637 638 639 640
        LOG_I(NR_RRC,
              "index %d, QOS flow %d, 5QI %ld \n",
              i,
              qos_flow_index,
              ue_p->pduSession[i].param.qos[qos_flow_index].fiveQI);
641 642
    }

643 644
    ue_p->pduSession[i].status = PDU_SESSION_STATUS_DONE;
    ue_p->pduSession[i].xid = xid;
645

646 647 648 649
    if (ue_p->pduSession[i].param.nas_pdu.buffer != NULL) {
      asn1cSequenceAdd(dedicatedNAS_MessageList->list,NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
      OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)ue_p->pduSession[i].param.nas_pdu.buffer, ue_p->pduSession[i].param.nas_pdu.length);
      LOG_I(NR_RRC, "add NAS info with size %d (pdusession id %d)\n", ue_p->pduSession[i].param.nas_pdu.length, ue_p->pduSession[i].param.pdusession_id);
650 651 652 653 654 655 656 657 658
    }
  }

  /* If list is empty free the list and reset the address */
  if (dedicatedNAS_MessageList->list.count == 0) {
    free(dedicatedNAS_MessageList);
    dedicatedNAS_MessageList = NULL;
  }

659
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false);
660
  uint8_t buffer[NR_RRC_BUF_SIZE];
661
  int size = do_RRCReconfiguration(ue_p,
662
                                   buffer,
663
                                   NR_RRC_BUF_SIZE,
664 665
                                   xid,
                                   NULL,
666
                                   DRBs,
667 668 669 670 671
                                   NULL,
                                   NULL,
                                   NULL,
                                   dedicatedNAS_MessageList,
                                   NULL);
672
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
673
  freeDRBlist(DRBs);
674 675

  /* Free all NAS PDUs */
676 677
  for (int i = 0; i < ue_p->nb_of_pdusessions; i++)
    clear_nas_pdu(&ue_p->pduSession[i].param.nas_pdu);
678

679
  LOG_I(NR_RRC, "UE %d: Generate RRCReconfiguration (bytes %d)\n", ue_p->rrc_ue_id, size);
680
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
681 682
}

683
//-----------------------------------------------------------------------------
684 685 686 687 688
void rrc_gNB_generate_dedicatedRRCReconfiguration_release(gNB_RRC_INST *rrc,
                                                          gNB_RRC_UE_t *ue_p,
                                                          uint8_t xid,
                                                          uint32_t nas_length,
                                                          uint8_t *nas_buffer)
689 690
//-----------------------------------------------------------------------------
{
691
  NR_DRB_ToReleaseList_t *DRB_Release_configList2 = CALLOC(sizeof(*DRB_Release_configList2), 1);
692

693
  for (int i = 0; i < NB_RB_MAX; i++) {
694
    if ((ue_p->pduSession[i].status == PDU_SESSION_STATUS_TORELEASE) && ue_p->pduSession[i].xid == xid) {
695 696
      asn1cSequenceAdd(DRB_Release_configList2->list, NR_DRB_Identity_t, DRB_release);
      *DRB_release = i + 1;
697 698 699 700
    }
  }

  /* If list is empty free the list and reset the address */
701
  struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL;
702
  if (nas_length > 0) {
703 704 705
    dedicatedNAS_MessageList = CALLOC(1, sizeof(*dedicatedNAS_MessageList));
    asn1cSequenceAdd(dedicatedNAS_MessageList->list, NR_DedicatedNAS_Message_t, dedicatedNAS_Message);
    OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)nas_buffer, nas_length);
706 707 708 709 710
    LOG_I(NR_RRC,"add NAS info with size %d\n", nas_length);
  } else {
    LOG_W(NR_RRC,"dedlicated NAS list is empty\n");
  }

711
  uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
712
  int size = do_RRCReconfiguration(ue_p,
713
                                   buffer,
714
                                   NR_RRC_BUF_SIZE,
715 716 717
                                   xid,
                                   NULL,
                                   NULL,
718
                                   DRB_Release_configList2,
719 720 721 722
                                   NULL,
                                   NULL,
                                   dedicatedNAS_MessageList,
                                   NULL);
723
  LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n");
724 725 726 727 728 729 730

  /* Free all NAS PDUs */
  if (nas_length > 0) {
    /* Free the NAS PDU buffer and invalidate it */
    free(nas_buffer);
  }

731
  LOG_I(NR_RRC, "UE %d: Generate NR_RRCReconfiguration (bytes %d)\n", ue_p->rrc_ue_id, size);
732
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
733 734
}

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
/* \brief find existing PDU session inside E1AP Bearer Modif message, or
 * point to new one.
 * \param bearer_modif E1AP Bearer Modification Message
 * \param pdu_id PDU session ID
 * \return pointer to existing PDU session, or to new/unused one. */
static pdu_session_to_mod_t *find_or_next_pdu_session(e1ap_bearer_mod_req_t *bearer_modif, int pdu_id)
{
  for (int i = 0; i < bearer_modif->numPDUSessionsMod; ++i) {
    if (bearer_modif->pduSessionMod[i].sessionId == pdu_id)
      return &bearer_modif->pduSessionMod[i];
  }
  /* E1AP Bearer Modification has no PDU session to modify with that ID, create
   * new entry */
  DevAssert(bearer_modif->numPDUSessionsMod < E1AP_MAX_NUM_PDU_SESSIONS - 1);
  bearer_modif->numPDUSessionsMod += 1;
  return &bearer_modif->pduSessionMod[bearer_modif->numPDUSessionsMod - 1];
}

Guido Casati's avatar
Guido Casati committed
753 754 755 756 757 758 759 760 761 762 763 764 765
/**
 * @brief Notify E1 re-establishment to CU-UP
 */
static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
{
  e1ap_bearer_mod_req_t req = {
      .gNB_cu_cp_ue_id = ue_p->rrc_ue_id,
      .gNB_cu_up_ue_id = ue_p->rrc_ue_id,
  };
  if (!ue_associated_to_cuup(rrc, ue_p))
    return;
  /* loop through active DRBs */
  for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) {
766 767
    drb_t *drb = get_drb(ue_p, drb_id);
    if (drb->status == DRB_INACTIVE)
Guido Casati's avatar
Guido Casati committed
768 769 770 771 772 773 774 775 776 777 778
      continue;
    /* fetch an existing PDU session for this DRB */
    rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id);
    if (pdu == NULL) {
      LOG_E(RRC, "UE %d: E1 Bearer Context Modification: no PDU session for DRB ID %d\n", ue_p->rrc_ue_id, drb_id);
      continue;
    }
    /* Get pointer to existing (or new one) PDU session to modify in E1 */
    pdu_session_to_mod_t *pdu_e1 = find_or_next_pdu_session(&req, pdu->param.pdusession_id);
    AssertError(pdu != NULL,
                continue,
francescomani's avatar
francescomani committed
779
                "UE %u: E1 Bearer Context Modification: PDU session %d to setup is null\n",
Guido Casati's avatar
Guido Casati committed
780 781 782 783 784 785 786 787
                ue_p->rrc_ue_id,
                pdu->param.pdusession_id);
    /* Prepare PDU for E1 Bearear Context Modification Request */
    pdu_e1->sessionId = pdu->param.pdusession_id;
    /* Fill DRB to setup with ID, DL TL and DL TEID */
    DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
    drb_e1->id = drb_id;
    drb_e1->numDlUpParam = 1;
788 789
    memcpy(&drb_e1->DlUpParamList[0].tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4);
    drb_e1->DlUpParamList[0].teId = drb->du_tunnel_config.teid;
Guido Casati's avatar
Guido Casati committed
790 791
    /* PDCP configuration */
    bearer_context_pdcp_config_t *pdcp_config = &drb_e1->pdcp_config;
792
    set_bearer_context_pdcp_config(pdcp_config, drb, rrc->configuration.um_on_default_drb);
Guido Casati's avatar
Guido Casati committed
793 794 795 796
    pdcp_config->pDCP_Reestablishment = true;
    /* increase DRB to modify counter */
    pdu_e1->numDRB2Modify += 1;
  }
797 798 799 800 801 802

  req.cipheringAlgorithm = rrc->security.do_drb_ciphering ? ue_p->ciphering_algorithm : 0;
  req.integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? ue_p->integrity_algorithm : 0;
  nr_derive_key(UP_ENC_ALG, req.cipheringAlgorithm, ue_p->kgnb, (uint8_t *)req.encryptionKey);
  nr_derive_key(UP_INT_ALG, req.integrityProtectionAlgorithm, ue_p->kgnb, (uint8_t *)req.integrityProtectionKey);

Guido Casati's avatar
Guido Casati committed
803 804 805 806 807 808 809 810 811
  /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */
  sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p);
  rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}

/**
 * @brief RRCReestablishment message
 *        Direction: Network to UE
 */
812
static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context_pP,
813
                                                const uint8_t *masterCellGroup_from_DU,
814
                                                const rnti_t old_rnti,
815
                                                const nr_rrc_du_container_t *du)
yaojie's avatar
yaojie committed
816
{
817 818
  module_id_t module_id = 0;
  gNB_RRC_INST *rrc = RC.nrrrc[module_id];
819
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
820
  uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
821 822
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(module_id);
  ue_p->xids[xid] = RRC_REESTABLISH;
823
  const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
824
  uint32_t ssb_arfcn = get_ssb_arfcn(du);
825
  int size = do_RRCReestablishment(ue_context_pP, buffer, NR_RRC_BUF_SIZE, xid, cell_info->nr_pci, ssb_arfcn);
826

827
  LOG_A(NR_RRC, "Send RRCReestablishment [%d bytes] to RNTI %04x\n", size, ue_p->rnti);
828

829
  /* Ciphering and Integrity according to TS 33.501 */
830
  nr_pdcp_entity_security_keys_and_algos_t security_parameters;
831
  /* Derive the keys from kgnb */
832 833
  nr_derive_key(RRC_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, security_parameters.ciphering_key);
  nr_derive_key(RRC_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, security_parameters.integrity_key);
834
  LOG_I(NR_RRC,
Cedric Roux's avatar
Cedric Roux committed
835
        "Set PDCP security UE %d RNTI %04x nea %ld nia %d in RRCReestablishment\n",
836 837 838 839
        ue_p->rrc_ue_id,
        ue_p->rnti,
        ue_p->ciphering_algorithm,
        ue_p->integrity_algorithm);
840 841 842 843
  /* RRCReestablishment is integrity protected but not ciphered,
   * so let's configure only integrity protection right now.
   * Ciphering is enabled below, after generating RRCReestablishment.
   */
844 845
  security_parameters.integrity_algorithm = ue_p->integrity_algorithm;
  security_parameters.ciphering_algorithm = 0;
846

847 848
  /* SRBs */
  for (int srb_id = 1; srb_id < NR_NUM_SRB; srb_id++) {
849
    if (ue_p->Srb[srb_id].Active)
850
      nr_pdcp_config_set_security(ue_p->rrc_ue_id, srb_id, true, &security_parameters);
851
  }
852
  /* Re-establish PDCP for SRB1, according to 5.3.7.4 of 3GPP TS 38.331 */
853 854 855
  nr_pdcp_reestablishment(ue_p->rrc_ue_id,
                          1,
                          true,
856
                          &security_parameters);
Guido Casati's avatar
Guido Casati committed
857
  /* F1AP DL RRC Message Transfer */
858
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
859
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
860 861 862 863 864
  uint32_t old_gNB_DU_ue_id = old_rnti;
  f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id,
                                  .gNB_DU_ue_id = ue_data.secondary_ue,
                                  .srb_id = DCCH,
                                  .old_gNB_DU_ue_id = &old_gNB_DU_ue_id};
Robert Schmidt's avatar
Robert Schmidt committed
865
  deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc, .assoc_id = ue_data.du_assoc_id};
866
  nr_pdcp_data_req_srb(ue_p->rrc_ue_id, DCCH, rrc_gNB_mui++, size, (unsigned char *const)buffer, rrc_deliver_dl_rrc_message, &data);
867 868

  /* RRCReestablishment has been generated, let's enable ciphering now. */
869
  security_parameters.ciphering_algorithm = ue_p->ciphering_algorithm;
870 871 872
  /* SRBs */
  for (int srb_id = 1; srb_id < NR_NUM_SRB; srb_id++) {
    if (ue_p->Srb[srb_id].Active)
873
      nr_pdcp_config_set_security(ue_p->rrc_ue_id, srb_id, true, &security_parameters);
874
  }
875 876
}

877
/// @brief Function tha processes RRCReestablishmentComplete message sent by the UE, after RRCReestasblishment request.
878
static void rrc_gNB_process_RRCReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const uint8_t xid)
879
{
880
  LOG_I(NR_RRC, "UE %d Processing NR_RRCReestablishmentComplete from UE\n", ue_p->rrc_ue_id);
881 882 883

  int i = 0;

884
  ue_p->xids[xid] = RRC_ACTION_NONE;
laurent's avatar
laurent committed
885 886

  ue_p->Srb[1].Active = 1;
887

888
  NR_CellGroupConfig_t *cellGroupConfig = calloc(1, sizeof(NR_CellGroupConfig_t));
889

890
  cellGroupConfig->spCellConfig = ue_p->masterCellGroup->spCellConfig;
891
  cellGroupConfig->mac_CellGroupConfig = ue_p->masterCellGroup->mac_CellGroupConfig;
892
  cellGroupConfig->physicalCellGroupConfig = ue_p->masterCellGroup->physicalCellGroupConfig;
893 894 895 896 897 898 899 900 901
  cellGroupConfig->rlc_BearerToReleaseList = NULL;
  cellGroupConfig->rlc_BearerToAddModList = calloc(1, sizeof(*cellGroupConfig->rlc_BearerToAddModList));

  /*
   * Get SRB2, DRB configuration from the existing UE context,
   * also start from SRB2 (i=1) and not from SRB1 (i=0).
   */
  for (i = 1; i < ue_p->masterCellGroup->rlc_BearerToAddModList->list.count; ++i)
    asn1cSeqAdd(&cellGroupConfig->rlc_BearerToAddModList->list, ue_p->masterCellGroup->rlc_BearerToAddModList->list.array[i]);
902

903 904 905 906
  /*
   * At this stage, PDCP entity are re-established and reestablishRLC is flagged
   * with RRCReconfiguration to complete RLC re-establishment of remaining bearers
   */
907
  for (i = 0; i < cellGroupConfig->rlc_BearerToAddModList->list.count; i++) {
908 909
    asn1cCallocOne(cellGroupConfig->rlc_BearerToAddModList->list.array[i]->reestablishRLC,
                   NR_RLC_BearerConfig__reestablishRLC_true);
910 911
  }

912 913 914 915 916 917
  /* TODO: remove a reconfigurationWithSync, we don't need it for
   * reestablishment. The whole reason why this might be here is that we store
   * the CellGroupConfig (after handover), and simply reuse it for
   * reestablishment, instead of re-requesting the CellGroupConfig from the DU.
   * Hence, add below hack; the solution would be to request the
   * CellGroupConfig from the DU when doing reestablishment. */
Robert Schmidt's avatar
Robert Schmidt committed
918
  if (cellGroupConfig->spCellConfig && cellGroupConfig->spCellConfig->reconfigurationWithSync) {
919 920 921 922
    ASN_STRUCT_FREE(asn_DEF_NR_ReconfigurationWithSync, cellGroupConfig->spCellConfig->reconfigurationWithSync);
    cellGroupConfig->spCellConfig->reconfigurationWithSync = NULL;
  }

923 924 925 926 927
  /* Re-establish SRB2 according to clause 5.3.5.6.3 of 3GPP TS 38.331
   * (SRB1 is re-established with RRCReestablishment message)
   */
  int srb_id = 2;
  if (ue_p->Srb[srb_id].Active) {
928 929 930 931 932
    nr_pdcp_entity_security_keys_and_algos_t security_parameters;
    security_parameters.ciphering_algorithm = ue_p->ciphering_algorithm;
    security_parameters.integrity_algorithm = ue_p->integrity_algorithm;
    nr_derive_key(RRC_ENC_ALG, ue_p->ciphering_algorithm, ue_p->kgnb, security_parameters.ciphering_key);
    nr_derive_key(RRC_INT_ALG, ue_p->integrity_algorithm, ue_p->kgnb, security_parameters.integrity_key);
933

934
    nr_pdcp_reestablishment(ue_p->rrc_ue_id, srb_id, true, &security_parameters);
935
  }
936 937
  /* PDCP Reestablishment of DRBs according to 5.3.5.6.5 of 3GPP TS 38.331 (over E1) */
  cuup_notify_reestablishment(rrc, ue_p);
938 939

  /* Create srb-ToAddModList */
940
  NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, true);
941
  /* Create drb-ToAddModList */
942 943
  NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, true);

944
  uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
945
  ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE;
946
  uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
947
  int size = do_RRCReconfiguration(ue_p,
948
                                   buffer,
949
                                   NR_RRC_BUF_SIZE,
950
                                   new_xid,
laurent's avatar
laurent committed
951
                                   SRBs,
952
                                   DRBs,
953 954 955 956 957
                                   NULL,
                                   NULL,
                                   NULL, // MeasObj_list,
                                   NULL,
                                   cellGroupConfig);
laurent's avatar
laurent committed
958
  freeSRBlist(SRBs);
959
  freeDRBlist(DRBs);
960
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Reconfiguration\n");
961

962 963 964 965 966 967 968
  AssertFatal(size > 0, "cannot encode RRC Reconfiguration\n");
  LOG_I(NR_RRC,
        "UE %d RNTI %04x: Generate NR_RRCReconfiguration after reestablishment complete (bytes %d)\n",
        ue_p->rrc_ue_id,
        ue_p->rnti,
        size);
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
969
}
970 971
//-----------------------------------------------------------------------------

972
int nr_rrc_reconfiguration_req(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p, const int dl_bwp_id, const int ul_bwp_id)
973
{
974
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
975
  ue_p->xids[xid] = RRC_DEDICATED_RECONF;
976

977
  NR_CellGroupConfig_t *masterCellGroup = ue_p->masterCellGroup;
978
  if (dl_bwp_id > 0) {
979 980 981
    *masterCellGroup->spCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = dl_bwp_id;
    *masterCellGroup->spCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = dl_bwp_id;
  }
982
  if (ul_bwp_id > 0) {
983 984
    *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id;
  }
985

986 987
  uint8_t buffer[NR_RRC_BUF_SIZE];
  int size = do_RRCReconfiguration(ue_p, buffer, NR_RRC_BUF_SIZE, xid, NULL, NULL, NULL, NULL, NULL, NULL, masterCellGroup);
988

989
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
luis_pereira87's avatar
luis_pereira87 committed
990

991 992 993
  return 0;
}

994 995 996 997
static void rrc_handle_RRCSetupRequest(gNB_RRC_INST *rrc,
                                       sctp_assoc_t assoc_id,
                                       const NR_RRCSetupRequest_IEs_t *rrcSetupRequest,
                                       const f1ap_initial_ul_rrc_message_t *msg)
998
{
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
  rrc_gNB_ue_context_t *ue_context_p = NULL;
  if (NR_InitialUE_Identity_PR_randomValue == rrcSetupRequest->ue_Identity.present) {
    /* randomValue                         BIT STRING (SIZE (39)) */
    if (rrcSetupRequest->ue_Identity.choice.randomValue.size != 5) { // 39-bit random value
      LOG_E(NR_RRC,
            "wrong InitialUE-Identity randomValue size, expected 5, provided %lu",
            (long unsigned int)rrcSetupRequest->ue_Identity.choice.randomValue.size);
      return;
    }
    uint64_t random_value = 0;
    memcpy(((uint8_t *)&random_value) + 3,
           rrcSetupRequest->ue_Identity.choice.randomValue.buf,
           rrcSetupRequest->ue_Identity.choice.randomValue.size);

Robert Schmidt's avatar
Robert Schmidt committed
1013
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1014 1015 1016 1017 1018 1019 1020 1021 1022
  } else if (NR_InitialUE_Identity_PR_ng_5G_S_TMSI_Part1 == rrcSetupRequest->ue_Identity.present) {
    /* <5G-S-TMSI> = <AMF Set ID><AMF Pointer><5G-TMSI> 48-bit */
    /* ng-5G-S-TMSI-Part1                  BIT STRING (SIZE (39)) */
    if (rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size != 5) {
      LOG_E(NR_RRC,
            "wrong ng_5G_S_TMSI_Part1 size, expected 5, provided %lu \n",
            (long unsigned int)rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1.size);
      return;
    }
1023

1024
    uint64_t s_tmsi_part1 = BIT_STRING_to_uint64(&rrcSetupRequest->ue_Identity.choice.ng_5G_S_TMSI_Part1);
1025
    LOG_I(NR_RRC, "Received UE 5G-S-TMSI-Part1 %ld\n", s_tmsi_part1);
1026

1027 1028 1029 1030 1031
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, s_tmsi_part1, msg->gNB_DU_ue_id);
    AssertFatal(ue_context_p != NULL, "out of memory\n");
    gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
    UE->Initialue_identity_5g_s_TMSI.presence = true;
    UE->ng_5G_S_TMSI_Part1 = s_tmsi_part1;
1032 1033 1034 1035 1036 1037
  } else {
    uint64_t random_value = 0;
    memcpy(((uint8_t *)&random_value) + 3,
           rrcSetupRequest->ue_Identity.choice.randomValue.buf,
           rrcSetupRequest->ue_Identity.choice.randomValue.size);

Robert Schmidt's avatar
Robert Schmidt committed
1038
    ue_context_p = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1039 1040 1041 1042
    LOG_E(NR_RRC, "RRCSetupRequest without random UE identity or S-TMSI not supported, let's reject the UE %04x\n", msg->crnti);
    rrc_gNB_generate_RRCReject(0, ue_context_p);
    return;
  }
yaojie's avatar
yaojie committed
1043

1044 1045 1046 1047 1048 1049 1050 1051
  NR_CellGroupConfig_t *cellGroupConfig = NULL;
  asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                 &asn_DEF_NR_CellGroupConfig,
                                                 (void **)&cellGroupConfig,
                                                 msg->du2cu_rrc_container,
                                                 msg->du2cu_rrc_container_length);
  AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");

1052 1053 1054
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  UE = &ue_context_p->ue_context;
  UE->establishment_cause = rrcSetupRequest->establishmentCause;
1055
  UE->nr_cellid = msg->nr_cellid;
1056
  UE->masterCellGroup = cellGroupConfig;
1057
  activate_srb(UE, 1);
1058 1059
  rrc_gNB_generate_RRCSetup(0, msg->crnti, ue_context_p, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
}
1060

1061
static const char *get_reestab_cause(NR_ReestablishmentCause_t c)
1062
{
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
  switch (c) {
    case NR_ReestablishmentCause_otherFailure:
      return "Other Failure";
    case NR_ReestablishmentCause_handoverFailure:
      return "Handover Failure";
    case NR_ReestablishmentCause_reconfigurationFailure:
      return "Reconfiguration Failure";
    default:
      break;
  }
  return "UNKNOWN Failure (ASN.1 decoder error?)";
}
1075

1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
static rrc_gNB_ue_context_t *rrc_gNB_get_ue_context_source_cell(gNB_RRC_INST *rrc_instance_pP, long pci, rnti_t rntiP)
{
  rrc_gNB_ue_context_t *ue_context_p;
  RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &rrc_instance_pP->rrc_ue_head) {
    gNB_RRC_UE_t *ue = &ue_context_p->ue_context;
    if (!ue->ho_context || !ue->ho_context->source)
      continue;
    nr_ho_source_cu_t *source_ctx = ue->ho_context->source;
    if (source_ctx->old_rnti == rntiP && source_ctx->du->setup_req->cell[0].info.nr_pci == pci)
      return ue_context_p;
  }
  return NULL;
}

1090 1091 1092 1093
static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc,
                                                 sctp_assoc_t assoc_id,
                                                 const NR_RRCReestablishmentRequest_IEs_t *req,
                                                 const f1ap_initial_ul_rrc_message_t *msg)
1094 1095 1096
{
  uint64_t random_value = 0;
  const char *scause = get_reestab_cause(req->reestablishmentCause);
1097
  const long physCellId = req->ue_Identity.physCellId;
1098 1099
  long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */
  rrc_gNB_ue_context_t *ue_context_p = NULL;
1100 1101 1102 1103 1104 1105
  LOG_I(NR_RRC,
        "Reestablishment RNTI %04x req C-RNTI %04lx physCellId %ld cause %s\n",
        msg->crnti,
        req->ue_Identity.c_RNTI,
        physCellId,
        scause);
1106

1107 1108 1109 1110 1111
  const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id);
  if (du == NULL) {
    LOG_E(RRC, "received CCCH message, but no corresponding DU found\n");
    return;
  }
1112

1113 1114 1115 1116 1117 1118 1119
  // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values
  if (req->ue_Identity.c_RNTI < 0x1 || req->ue_Identity.c_RNTI > 0xffef) {
    /* c_RNTI range error should not happen */
    LOG_E(NR_RRC, "NR_RRCReestablishmentRequest c_RNTI %04lx range error, fallback to RRC setup\n", req->ue_Identity.c_RNTI);
    goto fallback_rrc_setup;
  }

1120 1121 1122 1123 1124
  if (du->mib == NULL || du->sib1 == NULL) {
    /* we don't have MIB/SIB1 of the DU, and therefore cannot generate the
     * Reestablishment (as we would need the SSB's ARFCN, which we cannot
     * compute). So generate RRC Setup instead */
    LOG_E(NR_RRC, "Reestablishment request: no MIB/SIB1 of DU present, cannot do reestablishment, force setup request\n");
1125
    goto fallback_rrc_setup;
1126 1127
  }

1128 1129 1130 1131 1132 1133 1134 1135 1136
  if (du->mtc == NULL) {
    // some UEs don't send MeasurementTimingConfiguration, so we don't know the
    // SSB ARFCN and can't do reestablishment. handle it gracefully by doing
    // RRC setup procedure instead
    LOG_E(NR_RRC, "no MeasurementTimingConfiguration for this cell, cannot perform reestablishment\n");
    ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON;
    goto fallback_rrc_setup;
  }

1137
  rnti_t old_rnti = req->ue_Identity.c_RNTI;
1138
  ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti);
1139
  if (ue_context_p == NULL) {
1140 1141 1142 1143 1144
    ue_context_p = rrc_gNB_get_ue_context_source_cell(rrc, physCellId, old_rnti);
    if (ue_context_p == NULL) {
      LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n");
      goto fallback_rrc_setup;
    }
1145
  }
1146
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1147

1148
  /* should check phys cell ID to identify the correct cell */
1149
  const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info;
1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
  const f1ap_served_cell_info_t *previous_cell_info = get_cell_information_by_phycellId(physCellId);

  nr_ho_source_cu_t *source_ctx = UE->ho_context ? UE->ho_context->source : NULL;
  DevAssert(!source_ctx || source_ctx->du->setup_req->num_cells_available == 1);
  bool ho_reestab_on_source = source_ctx && previous_cell_info->nr_cellid == source_ctx->du->setup_req->cell[0].info.nr_cellid;

  if (ho_reestab_on_source) {
    /* the UE came back on the source DU while doing handover, release at
     * target DU and and update the association to the initial DU one */
    LOG_W(NR_RRC, "handover for UE %d/RNTI %04x failed, rollback to original cell\n", UE->rrc_ue_id, UE->rnti);

    // find the transaction of handover (the corresponding reconfig) and abort it
    for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
      if (UE->xids[i] == RRC_DEDICATED_RECONF)
        UE->xids[i] = RRC_ACTION_NONE;
    }

    source_ctx->ho_cancel(rrc, UE);

    /* we need the original CellGroupConfig */
    ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
    UE->masterCellGroup = source_ctx->old_cellGroupConfig;
    source_ctx->old_cellGroupConfig = NULL;

    /* update to old DU assoc id -- RNTI + secondary DU UE ID further below */
    f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
    ue_data.du_assoc_id = source_ctx->du->assoc_id;
    cu_remove_f1_ue_data(UE->rrc_ue_id);
    cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
    nr_rrc_finalize_ho(UE);
  } else if (physCellId != cell_info->nr_pci) {
1181 1182 1183 1184 1185
    /* UE was moving from previous cell so quickly that RRCReestablishment for previous cell was received in this cell */
    LOG_I(NR_RRC,
          "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n",
          physCellId,
          cell_info->nr_pci);
1186
    ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON;
1187 1188 1189 1190
    /* 38.401 8.7: "If the UE accessed from a gNB-DU other than the original
     * one, the gNB-CU should trigger the UE Context Setup procedure". Let's
     * assume that the old DU will trigger a release request, also freeing the
     * ongoing context at the CU. Hence, create new below */
1191
    goto fallback_rrc_setup;
1192 1193
  }

1194 1195 1196
  if (!UE->as_security_active) {
    /* no active security context, need to restart entire connection */
    LOG_E(NR_RRC, "UE requested Reestablishment without activated AS security\n");
1197
    ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON;
1198
    goto fallback_rrc_setup;
1199 1200
  }

1201
  /* TODO: start timer in ITTI and drop UE if it does not come back */
1202

1203 1204
  // update with new RNTI, and update secondary UE association
  UE->rnti = msg->crnti;
1205
  UE->nr_cellid = msg->nr_cellid;
1206 1207
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
  ue_data.secondary_ue = msg->gNB_DU_ue_id;
1208 1209
  cu_remove_f1_ue_data(UE->rrc_ue_id);
  cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
1210

1211
  rrc_gNB_generate_RRCReestablishment(ue_context_p, msg->du2cu_rrc_container, old_rnti, du);
1212 1213 1214 1215 1216 1217
  return;

fallback_rrc_setup:
  fill_random(&random_value, sizeof(random_value));
  random_value = random_value & 0x7fffffffff; /* random value is 39 bits */

1218 1219 1220 1221
  /* request release of the "old" UE in case it exists */
  if (ue_context_p != NULL)
    rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, ue_context_p, NGAP_CAUSE_RADIO_NETWORK, ngap_cause);

1222
  rrc_gNB_ue_context_t *new = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id);
1223
  activate_srb(&new->ue_context, 1);
1224 1225
  rrc_gNB_generate_RRCSetup(0, msg->crnti, new, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length);
  return;
1226 1227
}

1228
static void process_Periodical_Measurement_Report(gNB_RRC_UE_t *ue_ctxt, NR_MeasurementReport_t *measurementReport)
1229
{
Robert Schmidt's avatar
Robert Schmidt committed
1230 1231
  ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, ue_ctxt->measResults);
  ue_ctxt->measResults = NULL;
1232

1233 1234 1235
  const NR_MeasId_t id = measurementReport->criticalExtensions.choice.measurementReport->measResults.measId;
  AssertFatal(id, "unexpected MeasResult for MeasurementId %ld received\n", id);
  asn1cCallocOne(ue_ctxt->measResults, measurementReport->criticalExtensions.choice.measurementReport->measResults);
Robert Schmidt's avatar
Robert Schmidt committed
1236 1237 1238
  /* we "keep" the measurement report, so set to 0 */
  free(measurementReport->criticalExtensions.choice.measurementReport);
  measurementReport->criticalExtensions.choice.measurementReport = NULL;
1239 1240
}

1241
static void process_Event_Based_Measurement_Report(gNB_RRC_UE_t *ue, NR_ReportConfigNR_t *report, NR_MeasurementReport_t *measurementReport)
1242 1243 1244 1245 1246 1247 1248 1249 1250
{
  NR_EventTriggerConfig_t *event_triggered = report->reportType.choice.eventTriggered;

  int servingCellRSRP = 0;
  int neighbourCellRSRP = 0;
  int servingCellId = -1;

  switch (event_triggered->eventId.present) {
    case NR_EventTriggerConfig__eventId_PR_eventA2:
Robert Schmidt's avatar
Robert Schmidt committed
1251
      LOG_I(NR_RRC, "HO LOG: Event A2 (Serving becomes worse than threshold)\n");
1252 1253 1254
      break;

    case NR_EventTriggerConfig__eventId_PR_eventA3: {
1255
      LOG_W(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n");
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289
      const NR_MeasResults_t *measResults = &measurementReport->criticalExtensions.choice.measurementReport->measResults;

      for (int serving_cell_idx = 0; serving_cell_idx < measResults->measResultServingMOList.list.count; serving_cell_idx++) {
        const NR_MeasResultServMO_t *meas_result_serv_MO = measResults->measResultServingMOList.list.array[serving_cell_idx];
        servingCellId = *(meas_result_serv_MO->measResultServingCell.physCellId);
        if (meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsSSB_Cell) {
          servingCellRSRP = *(meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsSSB_Cell->rsrp) - 157;
        } else {
          servingCellRSRP = *(meas_result_serv_MO->measResultServingCell.measResult.cellResults.resultsCSI_RS_Cell->rsrp) - 157;
        }
        LOG_D(NR_RRC, "Serving Cell RSRP: %d\n", servingCellRSRP);
      }

      if (measResults->measResultNeighCells == NULL)
        break;

      if (measResults->measResultNeighCells->present != NR_MeasResults__measResultNeighCells_PR_measResultListNR)
        break;

      const NR_MeasResultListNR_t *measResultListNR = measResults->measResultNeighCells->choice.measResultListNR;
      for (int neigh_meas_idx = 0; neigh_meas_idx < measResultListNR->list.count; neigh_meas_idx++) {
        const NR_MeasResultNR_t *meas_result_neigh_cell = (measResultListNR->list.array[neigh_meas_idx]);
        const int neighbourCellId = *(meas_result_neigh_cell->physCellId);

        // TS 138 133 Table 10.1.6.1-1: SS-RSRP and CSI-RSRP measurement report mapping
        const struct NR_MeasResultNR__measResult__cellResults *cellResults = &(meas_result_neigh_cell->measResult.cellResults);

        if (cellResults->resultsSSB_Cell) {
          neighbourCellRSRP = *(cellResults->resultsSSB_Cell->rsrp) - 157;
        } else {
          neighbourCellRSRP = *(cellResults->resultsCSI_RS_Cell->rsrp) - 157;
        }

        LOG_I(NR_RRC,
Robert Schmidt's avatar
Robert Schmidt committed
1290
              "HO LOG: Measurement Report for the neighbour %d with RSRP: %d\n",
1291 1292 1293
              neighbourCellId,
              neighbourCellRSRP);

1294 1295
        const f1ap_served_cell_info_t *neigh_cell = get_cell_information_by_phycellId(neighbourCellId);
        const f1ap_served_cell_info_t *serving_cell = get_cell_information_by_phycellId(servingCellId);
1296
        const nr_neighbour_gnb_configuration_t *neighbour =
1297
            get_neighbour_cell_information(serving_cell->nr_cellid, neighbourCellId);
1298 1299
        // CU does not have f1 connection with neighbour cell context. So  check does serving cell has this phyCellId as a
        // neighbour.
1300
        if (!neigh_cell && neighbour) {
1301
          // No F1 connection but static neighbour configuration is available
1302
          const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->physicalCellId);
1303 1304 1305 1306 1307 1308
          // Additional check - This part can be modified according to additional cell specific Handover Margin
          if (a3_event_configuration
              && ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis)
                  < (neighbourCellRSRP - servingCellRSRP))) {
            LOG_D(NR_RRC, "HO LOG: Trigger N2 HO for the neighbour gnb: %u cell: %lu\n", neighbour->gNB_ID, neighbour->nrcell_id);
          }
1309 1310 1311 1312 1313 1314 1315 1316 1317
        } else if (neigh_cell && neighbour) {
          /* we know the cell and are connected to the DU! */
          gNB_RRC_INST *rrc = RC.nrrrc[0];
          nr_rrc_du_container_t *source_du = get_du_by_cell_id(rrc, serving_cell->nr_cellid);
          DevAssert(source_du);
          nr_rrc_du_container_t *target_du = get_du_by_cell_id(rrc, neigh_cell->nr_cellid);
          nr_rrc_trigger_f1_ho(rrc, ue, source_du, target_du);
        } else {
          LOG_W(NR_RRC, "UE %d: received A3 event for stronger neighbor PCI %d, but no such neighbour in configuration\n", ue->rrc_ue_id, neighbourCellId);
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
        }
      }

    } break;
    default:
      LOG_D(NR_RRC, "NR_EventTriggerConfig__eventId_PR_NOTHING or Other event report\n");
      break;
  }
}

1328
static void rrc_gNB_process_MeasurementReport(gNB_RRC_UE_t *UE, NR_MeasurementReport_t *measurementReport)
1329
{
1330 1331 1332 1333 1334 1335 1336
  NR_MeasurementReport__criticalExtensions_PR p = measurementReport->criticalExtensions.present;
  if (p != NR_MeasurementReport__criticalExtensions_PR_measurementReport
      || measurementReport->criticalExtensions.choice.measurementReport == NULL) {
    LOG_E(NR_RRC, "UE %d: expected presence of MeasurementReport, but has %d (%p)\n", UE->rrc_ue_id, p, measurementReport->criticalExtensions.choice.measurementReport);
    return;
  }

1337 1338 1339
  if (LOG_DEBUGFLAG(DEBUG_ASN1))
    xer_fprint(stdout, &asn_DEF_NR_MeasurementReport, (void *)measurementReport);

1340
  NR_MeasConfig_t *meas_config = UE->measConfig;
1341
  if (meas_config == NULL) {
1342
    LOG_I(NR_RRC, "Unexpected Measurement Report from UE with id: %d\n", UE->rrc_ue_id);
1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
    return;
  }

  NR_MeasurementReport_IEs_t *measurementReport_IEs = measurementReport->criticalExtensions.choice.measurementReport;
  const NR_MeasId_t measId = measurementReport_IEs->measResults.measId;

  NR_MeasIdToAddMod_t *meas_id_s = NULL;
  for (int meas_idx = 0; meas_idx < meas_config->measIdToAddModList->list.count; meas_idx++) {
    if (measId == meas_config->measIdToAddModList->list.array[meas_idx]->measId) {
      meas_id_s = meas_config->measIdToAddModList->list.array[meas_idx];
      break;
    }
  }

  if (meas_id_s == NULL) {
    LOG_E(NR_RRC, "Incoming Meas ID with id: %d Can not Found!\n", (int)measId);
    return;
  }

  LOG_D(NR_RRC, "HO LOG: Meas Id is found: %d\n", (int)meas_id_s->measId);

  struct NR_ReportConfigToAddMod__reportConfig *report_config = NULL;
  for (int rep_id = 0; rep_id < meas_config->reportConfigToAddModList->list.count; rep_id++) {
    if (meas_id_s->reportConfigId == meas_config->reportConfigToAddModList->list.array[rep_id]->reportConfigId) {
      report_config = &meas_config->reportConfigToAddModList->list.array[rep_id]->reportConfig;
    }
  }

  if (report_config == NULL) {
    LOG_E(NR_RRC, "There is no related report configuration for this measId!\n");
    return;
  }

  if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_periodical)
1377
    return process_Periodical_Measurement_Report(UE, measurementReport);
1378

1379 1380
  if (report_config->choice.reportConfigNR->reportType.present == NR_ReportConfigNR__reportType_PR_eventTriggered)
    return process_Event_Based_Measurement_Report(UE, report_config->choice.reportConfigNR, measurementReport);
1381

1382
  LOG_E(NR_RRC, "Incoming Report Type: %d is not supported! \n", report_config->choice.reportConfigNR->reportType.present);
1383 1384
}

1385
static int handle_rrcReestablishmentComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_RRCReestablishmentComplete_t *cplt)
1386
{
1387
  NR_RRCReestablishmentComplete__criticalExtensions_PR p = cplt->criticalExtensions.present;
1388 1389 1390 1391
  if (p != NR_RRCReestablishmentComplete__criticalExtensions_PR_rrcReestablishmentComplete) {
    LOG_E(NR_RRC, "UE %d: expected presence of rrcReestablishmentComplete, but message has %d\n", UE->rrc_ue_id, p);
    return -1;
  }
1392
  rrc_gNB_process_RRCReestablishmentComplete(rrc, UE, cplt->rrc_TransactionIdentifier);
1393 1394 1395 1396 1397

  UE->ue_reestablishment_counter++;
  return 0;
}

1398
void rrc_forward_ue_nas_message(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
1399 1400 1401 1402
{
  if (UE->nas_pdu.buffer == NULL || UE->nas_pdu.length == 0)
    return; // no problem: the UE will re-request a NAS PDU

1403 1404
  LOG_UE_DL_EVENT(UE, "Send DL Information Transfer [%d bytes]\n", UE->nas_pdu.length);

1405
  uint8_t buffer[4096];
1406
  unsigned int xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
1407 1408 1409 1410 1411 1412 1413 1414 1415
  uint32_t length = do_NR_DLInformationTransfer(buffer, sizeof(buffer), xid, UE->nas_pdu.length, UE->nas_pdu.buffer);
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, buffer, length, "[MSG] RRC DL Information Transfer\n");
  rb_id_t srb_id = UE->Srb[2].Active ? DCCH1 : DCCH;
  nr_rrc_transfer_protected_rrc_message(rrc, UE, srb_id, buffer, length);
  // no need to free UE->nas_pdu.buffer, do_NR_DLInformationTransfer() did that
  UE->nas_pdu.buffer = NULL;
  UE->nas_pdu.length = 0;
}

1416
static void handle_ueCapabilityInformation(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_UECapabilityInformation_t *ue_cap_info)
1417
{
1418
  int xid = ue_cap_info->rrc_TransactionIdentifier;
1419
  rrc_action_t a = UE->xids[xid];
1420
  UE->xids[xid] = RRC_ACTION_NONE;
1421 1422
  if (a != RRC_UECAPABILITY_ENQUIRY) {
    LOG_E(NR_RRC, "UE %d: received unsolicited UE Capability Information, aborting procedure\n", UE->rrc_ue_id);
1423
    return;
1424
  }
1425

1426 1427 1428 1429 1430
  int eutra_index = -1;

  if (ue_cap_info->criticalExtensions.present == NR_UECapabilityInformation__criticalExtensions_PR_ueCapabilityInformation) {
    const NR_UE_CapabilityRAT_ContainerList_t *ue_CapabilityRAT_ContainerList =
        ue_cap_info->criticalExtensions.choice.ueCapabilityInformation->ue_CapabilityRAT_ContainerList;
1431 1432 1433 1434 1435 1436 1437 1438 1439

    /* Encode UE-CapabilityRAT-ContainerList for sending to the DU */
    free(UE->ue_cap_buffer.buf);
    UE->ue_cap_buffer.len = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList,
                                                      NULL,
                                                      ue_CapabilityRAT_ContainerList,
                                                      (void **)&UE->ue_cap_buffer.buf);
    if (UE->ue_cap_buffer.len <= 0) {
      LOG_E(RRC, "could not encode UE-CapabilityRAT-ContainerList, abort handling capabilities\n");
1440
      return;
1441
    }
1442
    LOG_UE_UL_EVENT(UE, "Received UE capabilities\n");
1443

1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
    for (int i = 0; i < ue_CapabilityRAT_ContainerList->list.count; i++) {
      const NR_UE_CapabilityRAT_Container_t *ue_cap_container = ue_CapabilityRAT_ContainerList->list.array[i];
      if (ue_cap_container->rat_Type == NR_RAT_Type_nr) {
        if (UE->UE_Capability_nr) {
          ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
          UE->UE_Capability_nr = 0;
        }

        asn_dec_rval_t dec_rval = uper_decode(NULL,
                                              &asn_DEF_NR_UE_NR_Capability,
                                              (void **)&UE->UE_Capability_nr,
                                              ue_cap_container->ue_CapabilityRAT_Container.buf,
                                              ue_cap_container->ue_CapabilityRAT_Container.size,
                                              0,
                                              0);
        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
          xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
        }

        if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
1464
          LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
1465 1466 1467 1468 1469 1470 1471
          ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
          UE->UE_Capability_nr = 0;
        }

        UE->UE_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
        if (eutra_index != -1) {
          LOG_E(NR_RRC, "fatal: more than 1 eutra capability\n");
1472
          return;
1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494
        }
        eutra_index = i;
      }

      if (ue_cap_container->rat_Type == NR_RAT_Type_eutra_nr) {
        if (UE->UE_Capability_MRDC) {
          ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
          UE->UE_Capability_MRDC = 0;
        }
        asn_dec_rval_t dec_rval = uper_decode(NULL,
                                              &asn_DEF_NR_UE_MRDC_Capability,
                                              (void **)&UE->UE_Capability_MRDC,
                                              ue_cap_container->ue_CapabilityRAT_Container.buf,
                                              ue_cap_container->ue_CapabilityRAT_Container.size,
                                              0,
                                              0);

        if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
          xer_fprint(stdout, &asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
        }

        if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
1495
          LOG_E(NR_RRC, "UE %d: Failed to decode nr UE capabilities (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507
          ASN_STRUCT_FREE(asn_DEF_NR_UE_MRDC_Capability, UE->UE_Capability_MRDC);
          UE->UE_Capability_MRDC = 0;
        }
        UE->UE_MRDC_Capability_size = ue_cap_container->ue_CapabilityRAT_Container.size;
      }

      if (ue_cap_container->rat_Type == NR_RAT_Type_eutra) {
        // TODO
      }
    }

    if (eutra_index == -1)
1508
      return;
1509 1510
  }

1511
  rrc_gNB_send_NGAP_UE_CAPABILITIES_IND(rrc, UE, ue_cap_info);
1512

1513 1514 1515 1516 1517 1518 1519
  if (UE->n_initial_pdu > 0) {
    /* there were PDU sessions with the NG UE Context setup, but we had to set
     * up security and request capabilities, so trigger PDU sessions now. The
     * UE NAS message will be forwarded in the corresponding reconfiguration,
     * the Initial context setup response after reconfiguration complete. */
    trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0);
  } else {
1520 1521
    rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
    rrc_forward_ue_nas_message(rrc, UE);
1522
  }
1523

1524
  return;
1525 1526
}

1527
static void handle_rrcSetupComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_RRCSetupComplete_t *setup_complete)
1528
{
1529 1530
  uint8_t xid = setup_complete->rrc_TransactionIdentifier;
  UE->xids[xid] = RRC_ACTION_NONE;
1531

1532
  if (setup_complete->criticalExtensions.present != NR_RRCSetupComplete__criticalExtensions_PR_rrcSetupComplete) {
1533 1534
    LOG_E(NR_RRC, "malformed RRCSetupComplete received from UE %d\n", UE->rrc_ue_id);
    return;
1535 1536
  }

1537 1538 1539
  NR_RRCSetupComplete_IEs_t *setup_complete_ies = setup_complete->criticalExtensions.choice.rrcSetupComplete;

  if (setup_complete_ies->ng_5G_S_TMSI_Value != NULL) {
1540
    uint64_t fiveg_s_TMSI = 0;
1541
    if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI_Part2) {
1542 1543 1544
      const BIT_STRING_t *part2 = &setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI_Part2;
      if (part2->size != 2) {
        LOG_E(NR_RRC, "wrong ng_5G_S_TMSI_Part2 size, expected 2, provided %lu", part2->size);
1545
        return;
1546 1547
      }

1548 1549 1550 1551 1552 1553 1554 1555
      if (UE->Initialue_identity_5g_s_TMSI.presence) {
        uint16_t stmsi_part2 = BIT_STRING_to_uint16(part2);
        LOG_I(RRC, "s_tmsi part2 %d (%02x %02x)\n", stmsi_part2, part2->buf[0], part2->buf[1]);
        // Part2 is leftmost 9, Part1 is rightmost 39 bits of 5G-S-TMSI
        fiveg_s_TMSI = ((uint64_t) stmsi_part2) << 39 | UE->ng_5G_S_TMSI_Part1;
      } else {
        LOG_W(RRC, "UE %d received 5G-S-TMSI-Part2, but no 5G-S-TMSI-Part1 present, won't send 5G-S-TMSI to core\n", UE->rrc_ue_id);
        UE->Initialue_identity_5g_s_TMSI.presence = false;
1556 1557
      }
    } else if (setup_complete_ies->ng_5G_S_TMSI_Value->present == NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI) {
1558 1559 1560
      const NR_NG_5G_S_TMSI_t *bs_stmsi = &setup_complete_ies->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI;
      if (bs_stmsi->size != 6) {
        LOG_E(NR_RRC, "wrong ng_5G_S_TMSI size, expected 6, provided %lu", bs_stmsi->size);
1561
        return;
1562 1563
      }

1564 1565 1566 1567 1568 1569 1570 1571
      fiveg_s_TMSI = BIT_STRING_to_uint64(bs_stmsi);
      UE->Initialue_identity_5g_s_TMSI.presence = true;
    }

    if (UE->Initialue_identity_5g_s_TMSI.presence) {
      uint16_t amf_set_id = fiveg_s_TMSI >> 38;
      uint8_t amf_pointer = (fiveg_s_TMSI >> 32) & 0x3F;
      uint32_t fiveg_tmsi = (uint32_t) fiveg_s_TMSI;
1572
      LOG_I(NR_RRC,
1573
            "5g_s_TMSI: 0x%lX, amf_set_id: 0x%X (%d), amf_pointer: 0x%X (%d), 5g TMSI: 0x%X \n",
1574
            fiveg_s_TMSI,
1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
            amf_set_id,
            amf_set_id,
            amf_pointer,
            amf_pointer,
            fiveg_tmsi);
      UE->Initialue_identity_5g_s_TMSI.amf_set_id = amf_set_id;
      UE->Initialue_identity_5g_s_TMSI.amf_pointer = amf_pointer;
      UE->Initialue_identity_5g_s_TMSI.fiveg_tmsi = fiveg_tmsi;

      // update random identity with 5G-S-TMSI, which only contained Part1 of it
      UE->random_ue_identity = fiveg_s_TMSI;
1586 1587 1588
    }
  }

1589 1590
  rrc_gNB_process_RRCSetupComplete(rrc, UE, setup_complete->criticalExtensions.choice.rrcSetupComplete);
  return;
1591 1592
}

1593
static void handle_rrcReconfigurationComplete(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, const NR_RRCReconfigurationComplete_t *reconfig_complete)
1594 1595
{
  uint8_t xid = reconfig_complete->rrc_TransactionIdentifier;
1596
  UE->ue_reconfiguration_counter++;
1597

1598 1599 1600
  switch (UE->xids[xid]) {
    case RRC_PDUSESSION_RELEASE: {
      gtpv1u_gnb_delete_tunnel_req_t req = {0};
1601
      gtpv1u_delete_ngu_tunnel(rrc->module_id, &req);
1602
      // NGAP_PDUSESSION_RELEASE_RESPONSE
1603
      rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE(rrc, UE, xid);
1604 1605
    } break;
    case RRC_PDUSESSION_ESTABLISH:
1606 1607
      if (UE->n_initial_pdu > 0) {
        /* PDU sessions through initial UE context setup */
1608
        rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
1609 1610 1611 1612
        UE->n_initial_pdu = 0;
        free(UE->initial_pdus);
        UE->initial_pdus = NULL;
      } else if (UE->nb_of_pdusessions > 0)
1613
        rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(rrc, UE, xid);
1614 1615
      break;
    case RRC_PDUSESSION_MODIFY:
1616
      rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP(rrc, UE, xid);
1617 1618 1619 1620 1621
      break;
    case RRC_REESTABLISH_COMPLETE:
    case RRC_DEDICATED_RECONF:
      /* do nothing */
      break;
1622 1623 1624
    case RRC_ACTION_NONE:
      LOG_E(RRC, "UE %d: Received RRC Reconfiguration Complete with xid %d while no transaction is ongoing\n", UE->rrc_ue_id, xid);
      break;
1625
    default:
1626
      LOG_E(RRC, "UE %d: Received unexpected transaction type %d for xid %d\n", UE->rrc_ue_id, UE->xids[xid], xid);
1627
      break;
1628
  }
1629
  UE->xids[xid] = RRC_ACTION_NONE;
1630
  for (int i = 0; i < NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; ++i) {
1631 1632 1633 1634
    if (UE->xids[i] != RRC_ACTION_NONE) {
      LOG_I(RRC, "UE %d: transaction %d still ongoing for action %d\n", UE->rrc_ue_id, i, UE->xids[i]);
    }
  }
1635 1636 1637 1638 1639 1640 1641 1642

  if (UE->ho_context != NULL) {
    LOG_A(NR_RRC, "handover for UE %d/RNTI %04x complete!\n", UE->rrc_ue_id, UE->rnti);
    DevAssert(UE->ho_context->target != NULL);

    UE->ho_context->target->ho_success(rrc, UE);
    nr_rrc_finalize_ho(UE);
  }
1643
}
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659

static void rrc_gNB_generate_UECapabilityEnquiry(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue)
{
  uint8_t buffer[100];

  T(T_ENB_RRC_UE_CAPABILITY_ENQUIRY, T_INT(rrc->module_id), T_INT(0), T_INT(0), T_INT(ue->rrc_ue_id));
  uint8_t xid = rrc_gNB_get_next_transaction_identifier(rrc->module_id);
  ue->xids[xid] = RRC_UECAPABILITY_ENQUIRY;
  int size = do_NR_SA_UECapabilityEnquiry(buffer, xid);
  LOG_I(NR_RRC, "UE %d: Logical Channel DL-DCCH, Generate NR UECapabilityEnquiry (bytes %d, xid %d)\n", ue->rrc_ue_id, size, xid);

  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");

  nr_rrc_transfer_protected_rrc_message(rrc, ue, DCCH, buffer, size);
}

1660
static int rrc_gNB_decode_dcch(gNB_RRC_INST *rrc, const f1ap_ul_rrc_message_t *msg)
Xue Song's avatar
Xue Song committed
1661
{
1662
  /* we look up by CU UE ID! Do NOT change back to RNTI! */
1663
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, msg->gNB_CU_ue_id);
1664
  if (!ue_context_p) {
1665
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", msg->gNB_CU_ue_id);
1666 1667
    return -1;
  }
1668
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1669

1670 1671 1672
  if (msg->srb_id < 1 || msg->srb_id > 2) {
    LOG_E(NR_RRC, "Received message on SRB %d, discarding message\n", msg->srb_id);
    return -1;
1673 1674
  }

1675 1676
  LOG_D(NR_RRC, "UE %d: Decoding DCCH %d size %d\n", UE->rrc_ue_id, msg->srb_id, msg->rrc_container_length);
  LOG_DUMPMSG(RRC, DEBUG_RRC, (char *)msg->rrc_container, msg->rrc_container_length, "[MSG] RRC UL Information Transfer \n");
1677
  NR_UL_DCCH_Message_t *ul_dcch_msg = NULL;
1678 1679
  asn_dec_rval_t dec_rval =
      uper_decode(NULL, &asn_DEF_NR_UL_DCCH_Message, (void **)&ul_dcch_msg, msg->rrc_container, msg->rrc_container_length, 0, 0);
1680

1681
  if ((dec_rval.code != RC_OK) && (dec_rval.consumed == 0)) {
1682
    LOG_E(NR_RRC, "UE %d: Failed to decode UL-DCCH (%zu bytes)\n", UE->rrc_ue_id, dec_rval.consumed);
1683 1684 1685
    return -1;
  }

1686 1687 1688 1689
  if (LOG_DEBUGFLAG(DEBUG_ASN1)) {
    xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)ul_dcch_msg);
  }

1690 1691 1692
  if (ul_dcch_msg->message.present == NR_UL_DCCH_MessageType_PR_c1) {
    switch (ul_dcch_msg->message.choice.c1->present) {
      case NR_UL_DCCH_MessageType__c1_PR_NOTHING:
1693
        LOG_I(NR_RRC, "Received PR_NOTHING on UL-DCCH-Message\n");
1694
        break;
Xue Song's avatar
Xue Song committed
1695

1696
      case NR_UL_DCCH_MessageType__c1_PR_rrcReconfigurationComplete:
1697
        LOG_UE_UL_EVENT(UE, "Received RRCReconfigurationComplete\n");
1698
        handle_rrcReconfigurationComplete(rrc, UE, ul_dcch_msg->message.choice.c1->choice.rrcReconfigurationComplete);
1699 1700 1701
        break;

      case NR_UL_DCCH_MessageType__c1_PR_rrcSetupComplete:
1702
        LOG_UE_UL_EVENT(UE, "Received RRCSetupComplete (RRC_CONNECTED reached)\n");
1703
        handle_rrcSetupComplete(rrc, UE, ul_dcch_msg->message.choice.c1->choice.rrcSetupComplete);
1704
        break;
wujing's avatar
wujing committed
1705

1706
      case NR_UL_DCCH_MessageType__c1_PR_measurementReport:
1707
        rrc_gNB_process_MeasurementReport(UE, ul_dcch_msg->message.choice.c1->choice.measurementReport);
1708 1709
        break;

1710
      case NR_UL_DCCH_MessageType__c1_PR_ulInformationTransfer:
1711
        LOG_UE_UL_EVENT(UE, "Received RRC UL Information Transfer [%d bytes]\n", msg->rrc_container_length);
1712
        rrc_gNB_send_NGAP_UPLINK_NAS(rrc, UE, ul_dcch_msg);
1713
        break;
wujing's avatar
wujing committed
1714

1715
      case NR_UL_DCCH_MessageType__c1_PR_securityModeComplete:
1716
        LOG_UE_UL_EVENT(UE, "Received Security Mode Complete\n");
Cedric Roux's avatar
Cedric Roux committed
1717
        /* configure ciphering */
1718 1719
        nr_rrc_pdcp_config_security(UE, true);
        UE->as_security_active = true;
Cedric Roux's avatar
Cedric Roux committed
1720

1721
        /* trigger UE capability enquiry if we don't have them yet */
1722 1723
        if (UE->ue_cap_buffer.len == 0) {
          rrc_gNB_generate_UECapabilityEnquiry(rrc, UE);
1724
          /* else blocks are executed after receiving UE capability info */
1725
        } else if (UE->n_initial_pdu > 0) {
1726 1727 1728 1729
          /* there were PDU sessions with the NG UE Context setup, but we had
           * to set up security, so trigger PDU sessions now. The UE NAS
           * message will be forwarded in the corresponding reconfiguration,
           * the Initial context setup response after reconfiguration complete. */
1730
          trigger_bearer_setup(rrc, UE, UE->n_initial_pdu, UE->initial_pdus, 0);
1731
        } else {
1732 1733
          /* we already have capabilities, and no PDU sessions to setup, ack
           * this UE */
1734
          rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(rrc, UE);
1735
          rrc_forward_ue_nas_message(rrc, UE);
1736
        }
1737 1738
        break;

1739
      case NR_UL_DCCH_MessageType__c1_PR_securityModeFailure:
1740 1741
        LOG_E(NR_RRC, "UE %d: received securityModeFailure\n", ue_context_p->ue_context.rrc_ue_id);
        LOG_W(NR_RRC, "Cannot continue as no AS security is activated (implementation missing)\n");
1742
        break;
1743

1744
      case NR_UL_DCCH_MessageType__c1_PR_ueCapabilityInformation:
1745
        handle_ueCapabilityInformation(rrc, UE, ul_dcch_msg->message.choice.c1->choice.ueCapabilityInformation);
1746
        break;
1747

1748
      case NR_UL_DCCH_MessageType__c1_PR_rrcReestablishmentComplete:
1749
        LOG_UE_UL_EVENT(UE, "Received RRCReestablishmentComplete\n");
1750
        handle_rrcReestablishmentComplete(rrc, UE, ul_dcch_msg->message.choice.c1->choice.rrcReestablishmentComplete);
1751
        break;
1752

1753 1754
      default:
        break;
Xue Song's avatar
Xue Song committed
1755
    }
1756
  }
1757
  ASN_STRUCT_FREE(asn_DEF_NR_UL_DCCH_Message, ul_dcch_msg);
1758
  return 0;
Xue Song's avatar
Xue Song committed
1759 1760
}

Robert Schmidt's avatar
Robert Schmidt committed
1761
void rrc_gNB_process_initial_ul_rrc_message(sctp_assoc_t assoc_id, const f1ap_initial_ul_rrc_message_t *ul_rrc)
1762
{
Robert Schmidt's avatar
Robert Schmidt committed
1763
  AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n");
1764

1765
  gNB_RRC_INST *rrc = RC.nrrrc[0];
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776
  LOG_I(NR_RRC, "Decoding CCCH: RNTI %04x, payload_size %d\n", ul_rrc->crnti, ul_rrc->rrc_container_length);
  NR_UL_CCCH_Message_t *ul_ccch_msg = NULL;
  asn_dec_rval_t dec_rval = uper_decode(NULL,
                                        &asn_DEF_NR_UL_CCCH_Message,
                                        (void **)&ul_ccch_msg,
                                        ul_rrc->rrc_container,
                                        ul_rrc->rrc_container_length,
                                        0,
                                        0);
  if (dec_rval.code != RC_OK || dec_rval.consumed == 0) {
    LOG_E(NR_RRC, " FATAL Error in receiving CCCH\n");
1777 1778
    return;
  }
1779

1780 1781 1782 1783 1784
  if (ul_ccch_msg->message.present == NR_UL_CCCH_MessageType_PR_c1) {
    switch (ul_ccch_msg->message.choice.c1->present) {
      case NR_UL_CCCH_MessageType__c1_PR_NOTHING:
        LOG_W(NR_RRC, "Received PR_NOTHING on UL-CCCH-Message, ignoring message\n");
        break;
Robert Schmidt's avatar
Robert Schmidt committed
1785

1786 1787
      case NR_UL_CCCH_MessageType__c1_PR_rrcSetupRequest:
        LOG_D(NR_RRC, "Received RRCSetupRequest on UL-CCCH-Message (UE rnti %04x)\n", ul_rrc->crnti);
Robert Schmidt's avatar
Robert Schmidt committed
1788
        rrc_handle_RRCSetupRequest(rrc, assoc_id, &ul_ccch_msg->message.choice.c1->choice.rrcSetupRequest->rrcSetupRequest, ul_rrc);
1789
        break;
1790

1791 1792 1793
      case NR_UL_CCCH_MessageType__c1_PR_rrcResumeRequest:
        LOG_E(NR_RRC, "Received rrcResumeRequest message, but handling is not implemented\n");
        break;
Xue Song's avatar
Xue Song committed
1794

1795 1796 1797 1798
      case NR_UL_CCCH_MessageType__c1_PR_rrcReestablishmentRequest: {
        LOG_D(NR_RRC, "Received RRCReestablishmentRequest on UL-CCCH-Message (UE RNTI %04x)\n", ul_rrc->crnti);
        rrc_handle_RRCReestablishmentRequest(
            rrc,
Robert Schmidt's avatar
Robert Schmidt committed
1799
            assoc_id,
1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812
            &ul_ccch_msg->message.choice.c1->choice.rrcReestablishmentRequest->rrcReestablishmentRequest,
            ul_rrc);
      } break;

      case NR_UL_CCCH_MessageType__c1_PR_rrcSystemInfoRequest:
        LOG_I(NR_RRC, "UE %04x receive rrcSystemInfoRequest message \n", ul_rrc->crnti);
        /* TODO */
        break;

      default:
        LOG_E(NR_RRC, "UE %04x Unknown message\n", ul_rrc->crnti);
        break;
    }
1813
  }
1814
  ASN_STRUCT_FREE(asn_DEF_NR_UL_CCCH_Message, ul_ccch_msg);
1815 1816
}

Cedric Roux's avatar
Cedric Roux committed
1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828
void rrc_gNB_process_release_request(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_release_request_t *m)
{
  gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
  rrc_remove_nsa_user(rrc, m->rnti);
}

void rrc_gNB_process_dc_overall_timeout(const module_id_t gnb_mod_idP, x2ap_ENDC_dc_overall_timeout_t *m)
{
  gNB_RRC_INST *rrc = RC.nrrrc[gnb_mod_idP];
  rrc_remove_nsa_user(rrc, m->rnti);
}

1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839
/* \brief fill E1 bearer modification's DRB from F1 DRB
 * \param drb_e1 pointer to a DRB inside an E1 bearer modification message
 * \param drb_f1 pointer to a DRB inside an F1 UE Ctxt modification Response */
static void fill_e1_bearer_modif(DRB_nGRAN_to_setup_t *drb_e1, const f1ap_drb_to_be_setup_t *drb_f1)
{
  drb_e1->id = drb_f1->drb_id;
  drb_e1->numDlUpParam = drb_f1->up_dl_tnl_length;
  drb_e1->DlUpParamList[0].tlAddress = drb_f1->up_dl_tnl[0].tl_address;
  drb_e1->DlUpParamList[0].teId = drb_f1->up_dl_tnl[0].teid;
}

1840 1841 1842
/**
 * @brief Store F1-U DL TL and TEID in RRC
*/
1843
static void f1u_dl_gtp_update(f1u_tunnel_t *f1u, const f1ap_up_tnl_t *p)
1844
{
1845 1846 1847
  f1u->teid = p->teid;
  memcpy(&f1u->addr.buffer, &p->tl_address, sizeof(uint8_t) * 4);
  f1u->addr.length = sizeof(in_addr_t);
1848 1849
}

1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863
/**
 * @brief Update DRB TEID information in RRC storage from received DRB list
 */
static void store_du_f1u_tunnel(const f1ap_drb_to_be_setup_t *drbs, int n, gNB_RRC_UE_t *ue)
{
  for (int i = 0; i < n; i++) {
    const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i];
    AssertFatal(drb_f1->up_dl_tnl_length == 1, "can handle only one UP param\n");
    AssertFatal(drb_f1->drb_id < MAX_DRBS_PER_UE, "illegal DRB ID %ld\n", drb_f1->drb_id);
    drb_t *drb = get_drb(ue, drb_f1->drb_id);
    f1u_dl_gtp_update(&drb->du_tunnel_config, &drb_f1->up_dl_tnl[0]);
  }
}

1864 1865 1866 1867 1868 1869 1870 1871 1872 1873
/*
 * @brief Store F1-U UL TEID and address in RRC
 */
static void f1u_ul_gtp_update(f1u_tunnel_t *f1u, const up_params_t *p)
{
  f1u->teid = p->teId;
  memcpy(&f1u->addr.buffer, &p->tlAddress, sizeof(uint8_t) * 4);
  f1u->addr.length = sizeof(in_addr_t);
}

1874
/* \brief use list of DRBs and send the corresponding bearer update message via
1875
 * E1 to the CU of this UE. Also updates TEID info internally */
1876 1877 1878
static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_to_be_setup_t *drbs)
{
  // we assume the same UE ID in CU-UP and CU-CP
1879
  e1ap_bearer_mod_req_t req = {
1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894
    .gNB_cu_cp_ue_id = UE->rrc_ue_id,
    .gNB_cu_up_ue_id = UE->rrc_ue_id,
  };

  for (int i = 0; i < n; i++) {
    const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i];
    rrc_pdu_session_param_t *pdu_ue = find_pduSession_from_drbId(UE, drb_f1->drb_id);
    if (pdu_ue == NULL) {
      LOG_E(RRC, "UE %d: UE Context Modif Response: no PDU session for DRB ID %ld\n", UE->rrc_ue_id, drb_f1->drb_id);
      continue;
    }
    pdu_session_to_setup_t *pdu_e1 = find_or_next_pdu_session(&req, pdu_ue->param.pdusession_id);
    DevAssert(pdu_e1 != NULL);
    pdu_e1->sessionId = pdu_ue->param.pdusession_id;
    DRB_nGRAN_to_setup_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify];
1895
    /* Fill E1 bearer context modification */
1896 1897 1898 1899 1900 1901
    fill_e1_bearer_modif(drb_e1, drb_f1);
    pdu_e1->numDRB2Modify += 1;
  }
  DevAssert(req.numPDUSessionsMod > 0);
  DevAssert(req.numPDUSessions == 0);

1902 1903 1904 1905 1906
  req.cipheringAlgorithm = rrc->security.do_drb_ciphering ? UE->ciphering_algorithm : 0;
  req.integrityProtectionAlgorithm = rrc->security.do_drb_integrity ? UE->integrity_algorithm : 0;
  nr_derive_key(UP_ENC_ALG, req.cipheringAlgorithm, UE->kgnb, (uint8_t *)req.encryptionKey);
  nr_derive_key(UP_INT_ALG, req.integrityProtectionAlgorithm, UE->kgnb, (uint8_t *)req.integrityProtectionKey);

1907 1908 1909 1910 1911
  // send the E1 bearer modification request message to update F1-U tunnel info
  sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, UE);
  rrc->cucp_cuup.bearer_context_mod(assoc_id, &req);
}

laurent's avatar
tmp  
laurent committed
1912 1913
static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance_t instance)
{
1914
  f1ap_ue_context_setup_t *resp = &F1AP_UE_CONTEXT_SETUP_RESP(msg_p);
1915
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
1916 1917 1918 1919 1920
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
    return;
  }
1921
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
1922
  UE->f1_ue_context_active = true;
1923

1924 1925 1926 1927 1928 1929 1930
  NR_CellGroupConfig_t *cellGroupConfig = NULL;
  asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                 &asn_DEF_NR_CellGroupConfig,
                                                 (void **)&cellGroupConfig,
                                                 (uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
                                                 resp->du_to_cu_rrc_information->cellGroupConfig_length);
  AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
1931

1932 1933 1934
  if (UE->masterCellGroup) {
    ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
    LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
1935
  }
1936 1937
  UE->masterCellGroup = cellGroupConfig;
  if (LOG_DEBUGFLAG(DEBUG_ASN1))
1938
    xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
1939

1940
  if (resp->drbs_to_be_setup_length > 0) {
1941 1942 1943
    int num_drb = get_number_active_drbs(UE);
    DevAssert(num_drb == 0 || num_drb == resp->drbs_to_be_setup_length);

1944 1945 1946
    /* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU
     * seems buggy and does not ack, so simply check that locally we activated */
    AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment");
1947
    store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE);
1948 1949 1950 1951
    if (num_drb == 0)
      e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
    else
      cuup_notify_reestablishment(rrc, UE);
1952 1953
  }

1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974
  if (UE->ho_context == NULL) {
    rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
  } else {
    // case of handover
    // handling of "target CU" information
    DevAssert(UE->ho_context->target != NULL);
    DevAssert(resp->crnti != NULL);
    UE->ho_context->target->du_ue_id = resp->gNB_DU_ue_id;
    UE->ho_context->target->new_rnti = *resp->crnti;

    uint8_t xid = rrc_gNB_get_next_transaction_identifier(0);
    UE->xids[xid] = RRC_DEDICATED_RECONF;
    uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
    int size = rrc_gNB_encode_RRCReconfiguration(rrc, UE, xid, NULL, buffer, sizeof(buffer), true);
    DevAssert(size > 0 && size <= sizeof(buffer));

    // TODO N2 38.413 sec 9.3.1.21: admitted PDU sessions (in UE), Target to
    // Source Transparent Container (9.3.1.21) which encodes the RRC
    // reconfiguration above
    UE->ho_context->target->ho_req_ack(rrc, UE, buffer, size);
  }
1975 1976
}

1977
static void rrc_CU_process_ue_context_release_request(MessageDef *msg_p, sctp_assoc_t assoc_id)
1978 1979 1980 1981
{
  const int instance = 0;
  f1ap_ue_context_release_req_t *req = &F1AP_UE_CONTEXT_RELEASE_REQ(msg_p);
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
1982
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, req->gNB_CU_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
1983
  // TODO what happens if no AMF connected? should also handle, set an_release true
1984 1985
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", req->gNB_CU_ue_id);
Robert Schmidt's avatar
Robert Schmidt committed
1986
    // TODO just request the DU to release to make it happy
1987 1988
    return;
  }
1989

1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  if (UE->ho_context != NULL) {
    nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
    bool from_source_du = source_ctx && source_ctx->du->assoc_id == assoc_id;
    if (from_source_du) {
      // we received release request from the source DU, but HO is still
      // ongoing; free the UE, and remove the HO context.
      LOG_W(NR_RRC, "UE %d: release request from source DU ID %ld during HO, marking HO as complete\n", UE->rrc_ue_id, source_ctx->du->setup_req->gNB_DU_id);
      RETURN_IF_INVALID_ASSOC_ID(source_ctx->du->assoc_id);
      f1ap_ue_context_release_cmd_t cmd = {
          .gNB_CU_ue_id = UE->rrc_ue_id,
          .gNB_DU_ue_id = source_ctx->du_ue_id,
          .cause = F1AP_CAUSE_RADIO_NETWORK,
          .cause_value = 5, // 5 = F1AP_CauseRadioNetwork_interaction_with_other_procedure
          .srb_id = DCCH,
      };
      rrc->mac_rrc.ue_context_release_command(assoc_id, &cmd);
      nr_rrc_finalize_ho(UE);
      return;
    }
    // if we receive the release request from the target DU (regardless if
    // successful), we assume it is "genuine" and ask the AMF to release
    nr_rrc_finalize_ho(UE);
  }

2015
  /* TODO: marshall types correctly */
2016
  LOG_I(NR_RRC, "received UE Context Release Request for UE %u, forwarding to AMF\n", req->gNB_CU_ue_id);
2017 2018 2019 2020 2021 2022
  rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(instance,
                                           ue_context_p,
                                           NGAP_CAUSE_RADIO_NETWORK,
                                           NGAP_CAUSE_RADIO_NETWORK_RADIO_CONNECTION_WITH_UE_LOST);
}

Robert Schmidt's avatar
Robert Schmidt committed
2023 2024 2025 2026 2027
static void rrc_delete_ue_data(gNB_RRC_UE_t *UE)
{
  ASN_STRUCT_FREE(asn_DEF_NR_UE_NR_Capability, UE->UE_Capability_nr);
  ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
  ASN_STRUCT_FREE(asn_DEF_NR_MeasResults, UE->measResults);
2028
  free_MeasConfig(UE->measConfig);
Robert Schmidt's avatar
Robert Schmidt committed
2029 2030
}

2031 2032 2033 2034 2035 2036 2037
void rrc_remove_ue(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *ue_context_p)
{
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  /* we call nr_pdcp_remove_UE() in the handler of E1 bearer release, but if we
   * are in E1, we also need to free the UE in the CU-CP, so call it twice to
   * cover all cases */
  nr_pdcp_remove_UE(UE->rrc_ue_id);
2038 2039 2040 2041
  uint32_t pdu_sessions[256];
  for (int i = 0; i < UE->nb_of_pdusessions && i < 256; ++i)
    pdu_sessions[i] = UE->pduSession[i].param.pdusession_id;
  rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_COMPLETE(0, UE->rrc_ue_id, UE->nb_of_pdusessions, pdu_sessions);
2042 2043 2044 2045 2046
  LOG_I(NR_RRC, "removed UE CU UE ID %u/RNTI %04x \n", UE->rrc_ue_id, UE->rnti);
  rrc_delete_ue_data(UE);
  rrc_gNB_remove_ue_context(rrc, ue_context_p);
}

2047 2048 2049 2050 2051
static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p)
{
  const int instance = 0;
  f1ap_ue_context_release_complete_t *complete = &F1AP_UE_CONTEXT_RELEASE_COMPLETE(msg_p);
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
2052 2053 2054 2055 2056
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, complete->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", complete->gNB_CU_ue_id);
    return;
  }
2057

2058 2059 2060 2061 2062 2063 2064
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  if (UE->an_release) {
    /* only trigger release if it has been requested by core
     * otherwise, it might be CU that requested release on a DU during normal
     * operation (i.e, handover) */
    rrc_remove_ue(RC.nrrrc[0], ue_context_p);
  }
2065 2066
}

laurent's avatar
tmp  
laurent committed
2067 2068
static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, instance_t instance)
{
2069
  f1ap_ue_context_modif_resp_t *resp = &F1AP_UE_CONTEXT_MODIFICATION_RESP(msg_p);
2070
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
2071 2072 2073 2074 2075
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_CU_ue_id);
  if (!ue_context_p) {
    LOG_E(RRC, "could not find UE context for CU UE ID %u, aborting transaction\n", resp->gNB_CU_ue_id);
    return;
  }
2076
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
2077

2078
  if (resp->drbs_to_be_setup_length > 0) {
2079
    store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE);
2080
    e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup);
2081
  }
2082

2083 2084 2085 2086 2087 2088 2089 2090
  if (resp->du_to_cu_rrc_information != NULL && resp->du_to_cu_rrc_information->cellGroupConfig != NULL) {
    NR_CellGroupConfig_t *cellGroupConfig = NULL;
    asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                   &asn_DEF_NR_CellGroupConfig,
                                                   (void **)&cellGroupConfig,
                                                   (uint8_t *)resp->du_to_cu_rrc_information->cellGroupConfig,
                                                   resp->du_to_cu_rrc_information->cellGroupConfig_length);
    AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n");
matzakos's avatar
matzakos committed
2091

2092 2093 2094
    if (UE->masterCellGroup) {
      ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
      LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti);
2095
    }
2096
    UE->masterCellGroup = cellGroupConfig;
2097

2098
    rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE);
2099
  }
2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116

  // Reconfiguration should have been sent to the UE, so it will attempt the
  // handover. In the F1 case, update with new RNTI, and update secondary UE
  // association, so we can receive the new UE from the target DU (in N2/Xn,
  // nothing is to be done, we wait for confirmation to release the UE in the
  // CU/DU)
  if (UE->ho_context && UE->ho_context->target && UE->ho_context->source) {
    nr_ho_target_cu_t *target_ctx = UE->ho_context->target;
    f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
    ue_data.secondary_ue = target_ctx->du_ue_id;
    ue_data.du_assoc_id = target_ctx->du->assoc_id;
    cu_remove_f1_ue_data(UE->rrc_ue_id);
    cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data);
    LOG_I(NR_RRC, "UE %d handover: update RNTI from %04x to %04x\n", UE->rrc_ue_id, UE->rnti, target_ctx->new_rnti);
    nr_ho_source_cu_t *source_ctx = UE->ho_context->source;
    DevAssert(source_ctx->old_rnti == UE->rnti);
    UE->rnti = target_ctx->new_rnti;
2117
    UE->nr_cellid = target_ctx->du->setup_req->cell[0].info.nr_cellid;
2118
  }
2119 2120
}

2121
static void rrc_CU_process_ue_modification_required(MessageDef *msg_p, instance_t instance, sctp_assoc_t assoc_id)
2122
{
2123
  gNB_RRC_INST *rrc = RC.nrrrc[instance];
2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
  f1ap_ue_context_modif_required_t *required = &F1AP_UE_CONTEXT_MODIFICATION_REQUIRED(msg_p);
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, required->gNB_CU_ue_id);
  if (ue_context_p == NULL) {
    LOG_E(RRC, "Could not find UE context for CU UE ID %d, cannot handle UE context modification request\n", required->gNB_CU_ue_id);
    f1ap_ue_context_modif_refuse_t refuse = {
      .gNB_CU_ue_id = required->gNB_CU_ue_id,
      .gNB_DU_ue_id = required->gNB_DU_ue_id,
      .cause = F1AP_CAUSE_RADIO_NETWORK,
      .cause_value = F1AP_CauseRadioNetwork_unknown_or_already_allocated_gnb_cu_ue_f1ap_id,
    };
Robert Schmidt's avatar
Robert Schmidt committed
2134
    rrc->mac_rrc.ue_context_modification_refuse(msg_p->ittiMsgHeader.originInstance, &refuse);
2135 2136 2137
    return;
  }

2138 2139 2140 2141 2142 2143
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
  if (UE->ho_context && UE->ho_context->source && UE->ho_context->source->du && UE->ho_context->source->du->assoc_id == assoc_id) {
    LOG_W(NR_RRC, "UE %d: UE Context Modification Required during handover, ignoring message\n", UE->rrc_ue_id);
    return;
  }

2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164
  if (required->du_to_cu_rrc_information && required->du_to_cu_rrc_information->cellGroupConfig) {
    gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
    LOG_I(RRC,
          "UE Context Modification Required: new CellGroupConfig for UE ID %d/RNTI %04x, triggering reconfiguration\n",
          UE->rrc_ue_id,
          UE->rnti);

    NR_CellGroupConfig_t *cellGroupConfig = NULL;
    asn_dec_rval_t dec_rval = uper_decode_complete(NULL,
                                                   &asn_DEF_NR_CellGroupConfig,
                                                   (void **)&cellGroupConfig,
                                                   (uint8_t *)required->du_to_cu_rrc_information->cellGroupConfig,
                                                   required->du_to_cu_rrc_information->cellGroupConfig_length);
    if (dec_rval.code != RC_OK && dec_rval.consumed == 0) {
      LOG_E(RRC, "Cell group config decode error, refusing reconfiguration\n");
      f1ap_ue_context_modif_refuse_t refuse = {
        .gNB_CU_ue_id = required->gNB_CU_ue_id,
        .gNB_DU_ue_id = required->gNB_DU_ue_id,
        .cause = F1AP_CAUSE_PROTOCOL,
        .cause_value = F1AP_CauseProtocol_transfer_syntax_error,
      };
Robert Schmidt's avatar
Robert Schmidt committed
2165
      rrc->mac_rrc.ue_context_modification_refuse(msg_p->ittiMsgHeader.originInstance, &refuse);
2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177
      return;
    }

    if (UE->masterCellGroup) {
      ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);
      LOG_I(RRC, "UE %d/RNTI %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rrc_ue_id, UE->rnti);
    }
    UE->masterCellGroup = cellGroupConfig;
    if (LOG_DEBUGFLAG(DEBUG_ASN1))
      xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, UE->masterCellGroup);

    /* trigger reconfiguration */
2178
    nr_rrc_reconfiguration_req(rrc, UE, 0, 0);
2179 2180 2181 2182 2183 2184 2185 2186
    return;
  }
  LOG_W(RRC,
        "nothing to be done after UE Context Modification Required for UE ID %d/RNTI %04x\n",
        required->gNB_CU_ue_id,
        required->gNB_DU_ue_id);
}

2187 2188 2189 2190
unsigned int mask_flip(unsigned int x) {
  return((((x>>8) + (x<<8))&0xffff)>>6);
}

2191
pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, rrc_pdu_session_param_t *pduSession)
2192 2193 2194 2195 2196 2197 2198 2199 2200 2201
{
  pdusession_t *pdu = &pduSession->param;
  for (int i = 0; i < pdu->nb_qos; i++) {
    if (qfi == pdu->qos[i].qfi)
      return &pdu->qos[i];
  }
  AssertFatal(1 == 0, "The pdu session %d does not contain a qos flow with qfi = %d\n", pdu->pdusession_id, qfi);
  return NULL;
}

2202
/* \bref return F1AP QoS characteristics based on Qos flow parameters */
2203
f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param)
2204 2205
{
  f1ap_qos_characteristics_t qos_char = {0};
2206 2207
  if (qos_param->fiveQI_type == DYNAMIC) {
    qos_char.qos_type = DYNAMIC;
2208 2209 2210
    qos_char.dynamic.fiveqi = qos_param->fiveQI;
    qos_char.dynamic.qos_priority_level = qos_param->qos_priority;
  } else {
2211
    qos_char.qos_type = NON_DYNAMIC;
2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233
    qos_char.non_dynamic.fiveqi = qos_param->fiveQI;
    qos_char.non_dynamic.qos_priority_level = qos_param->qos_priority;
  }
  return qos_char;
}

/* \brief fills a list of DRBs to be setup from a number of PDU sessions in E1
 * bearer setup response */
static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc,
                                             gNB_RRC_UE_t *UE,
                                             const pdu_session_setup_t *pduSession,
                                             int numPduSession,
                                             f1ap_drb_to_be_setup_t drbs[32])
{
  int nb_drb = 0;
  for (int p = 0; p < numPduSession; ++p) {
    rrc_pdu_session_param_t *RRC_pduSession = find_pduSession(UE, pduSession[p].id, false);
    DevAssert(RRC_pduSession);
    for (int i = 0; i < pduSession[p].numDRBSetup; i++) {
      const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i];
      f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb];
      drb->drb_id = pduSession[p].DRBnGRanList[i].id;
2234
      drb->rlc_mode = rrc->configuration.um_on_default_drb ? F1AP_RLC_MODE_UM_BIDIR : F1AP_RLC_MODE_AM;
2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262
      drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tlAddress;
      drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd;
      drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].teId;
      drb->up_ul_tnl_length = 1;

      /* pass QoS info to MAC */
      int nb_qos_flows = drb_config->numQosFlowSetup;
      AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n");
      drb->drb_info.flows_to_be_setup_length = nb_qos_flows;
      drb->drb_info.flows_mapped_to_drb = calloc(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t));
      AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n");
      for (int j = 0; j < nb_qos_flows; j++) {
        drb->drb_info.flows_mapped_to_drb[j].qfi = drb_config->qosFlows[j].qfi;
        pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession);
        drb->drb_info.flows_mapped_to_drb[j].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char);
      }
      /* the DRB QoS parameters: we just reuse the ones from the first flow */
      drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params;

      /* pass NSSAI info to MAC */
      drb->nssai = RRC_pduSession->param.nssai;

      nb_drb++;
    }
  }
  return nb_drb;
}

2263
/**
2264
 * @brief E1AP Bearer Context Setup Response processing on CU-CP
2265
*/
2266
void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp, instance_t instance)
2267 2268
{
  gNB_RRC_INST *rrc = RC.nrrrc[0];
2269 2270
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
  AssertFatal(ue_context_p != NULL, "did not find UE with CU UE ID %d\n", resp->gNB_cu_cp_ue_id);
2271
  gNB_RRC_UE_t *UE = &ue_context_p->ue_context;
2272

2273 2274 2275 2276 2277 2278 2279 2280
  // currently: we don't have "infrastructure" to save the CU-UP UE ID, so we
  // assume (and below check) that CU-UP UE ID == CU-CP UE ID
  AssertFatal(resp->gNB_cu_cp_ue_id == resp->gNB_cu_up_ue_id,
              "cannot handle CU-UP UE ID different from CU-CP UE ID (%d vs %d)\n",
              resp->gNB_cu_cp_ue_id,
              resp->gNB_cu_up_ue_id);

  // save the tunnel address for the PDU sessions
2281
  for (int i = 0; i < resp->numPDUSessions; i++) {
2282 2283 2284 2285 2286 2287 2288 2289 2290
    pdu_session_setup_t *e1_pdu = &resp->pduSession[i];
    rrc_pdu_session_param_t *rrc_pdu = find_pduSession(UE, e1_pdu->id, false);
    if (rrc_pdu == NULL) {
      LOG_W(RRC, "E1: received setup for PDU session %ld, but has not been requested\n", e1_pdu->id);
      continue;
    }
    rrc_pdu->param.gNB_teid_N3 = e1_pdu->teId;
    memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tlAddress, sizeof(uint8_t) * 4);
    rrc_pdu->param.gNB_addr_N3.length = sizeof(in_addr_t);
2291 2292 2293 2294 2295 2296 2297 2298 2299

    // save the tunnel address for the DRBs
    for (int i = 0; i < e1_pdu->numDRBSetup; i++) {
      DRB_nGRAN_setup_t *drb_config = &e1_pdu->DRBnGRanList[i];
      // numUpParam only relevant in F1, but not monolithic
      AssertFatal(drb_config->numUpParam <= 1, "can only up to one UP param\n");
      drb_t *drb = get_drb(UE, drb_config->id);
      f1u_ul_gtp_update(&drb->cuup_tunnel_config, &drb_config->UpParamList[0]);
    }
2300 2301
  }

2302
  /* Instruction towards the DU for DRB configuration and tunnel creation */
2303
  f1ap_drb_to_be_setup_t drbs[32]; // maximum DRB can be 32
2304
  int nb_drb = fill_drb_to_be_setup_from_e1_resp(rrc, UE, resp->pduSession, resp->numPDUSessions, drbs);
2305

2306 2307 2308 2309
  if (!UE->f1_ue_context_active)
    rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, nb_drb, drbs);
  else
    rrc_gNB_generate_UeContextModificationRequest(rrc, ue_context_p, nb_drb, drbs, 0, NULL);
2310 2311
}

2312
/**
2313
 * @brief E1AP Bearer Context Modification Response processing on CU-CP
2314
 */
2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330
void rrc_gNB_process_e1_bearer_context_modif_resp(const e1ap_bearer_modif_resp_t *resp)
{
  gNB_RRC_INST *rrc = RC.nrrrc[0];
  rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(rrc, resp->gNB_cu_cp_ue_id);
  if (ue_context_p == NULL) {
    LOG_E(RRC, "no UE with CU-CP UE ID %d found\n", resp->gNB_cu_cp_ue_id);
    return;
  }

  // there is not really anything to do here as of now
  for (int i = 0; i < resp->numPDUSessionsMod; ++i) {
    const pdu_session_modif_t *pdu = &resp->pduSessionMod[i];
    LOG_I(RRC, "UE %d: PDU session ID %ld modified %d bearers\n", resp->gNB_cu_cp_ue_id, pdu->id, pdu->numDRBModified);
  }
}

2331 2332
/**
 * @brief E1AP Bearer Context Release processing
2333
 */
2334 2335 2336 2337 2338 2339 2340 2341 2342
void rrc_gNB_process_e1_bearer_context_release_cplt(const e1ap_bearer_release_cplt_t *cplt)
{
  // there is not really anything to do here as of now
  // note that we don't check for the UE: it does not exist anymore if the F1
  // UE context release complete arrived from the DU first, after which we free
  // the UE context
  LOG_I(RRC, "UE %d: received bearer release complete\n", cplt->gNB_cu_cp_ue_id);
}

2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361
static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults)
{
  DevAssert(measresults->measResultServingMOList.list.count >= 1);
  if (measresults->measResultServingMOList.list.count > 1)
    LOG_W(RRC, "Received %d MeasResultServMO, but handling only 1!\n", measresults->measResultServingMOList.list.count);

  NR_MeasResultServMO_t *measresultservmo = measresults->measResultServingMOList.list.array[0];
  NR_MeasResultNR_t *measresultnr = &measresultservmo->measResultServingCell;
  NR_MeasQuantityResults_t *mqr = measresultnr->measResult.cellResults.resultsSSB_Cell;

  fprintf(f, "    servingCellId %ld MeasResultNR for phyCellId %ld:\n      resultSSB:", measresultservmo->servCellId, *measresultnr->physCellId);
  if (mqr != NULL) {
    const long rrsrp = *mqr->rsrp - 156;
    const float rrsrq = (float) (*mqr->rsrq - 87) / 2.0f;
    const float rsinr = (float) (*mqr->sinr - 46) / 2.0f;
    fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr);
  } else {
    fprintf(f, "NOT PROVIDED\n");
  }
2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379

  if (measresults->measResultNeighCells
      && measresults->measResultNeighCells->present == NR_MeasResults__measResultNeighCells_PR_measResultListNR) {
    NR_MeasResultListNR_t *meas_neigh = measresults->measResultNeighCells->choice.measResultListNR;
    for (int i = 0; i < meas_neigh->list.count; ++i) {
      NR_MeasResultNR_t *measresultneigh = meas_neigh->list.array[i];
      NR_MeasQuantityResults_t *neigh_mqr = measresultneigh->measResult.cellResults.resultsSSB_Cell;
      fprintf(f, "    neighboring cell for phyCellId %ld:\n      resultSSB:", *measresultneigh->physCellId);
      if (mqr != NULL) {
        const long rrsrp = *neigh_mqr->rsrp - 156;
        const float rrsrq = (float)(*neigh_mqr->rsrq - 87) / 2.0f;
        const float rsinr = (float)(*neigh_mqr->sinr - 46) / 2.0f;
        fprintf(f, "RSRP %ld dBm RSRQ %.1f dB SINR %.1f dB\n", rrsrp, rrsrq, rsinr);
      } else {
        fprintf(f, "NOT PROVIDED\n");
      }
    }
  }
2380 2381
}

2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397
static const char *get_pdusession_status_text(pdu_session_status_t status)
{
  switch (status) {
    case PDU_SESSION_STATUS_NEW: return "new";
    case PDU_SESSION_STATUS_DONE: return "done";
    case PDU_SESSION_STATUS_ESTABLISHED: return "established";
    case PDU_SESSION_STATUS_REESTABLISHED: return "reestablished";
    case PDU_SESSION_STATUS_TOMODIFY: return "to-modify";
    case PDU_SESSION_STATUS_FAILED: return "failed";
    case PDU_SESSION_STATUS_TORELEASE: return "to-release";
    case PDU_SESSION_STATUS_RELEASED: return "released";
    default: AssertFatal(false, "illegal PDU status code %d\n", status); return "illegal";
  }
  return "illegal";
}

2398 2399 2400 2401 2402 2403 2404 2405 2406
static void write_rrc_stats(const gNB_RRC_INST *rrc)
{
  const char *filename = "nrRRC_stats.log";
  FILE *f = fopen(filename, "w");
  if (f == NULL) {
    LOG_E(NR_RRC, "cannot open %s for writing\n", filename);
    return;
  }

2407
  time_t now = time(NULL);
2408
  int i = 0;
2409 2410
  rrc_gNB_ue_context_t *ue_context_p = NULL;
  /* cast is necessary to eliminate warning "discards ‘const’ qualifier" */
2411 2412
  RB_FOREACH(ue_context_p, rrc_nr_ue_tree_s, &((gNB_RRC_INST *)rrc)->rrc_ue_head)
  {
2413
    const gNB_RRC_UE_t *ue_ctxt = &ue_context_p->ue_context;
2414
    f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_ctxt->rrc_ue_id);
2415

2416
    fprintf(f,
francescomani's avatar
francescomani committed
2417
            "UE %d CU UE ID %u DU UE ID %d RNTI %04x random identity %016lx",
2418 2419 2420 2421 2422
            i,
            ue_ctxt->rrc_ue_id,
            ue_data.secondary_ue,
            ue_ctxt->rnti,
            ue_ctxt->random_ue_identity);
2423
    if (ue_ctxt->Initialue_identity_5g_s_TMSI.presence)
2424
      fprintf(f, " S-TMSI %x", ue_ctxt->Initialue_identity_5g_s_TMSI.fiveg_tmsi);
2425
    fprintf(f, ":\n");
2426

2427 2428
    time_t last_seen = now - ue_ctxt->last_seen;
    fprintf(f, "    last RRC activity: %ld seconds ago\n", last_seen);
2429 2430 2431 2432 2433 2434 2435

    if (ue_ctxt->nb_of_pdusessions == 0)
      fprintf(f, "    (no PDU sessions)\n");
    for (int nb_pdu = 0; nb_pdu < ue_ctxt->nb_of_pdusessions; ++nb_pdu) {
      const rrc_pdu_session_param_t *pdu = &ue_ctxt->pduSession[nb_pdu];
      fprintf(f, "    PDU session %d ID %d status %s\n", nb_pdu, pdu->param.pdusession_id, get_pdusession_status_text(pdu->status));
    }
2436

2437 2438 2439 2440 2441 2442 2443 2444
    fprintf(f, "    associated DU: ");
    if (ue_data.du_assoc_id == -1)
      fprintf(f, " (local/integrated CU-DU)");
    else if (ue_data.du_assoc_id == 0)
      fprintf(f, " DU offline/unavailable");
    else
      fprintf(f, " DU assoc ID %d", ue_data.du_assoc_id);
    fprintf(f, "\n");
2445 2446 2447

    if (ue_ctxt->measResults)
      print_rrc_meas(f, ue_ctxt->measResults);
2448
    ++i;
2449 2450
  }

2451 2452 2453
  fprintf(f, "\n");
  dump_du_info(rrc, f);

2454 2455 2456
  fclose(f);
}

2457
void *rrc_gnb_task(void *args_p) {
laurent's avatar
tmp  
laurent committed
2458
  MessageDef *msg_p;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2459 2460
  instance_t                         instance;
  int                                result;
2461 2462

  long stats_timer_id = 1;
2463 2464 2465 2466 2467
  if (!IS_SOFTMODEM_NOSTATS_BIT) {
    /* timer to write stats to file */
    timer_setup(1, 0, TASK_RRC_GNB, 0, TIMER_PERIODIC, NULL, &stats_timer_id);
  }
  
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2468 2469
  itti_mark_task_ready(TASK_RRC_GNB);
  LOG_I(NR_RRC,"Entering main loop of NR_RRC message task\n");
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2470 2471

  while (1) {
2472
    // Wait for a message
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2473
    itti_receive_msg(TASK_RRC_GNB, &msg_p);
laurent's avatar
tmp  
laurent committed
2474
    const char *msg_name_p = ITTI_MSG_NAME(msg_p);
2475
    instance = ITTI_MSG_DESTINATION_INSTANCE(msg_p);
2476 2477 2478 2479 2480
    LOG_D(NR_RRC,
          "RRC GNB Task Received %s for instance %ld from task %s\n",
          ITTI_MSG_NAME(msg_p),
          ITTI_MSG_DESTINATION_INSTANCE(msg_p),
          ITTI_MSG_ORIGIN_NAME(msg_p));
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2481
    switch (ITTI_MSG_ID(msg_p)) {
2482
      case TERMINATE_MESSAGE:
Xue Song's avatar
Xue Song committed
2483
        LOG_W(NR_RRC, " *** Exiting NR_RRC thread\n");
2484 2485
        itti_exit_task();
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2486

2487
      case MESSAGE_TEST:
2488
        LOG_I(NR_RRC, "[gNB %ld] Received %s\n", instance, msg_name_p);
2489
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2490

2491
      case TIMER_HAS_EXPIRED:
2492 2493 2494
        /* only this one handled for now */
        DevAssert(TIMER_HAS_EXPIRED(msg_p).timer_id == stats_timer_id);
        write_rrc_stats(RC.nrrrc[0]);
2495 2496
        break;

2497 2498 2499
      case F1AP_INITIAL_UL_RRC_MESSAGE:
        AssertFatal(NODE_IS_CU(RC.nrrrc[instance]->node_type) || NODE_IS_MONOLITHIC(RC.nrrrc[instance]->node_type),
                    "should not receive F1AP_INITIAL_UL_RRC_MESSAGE, need call by CU!\n");
Robert Schmidt's avatar
Robert Schmidt committed
2500
        rrc_gNB_process_initial_ul_rrc_message(msg_p->ittiMsgHeader.originInstance, &F1AP_INITIAL_UL_RRC_MESSAGE(msg_p));
2501
        free_initial_ul_rrc_message_transfer(&F1AP_INITIAL_UL_RRC_MESSAGE(msg_p));
2502
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2503 2504

      /* Messages from PDCP */
2505
      /* From DU -> CU */
2506
      case F1AP_UL_RRC_MESSAGE:
2507
        rrc_gNB_decode_dcch(RC.nrrrc[instance], &F1AP_UL_RRC_MESSAGE(msg_p));
2508
        free_ul_rrc_message_transfer(&F1AP_UL_RRC_MESSAGE(msg_p));
Xue Song's avatar
Xue Song committed
2509
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2510

Xue Song's avatar
Xue Song committed
2511
      case NGAP_DOWNLINK_NAS:
laurent's avatar
tmp  
laurent committed
2512
        rrc_gNB_process_NGAP_DOWNLINK_NAS(msg_p, instance, &rrc_gNB_mui);
Xue Song's avatar
Xue Song committed
2513 2514
        break;

2515
      case NGAP_PDUSESSION_SETUP_REQ:
laurent's avatar
tmp  
laurent committed
2516
        rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ(msg_p, instance);
2517 2518
        break;

2519
      case NGAP_PDUSESSION_MODIFY_REQ:
laurent's avatar
tmp  
laurent committed
2520
        rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ(msg_p, instance);
2521 2522 2523
        break;

      case NGAP_PDUSESSION_RELEASE_COMMAND:
laurent's avatar
tmp  
laurent committed
2524
        rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND(msg_p, instance);
2525 2526
        break;

Xue Song's avatar
Xue Song committed
2527 2528
      /* Messages from F1AP task */
      case F1AP_SETUP_REQ:
2529
        AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
2530
        rrc_gNB_process_f1_setup_req(&F1AP_SETUP_REQ(msg_p), msg_p->ittiMsgHeader.originInstance);
2531
        free_f1ap_setup_request(&F1AP_SETUP_REQ(msg_p));
Xue Song's avatar
Xue Song committed
2532
        break;
2533

2534
      case F1AP_UE_CONTEXT_SETUP_RESP:
laurent's avatar
tmp  
laurent committed
2535
        rrc_CU_process_ue_context_setup_response(msg_p, instance);
2536
        break;
2537

2538
      case F1AP_UE_CONTEXT_MODIFICATION_RESP:
laurent's avatar
tmp  
laurent committed
2539
        rrc_CU_process_ue_context_modification_response(msg_p, instance);
2540 2541
        break;

2542
      case F1AP_UE_CONTEXT_MODIFICATION_REQUIRED:
2543
        rrc_CU_process_ue_modification_required(msg_p, instance, msg_p->ittiMsgHeader.originInstance);
2544 2545
        break;

2546
      case F1AP_UE_CONTEXT_RELEASE_REQ:
2547
        rrc_CU_process_ue_context_release_request(msg_p, msg_p->ittiMsgHeader.originInstance);
2548 2549 2550 2551
        break;

      case F1AP_UE_CONTEXT_RELEASE_COMPLETE:
        rrc_CU_process_ue_context_release_complete(msg_p);
2552
        break;
Xue Song's avatar
Xue Song committed
2553

2554 2555 2556 2557
      case F1AP_LOST_CONNECTION:
        rrc_CU_process_f1_lost_connection(RC.nrrrc[0], &F1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
        break;

2558 2559 2560
      case F1AP_GNB_DU_CONFIGURATION_UPDATE:
        AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type), "should not receive F1AP_SETUP_REQUEST in DU!\n");
        rrc_gNB_process_f1_du_configuration_update(&F1AP_GNB_DU_CONFIGURATION_UPDATE(msg_p), msg_p->ittiMsgHeader.originInstance);
2561
        free_f1ap_du_configuration_update(&F1AP_GNB_DU_CONFIGURATION_UPDATE(msg_p));
2562 2563
        break;

2564 2565 2566 2567 2568 2569
      case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE:
        AssertFatal(!NODE_IS_DU(RC.nrrrc[instance]->node_type),
                    "should not receive F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE in DU!\n");
        LOG_E(NR_RRC, "Handling of F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE not implemented\n");
        break;

2570
      /* Messages from X2AP */
2571
      case X2AP_ENDC_SGNB_ADDITION_REQ:
2572
        LOG_I(NR_RRC, "Received ENDC sgNB addition request from X2AP \n");
2573
        rrc_gNB_process_AdditionRequestInformation(instance, &X2AP_ENDC_SGNB_ADDITION_REQ(msg_p));
2574
        break;
2575

2576
      case X2AP_ENDC_SGNB_RECONF_COMPLETE:
Melissa Elkadi's avatar
Melissa Elkadi committed
2577
        LOG_A(NR_RRC, "Handling of reconfiguration complete message at RRC gNB is pending \n");
2578
        break;
Xue Song's avatar
Xue Song committed
2579

2580
      case NGAP_INITIAL_CONTEXT_SETUP_REQ:
laurent's avatar
tmp  
laurent committed
2581
        rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p, instance);
yaojie's avatar
yaojie committed
2582
        break;
2583

Cedric Roux's avatar
Cedric Roux committed
2584 2585
      case X2AP_ENDC_SGNB_RELEASE_REQUEST:
        LOG_I(NR_RRC, "Received ENDC sgNB release request from X2AP \n");
2586
        rrc_gNB_process_release_request(instance, &X2AP_ENDC_SGNB_RELEASE_REQUEST(msg_p));
Cedric Roux's avatar
Cedric Roux committed
2587 2588 2589
        break;

      case X2AP_ENDC_DC_OVERALL_TIMEOUT:
2590
        rrc_gNB_process_dc_overall_timeout(instance, &X2AP_ENDC_DC_OVERALL_TIMEOUT(msg_p));
Cedric Roux's avatar
Cedric Roux committed
2591 2592
        break;

2593
      case NGAP_UE_CONTEXT_RELEASE_REQ:
laurent's avatar
tmp  
laurent committed
2594
        rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_REQ(msg_p, instance);
2595 2596 2597
        break;

      case NGAP_UE_CONTEXT_RELEASE_COMMAND:
laurent's avatar
tmp  
laurent committed
2598
        rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p, instance);
2599 2600
        break;

2601
      case E1AP_SETUP_REQ:
2602
        rrc_gNB_process_e1_setup_req(msg_p->ittiMsgHeader.originInstance, &E1AP_SETUP_REQ(msg_p));
2603 2604 2605 2606
        break;

      case E1AP_BEARER_CONTEXT_SETUP_RESP:
        rrc_gNB_process_e1_bearer_context_setup_resp(&E1AP_BEARER_CONTEXT_SETUP_RESP(msg_p), instance);
2607
        break;
2608

2609 2610 2611 2612
      case E1AP_BEARER_CONTEXT_MODIFICATION_RESP:
        rrc_gNB_process_e1_bearer_context_modif_resp(&E1AP_BEARER_CONTEXT_MODIFICATION_RESP(msg_p));
        break;

2613 2614 2615 2616
      case E1AP_BEARER_CONTEXT_RELEASE_CPLT:
        rrc_gNB_process_e1_bearer_context_release_cplt(&E1AP_BEARER_CONTEXT_RELEASE_CPLT(msg_p));
        break;

2617 2618 2619 2620
      case E1AP_LOST_CONNECTION: /* CUCP */
        rrc_gNB_process_e1_lost_connection(RC.nrrrc[0], &E1AP_LOST_CONNECTION(msg_p), msg_p->ittiMsgHeader.originInstance);
        break;

2621
      case NGAP_PAGING_IND:
laurent's avatar
tmp  
laurent committed
2622
        rrc_gNB_process_PAGING_IND(msg_p, instance);
2623 2624
        break;

2625
      default:
2626
        LOG_E(NR_RRC, "[gNB %ld] Received unexpected message %s\n", instance, msg_name_p);
2627
        break;
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2628 2629 2630 2631 2632 2633 2634
    }

    result = itti_free(ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
    AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
    msg_p = NULL;
  }
}
WEI-TAI CHEN's avatar
WEI-TAI CHEN committed
2635

heshanyun's avatar
heshanyun committed
2636
//-----------------------------------------------------------------------------
2637
void rrc_gNB_generate_SecurityModeCommand(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p)
heshanyun's avatar
heshanyun committed
2638 2639
//-----------------------------------------------------------------------------
{
2640
  uint8_t buffer[100];
2641
  AssertFatal(!ue_p->as_security_active, "logic error: security already active\n");
2642

2643
  T(T_ENB_RRC_SECURITY_MODE_COMMAND, T_INT(0), T_INT(0), T_INT(0), T_INT(ue_p->rrc_ue_id));
2644
  NR_IntegrityProtAlgorithm_t integrity_algorithm = (NR_IntegrityProtAlgorithm_t)ue_p->integrity_algorithm;
2645
  int size = do_NR_SecurityModeCommand(buffer,
2646
                                       rrc_gNB_get_next_transaction_identifier(rrc->module_id),
2647 2648
                                       ue_p->ciphering_algorithm,
                                       integrity_algorithm);
2649
  LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buffer, size, "[MSG] RRC Security Mode Command\n");
2650
  LOG_I(NR_RRC, "UE %u Logical Channel DL-DCCH, Generate SecurityModeCommand (bytes %d)\n", ue_p->rrc_ue_id, size);
2651

2652
  nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size);
heshanyun's avatar
heshanyun committed
2653 2654
}

2655 2656 2657
typedef struct deliver_ue_ctxt_release_data_t {
  gNB_RRC_INST *rrc;
  f1ap_ue_context_release_cmd_t *release_cmd;
Robert Schmidt's avatar
Robert Schmidt committed
2658
  sctp_assoc_t assoc_id;
2659
} deliver_ue_ctxt_release_data_t;
2660 2661 2662
static void rrc_deliver_ue_ctxt_release_cmd(void *deliver_pdu_data, ue_id_t ue_id, int srb_id, char *buf, int size, int sdu_id)
{
  DevAssert(deliver_pdu_data != NULL);
2663 2664 2665
  deliver_ue_ctxt_release_data_t *data = deliver_pdu_data;
  data->release_cmd->rrc_container = (uint8_t*) buf;
  data->release_cmd->rrc_container_length = size;
Robert Schmidt's avatar
Robert Schmidt committed
2666
  data->rrc->mac_rrc.ue_context_release_command(data->assoc_id, data->release_cmd);
2667 2668
}

heshanyun's avatar
heshanyun committed
2669 2670 2671 2672 2673
//-----------------------------------------------------------------------------
/*
* Generate the RRC Connection Release to UE.
* If received, UE should switch to RRC_IDLE mode.
*/
2674
void rrc_gNB_generate_RRCRelease(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE)
heshanyun's avatar
heshanyun committed
2675
{
2676
  uint8_t buffer[NR_RRC_BUF_SIZE] = {0};
2677
  int size = do_NR_RRCRelease(buffer, NR_RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(rrc->module_id));
2678

2679
  LOG_UE_DL_EVENT(UE, "Send RRC Release\n");
2680
  f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id);
2681
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
2682
  f1ap_ue_context_release_cmd_t ue_context_release_cmd = {
2683
    .gNB_CU_ue_id = UE->rrc_ue_id,
2684 2685 2686 2687 2688
    .gNB_DU_ue_id = ue_data.secondary_ue,
    .cause = F1AP_CAUSE_RADIO_NETWORK,
    .cause_value = 10, // 10 = F1AP_CauseRadioNetwork_normal_release
    .srb_id = DCCH,
  };
Robert Schmidt's avatar
Robert Schmidt committed
2689
  deliver_ue_ctxt_release_data_t data = {.rrc = rrc, .release_cmd = &ue_context_release_cmd, .assoc_id = ue_data.du_assoc_id};
2690
  nr_pdcp_data_req_srb(UE->rrc_ue_id, DCCH, rrc_gNB_mui++, size, buffer, rrc_deliver_ue_ctxt_release_cmd, &data);
heshanyun's avatar
heshanyun committed
2691
}
2692

2693 2694 2695
int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib1, uint32_t tmsi, uint8_t paging_drx)
{
  instance_t instance = 0;
2696 2697 2698 2699 2700 2701 2702
  const unsigned int Ttab[4] = {32,64,128,256};
  uint8_t Tc;
  uint8_t Tue;
  uint32_t pfoffset;
  uint32_t N;  /* N: min(T,nB). total count of PF in one DRX cycle */
  uint32_t Ns = 0;  /* Ns: max(1,nB/T) */
  uint32_t T;  /* DRX cycle */
2703
  uint8_t buffer[NR_RRC_BUF_SIZE];
2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761

  /* get default DRX cycle from configuration */
  Tc = sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.defaultPagingCycle;

  Tue = paging_drx;
  /* set T = min(Tc,Tue) */
  T = Tc < Tue ? Ttab[Tc] : Ttab[Tue];
  /* set N = PCCH-Config->nAndPagingFrameOffset */
  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present) {
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneT:
      N = T;
      pfoffset = 0;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_halfT:
      N = T/2;
      pfoffset = 1;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_quarterT:
      N = T/4;
      pfoffset = 3;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneEighthT:
      N = T/8;
      pfoffset = 7;
      break;
    case NR_PCCH_Config__nAndPagingFrameOffset_PR_oneSixteenthT:
      N = T/16;
      pfoffset = 15;
      break;
    default:
      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  pfoffset error (pfoffset %d)\n",
            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.nAndPagingFrameOffset.present);
      return (-1);

  }

  switch (sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns) {
    case NR_PCCH_Config__ns_four:
      if(*sib1->servingCellConfigCommon->downlinkConfigCommon.initialDownlinkBWP.pdcch_ConfigCommon->choice.setup->pagingSearchSpace == 0){
        LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg:  ns error only 1 or 2 is allowed when pagingSearchSpace is 0\n",
              instance);
        return (-1);
      } else {
        Ns = 4;
      }
      break;
    case NR_PCCH_Config__ns_two:
      Ns = 2;
      break;
    case NR_PCCH_Config__ns_one:
      Ns = 1;
      break;
    default:
      LOG_E(RRC, "[gNB %ld] In rrc_gNB_generate_pcch_msg: ns error (ns %ld)\n",
            instance, sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.ns);
      return (-1);
  }

2762 2763 2764
  (void) N; /* not used, suppress warning */
  (void) Ns; /* not used, suppress warning */
  (void) pfoffset; /* not used, suppress warning */
2765 2766

  /* Create message for PDCP (DLInformationTransfer_t) */
2767
  int length = do_NR_Paging(instance, buffer, tmsi);
2768 2769 2770 2771 2772 2773

  if (length == -1) {
    LOG_I(NR_RRC, "do_Paging error\n");
    return -1;
  }
  // TODO, send message to pdcp
2774
  (void) assoc_id;
2775 2776 2777

  return 0;
}
2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806

/* F1AP UE Context Management Procedures */

//-----------------------------------------------------------------------------
void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc,
                                            rrc_gNB_ue_context_t *const ue_context_pP,
                                            int n_drbs,
                                            const f1ap_drb_to_be_setup_t *drbs)
//-----------------------------------------------------------------------------
{
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
  AssertFatal(!ue_p->f1_ue_context_active, "logic error: ue context already active\n");

  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");

  cu_to_du_rrc_information_t cu2du = {0};
  cu_to_du_rrc_information_t *cu2du_p = NULL;
  if (ue_p->ue_cap_buffer.len > 0 && ue_p->ue_cap_buffer.buf != NULL) {
    cu2du_p = &cu2du;
    cu2du.uE_CapabilityRAT_ContainerList = ue_p->ue_cap_buffer.buf;
    cu2du.uE_CapabilityRAT_ContainerList_length = ue_p->ue_cap_buffer.len;
  }

  int nb_srb = 1;
  f1ap_srb_to_be_setup_t srbs[1] = {{.srb_id = 2, .lcid = 2}};
  activate_srb(ue_p, 2);

  /* the callback will fill the UE context setup request and forward it */
  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
2807
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850
  f1ap_ue_context_setup_t ue_context_setup_req = {
      .gNB_CU_ue_id = ue_p->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 = nb_srb,
      .srbs_to_be_setup = srbs,
      .drbs_to_be_setup_length = n_drbs,
      .drbs_to_be_setup = (f1ap_drb_to_be_setup_t *)drbs,
      .cu_to_du_rrc_information = cu2du_p,
  };

  rrc->mac_rrc.ue_context_setup_request(ue_data.du_assoc_id, &ue_context_setup_req);
  LOG_I(RRC, "UE %d trigger UE context setup request with %d DRBs\n", ue_p->rrc_ue_id, n_drbs);
}

//-----------------------------------------------------------------------------
void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc,
                                                   rrc_gNB_ue_context_t *const ue_context_pP,
                                                   int n_drbs,
                                                   const f1ap_drb_to_be_setup_t *drbs,
                                                   int n_rel_drbs,
                                                   const f1ap_drb_to_be_released_t *rel_drbs)
//-----------------------------------------------------------------------------
{
  gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context;
  AssertFatal(ue_p->f1_ue_context_active, "logic error: calling ue context modification when context not established\n");
  AssertFatal(ue_p->Srb[1].Active && ue_p->Srb[2].Active, "SRBs should already be active\n");

  AssertFatal(!NODE_IS_DU(rrc->node_type), "illegal node type DU!\n");

  cu_to_du_rrc_information_t cu2du = {0};
  cu_to_du_rrc_information_t *cu2du_p = NULL;
  if (ue_p->ue_cap_buffer.len > 0 && ue_p->ue_cap_buffer.buf != NULL) {
    cu2du_p = &cu2du;
    cu2du.uE_CapabilityRAT_ContainerList = ue_p->ue_cap_buffer.buf;
    cu2du.uE_CapabilityRAT_ContainerList_length = ue_p->ue_cap_buffer.len;
  }

  f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id);
2851
  RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id);
2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868
  f1ap_ue_context_modif_req_t ue_context_modif_req = {
      .gNB_CU_ue_id = ue_p->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? */
      .drbs_to_be_setup_length = n_drbs,
      .drbs_to_be_setup = (f1ap_drb_to_be_setup_t *)drbs,
      .drbs_to_be_released_length = n_rel_drbs,
      .drbs_to_be_released = (f1ap_drb_to_be_released_t *)rel_drbs,
      .cu_to_du_rrc_information = cu2du_p,
  };
  rrc->mac_rrc.ue_context_modification_request(ue_data.du_assoc_id, &ue_context_modif_req);
  LOG_I(RRC, "UE %d trigger UE context modification request with %d DRBs\n", ue_p->rrc_ue_id, n_drbs);
}