From 0d8a1327b0abc3d15b99b7321b5aa94aed4ae166 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 3 Jan 2023 18:09:22 +0100 Subject: [PATCH 01/26] remove ss0 and cset 0 from config file --- .../gnb.band78.tm1.fr1.106PRB.usrpb210.conf | 3 --- openair2/GNB_APP/RRC_nr_paramsvalues.h | 8 ++++---- openair2/GNB_APP/gnb_config.c | 14 ++++++++++++-- openair2/RRC/NR/MESSAGES/asn1_msg.c | 9 +++++++-- openair2/RRC/NR/rrc_gNB.c | 6 ++++-- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf index 92362ebdc9..b8d2b8c34a 100644 --- a/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf +++ b/ci-scripts/conf_files/gnb.band78.tm1.fr1.106PRB.usrpb210.conf @@ -46,9 +46,6 @@ gNBs = # subcarrierSpacing # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; - #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 0; - initialDLBWPsearchSpaceZero = 0; #uplinkConfigCommon #frequencyInfoUL diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index 089569f84e..5e04eb3e1b 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -167,8 +167,8 @@ #define SEARCH_SPACE_ZERO "searchSpaceZero" #define PDCCH_CONFIGSIB1PARAMS_DESC(pdcch_ConfigSIB1) { \ -{CONTROL_RESOURCE_SET_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->controlResourceSetZero, defintval:0, TYPE_INT64, 0}, \ -{SEARCH_SPACE_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->searchSpaceZero, defintval:0, TYPE_INT64, 0} \ +{CONTROL_RESOURCE_SET_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->controlResourceSetZero, defintval:0, TYPE_INT64, 0}, \ +{SEARCH_SPACE_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->searchSpaceZero, defintval:0, TYPE_INT64, 0} \ } #define CONTROL_RESOURCE_SET_ZERO_IDX 0 @@ -201,8 +201,8 @@ {GNB_CONFIG_STRING_DLCARRIERBANDWIDTH,NULL,0,i64ptr:&scc->downlinkConfigCommon->frequencyInfoDL->scs_SpecificCarrierList.list.array[0]->carrierBandwidth,defint64val:217,TYPE_INT64,0 /*10*/}, \ {GNB_CONFIG_STRING_INITIALDLBWPLOCATIONANDBANDWIDTH,NULL,0,i64ptr:&scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.locationAndBandwidth,defint64val:13036,TYPE_INT64,0/*11*/},\ {GNB_CONFIG_STRING_INITIALDLBWPSUBCARRIERSPACING,NULL,0,i64ptr:&scc->downlinkConfigCommon->initialDownlinkBWP->genericParameters.subcarrierSpacing,defint64val:NR_SubcarrierSpacing_kHz30,TYPE_INT64,0/*12*/},\ -{GNB_CONFIG_STRING_INITIALDLBWPCONTROLRESOURCESETZERO,NULL,0,i64ptr:scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero,defint64val:12,TYPE_INT64,0/*13*/},\ -{GNB_CONFIG_STRING_INITIALDLBWPSEARCHSPACEZERO,NULL,0,i64ptr:scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero,defint64val:0,TYPE_INT64,0/*14*/},\ +{GNB_CONFIG_STRING_INITIALDLBWPCONTROLRESOURCESETZERO,NULL,0,i64ptr:scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero,defint64val:-1,TYPE_INT64,0/*13*/},\ +{GNB_CONFIG_STRING_INITIALDLBWPSEARCHSPACEZERO,NULL,0,i64ptr:scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero,defint64val:-1,TYPE_INT64,0/*14*/},\ {GNB_CONFIG_STRING_ULFREQUENCYBAND,NULL,0,i64ptr:scc->uplinkConfigCommon->frequencyInfoUL->frequencyBandList->list.array[0],defint64val:-1,TYPE_INT64,0/*63*/},\ {GNB_CONFIG_STRING_ULABSOLUEFREQUENCYPOINTA,NULL,0,i64ptr:scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA,defint64val:-1,TYPE_INT64,0/*64*/},\ {GNB_CONFIG_STRING_ULOFFSETTOCARRIER,NULL,0,i64ptr:&scc->uplinkConfigCommon->frequencyInfoUL->scs_SpecificCarrierList.list.array[0]->offsetToCarrier,defint64val:0,TYPE_INT64,0/*65*/},\ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index a33bbd0741..efc3be65f7 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -347,6 +347,16 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } } + // fix SS0 and Coreset0 + if((int)*scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero == -1) { + free(scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero); + scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero = NULL; + } + if((int)*scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero == -1) { + free(scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero); + scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero = NULL; + } + // fix UL absolute frequency if ((int)*scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA==-1) { free(scc->uplinkConfigCommon->frequencyInfoUL->absoluteFrequencyPointA); @@ -1103,8 +1113,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); config_getlist(&pdcch_ConfigSIB1ParamList, NULL, 0, aprefix); if (pdcch_ConfigSIB1ParamList.numelt > 0) { - sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_PDCCH_CONFIGSIB1, 0); - config_get(pdcch_ConfigSIB1,sizeof(pdcch_ConfigSIB1)/sizeof(paramdef_t),aprefix); + sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_PDCCH_CONFIGSIB1, 0); + config_get(pdcch_ConfigSIB1,sizeof(pdcch_ConfigSIB1)/sizeof(paramdef_t),aprefix); } sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index d46d85897a..3a1112f70a 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -241,12 +241,17 @@ uint8_t do_MIB_NR(gNB_RRC_INST *rrc,uint32_t frame) { * The SIB1 will be sent in this allocation (Type0-PDCCH) : 38.213, 13-4 Table and 38.213 13-11 to 13-14 tables * the reverse allocation is in nr_ue_decode_mib() */ + long cset0 = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero ? + *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero : 0; + long ss0 = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero ? + *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero : 0; + if(rrc->carrier.pdcch_ConfigSIB1) { mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = rrc->carrier.pdcch_ConfigSIB1->controlResourceSetZero; mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = rrc->carrier.pdcch_ConfigSIB1->searchSpaceZero; } else { - mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero; - mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero; + mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = cset0; + mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = ss0; } switch (*scc->ssbSubcarrierSpacing) { diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index edc4a8dcfb..974b47b479 100755 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -141,14 +141,16 @@ void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { ///---------------------------------------------------------------------------------------------------------------/// ///---------------------------------------------------------------------------------------------------------------/// -static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration) { +static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration) +{ + LOG_D(RRC,"%s()\n\n\n\n",__FUNCTION__); if (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)) { rrc->carrier.MIB = (uint8_t *) malloc16(4); rrc->carrier.sizeof_MIB = do_MIB_NR(rrc,0); } - if((get_softmodem_params()->sa) && ( (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)))) { + if((get_softmodem_params()->sa) && ( (NODE_IS_DU(rrc->node_type) || NODE_IS_MONOLITHIC(rrc->node_type)))) { rrc->carrier.sizeof_SIB1 = do_SIB1_NR(&rrc->carrier,configuration); } From 3ab368f9b0a555a1fe14d846c4a4f8ee85e428b6 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 6 Jan 2023 09:44:12 +0100 Subject: [PATCH 02/26] removing duplicate cset0 ss0 from config files --- .../episci_rcc.band78.tm1.106PRB.nfapi.conf | 6 ------ .../gnb_band78.sa.fr1.106PRB.2x2.l2sim.conf | 6 ------ ...oxy_gnb.band78.sa.fr1.106PRB.usrpn310.conf | 7 ------- ci-scripts/conf_files/gNB_SA_CU.conf | 7 ------- ci-scripts/conf_files/gNB_SA_DU.conf | 7 ------- .../gNB_SA_n78_106PRB.2x2_usrpn310.conf | 7 ------- .../gNB_SA_n78_133PRB.2x2_usrpn310.conf | 7 ------- ...and78.sa.fr1.106PRB.2x2.usrpn310.asue.conf | 7 ------- ...gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf | 7 ------- ...nd78.sa.fr1.106PRB.ddsuu.2x2.usrpn310.conf | 7 ------- .../gnb.band78.sa.fr1.106PRB.usrpn310.conf | 7 ------- ...gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf | 7 ------- .../gnb.sa.band66.fr1.106PRB.usrpn300.conf | 7 ------- .../gnb.sa.band78.fr1.24PRB.usrpb210.conf | 7 ------- .../gnb.sa.band78.fr1.51PRB.usrpb210.conf | 7 ------- openair2/GNB_APP/RRC_nr_paramsvalues.h | 14 -------------- openair2/GNB_APP/gnb_config.c | 11 ----------- openair2/RRC/NR/L2_nr_interface.c | 5 ----- openair2/RRC/NR/MESSAGES/asn1_msg.c | 9 ++------- openair2/RRC/NR/nr_rrc_defs.h | 1 - .../PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf | 9 +-------- .../PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf | 9 +-------- .../GENERIC-NR-5GC/CONF/gNB_SA_40_2x2.conf | 19 ------------------- ...gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf | 7 ------- ...gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf | 7 ------- .../gnb.sa.band41.fr1.106PRB.usrpb210.conf | 7 ------- .../gnb.sa.band66.fr1.106PRB.usrpn300.conf | 7 ------- .../gnb.sa.band66.fr1.106PRB.usrpx300.conf | 7 ------- .../gnb.sa.band66.fr1.24PRB.usrpx300.conf | 7 ------- .../gnb.sa.band66.fr1.25PRB.usrpx300.conf | 7 ------- ...gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf | 5 ----- ...gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf | 7 ------- ....sa.band78.fr1.106PRB.usrpb210.4layer.conf | 7 ------- .../gnb.sa.band78.fr1.106PRB.usrpb210.conf | 7 ------- ...b.sa.band78.fr1.106PRB.usrpb210.sabox.conf | 7 ------- ...gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf | 6 ------ ...gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf | 7 ------- .../gnb.sa.band78.fr1.24PRB.usrpb210.conf | 7 ------- ...nb0.sa.band78.fr1.106PRB.prs.usrpx310.conf | 6 ------ ...nb1.sa.band78.fr1.106PRB.prs.usrpx310.conf | 7 ------- 40 files changed, 4 insertions(+), 291 deletions(-) diff --git a/ci-scripts/conf_files/episci/episci_rcc.band78.tm1.106PRB.nfapi.conf b/ci-scripts/conf_files/episci/episci_rcc.band78.tm1.106PRB.nfapi.conf index 354a1eb076..491201426b 100644 --- a/ci-scripts/conf_files/episci/episci_rcc.band78.tm1.106PRB.nfapi.conf +++ b/ci-scripts/conf_files/episci/episci_rcc.band78.tm1.106PRB.nfapi.conf @@ -23,12 +23,6 @@ gNBs = min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/episci/gnb_band78.sa.fr1.106PRB.2x2.l2sim.conf b/ci-scripts/conf_files/episci/gnb_band78.sa.fr1.106PRB.2x2.l2sim.conf index 71bb34cb31..38b1cbfa40 100644 --- a/ci-scripts/conf_files/episci/gnb_band78.sa.fr1.106PRB.2x2.l2sim.conf +++ b/ci-scripts/conf_files/episci/gnb_band78.sa.fr1.106PRB.2x2.l2sim.conf @@ -21,12 +21,6 @@ gNBs = pusch_AntennaPorts = 2; do_CSIRS = 1; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); servingCellConfigCommon = ( { diff --git a/ci-scripts/conf_files/episci/proxy_gnb.band78.sa.fr1.106PRB.usrpn310.conf b/ci-scripts/conf_files/episci/proxy_gnb.band78.sa.fr1.106PRB.usrpn310.conf index 20b0d3caf7..669558f69c 100644 --- a/ci-scripts/conf_files/episci/proxy_gnb.band78.sa.fr1.106PRB.usrpn310.conf +++ b/ci-scripts/conf_files/episci/proxy_gnb.band78.sa.fr1.106PRB.usrpn310.conf @@ -19,13 +19,6 @@ gNBs = min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gNB_SA_CU.conf b/ci-scripts/conf_files/gNB_SA_CU.conf index a5036e354d..9910e761b2 100644 --- a/ci-scripts/conf_files/gNB_SA_CU.conf +++ b/ci-scripts/conf_files/gNB_SA_CU.conf @@ -32,13 +32,6 @@ gNBs = remote_s_portd = 2152; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gNB_SA_DU.conf b/ci-scripts/conf_files/gNB_SA_DU.conf index ca5aba10c0..b554326c91 100644 --- a/ci-scripts/conf_files/gNB_SA_DU.conf +++ b/ci-scripts/conf_files/gNB_SA_DU.conf @@ -24,13 +24,6 @@ gNBs = min_rxtxtime = 6; force_256qam_off = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf b/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf index e28eab1fb6..4c68ea1f94 100644 --- a/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf +++ b/ci-scripts/conf_files/gNB_SA_n78_106PRB.2x2_usrpn310.conf @@ -26,13 +26,6 @@ gNBs = pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf b/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf index 0902eebaeb..7a474f3bb4 100644 --- a/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf +++ b/ci-scripts/conf_files/gNB_SA_n78_133PRB.2x2_usrpn310.conf @@ -27,13 +27,6 @@ gNBs = pdsch_AntennaPorts_XP = 2; pusch_AntennaPorts = 2; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 10; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.asue.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.asue.conf index 16ae7a7afc..6f8c53e681 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.asue.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.asue.conf @@ -29,13 +29,6 @@ gNBs = do_SRS = 0; do_CSIRS = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf index 0d4289ab80..a78fa953c1 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf @@ -27,13 +27,6 @@ gNBs = pusch_AntennaPorts = 2; do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.ddsuu.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.ddsuu.2x2.usrpn310.conf index 87445e2b68..92e1ca573f 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.ddsuu.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.ddsuu.2x2.usrpn310.conf @@ -27,13 +27,6 @@ gNBs = ul_prbblacklist = "51,52,53,54" do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf index 53adb4eae1..cc363e215f 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.106PRB.usrpn310.conf @@ -29,13 +29,6 @@ gNBs = min_rxtxtime = 6; do_SRS = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf index 99add7d367..d83770c852 100644 --- a/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf +++ b/ci-scripts/conf_files/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf @@ -30,13 +30,6 @@ gNBs = ul_prbblacklist = "79,80,81,82" do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.sa.band66.fr1.106PRB.usrpn300.conf b/ci-scripts/conf_files/gnb.sa.band66.fr1.106PRB.usrpn300.conf index d45f9bfd62..73c265c955 100644 --- a/ci-scripts/conf_files/gnb.sa.band66.fr1.106PRB.usrpn300.conf +++ b/ci-scripts/conf_files/gnb.sa.band66.fr1.106PRB.usrpn300.conf @@ -20,13 +20,6 @@ gNBs = min_rxtxtime = 6; enable_sdap = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.sa.band78.fr1.24PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.sa.band78.fr1.24PRB.usrpb210.conf index 6419dedf43..7cae9a9500 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.fr1.24PRB.usrpb210.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.fr1.24PRB.usrpb210.conf @@ -20,13 +20,6 @@ gNBs = sib1_tda = 15; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 2; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb.sa.band78.fr1.51PRB.usrpb210.conf b/ci-scripts/conf_files/gnb.sa.band78.fr1.51PRB.usrpb210.conf index 6080ac58aa..6e012b5098 100644 --- a/ci-scripts/conf_files/gnb.sa.band78.fr1.51PRB.usrpb210.conf +++ b/ci-scripts/conf_files/gnb.sa.band78.fr1.51PRB.usrpb210.conf @@ -20,13 +20,6 @@ gNBs = do_CSIRS = 0; do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/openair2/GNB_APP/RRC_nr_paramsvalues.h b/openair2/GNB_APP/RRC_nr_paramsvalues.h index 5e04eb3e1b..e5bcd0e228 100644 --- a/openair2/GNB_APP/RRC_nr_paramsvalues.h +++ b/openair2/GNB_APP/RRC_nr_paramsvalues.h @@ -39,7 +39,6 @@ /* cell configuration section name */ #define GNB_CONFIG_STRING_GNB_LIST "gNBs" -#define GNB_CONFIG_STRING_PDCCH_CONFIGSIB1 "pdcch_ConfigSIB1" #define GNB_CONFIG_STRING_SERVINGCELLCONFIGCOMMON "servingCellConfigCommon" #define GNB_CONFIG_STRING_PHYSCELLID "physCellId" #define GNB_CONFIG_STRING_NTIMINGADVANCEOFFSET "n_TimingAdvanceOffset" @@ -160,19 +159,6 @@ #define GNB_CONFIG_STRING_SCS_ULBWP4 "ul_bwp4_subcarrierSpacing" #define GNB_CONFIG_STRING_FIRSTACTIVEULBWP_ID "firstActiveUplinkBWP-Id" -/*--------------------------------------------------------------------------------------------------------------------*/ -/* pdcch_ConfigSIB1 parameters */ -/*--------------------------------------------------------------------------------------------------------------------*/ -#define CONTROL_RESOURCE_SET_ZERO "controlResourceSetZero" -#define SEARCH_SPACE_ZERO "searchSpaceZero" - -#define PDCCH_CONFIGSIB1PARAMS_DESC(pdcch_ConfigSIB1) { \ -{CONTROL_RESOURCE_SET_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->controlResourceSetZero, defintval:0, TYPE_INT64, 0}, \ -{SEARCH_SPACE_ZERO, NULL, 0, i64ptr:&pdcch_ConfigSIB1->searchSpaceZero, defintval:0, TYPE_INT64, 0} \ -} - -#define CONTROL_RESOURCE_SET_ZERO_IDX 0 -#define SEARCH_SPACE_ZERO_IDX 1 /*--------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index efc3be65f7..4822c799ee 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1106,17 +1106,6 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { gnb_id = *(GNBParamList.paramarray[i][GNB_GNB_ID_IDX].uptr); } - // pdcch_ConfigSIB1 - rrc->carrier.pdcch_ConfigSIB1 = calloc(1,sizeof(NR_PDCCH_ConfigSIB1_t)); - paramdef_t pdcch_ConfigSIB1[] = PDCCH_CONFIGSIB1PARAMS_DESC(rrc->carrier.pdcch_ConfigSIB1); - paramlist_def_t pdcch_ConfigSIB1ParamList = {GNB_CONFIG_STRING_PDCCH_CONFIGSIB1, NULL, 0}; - sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); - config_getlist(&pdcch_ConfigSIB1ParamList, NULL, 0, aprefix); - if (pdcch_ConfigSIB1ParamList.numelt > 0) { - sprintf(aprefix, "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_PDCCH_CONFIGSIB1, 0); - config_get(pdcch_ConfigSIB1,sizeof(pdcch_ConfigSIB1)/sizeof(paramdef_t),aprefix); - } - sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); config_getlist(&SCCsParamList, NULL, 0, aprefix); diff --git a/openair2/RRC/NR/L2_nr_interface.c b/openair2/RRC/NR/L2_nr_interface.c index 6ff90c20fa..9452a1f1a9 100644 --- a/openair2/RRC/NR/L2_nr_interface.c +++ b/openair2/RRC/NR/L2_nr_interface.c @@ -116,9 +116,7 @@ uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, uint8_t *const buffer_pP) { -#ifdef DEBUG_RRC LOG_D(RRC,"[eNB %d] mac_rrc_data_req to SRB ID=%ld\n",Mod_idP,Srb_id); -#endif // MIBCH if ((Srb_id & RAB_OFFSET) == MIBCH) { @@ -128,9 +126,6 @@ uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, rrc_gNB_carrier_data_t *carrier = &RC.nrrrc[Mod_idP]->carrier; NR_BCCH_BCH_Message_t *mib = &carrier->mib; - mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = carrier->pdcch_ConfigSIB1->controlResourceSetZero; - mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = carrier->pdcch_ConfigSIB1->searchSpaceZero; - mib->message.choice.mib->systemFrameNumber.buf[0] = sfn_msb << 2; enc_rval = uper_encode_to_buffer(&asn_DEF_NR_BCCH_BCH_Message, NULL, diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 3a1112f70a..54edbaa214 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -246,13 +246,8 @@ uint8_t do_MIB_NR(gNB_RRC_INST *rrc,uint32_t frame) { long ss0 = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero ? *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero : 0; - if(rrc->carrier.pdcch_ConfigSIB1) { - mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = rrc->carrier.pdcch_ConfigSIB1->controlResourceSetZero; - mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = rrc->carrier.pdcch_ConfigSIB1->searchSpaceZero; - } else { - mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = cset0; - mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = ss0; - } + mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = cset0; + mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = ss0; switch (*scc->ssbSubcarrierSpacing) { case NR_SubcarrierSpacing_kHz15: diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index a8245dd66d..d65f4115e1 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -448,7 +448,6 @@ typedef struct { NR_BCCH_DL_SCH_Message_t *siblock1; NR_ServingCellConfigCommon_t *servingcellconfigcommon; NR_ServingCellConfig_t *servingcellconfig; - NR_PDCCH_ConfigSIB1_t *pdcch_ConfigSIB1; NR_CellGroupConfig_t *secondaryCellGroup[MAX_NR_RRC_UE_CONTEXTS]; NR_SRB_INFO SI; int p_gNB; diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf index 5dcff74a6f..88742a5ffa 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf @@ -31,13 +31,6 @@ gNBs = remote_s_portd = 2152; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon @@ -65,7 +58,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPcontrolResourceSetZero = 12; initialDLBWPsearchSpaceZero = 0; #uplinkConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf index bbce460a11..3007cb2af8 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf @@ -23,13 +23,6 @@ gNBs = min_rxtxtime = 6; force_256qam_off = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon @@ -57,7 +50,7 @@ gNBs = # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 initialDLBWPsubcarrierSpacing = 1; #pdcch-ConfigCommon - initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPcontrolResourceSetZero = 12; initialDLBWPsearchSpaceZero = 0; #uplinkConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gNB_SA_40_2x2.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gNB_SA_40_2x2.conf index 81e5334206..926d8f64d7 100755 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gNB_SA_40_2x2.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gNB_SA_40_2x2.conf @@ -41,12 +41,6 @@ gNBs = pusch_AntennaPorts = 2; min_rxtxtime_pdsch = 2; ul_prbblacklist = "51,52,53,54" - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); servingCellConfigCommon = ( { @@ -89,19 +83,6 @@ gNBs = #pdcch-ConfigCommon initialDLBWPcontrolResourceSetZero = 11; initialDLBWPsearchSpaceZero = 0; - #pdsch-ConfigCommon - #pdschTimeDomainAllocationList (up to 16 entries) - initialDLBWPk0_0 = 0; - #initialULBWPmappingType - #0=typeA,1=typeB - initialDLBWPmappingType_0 = 0; - #this is SS=1,L=13 - initialDLBWPstartSymbolAndLength_0 = 40; - - initialDLBWPk0_1 = 0; - initialDLBWPmappingType_1 = 0; - #this is SS=1,L=5 - initialDLBWPstartSymbolAndLength_1 = 57; #uplinkConfigCommon #frequencyInfoUL diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf index 538ce4f615..0a61a690cd 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.2x2.usrpn310.conf @@ -42,13 +42,6 @@ gNBs = min_rxtxtime_pdsch = 5; ul_prbblacklist = "51,52,53,54" - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf index 8d409bf423..6533f6e542 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.162PRB.2x2.usrpn310.conf @@ -44,13 +44,6 @@ gNBs = ul_prbblacklist = "79,80,81,82" min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf index c4b4774e48..073a1c5d17 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band41.fr1.106PRB.usrpb210.conf @@ -19,13 +19,6 @@ gNBs = do_SRS = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf index 30742d88f4..cf60b9b764 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpn300.conf @@ -19,13 +19,6 @@ gNBs = min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpx300.conf index 560febad88..88bc9429cc 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.106PRB.usrpx300.conf @@ -34,13 +34,6 @@ gNBs = min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf index 3e7f50f506..f9b7bf13fc 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.24PRB.usrpx300.conf @@ -35,13 +35,6 @@ gNBs = sib1_tda = 5; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 4; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf index dbeb20c23f..f5baeba81c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band66.fr1.25PRB.usrpx300.conf @@ -35,13 +35,6 @@ gNBs = sib1_tda = 5; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 2; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf index 7e67d417d1..edcf7986e0 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.fr1.273PRB.2x2.usrpn300.conf @@ -22,11 +22,6 @@ gNBs = ( do_SRS = 1; ul_prbblacklist = "135,136,137,138" - pdcch_ConfigSIB1 = ({ - controlResourceSetZero = 12; - searchSpaceZero = 0; - }); - servingCellConfigCommon = ( { # spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf index 4d3b120ab1..eb7c27149c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.2x2.usrpn300.conf @@ -23,13 +23,6 @@ gNBs = do_SRS = 1; ul_prbblacklist = "51,52,53,54" - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf index 2da02b0353..0ad8b6066d 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.4layer.conf @@ -21,13 +21,6 @@ gNBs = pdsch_AntennaPorts_N1 = 2; pusch_AntennaPorts = 4; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf index ccfd5ea56c..ceb312e9c2 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf @@ -20,13 +20,6 @@ gNBs = do_CSIRS = 1; do_SRS = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.sabox.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.sabox.conf index 9e4906af0f..60a29c50e4 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.sabox.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.sabox.conf @@ -20,13 +20,6 @@ gNBs = ////////// Physical parameters: - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf index 7f052820c2..17959af775 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.162PRB.2x2.usrpn300.conf @@ -22,12 +22,6 @@ gNBs = do_CSIRS = 1; do_SRS = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); servingCellConfigCommon = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf index 785d06dde0..b5101c1112 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.217PRB.2x2.usrpn300.conf @@ -22,13 +22,6 @@ gNBs = do_CSIRS = 0; ul_prbblacklist = "107,108,109,110" - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 10; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.24PRB.usrpb210.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.24PRB.usrpb210.conf index 1defbf91ed..a982ed59cd 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.24PRB.usrpb210.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.24PRB.usrpb210.conf @@ -20,13 +20,6 @@ gNBs = sib1_tda = 15; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 2; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.sa.band78.fr1.106PRB.prs.usrpx310.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.sa.band78.fr1.106PRB.prs.usrpx310.conf index 129056d191..01259ac5a6 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.sa.band78.fr1.106PRB.prs.usrpx310.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.sa.band78.fr1.106PRB.prs.usrpx310.conf @@ -43,12 +43,6 @@ gNBs = min_rxtxtime = 6; do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); servingCellConfigCommon = ( { diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb1.sa.band78.fr1.106PRB.prs.usrpx310.conf b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb1.sa.band78.fr1.106PRB.prs.usrpx310.conf index 025a3bfe66..c2fa1bb623 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb1.sa.band78.fr1.106PRB.prs.usrpx310.conf +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb1.sa.band78.fr1.106PRB.prs.usrpx310.conf @@ -43,13 +43,6 @@ gNBs = min_rxtxtime = 6; do_SRS = 0; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 11; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon From dd24ddd645dc57b4729284710017dd35c9712bd1 Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 29 Nov 2022 19:05:56 +0100 Subject: [PATCH 03/26] remove NUMBER_OF_NR_PUCCH_MAX --- common/openairinterface5g_limits.h | 1 - executables/nr-gnb.c | 3 +- openair1/PHY/INIT/nr_init.c | 41 +++++++++++++++++---- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 15 ++++---- openair1/PHY/defs_gNB.h | 3 +- openair1/SCHED_NR/phy_frame_config_nr.c | 44 +++-------------------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 +-- 7 files changed, 55 insertions(+), 56 deletions(-) diff --git a/common/openairinterface5g_limits.h b/common/openairinterface5g_limits.h index 2349941cf2..8f3a2b40fd 100644 --- a/common/openairinterface5g_limits.h +++ b/common/openairinterface5g_limits.h @@ -18,7 +18,6 @@ # define NUMBER_OF_NR_SCH_STATS_MAX 16 -# define NUMBER_OF_NR_PUCCH_MAX 16 # define NUMBER_OF_NR_PDCCH_MAX 16 #define MAX_MANAGED_ENB_PER_MOBILE 2 diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 852e805e6d..9660f89f5c 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -198,7 +198,8 @@ void rx_func(void *param) { } up_removed++; } - for (j = 0; j < NUMBER_OF_NR_PUCCH_MAX; j++) + + for (j = 0; j < gNB->max_nb_pucch; j++) if (gNB->pucch[j]->active > 0 && gNB->pucch[j]->pucch_pdu.rnti == rnti_to_remove[i]) { gNB->pucch[j]->active = 0; diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 6f983f613c..515950c15e 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -134,7 +134,7 @@ int init_codebook_gNB(PHY_VARS_gNB *gNB) { int max_mimo_layers = (CSI_RS_antenna_portsnr_mimo_precoding_matrix = (int32_t ***)malloc16(max_mimo_layers* sizeof(int32_t **)); + gNB->nr_mimo_precoding_matrix = (int32_t ***)malloc16(max_mimo_layers * sizeof(int32_t **)); int32_t ***mat = gNB->nr_mimo_precoding_matrix; double complex res_code; @@ -1002,14 +1002,43 @@ void reset_DLSCH_struct(const PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) free_gNB_dlsch(&msg->dlsch[i][j], grid_size, fp); } -void init_nr_transport(PHY_VARS_gNB *gNB) { +void init_nr_transport(PHY_VARS_gNB *gNB) +{ + NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + const nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; LOG_I(PHY, "Initialise nr transport\n"); - for (int i=0; icell_config.frame_duplex_type.value ? + fp->slots_per_frame / get_nb_periods_per_frame(cfg->tdd_table.tdd_period.value) : + fp->slots_per_frame; + int nb_ul_slots_period = 0; + if (cfg->cell_config.frame_duplex_type.value) { + for(int i=0; itdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list[j].slot_config.value == 1) { // UL symbol + nb_ul_slots_period++; + break; + } + } + } + } + else + nb_ul_slots_period = fp->slots_per_frame; + + int pucch_slots; + if (gNB->if_inst->sl_ahead > nb_slots_per_period) + pucch_slots = nb_ul_slots_period + (gNB->if_inst->sl_ahead - nb_slots_per_period); + else + pucch_slots = (nb_ul_slots_period < gNB->if_inst->sl_ahead) ? nb_ul_slots_period : gNB->if_inst->sl_ahead; + + gNB->max_nb_pucch = MAX_MOBILES_PER_GNB * pucch_slots; + + gNB->pucch = (NR_gNB_PUCCH_t **) malloc16(gNB->max_nb_pucch * sizeof(NR_gNB_PUCCH_t*)); + for (int i = 0; i < gNB->max_nb_pucch; i++) { + LOG_I(PHY,"Allocating Transport Channel Buffers for PUCCH %d/%d\n", i, gNB->max_nb_pucch); gNB->pucch[i] = new_gNB_pucch(); - AssertFatal(gNB->pucch[i]!=NULL,"Can't initialize pucch %d \n", i); + AssertFatal(gNB->pucch[i] != NULL,"Can't initialize pucch %d \n", i); } for (int i=0; iframe_parms; - for (int i = 0; i < NUMBER_OF_NR_PUCCH_MAX; i++) + for (int i = 0; i < gNB->max_nb_pucch; i++) free_gNB_pucch(gNB->pucch[i]); for (int i = 0; i < NUMBER_OF_NR_SRS_MAX; i++) diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 905e03801c..03ed927c37 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -70,20 +70,21 @@ void free_gNB_pucch(NR_gNB_PUCCH_t *pucch) int nr_find_pucch(uint16_t rnti, int frame, int slot, - PHY_VARS_gNB *gNB) { + PHY_VARS_gNB *gNB) +{ AssertFatal(gNB!=NULL,"gNB is null\n"); int index = -1; - for (int i = 0; i < NUMBER_OF_NR_PUCCH_MAX; i++) { + for (int i = 0; i < gNB->max_nb_pucch; i++) { AssertFatal(gNB->pucch[i] != NULL,"gNB->pucch[%d] is null\n",i); if ((gNB->pucch[i]->active > 0) && (gNB->pucch[i]->pucch_pdu.rnti == rnti) && (gNB->pucch[i]->frame == frame) && (gNB->pucch[i]->slot == slot)) - return(i); + return i; else if ((gNB->pucch[i]->active == 0) && (index == -1)) - index=i; + index = i; } if (index==-1) @@ -95,11 +96,13 @@ int nr_find_pucch(uint16_t rnti, void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, - nfapi_nr_pucch_pdu_t *pucch_pdu) { + nfapi_nr_pucch_pdu_t *pucch_pdu) +{ + if (NFAPI_MODE == NFAPI_MODE_PNF) gNB->pucch[0]->active = 0; //check if ture in monolithic mode int id = nr_find_pucch(pucch_pdu->rnti,frame,slot,gNB); - AssertFatal((id >= 0) && (id < NUMBER_OF_NR_PUCCH_MAX), + AssertFatal((id >= 0) && (id < gNB->max_nb_pucch), "invalid id found for pucch !!! rnti %04x id %d\n",pucch_pdu->rnti,id); NR_gNB_PUCCH_t *pucch = gNB->pucch[id]; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 0f2f41c470..73cad1fc87 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -617,12 +617,13 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_ul_tti_request_t UL_tti_req; nfapi_nr_uci_indication_t uci_indication; + int max_nb_pucch; NR_gNB_PBCH pbch; NR_gNB_COMMON common_vars; NR_gNB_PRACH prach_vars; NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars[NUMBER_OF_NR_ULSCH_MAX]; - NR_gNB_PUCCH_t *pucch[NUMBER_OF_NR_PUCCH_MAX]; + NR_gNB_PUCCH_t **pucch; NR_gNB_SRS_t *srs[NUMBER_OF_NR_SRS_MAX]; NR_gNB_PDCCH_t pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; NR_gNB_UL_PDCCH_t ul_pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; diff --git a/openair1/SCHED_NR/phy_frame_config_nr.c b/openair1/SCHED_NR/phy_frame_config_nr.c index 25c39846fe..9ed7255a84 100644 --- a/openair1/SCHED_NR/phy_frame_config_nr.c +++ b/openair1/SCHED_NR/phy_frame_config_nr.c @@ -33,6 +33,7 @@ #include "PHY/defs_gNB.h" #include "PHY/defs_nr_UE.h" #include "SCHED_NR/phy_frame_config_nr.h" +#include "common/utils/nr/nr_common.h" /******************************************************************* * @@ -53,48 +54,13 @@ int set_tdd_config_nr( nfapi_nr_config_request_scf_t *cfg, int mu, int nrofDownlinkSlots, int nrofDownlinkSymbols, - int nrofUplinkSlots, int nrofUplinkSymbols) { + int nrofUplinkSlots, int nrofUplinkSymbols) +{ + int slot_number = 0; - int nb_periods_per_frame; + int nb_periods_per_frame = get_nb_periods_per_frame(cfg->tdd_table.tdd_period.value); int nb_slots_to_set = TDD_CONFIG_NB_FRAMES*(1<tdd_table.tdd_period.value) { - case 0: - nb_periods_per_frame = 20; // 10ms/0p5ms - break; - - case 1: - nb_periods_per_frame = 16; // 10ms/0p625ms - break; - - case 2: - nb_periods_per_frame = 10; // 10ms/1ms - break; - - case 3: - nb_periods_per_frame = 8; // 10ms/1p25ms - break; - - case 4: - nb_periods_per_frame = 5; // 10ms/2ms - break; - - case 5: - nb_periods_per_frame = 4; // 10ms/2p5ms - break; - - case 6: - nb_periods_per_frame = 2; // 10ms/5ms - break; - - case 7: - nb_periods_per_frame = 1; // 10ms/10ms - break; - - default: - AssertFatal(1==0,"Undefined tdd period %d\n", cfg->tdd_table.tdd_period.value); - } - int nb_slots_per_period = ((1<max_nb_pucch; i++){ NR_gNB_PUCCH_t *pucch = gNB->pucch[i]; if (pucch) { if ((pucch->active == 1) && @@ -713,7 +713,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { start_meas(&gNB->phy_proc_rx); - for (int i=0;imax_nb_pucch; i++){ NR_gNB_PUCCH_t *pucch = gNB->pucch[i]; if (pucch) { if (NFAPI_MODE == NFAPI_MODE_PNF) From 571f488ddb02cf4dfc11c600b083288f00d837fb Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 30 Nov 2022 10:35:51 +0100 Subject: [PATCH 04/26] remove NUMBER_OF_NR_CSIRS_MAX --- common/openairinterface5g_limits.h | 1 - openair1/PHY/defs_gNB.h | 2 +- openair1/SCHED_NR/fapi_nr_l1.c | 6 +++--- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/common/openairinterface5g_limits.h b/common/openairinterface5g_limits.h index 8f3a2b40fd..ad36fae476 100644 --- a/common/openairinterface5g_limits.h +++ b/common/openairinterface5g_limits.h @@ -13,7 +13,6 @@ # define NUMBER_OF_NR_DLSCH_MAX 8 # define NUMBER_OF_NR_UCI_MAX 16 # define NUMBER_OF_NR_SRS_MAX 16 -# define NUMBER_OF_NR_CSIRS_MAX 16 # define NUMBER_OF_SCH_STATS_MAX 16 # define NUMBER_OF_NR_SCH_STATS_MAX 16 diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 73cad1fc87..b21f3c1192 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -828,7 +828,7 @@ typedef struct processingData_L1tx { PHY_VARS_gNB *gNB; nfapi_nr_dl_tti_pdcch_pdu pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; nfapi_nr_ul_dci_request_pdus_t ul_pdcch_pdu[NFAPI_NR_MAX_NB_CORESETS]; - NR_gNB_CSIRS_t csirs_pdu[NUMBER_OF_NR_CSIRS_MAX]; + NR_gNB_CSIRS_t csirs_pdu[NR_SYMBOLS_PER_SLOT]; NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; NR_gNB_SSB_t ssb[64]; uint16_t num_pdsch_slot; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index 49e6edd54e..c6b6f69126 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -102,12 +102,12 @@ void handle_nr_nfapi_ssb_pdu(processingData_L1tx_t *msgTx,int frame,int slot, void handle_nfapi_nr_csirs_pdu(processingData_L1tx_t *msgTx, - int frame,int slot, - nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) { + int frame,int slot, + nfapi_nr_dl_tti_csi_rs_pdu *csirs_pdu) { int found = 0; - for (int id=0; idcsirs_pdu[id]; if (csirs->active == 0) { LOG_D(PHY,"Frame %d Slot %d CSI_RS with ID %d is now active\n",frame,slot,id); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index d53eed2c1e..bed81d2f7c 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -184,7 +184,7 @@ void phy_procedures_gNB_TX(processingData_L1tx_t *msgTx, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_GENERATE_DLSCH,0); } - for (int i=0;icsirs_pdu[i]; if (csirs->active == 1) { LOG_D(PHY, "CSI-RS generation started in frame %d.%d\n",frame,slot); From b4b5516ca79d9d344139ca4a1a469c6758d5fca6 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 30 Nov 2022 16:11:53 +0100 Subject: [PATCH 05/26] remove NUMBER_OF_NR_SRS_MAX --- common/openairinterface5g_limits.h | 3 -- openair1/PHY/INIT/nr_init.c | 35 ++++++++++++----------- openair1/PHY/NR_TRANSPORT/srs_rx.c | 6 ++-- openair1/PHY/defs_gNB.h | 5 ++-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 +-- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/common/openairinterface5g_limits.h b/common/openairinterface5g_limits.h index ad36fae476..f5d9c21ad5 100644 --- a/common/openairinterface5g_limits.h +++ b/common/openairinterface5g_limits.h @@ -11,10 +11,7 @@ # define NUMBER_OF_SRS_MAX 16 # define NUMBER_OF_NR_ULSCH_MAX 8 # define NUMBER_OF_NR_DLSCH_MAX 8 -# define NUMBER_OF_NR_UCI_MAX 16 -# define NUMBER_OF_NR_SRS_MAX 16 # define NUMBER_OF_SCH_STATS_MAX 16 - # define NUMBER_OF_NR_SCH_STATS_MAX 16 # define NUMBER_OF_NR_PDCCH_MAX 16 diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 515950c15e..859994e1a5 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -607,14 +607,6 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB) //PRS init nr_init_prs(gNB); - for (int id=0; idnr_srs_info[id] = (nr_srs_info_t *)malloc16_clear(sizeof(nr_srs_info_t)); - gNB->nr_srs_info[id]->srs_generated_signal = (int32_t**)malloc16_clear(MAX_NUM_NR_SRS_AP*sizeof(int32_t*)); - for(int ap=0; apnr_srs_info[id]->srs_generated_signal[ap] = (int32_t*)malloc16_clear(fp->ofdm_symbol_size*MAX_NUM_NR_SRS_SYMBOLS*sizeof(int32_t)); - } - } - generate_ul_reference_signal_sequences(SHRT_MAX); /* Generate low PAPR type 1 sequences for PUSCH DMRS, these are used if transform precoding is enabled. */ @@ -623,6 +615,15 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB) /// Transport init necessary for NR synchro init_nr_transport(gNB); + gNB->nr_srs_info = (nr_srs_info_t **)malloc16_clear(gNB->max_nb_srs * sizeof(nr_srs_info_t*)); + for (int id = 0; id < gNB->max_nb_srs; id++) { + gNB->nr_srs_info[id] = (nr_srs_info_t *)malloc16_clear(sizeof(nr_srs_info_t)); + gNB->nr_srs_info[id]->srs_generated_signal = (int32_t**)malloc16_clear(MAX_NUM_NR_SRS_AP*sizeof(int32_t*)); + for(int ap=0; apnr_srs_info[id]->srs_generated_signal[ap] = (int32_t*)malloc16_clear(fp->ofdm_symbol_size*MAX_NUM_NR_SRS_SYMBOLS*sizeof(int32_t)); + } + } + common_vars->txdataF = (int32_t **)malloc16(Ptx*sizeof(int32_t*)); common_vars->rxdataF = (int32_t **)malloc16(Prx*sizeof(int32_t*)); /* Do NOT allocate per-antenna txdataF/rxdataF: the gNB gets a pointer to the @@ -760,7 +761,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(nr_gold_csi_rs); free_and_zero(gNB->nr_csi_info); - for (int id = 0; id < NUMBER_OF_NR_SRS_MAX; id++) { + for (int id = 0; id < gNB->max_nb_srs; id++) { for(int i=0; inr_srs_info[id]->srs_generated_signal[i]); } @@ -1026,13 +1027,14 @@ void init_nr_transport(PHY_VARS_gNB *gNB) else nb_ul_slots_period = fp->slots_per_frame; - int pucch_slots; + int buffer_ul_slots; // the UL channels are scheduled sl_ahead before they are transmitted if (gNB->if_inst->sl_ahead > nb_slots_per_period) - pucch_slots = nb_ul_slots_period + (gNB->if_inst->sl_ahead - nb_slots_per_period); + buffer_ul_slots = nb_ul_slots_period + (gNB->if_inst->sl_ahead - nb_slots_per_period); else - pucch_slots = (nb_ul_slots_period < gNB->if_inst->sl_ahead) ? nb_ul_slots_period : gNB->if_inst->sl_ahead; + buffer_ul_slots = (nb_ul_slots_period < gNB->if_inst->sl_ahead) ? nb_ul_slots_period : gNB->if_inst->sl_ahead; - gNB->max_nb_pucch = MAX_MOBILES_PER_GNB * pucch_slots; + gNB->max_nb_pucch = MAX_MOBILES_PER_GNB * buffer_ul_slots; + gNB->max_nb_srs = buffer_ul_slots << 1; // assuming at most 2 SRS per slot gNB->pucch = (NR_gNB_PUCCH_t **) malloc16(gNB->max_nb_pucch * sizeof(NR_gNB_PUCCH_t*)); for (int i = 0; i < gNB->max_nb_pucch; i++) { @@ -1041,8 +1043,9 @@ void init_nr_transport(PHY_VARS_gNB *gNB) AssertFatal(gNB->pucch[i] != NULL,"Can't initialize pucch %d \n", i); } - for (int i=0; isrs = (NR_gNB_SRS_t **) malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t*)); + for (int i = 0; i < gNB->max_nb_srs; i++) { + LOG_I(PHY,"Allocating Transport Channel Buffers for SRS %d/%d\n", i, gNB->max_nb_srs); gNB->srs[i] = new_gNB_srs(); AssertFatal(gNB->srs[i]!=NULL,"Can't initialize srs %d \n", i); } @@ -1071,7 +1074,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) for (int i = 0; i < gNB->max_nb_pucch; i++) free_gNB_pucch(gNB->pucch[i]); - for (int i = 0; i < NUMBER_OF_NR_SRS_MAX; i++) + for (int i = 0; i < gNB->max_nb_srs; i++) free_gNB_srs(gNB->srs[i]); for (int i=0; inumber_of_nr_ulsch_max; i++) diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index 5d855ae6b5..7c0e241af8 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -64,7 +64,7 @@ int nr_find_srs(rnti_t rnti, AssertFatal(gNB!=NULL,"gNB is null\n"); int index = -1; - for (int i=0; imax_nb_srs; i++) { AssertFatal(gNB->srs[i]!=NULL,"gNB->srs[%d] is null\n",i); if ((gNB->srs[i]->active>0) && (gNB->srs[i]->srs_pdu.rnti==rnti) && @@ -85,8 +85,8 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, nfapi_nr_srs_pdu_t *srs_pdu) { int id = nr_find_srs(srs_pdu->rnti,frame,slot,gNB); - AssertFatal( (id>=0) && (idrnti,id); + AssertFatal((id >= 0) && (id < gNB->max_nb_srs), + "invalid id found for srs !!! rnti %04x id %d\n",srs_pdu->rnti,id); NR_gNB_SRS_t *srs = gNB->srs[id]; srs->frame = frame; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index b21f3c1192..739eddc052 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -618,13 +618,14 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_uci_indication_t uci_indication; int max_nb_pucch; + int max_nb_srs; NR_gNB_PBCH pbch; NR_gNB_COMMON common_vars; NR_gNB_PRACH prach_vars; NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars[NUMBER_OF_NR_ULSCH_MAX]; NR_gNB_PUCCH_t **pucch; - NR_gNB_SRS_t *srs[NUMBER_OF_NR_SRS_MAX]; + NR_gNB_SRS_t **srs; NR_gNB_PDCCH_t pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; NR_gNB_UL_PDCCH_t ul_pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; // Nusers times two spatial streams @@ -637,7 +638,7 @@ typedef struct PHY_VARS_gNB_s { t_nrPolar_params **polarParams; /// SRS variables - nr_srs_info_t *nr_srs_info[NUMBER_OF_NR_SRS_MAX]; + nr_srs_info_t **nr_srs_info; /// CSI variables nr_csi_info_t *nr_csi_info; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index bed81d2f7c..78f75b9e51 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -582,7 +582,7 @@ void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { } } - for (int i=0;imax_nb_srs; i++) { NR_gNB_SRS_t *srs = gNB->srs[i]; if (srs) { if ((srs->active == 1) && (srs->frame == frame_rx) && (srs->slot == slot_rx)) { @@ -870,7 +870,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { } } - for (int i = 0; i < NUMBER_OF_NR_SRS_MAX; i++) { + for (int i = 0; i < gNB->max_nb_srs; i++) { NR_gNB_SRS_t *srs = gNB->srs[i]; if (srs) { if ((srs->active == 1) && (srs->frame == frame_rx) && (srs->slot == slot_rx)) { From d8612a6ca7d3c6bc7971ed1e557f4c6f2bd16044 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 30 Nov 2022 17:40:40 +0100 Subject: [PATCH 06/26] some more cleanup --- common/openairinterface5g_limits.h | 2 -- executables/nr-gnb.c | 8 -------- openair1/PHY/NR_TRANSPORT/nr_prach.c | 17 ++++++++--------- openair1/PHY/defs_gNB.h | 2 -- 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/common/openairinterface5g_limits.h b/common/openairinterface5g_limits.h index f5d9c21ad5..485c775b77 100644 --- a/common/openairinterface5g_limits.h +++ b/common/openairinterface5g_limits.h @@ -14,8 +14,6 @@ # define NUMBER_OF_SCH_STATS_MAX 16 # define NUMBER_OF_NR_SCH_STATS_MAX 16 -# define NUMBER_OF_NR_PDCCH_MAX 16 - #define MAX_MANAGED_ENB_PER_MOBILE 2 #define MAX_MANAGED_GNB_PER_MOBILE 2 diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 9660f89f5c..645091eeae 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -206,14 +206,6 @@ void rx_func(void *param) { gNB->pucch[j]->pucch_pdu.rnti = 0; pucch_removed++; } -#if 0 - for (j = 0; j < NUMBER_OF_NR_PDCCH_MAX; j++) - gNB->pdcch_pdu[j].frame = -1; - for (j = 0; j < NUMBER_OF_NR_PDCCH_MAX; j++) - gNB->ul_pdcch_pdu[j].frame = -1; - for (j = 0; j < NUMBER_OF_NR_PRACH_MAX; j++) - gNB->prach_vars.list[j].frame = -1; -#endif } if (rnti_to_remove_count) LOG_W(NR_PHY, "to remove rnti_to_remove_count=%d, up_removed=%d down_removed=%d pucch_removed=%d\n", rnti_to_remove_count, up_removed, down_removed, pucch_removed); rnti_to_remove_count = 0; diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index b3d492d025..461f012687 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -39,20 +39,19 @@ extern uint16_t prach_root_sequence_map_abc[138]; extern uint16_t nr_du[838]; extern const char *prachfmt[]; -void init_prach_list(PHY_VARS_gNB *gNB) { - +void init_prach_list(PHY_VARS_gNB *gNB) +{ AssertFatal(gNB!=NULL,"gNB is null\n"); for (int i=0; iprach_vars.list[i].frame = -1; - gNB->prach_vars.list[i].slot = -1; - } + gNB->prach_vars.list[i].frame = -1; + gNB->prach_vars.list[i].slot = -1; + } } -void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id) { - +void free_nr_prach_entry(PHY_VARS_gNB *gNB, int prach_id) +{ gNB->prach_vars.list[prach_id].frame = -1; - gNB->prach_vars.list[prach_id].slot = -1; - + gNB->prach_vars.list[prach_id].slot = -1; } int16_t find_nr_prach(PHY_VARS_gNB *gNB,int frame, int slot, find_type_t type) { diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 739eddc052..136daf44aa 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -626,8 +626,6 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PUSCH *pusch_vars[NUMBER_OF_NR_ULSCH_MAX]; NR_gNB_PUCCH_t **pucch; NR_gNB_SRS_t **srs; - NR_gNB_PDCCH_t pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; - NR_gNB_UL_PDCCH_t ul_pdcch_pdu[NUMBER_OF_NR_PDCCH_MAX]; NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; // Nusers times two spatial streams NR_gNB_ULSCH_t *ulsch[NUMBER_OF_NR_ULSCH_MAX]; // [Nusers times] /// statistics for DLSCH measurement collection From 43685afe1abcaa0feb31289ea8f9a0c53ec9d575 Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 5 Dec 2022 17:10:17 +0100 Subject: [PATCH 07/26] removing NUMBER_OF_NR_UE_MAX --- openair1/SIMULATION/NR_PHY/ulsim.c | 1 - 1 file changed, 1 deletion(-) diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 210b33774e..22d0151711 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -318,7 +318,6 @@ int main(int argc, char **argv) int ldpc_offload_flag = 0; uint8_t max_rounds = 4; int chest_type[2] = {0}; - int enable_ptrs = 0; int modify_dmrs = 0; /* L_PTRS = ptrs_arg[0], K_PTRS = ptrs_arg[1] */ From 721b8e9d9f232054301c86a5bc1ecfc9a6c3b91a Mon Sep 17 00:00:00 2001 From: francescomani Date: Tue, 13 Dec 2022 18:30:32 +0100 Subject: [PATCH 08/26] setting max dlsch/ulsch limit --- common/openairinterface5g_limits.h | 7 ++++--- common/platform_constants.h | 3 --- nfapi/oai_integration/nfapi_pnf.c | 4 ++-- openair1/PHY/INIT/nr_init.c | 19 ++++++++----------- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 2 +- openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 10 +++++----- openair1/PHY/defs_gNB.h | 3 --- openair1/SCHED_NR/fapi_nr_l1.c | 4 ++-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 ++-- 9 files changed, 24 insertions(+), 32 deletions(-) diff --git a/common/openairinterface5g_limits.h b/common/openairinterface5g_limits.h index 485c775b77..b86f01df6a 100644 --- a/common/openairinterface5g_limits.h +++ b/common/openairinterface5g_limits.h @@ -1,6 +1,7 @@ #ifndef OPENAIRINTERFACE5G_LIMITS_H_ #define OPENAIRINTERFACE5G_LIMITS_H_ +# define MAX_MOBILES_PER_GNB 16 # define NUMBER_OF_eNB_MAX 1 # define NUMBER_OF_gNB_MAX 1 # define NUMBER_OF_RU_MAX 2 @@ -9,10 +10,10 @@ # define NUMBER_OF_ULSCH_MAX 8 # define NUMBER_OF_DLSCH_MAX 8 # define NUMBER_OF_SRS_MAX 16 -# define NUMBER_OF_NR_ULSCH_MAX 8 -# define NUMBER_OF_NR_DLSCH_MAX 8 +# define NUMBER_OF_NR_ULSCH_MAX MAX_MOBILES_PER_GNB +# define NUMBER_OF_NR_DLSCH_MAX MAX_MOBILES_PER_GNB # define NUMBER_OF_SCH_STATS_MAX 16 -# define NUMBER_OF_NR_SCH_STATS_MAX 16 +# define NUMBER_OF_NR_SCH_STATS_MAX MAX_MOBILES_PER_GNB #define MAX_MANAGED_ENB_PER_MOBILE 2 #define MAX_MANAGED_GNB_PER_MOBILE 2 diff --git a/common/platform_constants.h b/common/platform_constants.h index 9d67b76cd7..38ec11d49e 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -69,20 +69,17 @@ #ifdef LARGE_SCALE #define MAX_MOBILES_PER_ENB 128 #define MAX_MOBILES_PER_ENB_NB_IoT 128 -#define MAX_MOBILES_PER_GNB 128 #define MAX_eNB 2 #define MAX_gNB 2 #else #define MAX_MOBILES_PER_ENB 40 #define MAX_MOBILES_PER_ENB_NB_IoT 40 -#define MAX_MOBILES_PER_GNB 16 #define MAX_eNB 2 #define MAX_gNB 2 #endif #else #define MAX_MOBILES_PER_ENB 256 #define MAX_MOBILES_PER_ENB_NB_IoT 256 -#define MAX_MOBILES_PER_GNB 256 #define MAX_eNB 2 #define MAX_gNB 2 #endif diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 864791141f..bc6cd58a79 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -1221,8 +1221,8 @@ int pnf_phy_dl_tti_req(gNB_L1_rxtx_proc_t *proc, nfapi_pnf_p7_config_t *pnf_p7, if (tx_data != NULL) { uint8_t *dlsch_sdu = (uint8_t *)tx_data->TLVs[0].value.direct; //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() DLSCH:pdu_index:%d handle_nfapi_dlsch_pdu(eNB, proc_rxtx, dlsch_pdu, transport_blocks:%d sdu:%p) eNB->pdcch_vars[proc->subframe_tx & 1].num_pdcch_symbols:%d\n", __FUNCTION__, rel8_pdu->pdu_index, rel8_pdu->transport_blocks, dlsch_sdu, eNB->pdcch_vars[proc->subframe_tx & 1].num_pdcch_symbols); - AssertFatal(msgTx->num_pdsch_slot < gNB->number_of_nr_dlsch_max,"Number of PDSCH PDUs %d exceeded the limit %d\n", - msgTx->num_pdsch_slot,gNB->number_of_nr_dlsch_max); + AssertFatal(msgTx->num_pdsch_slot < NUMBER_OF_NR_DLSCH_MAX,"Number of PDSCH PDUs %d exceeded the limit %d\n", + msgTx->num_pdsch_slot, NUMBER_OF_NR_DLSCH_MAX); handle_nr_nfapi_pdsch_pdu(msgTx, pdsch_pdu, dlsch_sdu); } else { diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 859994e1a5..b9e1139d14 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -489,9 +489,6 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB) while(gNB->configured == 0) usleep(10000); - gNB->number_of_nr_dlsch_max = NUMBER_OF_NR_DLSCH_MAX; - gNB->number_of_nr_ulsch_max = NUMBER_OF_NR_ULSCH_MAX; - load_dftslib(); crcTableInit(); @@ -654,7 +651,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB) int nb_re_pusch = N_RB_UL * NR_NB_SC_PER_RB; int nb_re_pusch2 = nb_re_pusch + (nb_re_pusch&7); - for (int ULSCH_id=0; ULSCH_idnumber_of_nr_ulsch_max; ULSCH_id++) { + for (int ULSCH_id = 0; ULSCH_id < NUMBER_OF_NR_ULSCH_MAX; ULSCH_id++) { pusch_vars[ULSCH_id] = (NR_gNB_PUSCH *)malloc16_clear( sizeof(NR_gNB_PUSCH) ); pusch_vars[ULSCH_id]->rxdataF_ext = (int32_t **)malloc16(Prx*sizeof(int32_t *) ); pusch_vars[ULSCH_id]->ul_ch_estimates = (int32_t **)malloc16(n_buf*sizeof(int32_t *) ); @@ -805,7 +802,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(prach_vars->prach_ifft); NR_gNB_PUSCH** pusch_vars = gNB->pusch_vars; - for (int ULSCH_id=0; ULSCH_idnumber_of_nr_ulsch_max; ULSCH_id++) { + for (int ULSCH_id = 0; ULSCH_id < NUMBER_OF_NR_ULSCH_MAX; ULSCH_id++) { for (int i=0; i< max_ul_mimo_layers; i++) free_and_zero(pusch_vars[ULSCH_id]->llr_layers[i]); for (int i = 0; i < Prx; i++) { @@ -983,8 +980,8 @@ void init_DLSCH_struct(PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) { msg->num_pdsch_slot = 0; int num_cw = NR_MAX_NB_LAYERS > 4? 2:1; - for (int i=0; inumber_of_nr_dlsch_max; i++) { - LOG_I(PHY,"Allocating Transport Channel Buffers for DLSCH %d/%d\n",i,gNB->number_of_nr_dlsch_max); + for (int i = 0; i < NUMBER_OF_NR_DLSCH_MAX; i++) { + LOG_I(PHY,"Allocating Transport Channel Buffers for DLSCH %d/%d\n", i, NUMBER_OF_NR_DLSCH_MAX); for (int j=0; jdlsch[i][j] = new_gNB_dlsch(fp, grid_size); AssertFatal(msg->dlsch[i][j]!=NULL,"Can't initialize dlsch %d \n", i); @@ -998,7 +995,7 @@ void reset_DLSCH_struct(const PHY_VARS_gNB *gNB, processingData_L1tx_t *msg) const nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config; const uint16_t grid_size = cfg->carrier_config.dl_grid_size[fp->numerology_index].value; int num_cw = NR_MAX_NB_LAYERS > 4? 2:1; - for (int i=0; inumber_of_nr_dlsch_max; i++) + for (int i = 0; i < NUMBER_OF_NR_DLSCH_MAX; i++) for (int j=0; jdlsch[i][j], grid_size, fp); } @@ -1050,9 +1047,9 @@ void init_nr_transport(PHY_VARS_gNB *gNB) AssertFatal(gNB->srs[i]!=NULL,"Can't initialize srs %d \n", i); } - for (int i=0; inumber_of_nr_ulsch_max; i++) { + for (int i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) { - LOG_I(PHY,"Allocating Transport Channel Buffers for ULSCH %d/%d\n",i,gNB->number_of_nr_ulsch_max); + LOG_I(PHY,"Allocating Transport Channel Buffers for ULSCH %d/%d\n", i, NUMBER_OF_NR_ULSCH_MAX); gNB->ulsch[i] = new_gNB_ulsch(gNB->max_ldpc_iterations, fp->N_RB_UL); @@ -1077,6 +1074,6 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) for (int i = 0; i < gNB->max_nb_srs; i++) free_gNB_srs(gNB->srs[i]); - for (int i=0; inumber_of_nr_ulsch_max; i++) + for (int i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) free_gNB_ulsch(&gNB->ulsch[i], fp->N_RB_UL); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index fa3c961a29..9b6adb8702 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -582,6 +582,6 @@ void dump_pdsch_stats(FILE *fd,PHY_VARS_gNB *gNB) { void clear_pdsch_stats(PHY_VARS_gNB *gNB) { - for (int i=0;inumber_of_nr_dlsch_max;i++) + for (int i = 0;i < NUMBER_OF_NR_SCH_STATS_MAX; i++) memset((void*)&gNB->dlsch_stats[i],0,sizeof(gNB->dlsch_stats[i])); } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index e9fcd35f6b..9fb95ac273 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -40,7 +40,7 @@ int16_t find_nr_ulsch(uint16_t rnti, PHY_VARS_gNB *gNB,find_type_t type) { int16_t first_free_index=-1; AssertFatal(gNB!=NULL,"gNB is null\n"); - for (i=0; inumber_of_nr_ulsch_max; i++) { + for (i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) { AssertFatal(gNB->ulsch[i]!=NULL,"gNB->ulsch[%d] is null\n",i); LOG_D(PHY,"searching for rnti %x : ulsch_index %d=> harq_mask %x, rnti %x, first_free_index %d\n", rnti,i,gNB->ulsch[i]->harq_mask,gNB->ulsch[i]->rnti,first_free_index); if ((gNB->ulsch[i]->harq_mask >0) && @@ -60,7 +60,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int ulsch_id = find_nr_ulsch(ulsch_pdu->rnti,gNB,SEARCH_EXIST_OR_FREE); - AssertFatal( (ulsch_id>=0) && (ulsch_idnumber_of_nr_ulsch_max), + AssertFatal((ulsch_id >= 0) && (ulsch_id < NUMBER_OF_NR_ULSCH_MAX), "illegal or no ulsch_id found!!! rnti %04x ulsch_id %d\n",ulsch_pdu->rnti,ulsch_id); NR_gNB_ULSCH_t *ulsch = gNB->ulsch[ulsch_id]; @@ -121,7 +121,7 @@ void nr_ulsch_layer_demapping(int16_t *llr_cw, void dump_pusch_stats(FILE *fd,PHY_VARS_gNB *gNB) { - for (int i=0;inumber_of_nr_ulsch_max;i++) { + for (int i = 0;i < NUMBER_OF_NR_SCH_STATS_MAX;i++) { if (gNB->ulsch_stats[i].rnti>0 && gNB->ulsch_stats[i].frame != gNB->ulsch_stats[i].dump_frame) { gNB->ulsch_stats[i].dump_frame = gNB->ulsch_stats[i].frame; @@ -155,14 +155,14 @@ void dump_pusch_stats(FILE *fd,PHY_VARS_gNB *gNB) { void clear_pusch_stats(PHY_VARS_gNB *gNB) { - for (int i=0;inumber_of_nr_ulsch_max;i++) + for (int i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) memset((void*)&gNB->ulsch_stats[i],0,sizeof(gNB->ulsch_stats[i])); } NR_gNB_SCH_STATS_t *get_ulsch_stats(PHY_VARS_gNB *gNB,NR_gNB_ULSCH_t *ulsch) { NR_gNB_SCH_STATS_t *stats=NULL; int first_free=-1; - for (int i=0;inumber_of_nr_ulsch_max;i++) { + for (int i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) { if (gNB->ulsch_stats[i].rnti == 0 && first_free == -1) { first_free = i; stats=&gNB->ulsch_stats[i]; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 136daf44aa..469218addc 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -626,7 +626,6 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PUSCH *pusch_vars[NUMBER_OF_NR_ULSCH_MAX]; NR_gNB_PUCCH_t **pucch; NR_gNB_SRS_t **srs; - NR_gNB_DLSCH_t *dlsch[NUMBER_OF_NR_DLSCH_MAX][2]; // Nusers times two spatial streams NR_gNB_ULSCH_t *ulsch[NUMBER_OF_NR_ULSCH_MAX]; // [Nusers times] /// statistics for DLSCH measurement collection NR_gNB_SCH_STATS_t dlsch_stats[NUMBER_OF_NR_SCH_STATS_MAX]; @@ -763,8 +762,6 @@ typedef struct PHY_VARS_gNB_s { notifiedFIFO_t resp_RU_tx; tpool_t threadPool; int nbDecode; - int number_of_nr_dlsch_max; - int number_of_nr_ulsch_max; void *scopeData; /// structure for analyzing high-level RT measurements rt_L1_profiling_t rt_L1_profiling; diff --git a/openair1/SCHED_NR/fapi_nr_l1.c b/openair1/SCHED_NR/fapi_nr_l1.c index c6b6f69126..b9f0d8e681 100644 --- a/openair1/SCHED_NR/fapi_nr_l1.c +++ b/openair1/SCHED_NR/fapi_nr_l1.c @@ -203,8 +203,8 @@ void nr_schedule_response(NR_Sched_Rsp_t *Sched_INFO){ AssertFatal(TX_req->pdu_list[pduIndex].num_TLV == 1, "TX_req->pdu_list[%d].num_TLV %d != 1\n", pduIndex,TX_req->pdu_list[pduIndex].num_TLV); uint8_t *sdu = (uint8_t *)TX_req->pdu_list[pduIndex].TLVs[0].value.direct; - AssertFatal(msgTx->num_pdsch_slot < gNB->number_of_nr_dlsch_max,"Number of PDSCH PDUs %d exceeded the limit %d\n", - msgTx->num_pdsch_slot,gNB->number_of_nr_dlsch_max); + AssertFatal(msgTx->num_pdsch_slot < NUMBER_OF_NR_DLSCH_MAX,"Number of PDSCH PDUs %d exceeded the limit %d\n", + msgTx->num_pdsch_slot, NUMBER_OF_NR_DLSCH_MAX); handle_nr_nfapi_pdsch_pdu(msgTx,&dl_tti_pdu->pdsch_pdu, sdu); } } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 78f75b9e51..6a1a8f7b36 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -552,7 +552,7 @@ void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { } } - for (int ULSCH_id=0;ULSCH_idnumber_of_nr_ulsch_max;ULSCH_id++) { + for (int ULSCH_id = 0; ULSCH_id < NUMBER_OF_NR_ULSCH_MAX; ULSCH_id++) { NR_gNB_ULSCH_t *ulsch = gNB->ulsch[ULSCH_id]; int harq_pid; NR_UL_gNB_HARQ_t *ulsch_harq; @@ -775,7 +775,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx) { } } - for (int ULSCH_id=0;ULSCH_idnumber_of_nr_ulsch_max;ULSCH_id++) { + for (int ULSCH_id = 0; ULSCH_id < NUMBER_OF_NR_ULSCH_MAX; ULSCH_id++) { NR_gNB_ULSCH_t *ulsch = gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq; From 358772429e5a627a73a5666400fef7d56de36aae Mon Sep 17 00:00:00 2001 From: francescomani Date: Mon, 19 Dec 2022 18:27:19 +0100 Subject: [PATCH 09/26] fix asan failures --- openair1/PHY/INIT/nr_init.c | 10 +++++++--- openair1/SIMULATION/NR_PHY/dlschsim.c | 7 +++++++ openair1/SIMULATION/NR_PHY/pbchsim.c | 7 +++++++ openair1/SIMULATION/NR_PHY/prachsim.c | 2 +- openair1/SIMULATION/NR_PHY/pucchsim.c | 7 +++++++ openair1/SIMULATION/NR_PHY/ulschsim.c | 8 +++++++- 6 files changed, 36 insertions(+), 5 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index b9e1139d14..b65068716b 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -765,6 +765,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_and_zero(gNB->nr_srs_info[id]->srs_generated_signal); free_and_zero(gNB->nr_srs_info[id]); } + free_and_zero(gNB->nr_srs_info); free_ul_reference_signal_sequences(); free_gnb_lowpapr_sequences(); @@ -1025,10 +1026,11 @@ void init_nr_transport(PHY_VARS_gNB *gNB) nb_ul_slots_period = fp->slots_per_frame; int buffer_ul_slots; // the UL channels are scheduled sl_ahead before they are transmitted - if (gNB->if_inst->sl_ahead > nb_slots_per_period) - buffer_ul_slots = nb_ul_slots_period + (gNB->if_inst->sl_ahead - nb_slots_per_period); + int slot_ahead = gNB->if_inst ? gNB->if_inst->sl_ahead : 6; + if (slot_ahead > nb_slots_per_period) + buffer_ul_slots = nb_ul_slots_period + (slot_ahead - nb_slots_per_period); else - buffer_ul_slots = (nb_ul_slots_period < gNB->if_inst->sl_ahead) ? nb_ul_slots_period : gNB->if_inst->sl_ahead; + buffer_ul_slots = (nb_ul_slots_period < slot_ahead) ? nb_ul_slots_period : slot_ahead; gNB->max_nb_pucch = MAX_MOBILES_PER_GNB * buffer_ul_slots; gNB->max_nb_srs = buffer_ul_slots << 1; // assuming at most 2 SRS per slot @@ -1070,9 +1072,11 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) for (int i = 0; i < gNB->max_nb_pucch; i++) free_gNB_pucch(gNB->pucch[i]); + free(gNB->pucch); for (int i = 0; i < gNB->max_nb_srs; i++) free_gNB_srs(gNB->srs[i]); + free(gNB->srs); for (int i = 0; i < NUMBER_OF_NR_ULSCH_MAX; i++) free_gNB_ulsch(&gNB->ulsch[i], fp->N_RB_UL); diff --git a/openair1/SIMULATION/NR_PHY/dlschsim.c b/openair1/SIMULATION/NR_PHY/dlschsim.c index bacd100114..f5de60a297 100644 --- a/openair1/SIMULATION/NR_PHY/dlschsim.c +++ b/openair1/SIMULATION/NR_PHY/dlschsim.c @@ -366,6 +366,8 @@ int main(int argc, char **argv) frame_parms->Ncp = extended_prefix_flag ? EXTENDED : NORMAL; crcTableInit(); nr_phy_config_request_sim(gNB, N_RB_DL, N_RB_DL, mu, Nid_cell,SSB_positions); + gNB->gNB_config.tdd_table.tdd_period.value = 6; + set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); phy_init_nr_gNB(gNB); //init_eNB_afterRU(); frame_length_complex_samples = frame_parms->samples_per_subframe; @@ -612,6 +614,11 @@ int main(int argc, char **argv) reset_DLSCH_struct(gNB, &msgDataTx); + int nb_slots_to_set = TDD_CONFIG_NB_FRAMES * (1 << mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; + for (int i = 0; i < nb_slots_to_set; ++i) + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list); + phy_free_nr_gNB(gNB); free(RC.gNB[0]); free(RC.gNB); diff --git a/openair1/SIMULATION/NR_PHY/pbchsim.c b/openair1/SIMULATION/NR_PHY/pbchsim.c index 7d0be09585..3b461a081e 100644 --- a/openair1/SIMULATION/NR_PHY/pbchsim.c +++ b/openair1/SIMULATION/NR_PHY/pbchsim.c @@ -471,6 +471,8 @@ int main(int argc, char **argv) frame_parms->freq_range = mu<2 ? nr_FR1 : nr_FR2; nr_phy_config_request_sim_pbchsim(gNB,N_RB_DL,N_RB_DL,mu,Nid_cell,SSB_positions); + gNB->gNB_config.tdd_table.tdd_period.value = 6; + set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); phy_init_nr_gNB(gNB); frame_parms->ssb_start_subcarrier = 12 * gNB->gNB_config.ssb_table.ssb_offset_point_a.value + ssb_subcarrier_offset; @@ -816,6 +818,11 @@ int main(int argc, char **argv) free_channel_desc_scm(gNB2UE); + int nb_slots_to_set = TDD_CONFIG_NB_FRAMES * (1 << mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; + for (int i = 0; i < nb_slots_to_set; ++i) + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list); + phy_free_nr_gNB(gNB); free(RC.gNB[0]); free(RC.gNB); diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index fd145c568f..c7b8474a46 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -672,9 +672,9 @@ int main(int argc, char **argv){ memcpy((void*)&ru->config,(void*)&RC.gNB[0]->gNB_config,sizeof(ru->config)); RC.nb_nr_L1_inst=1; + set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); phy_init_nr_gNB(gNB); nr_phy_init_RU(ru); - set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); // Configure UE UE = malloc(sizeof(PHY_VARS_NR_UE)); diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c index ca29219e31..498b31ec8f 100644 --- a/openair1/SIMULATION/NR_PHY/pucchsim.c +++ b/openair1/SIMULATION/NR_PHY/pucchsim.c @@ -404,6 +404,8 @@ int main(int argc, char **argv) cfg->carrier_config.num_tx_ant.value = n_tx; cfg->carrier_config.num_rx_ant.value = n_rx; nr_phy_config_request_sim(gNB,N_RB_DL,N_RB_DL,mu,Nid_cell,SSB_positions); + gNB->gNB_config.tdd_table.tdd_period.value = 6; + set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); phy_init_nr_gNB(gNB); /* RU handles rxdataF, and gNB just has a pointer. Here, we don't have an RU, * so we need to allocate that memory as well. */ @@ -721,6 +723,11 @@ int main(int argc, char **argv) free_channel_desc_scm(UE2gNB); term_freq_channel(); + int nb_slots_to_set = TDD_CONFIG_NB_FRAMES * (1 << mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; + for (int i = 0; i < nb_slots_to_set; ++i) + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list); + for (i = 0; i < n_rx; i++) free(gNB->common_vars.rxdataF[i]); phy_free_nr_gNB(gNB); diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 39ab2273e7..0227788877 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -418,7 +418,8 @@ int main(int argc, char **argv) gNB->frame_parms.nb_antennas_rx = n_rx; nr_phy_config_request_sim(gNB, N_RB_UL, N_RB_UL, mu, Nid_cell, SSB_positions); - + gNB->gNB_config.tdd_table.tdd_period.value = 6; + set_tdd_config_nr(&gNB->gNB_config, mu, 7, 6, 2, 4); phy_init_nr_gNB(gNB); //configure UE @@ -639,6 +640,11 @@ int main(int argc, char **argv) free_nr_ue_ul_harq(UE->ul_harq_processes, NR_MAX_ULSCH_HARQ_PROCESSES, UE->frame_parms.N_RB_UL, UE->frame_parms.nb_antennas_tx); + int nb_slots_to_set = TDD_CONFIG_NB_FRAMES * (1 << mu) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME; + for (int i = 0; i < nb_slots_to_set; ++i) + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list[i].max_num_of_symbol_per_slot_list); + free(gNB->gNB_config.tdd_table.max_tdd_periodicity_list); + term_nr_ue_signal(UE, 1); free(UE); From 817ee1a2173099e0e3cc4c429625643230117026 Mon Sep 17 00:00:00 2001 From: Gabriele Gemmi Date: Wed, 7 Dec 2022 17:24:25 +0000 Subject: [PATCH 10/26] Cleanup of external dependencies in the installs script Many useless dependencies have been removed or moved to the additional tools list of packages. Xforms and ue-ip module must now be built explicitly Removed support for Ubuntu 16. Addedd support for Debian 11 and Fedora 37 Squashed commits: removed all unused dependencies Applied patch from Robert + cleanup in the dependencies Applied patch from Robert + cleanup in the dependencies Moved xforms to optional packages, minor fixes remvoed ue_ip module from ue build fixes in the build script styling fixes Make RHEL8 ran-base build - Use EPEL 8 - Inside container there is no sudo, so remove superfluous -H - Install python3-mako Working build on fedora 36 Make build by install xxd Drop Ubuntu 16 support add pip3 again Correct software Drop useless software, maybe drop python too Fix: comment mistaken for package Remove Netinterfaces from optional packages (Ubuntu) Don't link xforms into 4G simulators: not needed Install xmlstarlet, required for physims Support for fedora 37 Remove irrelevant svn executable from physims Option to compile UE ip kernel module Install only relevant boost libs Removed --skip-broken Remove unnecessary vconfig,numpy,scipy dependencies Remove packages that have been installed (pkg-config), python, awk Remove unnecessary ntpsec package addedd support for debian 11 styiling fixes --- CMakeLists.txt | 9 +- cmake_targets/build_oai | 6 +- cmake_targets/tools/build_helper | 249 ++++++---------------------- docker/Dockerfile.base.rhel8.2 | 2 +- docker/Dockerfile.base.ubuntu18 | 2 +- docker/Dockerfile.phySim.rhel8.2 | 1 - executables/nr-softmodem.c | 1 - openair1/SIMULATION/LTE_PHY/dlsim.c | 29 ---- openair1/SIMULATION/LTE_PHY/ulsim.c | 15 -- 9 files changed, 63 insertions(+), 251 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a781499ce..60f1f12e94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2377,7 +2377,7 @@ target_link_libraries(nr-softmodem PRIVATE -Wl,--end-group z dl) target_link_libraries(nr-softmodem PRIVATE ${LIBXML2_LIBRARIES}) -target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} sctp ${XFORMS_LIBRARIES} ${CMAKE_DL_LIBS} ${ATLAS_LIBRARIES}) +target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} sctp ${CMAKE_DL_LIBS} ${ATLAS_LIBRARIES}) target_link_libraries(nr-softmodem PRIVATE ${LIB_LMS_LIBRARIES}) target_link_libraries(nr-softmodem PRIVATE ${T_LIB}) target_link_libraries(nr-softmodem PRIVATE asn1_nr_rrc asn1_lte_rrc) @@ -2424,7 +2424,7 @@ target_link_libraries(nr-uesoftmodem PRIVATE -Wl,--end-group z dl) target_link_libraries(nr-uesoftmodem PRIVATE ${LIBXML2_LIBRARIES}) -target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${XFORMS_LIBRARIES} ${CMAKE_DL_LIBS} ${ATLAS_LIBRARIES}) +target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt crypt ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${CMAKE_DL_LIBS} ${ATLAS_LIBRARIES}) target_link_libraries(nr-uesoftmodem PRIVATE ${LIB_LMS_LIBRARIES}) target_link_libraries(nr-uesoftmodem PRIVATE ${T_LIB}) target_link_libraries(nr-uesoftmodem PRIVATE asn1_nr_rrc asn1_lte_rrc) @@ -2465,7 +2465,7 @@ add_executable(rftest ${OPENAIR_DIR}/executables/softmodem-common.c ${SHLIB_LOADER_SOURCES} ) -target_link_libraries(rftest PRIVATE minimal_lib CONFIG_LIB PHY_NR_COMMON UTIL pthread dl m forms ${T_LIB} ) +target_link_libraries(rftest PRIVATE minimal_lib CONFIG_LIB PHY_NR_COMMON UTIL pthread dl m ${T_LIB} ) target_link_libraries(rftest PRIVATE asn1_lte_rrc asn1_nr_rrc) add_executable(polartest @@ -2631,14 +2631,13 @@ foreach(myExe dlsim dlsim_tm7 ulsim pbchsim scansim mbmssim pdcchsim pucchsim pr ${OPENAIR_DIR}/common/utils/lte/ue_power.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c ${OPENAIR1_DIR}/PHY/INIT/lte_param_init.c - ${XFORMS_SOURCE} ${T_SOURCE} ${SHLIB_LOADER_SOURCES} ${NFAPI_USER_DIR}/nfapi.c ) target_link_libraries (${myExe} PRIVATE -Wl,--start-group SIMU UTIL SCHED_LIB SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_NR_COMMON PHY PHY_UE PHY_RU ITTI -Wl,--end-group - pthread m rt CONFIG_LIB ${ATLAS_LIBRARIES} ${XFORMS_LIBRARIES} ${T_LIB} dl + pthread m rt CONFIG_LIB ${ATLAS_LIBRARIES} ${T_LIB} dl ) target_link_libraries(${myExe} PRIVATE asn1_nr_rrc asn1_lte_rrc) diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 720d0f0c96..9fb2d64e4a 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -285,6 +285,10 @@ function main() { --UE-gen-nvram) gen_nvram_path=$(readlink -f "$2") shift 2;; + --UE-ip) + UE_ip=1 + echo_info "Will compile UE" + shift;; -w | --hardware) # Use OAI_USRP as the key word USRP is used inside UHD driver case "$2" in @@ -588,7 +592,7 @@ function main() { echo_info "Compiling UE specific part" - if [ $IS_CONTAINER -eq 0 ] + if [ $UE_ip -eq 1 ] then echo_info "Building ue_ip module" compilations $BUILD_DIR ue_ip diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 029d25b236..3efac9da50 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -32,8 +32,8 @@ OS_DISTRO=$(grep "^ID=" /etc/os-release | sed "s/ID=//" | sed "s/\"//g") OS_RELEASE=$(grep "^VERSION_ID=" /etc/os-release | sed "s/VERSION_ID=//" | sed "s/\"//g") case "$OS_DISTRO" in fedora) OS_BASEDISTRO="fedora"; INSTALLER="dnf"; CMAKE="cmake" ;; - rhel) OS_BASEDISTRO="fedora"; INSTALLER="yum --skip-broken"; CMAKE="cmake3" ;; - centos) OS_BASEDISTRO="centos"; INSTALLER="yum --skip-broken"; CMAKE="cmake3" ;; + rhel) OS_BASEDISTRO="fedora"; INSTALLER="yum"; CMAKE="cmake3" ;; + centos) OS_BASEDISTRO="centos"; INSTALLER="yum"; CMAKE="cmake3" ;; debian) OS_BASEDISTRO="debian"; INSTALLER="apt-get"; CMAKE="cmake" ;; ubuntu) OS_BASEDISTRO="debian"; INSTALLER="apt-get"; CMAKE="cmake" ;; esac @@ -90,7 +90,6 @@ echo_info() { cecho "$*" $blue ;} # If we can't check the distribution, it returns "Unknown" # This function return always true as exit code by design # Examples: -# ubuntu16.04 # debian8.5 get_distribution_release() { if [[ ! -z "$OS_DISTRO$OS_RELEASE" ]]; then @@ -107,9 +106,9 @@ check_supported_distribution() { "ubuntu21.04") return 0 ;; "ubuntu20.04") return 0 ;; "ubuntu18.04") return 0 ;; - "ubuntu16.04") return 0 ;; - "fedora35") return 0 ;; + "debian11") return 0 ;; "fedora36") return 0 ;; + "fedora37") return 0 ;; "rhel7") return 0 ;; "rhel7.6") return 0 ;; "rhel7.7") return 0 ;; @@ -306,7 +305,8 @@ install_usrp_uhd_driver_from_source(){ } check_install_usrp_uhd_driver(){ - if [[ "$OS_DISTRO" == "ubuntu" ]]; then + if [[ "$OS_BASEDISTRO" == "debian" ]]; then + boost_libs_ubuntu="libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-program-options-dev libboost-thread-dev libboost-test-dev libboost-regex-dev" #first we remove old installation $SUDO apt-get remove uhd -y || true $SUDO apt-get remove uhd-host -y || true @@ -323,8 +323,8 @@ check_install_usrp_uhd_driver(){ fi v=$(lsb_release -cs) $SUDO apt-add-repository --remove "deb http://files.ettus.com/binaries/uhd/repo/uhd/ubuntu/$v $v main" - if [[ -v BUILD_UHD_FROM_SOURCE ]]; then - $SUDO apt-get install -y libboost-all-dev libusb-1.0-0-dev doxygen python3-docutils python3-mako python3-numpy python3-requests python3-setuptools ninja-build + if [[ -v BUILD_UHD_FROM_SOURCE ]] || [[ "$OS_DISTRO" == "debian" ]]; then + $SUDO apt-get install -y $boost_libs_ubuntu libusb-1.0-0-dev doxygen python3-docutils python3-mako python3-numpy python3-requests python3-setuptools ninja-build install_usrp_uhd_driver_from_source return fi @@ -346,22 +346,20 @@ check_install_usrp_uhd_driver(){ x=$((x + 1)) done $SUDO apt-get update - $SUDO apt-get -y install python-tk libboost-all-dev libusb-1.0-0-dev + $SUDO apt-get -y install python-tk $boost_libs_ubuntu libusb-1.0-0-dev case "$(get_distribution_release)" in - "ubuntu16.04") - $SUDO apt-get -y install libuhd-dev libuhd3.15.0 uhd-host - ;; "ubuntu18.04" | "ubuntu20.04" | "ubuntu22.04") $SUDO apt-get -y install libuhd-dev libuhd4.2.0 uhd-host ;; esac elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then + boost_libs_fedora="boost-chrono boost-date-time boost-filesystem boost-program-options boost-thread boost-test boost-regex boost-devel" if [ $IS_CONTAINER -eq 0 ] then - $SUDO $INSTALLER -y install python boost libusb-devel libusbx-devel boost-devel python-mako python-docutils $CMAKE - $SUDO -H pip install requests + $SUDO $INSTALLER -y install python3 $boost_libs_fedora libusb-devel libusbx-devel python3-mako python3-docutils python3-pip $CMAKE + $SUDO pip3 install requests else - $SUDO $INSTALLER -y install boost boost-devel $CMAKE + $SUDO $INSTALLER -y install $boost_libs_fedora $CMAKE $SUDO pip3 install mako requests fi if [[ "$OS_DISTRO" == "rhel" ]] || [[ "$OS_DISTRO" == "centos" ]]; then @@ -419,6 +417,8 @@ install_bladerf_driver_from_source(){ $SUDO ldconfig echo "Downloading FPGA and firmware images" cd /tmp/bladeRF + #wget has been removed from default dependencies, so we must be sure that's installed + $SUDO $INSTALLER -y install wget wget --tries=3 --retry-connrefused https://www.nuand.com/fx3/bladeRF_fw_latest.img wget --tries=3 --retry-connrefused https://www.nuand.com/fpga/hostedx40-latest.rbf sudo mkdir -p /usr/share/Nuand/bladeRF @@ -529,110 +529,42 @@ check_install_soapy () { check_install_additional_tools (){ $SUDO $INSTALLER update -y local optional_packages="" - if [[ "$OS_DISTRO" == "ubuntu" ]]; then + if [[ "$OS_BASEDISTRO" == "debian" ]]; then case "$(get_distribution_release)" in - "ubuntu16.04"| "ubuntu18.04") - optional_packages="python-dev python-pexpect python-numpy python-scipy python-matplotlib ctags" + "ubuntu18.04") + optional_packages="python-dev python-pip python-pyroute2 python python-numpy python-scipy python-matplotlib ctags" ;; - "ubuntu20.04" | "ubuntu21.04" | "ubuntu22.04" ) - optional_packages="python3 python3-pip python3-dev python3-scipy python3-matplotlib universal-ctags" + "ubuntu20.04" | "ubuntu21.04" | "ubuntu22.04" | "debian11" ) + optional_packages="python3 python3-pip python3-dev python3-scipy python3-matplotlib python3-pyroute2 universal-ctags" ;; esac PACKAGE_LIST="\ - check \ - dialog \ - dkms \ - gawk \ - libboost-all-dev \ + doxygen \ libpthread-stubs0-dev \ - openvpn \ - pkg-config \ - sshfs \ - swig \ tshark \ uml-utilities \ - unzip \ - valgrind \ - vlan \ - ntpdate \ iperf3 \ - android-tools-adb \ - wvdial \ - sshpass \ - bc \ - ntp" + libforms-bin \ + libforms-dev \ + xmlstarlet" elif [[ "$OS_DISTRO" == "rhel" ]] || [[ "$OS_DISTRO" == "centos" ]]; then PACKAGE_LIST="\ - check \ - dialog \ - dkms \ - gawk \ - boost-devel \ - openvpn \ - pkgconfig \ - pexpect \ - sshfs \ - swig \ - wireshark \ - unzip \ - valgrind \ - vconfig \ + doxygen \ ctags \ - ntpdate \ iperf3 \ - wvdial \ - numpy \ - sshpass \ - python2-paramiko \ - python-pyroute2 \ - python-netifaces \ - scipy \ - python-matplotlib" + xforms \ + xforms-devel \ + xmlstarlet" elif [[ "$OS_DISTRO" == "fedora" ]]; then PACKAGE_LIST=" \ - check \ - dialog \ - dkms \ - gawk \ - boost-devel \ - openvpn \ - pkgconfig \ - python-pexpect \ - sshfs \ - swig \ - wireshark \ - unzip \ - valgrind \ - vconfig \ + doxygen \ ctags \ - ntpsec \ iperf3 \ - wvdial \ - python-numpy \ - sshpass \ - python-paramiko \ - python-pyroute2 \ - python-netifaces \ - python-scipy \ - python-matplotlib" + xforms \ + xforms-devel \ + xmlstarlet" fi $SUDO $INSTALLER install -y $PACKAGE_LIST $optional_packages - - #The packages below are already installed for Redhat distros (RHEL, CentOS, Fedora) - if [[ "$OS_DISTRO" == "ubuntu" ]]; then - $SUDO -H pip install paramiko - $SUDO -H pip install pyroute2 colorama - log_netiface=$OPENAIR_DIR/cmake_targets/log/netiface_install_log.txt - echo_info "Installing Netinterfaces package. The logfile for installation is in $log_netiface" - ( - $SUDO rm -fr /tmp/netifaces-0.10.4.tar.gz /tmp/netifaces - wget --tries=3 --retry-connrefused -P /tmp https://pypi.python.org/packages/18/fa/dd13d4910aea339c0bb87d2b3838d8fd923c11869b1f6e741dbd0ff3bc00/netifaces-0.10.4.tar.gz - tar -xzvf /tmp/netifaces-0.10.4.tar.gz -C /tmp - cd /tmp/netifaces-0.10.4 - $SUDO python setup.py install - cd - - ) >& $log_netiface - fi } check_install_oai_software() { @@ -642,91 +574,48 @@ check_install_oai_software() { exit 1 fi $SUDO $INSTALLER update -y - if [[ "$OS_DISTRO" == "ubuntu" ]]; then - local LAPACK_LIBNAME="liblapack.so" - local LAPACK_TARGET="/usr/lib/atlas-base/atlas/liblapack.so" + if [[ "$OS_BASEDISTRO" == "debian" ]]; then $SUDO apt install -y software-properties-common case "$(get_distribution_release)" in - "ubuntu16.04") - specific_packages="libtasn1-6-dev libgnutls-dev libatlas-dev iproute libconfig8-dev iptables-dev libgcrypt11-dev python-pip pydb python guile-2.0-dev" - ;; "ubuntu18.04") - specific_packages="libtasn1-6-dev libgnutls28-dev iproute2 libconfig-dev iptables-dev libgcrypt11-dev python-pip pydb python guile-2.0-dev" - LAPACK_LIBNAME="liblapack.so-x86_64-linux-gnu" - LAPACK_TARGET="/usr/lib/x86_64-linux-gnu/atlas/liblapack.so" + specific_packages="libgcrypt11-dev guile-2.0-dev" ;; "ubuntu20.04") - specific_packages="libtasn1-6-dev libgnutls28-dev iproute2 libconfig-dev python guile-2.0-dev" - LAPACK_LIBNAME="liblapack.so-x86_64-linux-gnu" - LAPACK_TARGET="/usr/lib/x86_64-linux-gnu/atlas/liblapack.so" + specific_packages="guile-2.0-dev" ;; "ubuntu21.04") - specific_packages="libtasn1-6-dev libgnutls28-dev iproute2 libconfig-dev python guile-2.0-dev" - LAPACK_LIBNAME="liblapack.so-x86_64-linux-gnu" - LAPACK_TARGET="/usr/lib/x86_64-linux-gnu/atlas/liblapack.so" + specific_packages="guile-2.0-dev" ;; "ubuntu22.04") - specific_packages="libtasn1-6-dev libgnutls28-dev iproute2 libconfig-dev python2 guile-2.2-dev" - LAPACK_LIBNAME="liblapack.so-x86_64-linux-gnu" - LAPACK_TARGET="/usr/lib/x86_64-linux-gnu/atlas/liblapack.so" + specific_packages="guile-2.2-dev" + ;; + "debian11") + specific_packages="guile-2.2-dev libz-dev" ;; esac $SUDO apt-get install -y \ $specific_packages \ - autoconf \ automake \ - bison \ build-essential \ $CMAKE \ - cmake-curses-gui \ - curl \ ninja-build \ - doxygen \ - doxygen-gui \ - texlive-latex-base \ - ethtool \ - flex \ - g++ \ - gcc \ - gdb \ + pkg-config \ git \ - graphviz \ - gtkwave \ - iperf \ - iptables \ libatlas-base-dev \ libblas-dev \ liblapack-dev \ liblapacke-dev \ - libffi-dev \ - libforms-bin \ - libforms-dev \ - libgmp-dev \ - libgtk-3-dev \ - libidn2-0-dev \ - libidn11-dev \ - libmysqlclient-dev \ - libpython2.7-dev \ - libreadline-dev \ - libsctp1 \ + libreadline-dev \ + libgnutls28-dev \ + libconfig-dev \ libsctp-dev \ libssl-dev \ libtool \ - libusb-1.0-0-dev \ - libxml2 \ - libxml2-dev \ libxslt1-dev \ - octave-signal \ - openssh-client \ - openssh-server \ + libtasn1-6-dev \ + patch \ openssl \ - subversion \ - xmlstarlet \ - wget \ - libxpm-dev \ - libboost-all-dev - - $SUDO update-alternatives --set "$LAPACK_LIBNAME" "$LAPACK_TARGET" + xxd $SUDO apt-get install -y nettle-dev nettle-bin elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then @@ -735,74 +624,40 @@ check_install_oai_software() { echo "EPEL repos already present. Good." else echo "EPEL repos not present. Installing them." - $SUDO $INSTALLER install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm + $SUDO $INSTALLER install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm fi - $SUDO $INSTALLER install -y python-epdb vim-common fi $SUDO $INSTALLER install -y \ - autoconf \ automake \ - bc \ - bison \ $CMAKE \ - doxygen \ - ethtool \ - flex \ - g++ \ gcc \ - gdb \ + gcc-c++ \ git \ - graphviz \ - gtkwave \ guile-devel \ - iperf \ - iproute \ - iptables \ - iptables-devel \ atlas-devel \ - blas-devel \ + pkgconfig \ libconfig-devel \ libffi-devel \ - xforms \ - xforms-devel \ libgcrypt-devel \ - gmp-devel \ - gtk3-devel \ - libidn2-devel \ - libidn-devel \ - mariadb-devel \ lksctp-tools \ lksctp-tools-devel \ openssl-devel \ libtasn1 \ libtool \ - libusb-devel \ - libxml2 \ - libxml2-devel \ libxslt-devel \ ninja-build \ make \ - openssh-clients \ - openssh-server \ openssl \ patch \ - psmisc \ - python \ readline-devel \ - subversion \ - xmlstarlet \ - python-pip \ - wget \ - kernel-headers \ - kernel-devel \ nettle-devel \ gnutls-devel \ - libXpm-devel \ lapack \ lapack-devel \ blas \ - blas-devel + blas-devel \ + vim-common fi install_asn1c_from_source $1 diff --git a/docker/Dockerfile.base.rhel8.2 b/docker/Dockerfile.base.rhel8.2 index ae41956379..17a559379d 100644 --- a/docker/Dockerfile.base.rhel8.2 +++ b/docker/Dockerfile.base.rhel8.2 @@ -68,5 +68,5 @@ COPY . . RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai -I -w USRP && \ + ./build_oai -I --install-optional-packages -w USRP && \ rm /etc/pki/entitlement/*pem diff --git a/docker/Dockerfile.base.ubuntu18 b/docker/Dockerfile.base.ubuntu18 index 1a19304c10..7cf2ecceaa 100644 --- a/docker/Dockerfile.base.ubuntu18 +++ b/docker/Dockerfile.base.ubuntu18 @@ -57,4 +57,4 @@ COPY . . RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai -I -w USRP + ./build_oai -I -w USRP --install-optional-packages diff --git a/docker/Dockerfile.phySim.rhel8.2 b/docker/Dockerfile.phySim.rhel8.2 index c3f6d258b3..62378bf7dd 100644 --- a/docker/Dockerfile.phySim.rhel8.2 +++ b/docker/Dockerfile.phySim.rhel8.2 @@ -101,7 +101,6 @@ WORKDIR /usr/bin/ COPY --from=phy-sim-build \ /usr/bin/killall \ /usr/bin/xmlstarlet \ - /usr/bin/svn \ ./ # Copy the relevant configuration files for phySim diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 2cbea144cf..dddd1da641 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -68,7 +68,6 @@ unsigned short config_frames[4] = {2,9,11,13}; #include #include "PHY/TOOLS/phy_scope_interface.h" #include "PHY/TOOLS/nr_phy_scope.h" -#include "stats.h" #include "nr-softmodem.h" #include "executables/softmodem-common.h" #include "executables/thread-common.h" diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index c4ff82c22b..44e79121b2 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -49,7 +49,6 @@ #include "PHY/MODULATION/modulation_common.h" #include "PHY/MODULATION/modulation_eNB.h" #include "PHY/MODULATION/modulation_UE.h" -#include "PHY/TOOLS/lte_phy_scope.h" #include "SCHED/sched_eNB.h" #include "SCHED_UE/sched_UE.h" #include "SIMULATION/TOOLS/sim.h" @@ -493,7 +492,6 @@ int test_perf=0; int n_frames; int n_ch_rlz = 1; int rx_sample_offset = 0; -int xforms=0; int dump_table=0; int loglvl=OAILOG_INFO; int mcs1=0,mcs2=0,mcs_i=0,dual_stream_UE = 0,awgn_flag=0; @@ -565,7 +563,6 @@ int main(int argc, char **argv) { short *uncoded_ber_bit=NULL; int osf=1; frame_t frame_type = FDD; - FD_lte_phy_scope_ue *form_ue = NULL; char title[255]; int numCCE=0; //int dci_length_bytes=0,dci_length=0; @@ -679,7 +676,6 @@ int main(int argc, char **argv) { { "uEdual", "Enables the Interference Aware Receiver for TM5 (default is normal receiver)",0, iptr:NULL, defintval:0, TYPE_INT, 0 }, { "xTransmission","Transmission mode (1,2,6,7 for the moment)",0, iptr:NULL, defintval:25, TYPE_INT, 0 }, { "yn_tx_phy","Number of TX antennas used in eNB",0, iptr:NULL, defintval:25, TYPE_INT, 0 }, - { "XForms", "Display the soft scope", PARAMFLAG_BOOL, iptr:&xforms, defintval:0, TYPE_INT, 0 }, { "Yperfect_ce","Perfect CE", PARAMFLAG_BOOL, iptr:&perfect_ce, defintval:0, TYPE_INT, 0 }, { "Zdump", "dump table",PARAMFLAG_BOOL, iptr:&dump_table, defintval:0, TYPE_INT, 0 }, { "Loglvl", "log level",0, iptr:&loglvl, defintval:OAILOG_INFO, TYPE_INT, 0 }, @@ -952,24 +948,6 @@ int main(int argc, char **argv) { AssertFatal(NB_RB <= N_RB_DL,"illegal NB_RB %d\n",NB_RB); } - if (xforms==1) { - fl_initialize (&argc, argv, NULL, 0, 0); - form_ue = create_lte_phy_scope_ue(); - sprintf (title, "LTE PHY SCOPE eNB"); - fl_show_form (form_ue->lte_phy_scope_ue, FL_PLACE_HOTSPOT, FL_FULLBORDER, title); - - if (!dual_stream_UE==0) { - if (UE) { - UE->use_ia_receiver = 1; - fl_set_button(form_ue->button_0,1); - fl_set_object_label(form_ue->button_0, "IA Receiver ON"); - fl_set_object_color(form_ue->button_0, FL_GREEN, FL_GREEN); - } else { - printf("UE is NULL\n"); - exit(-1); - } - } - } if (transmission_mode==5) { n_users = 2; @@ -1792,13 +1770,6 @@ int main(int argc, char **argv) { // UE->dlsch[UE->current_thread_id[subframe]][0][0]->harq_processes[0]->round++; } - if (xforms==1) { - phy_scope_UE(form_ue, - UE, - eNB_id, - 0,// UE_id - subframe); - } UE->proc.proc_rxtx[UE->current_thread_id[subframe]].frame_rx++; } //round diff --git a/openair1/SIMULATION/LTE_PHY/ulsim.c b/openair1/SIMULATION/LTE_PHY/ulsim.c index c03d081387..7c75c27378 100644 --- a/openair1/SIMULATION/LTE_PHY/ulsim.c +++ b/openair1/SIMULATION/LTE_PHY/ulsim.c @@ -42,7 +42,6 @@ #include "PHY/INIT/phy_init.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" #include "PHY/LTE_UE_TRANSPORT/transport_proto_ue.h" -#include "PHY/TOOLS/lte_phy_scope.h" #include "SCHED/sched_eNB.h" #include "SCHED_UE/sched_UE.h" #include "SIMULATION/TOOLS/sim.h" @@ -69,18 +68,15 @@ double cpuf; #include channel_desc_t *eNB2UE[NUMBER_OF_eNB_MAX][NUMBER_OF_UE_MAX]; channel_desc_t *UE2eNB[NUMBER_OF_UE_MAX][NUMBER_OF_eNB_MAX]; -//Added for PHY abstractionopenair1/PHY/TOOLS/lte_phy_scope.h node_desc_t *enb_data[NUMBER_OF_eNB_MAX]; node_desc_t *ue_data[NUMBER_OF_UE_MAX]; //double sinr_bler_map[MCS_COUNT][2][16]; extern uint16_t beta_ack[16],beta_ri[16],beta_cqi[16]; //extern char* namepointer_chMag ; -int xforms=0; THREAD_STRUCT thread_struct; nfapi_ue_release_request_body_t release_rntis; -FD_lte_phy_scope_enb *form_enb; char title[255]; /*the following parameters are used to control the processing times*/ @@ -435,7 +431,6 @@ int main(int argc, char **argv) { { "delay_chan", "Channel delay",0, iptr:&delay, defintval:0, TYPE_INT, 0 }, { "Doppler", "Maximum doppler shift",0, dblptr:&maxDoppler, defdblval:0.0, TYPE_DOUBLE, 0 }, { "Zdump", "dump table",PARAMFLAG_BOOL, iptr:&dump_table, defintval:0, TYPE_INT, 0 }, - { "Forms", "Display the soft scope", PARAMFLAG_BOOL, iptr:&xforms, defintval:0, TYPE_INT, 0 }, { "Lparallel", "Enable parallel execution",0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0 }, { "Iterations", "Number of iterations of turbo decoder", 0, iptr:&max_turbo_iterations, defintval:4, TYPE_INT, 0 }, { "Performance", "Display CPU perfomance of each L1 piece", PARAMFLAG_BOOL, iptr:NULL, defintval:0, TYPE_INT, 0 }, @@ -695,13 +690,6 @@ int main(int argc, char **argv) { fprintf(csv_fdUL,"data_all%d=[",mcs); } - if (xforms==1) { - fl_initialize (&argc, argv, NULL, 0, 0); - form_enb = create_lte_phy_scope_enb(); - sprintf (title, "LTE PHY SCOPE eNB"); - fl_show_form (form_enb->lte_phy_scope_enb, FL_PLACE_HOTSPOT, FL_FULLBORDER, title); - } - UE->pdcch_vars[0][0]->crnti = 14; UE->frame_parms.soundingrs_ul_config_common.enabled_flag = srs_flag; UE->frame_parms.soundingrs_ul_config_common.srs_BandwidthConfig = 2; @@ -1265,9 +1253,6 @@ int main(int argc, char **argv) { if ((errs[0]>=100) && (trials>(n_frames/2))) break; - if (xforms==1) - phy_scope_eNB(form_enb,eNB,0); - double t_tx = inMicroS(UE->phy_proc_tx.p_time); double t_tx_ifft = inMicroS(UE->ofdm_mod_stats.p_time); double t_tx_mod = inMicroS(UE->ulsch_modulation_stats.p_time); From 344f8b75e66314a6914fe6def9bf035c3ecb0465 Mon Sep 17 00:00:00 2001 From: Cedric Roux Date: Tue, 31 Jan 2023 17:29:42 +0100 Subject: [PATCH 11/26] bugfix: try to handle USRP devices correctly to avoid crashes Some problems when we release the device: - Calling the destructors is wrong, we should set the pointers to NULL. These are shared pointers, this is C++. - We have to stop rx by doing a STREAM_MODE_STOP_CONTINUOUS. - The end-of-burst for tx has been simplified. This problem when we acquire the device: - Calling get_rx_stream() many times seems to be problematic. We now should have a better behavior with UHD 4.2 and N310. (Hopefully with others too.) No crash seen so far. --- radio/USRP/USERSPACE/LIB/usrp_lib.cpp | 49 +++++++++++++++++---------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/radio/USRP/USERSPACE/LIB/usrp_lib.cpp b/radio/USRP/USERSPACE/LIB/usrp_lib.cpp index 1f6fbd34b1..c7a6b94fa8 100644 --- a/radio/USRP/USERSPACE/LIB/usrp_lib.cpp +++ b/radio/USRP/USERSPACE/LIB/usrp_lib.cpp @@ -304,21 +304,31 @@ static int trx_usrp_start(openair0_device *device) { return 0; } -static void trx_usrp_send_end_of_burst(usrp_state_t *s) { +static void trx_usrp_send_end_of_burst(usrp_state_t *s) +{ // if last packet sent was end of burst no need to do anything. otherwise send end of burst packet if (s->tx_md.end_of_burst) return; - s->tx_md.end_of_burst = true; s->tx_md.start_of_burst = false; s->tx_md.has_time_spec = false; + s->tx_stream->send("", 0, s->tx_md); +} - int32_t dummy = 0; - std::vector buffs; - for (size_t ch = 0; ch < s->tx_stream->get_num_channels(); ch++) - buffs.push_back(&dummy); // same buffer for each channel +static void trx_usrp_finish_rx(usrp_state_t *s) +{ + /* finish rx by sending STREAM_MODE_STOP_CONTINUOUS */ + uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + s->rx_stream->issue_stream_cmd(cmd); - s->tx_stream->send(buffs, 0, s->tx_md); + /* collect all remaining samples (not sure if needed) */ + size_t samples; + uint8_t buf[1024]; + std::vector buff_ptrs; + for (size_t i = 0; i < s->usrp->get_rx_num_channels(); i++) buff_ptrs.push_back(buf); + do { + samples = s->rx_stream->recv(buff_ptrs, sizeof(buf)/4, s->rx_md); + } while (samples > 0); } static void trx_usrp_write_reset(openair0_thread_t *wt); @@ -339,10 +349,13 @@ static void trx_usrp_end(openair0_device *device) { if (usrp_tx_thread != 0) trx_usrp_write_reset(&device->write_thread); + /* finish tx and rx */ trx_usrp_send_end_of_burst(s); - s->tx_stream->~tx_streamer(); - s->rx_stream->~rx_streamer(); - s->usrp->~multi_usrp(); + trx_usrp_finish_rx(s); + /* set tx_stream, rx_stream, and usrp to NULL to clear/free them */ + s->tx_stream = NULL; + s->rx_stream = NULL; + s->usrp = NULL; free(s); device->priv = NULL; device->trx_start_func = NULL; @@ -1391,8 +1404,14 @@ extern "C" { // create tx & rx streamer uhd::stream_args_t stream_args_rx("sc16", "sc16"); + for (int i = 0; irx_stream = s->usrp->get_rx_stream(stream_args_rx); + int samples=openair0_cfg[0].sample_rate; - int max=s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps(); + int max=s->rx_stream->get_max_num_samps(); samples/=10000; LOG_I(HW,"RF board max packet size %u, size for 100µs jitter %d \n", max, samples); @@ -1401,14 +1420,8 @@ extern "C" { } LOG_I(HW,"rx_max_num_samps %zu\n", - s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps()); + s->rx_stream->get_max_num_samps()); - for (int i = 0; irx_stream = s->usrp->get_rx_stream(stream_args_rx); uhd::stream_args_t stream_args_tx("sc16", "sc16"); for (int i = 0; i Date: Thu, 2 Feb 2023 15:44:25 +0100 Subject: [PATCH 12/26] Remove legacy dashboard --- ci-scripts/Jenkinsfile-git-dashboard | 48 -- ci-scripts/Jenkinsfile-tmp-multi-enb-nsa | 13 - ci-scripts/ran_dashboard/Hdashboard.py | 520 +----------------- ci-scripts/ran_dashboard/mr_styles.css | 136 ----- ci-scripts/ran_dashboard/ran_dashboard.py | 405 -------------- .../ran_dashboard/ran_dashboard_cfg.yaml | 26 - ci-scripts/ran_dashboard/readme.txt | 3 - ci-scripts/ran_dashboard/sqlconnect.py | 107 ---- ci-scripts/ran_dashboard/test_styles.css | 103 ---- 9 files changed, 9 insertions(+), 1352 deletions(-) delete mode 100644 ci-scripts/Jenkinsfile-git-dashboard delete mode 100755 ci-scripts/ran_dashboard/mr_styles.css delete mode 100644 ci-scripts/ran_dashboard/ran_dashboard.py delete mode 100644 ci-scripts/ran_dashboard/ran_dashboard_cfg.yaml delete mode 100644 ci-scripts/ran_dashboard/readme.txt delete mode 100644 ci-scripts/ran_dashboard/sqlconnect.py delete mode 100755 ci-scripts/ran_dashboard/test_styles.css diff --git a/ci-scripts/Jenkinsfile-git-dashboard b/ci-scripts/Jenkinsfile-git-dashboard deleted file mode 100644 index 87e781fe8a..0000000000 --- a/ci-scripts/Jenkinsfile-git-dashboard +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/groovy -/* - * 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 - */ - -// Template Jenkins Declarative Pipeline script to run Test w/ RF HW - -// Location of the python executor node shall be in the same subnet as the others servers -def pythonExecutor = params.pythonExecutor - - -pipeline { - agent { - label pythonExecutor - } - stages { - stage ("gDashboard") { - steps { - script { - dir ("ci-scripts/ran_dashboard") { - //retrieve MR data from gitlab / mySQL db, build HTML pages and load them to AWS S3 bucket (configured as static web page hosting) - //deprecated method : sh returnStdout: true, script: 'python3 ran_dashboard.py' - sh returnStdout: true, script: 'python3 Hdashboard.py' - } - } - } - } - } -} - - diff --git a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa b/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa index 1d977f00d3..43d6372a33 100644 --- a/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa +++ b/ci-scripts/Jenkinsfile-tmp-multi-enb-nsa @@ -376,19 +376,6 @@ pipeline { } } } - stage ("Result Update"){ - when { - expression { params.DataBaseHost != "none" } - } - agent {label DataBaseHost} - steps { - script { - dir ('ci-scripts/ran_dashboard') { - sh "python3 Hdashboard.py testevent ${params.eNB_MR} " - } - } - } - } } } } diff --git a/ci-scripts/ran_dashboard/Hdashboard.py b/ci-scripts/ran_dashboard/Hdashboard.py index bfa07557bd..625baa8f46 100644 --- a/ci-scripts/ran_dashboard/Hdashboard.py +++ b/ci-scripts/ran_dashboard/Hdashboard.py @@ -32,488 +32,34 @@ #Author Remi -import boto3 import shlex import subprocess import json #json structures -import datetime #now() and date formating -from datetime import datetime -import re import gitlab -import yaml -import os -import time import sys -from sqlconnect import SQLConnect - #----------------------------------------------------------- # Class Declaration #----------------------------------------------------------- class Dashboard: def __init__(self): - - - #init with data sources : git, yaml config file, test results databases + #init with data sources : git, test results databases print("Collecting Data") cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """ self.git = self.__getGitData(cmd) #git data from Gitlab - self.tests = self.__loadCfg('ran_dashboard_cfg.yaml') #tests table setup from yaml - self.db = self.__loadFromDB() #test results from database self.mr_list=[] #mr list in string format for x in range(len(self.git)): self.mr_list.append(str(self.git[x]['iid'])) - def __loadCfg(self,yaml_file): - with open(yaml_file,'r') as f: - tests = yaml.load(f) - return tests - def __getGitData(self,cmd): - #cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """ process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE) output = process.stdout.readline() tmp=output.decode("utf-8") d = json.loads(tmp) return d - def __loadFromDB(self): - mr_list=[] - for x in range(len(self.git)): - mr_list.append(str(self.git[x]['iid'])) - mydb=SQLConnect() - for MR in mr_list: - mydb.get(MR) - mydb.close_connection() - return mydb.data - - def singleMR_initHTML(self, date): - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('Test Dashboard\n') - self.f_html.write('\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('
OAI RAN TEST Status Dashboard
Update : '+date+'
\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - - - def Test_initHTML(self, date): - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('Test Dashboard\n') - self.f_html.write('\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('
OAI RAN TEST Status Dashboard
Update : '+date+'
\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - - def Test_terminateHTML(self): - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.close() - - - def MR_initHTML(self,date): - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('MR Dashboard\n') - self.f_html.write('\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('
OAI RAN MR Status Dashboard
Update : '+date+'
\n') - self.f_html.write('
\n') - self.f_html.write('
\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - - def MR_terminateHTML(self): - self.f_html.write('
MRCreated_AtAuthorTitleAssigneeReviewerCAN STARTIN PROGRESSCOMPLETEDReview FormOK MergeMerge Conflicts
\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.close() - - - def MR_rowHTML(self,row): - self.f_html.write('\n') - self.f_html.write(''+row[1]+'\n') - self.f_html.write(''+row[2]+'\n') - self.f_html.write(''+row[3]+'\n') - self.f_html.write(''+row[4]+'\n') - self.f_html.write(''+row[5]+'\n') - self.f_html.write(''+row[6]+'\n') - if row[7]=='X': - self.f_html.write(''+row[7]+'\n') - else: - self.f_html.write('\n') - if row[8]=='X': - self.f_html.write(''+row[8]+'\n') - else: - self.f_html.write('\n') - if row[9]=='X': - self.f_html.write(''+row[9]+'\n') - else: - self.f_html.write('\n') - if row[10]=='X': - self.f_html.write(''+row[10]+'\n') - else: - self.f_html.write('\n') - if row[11]=='X': - self.f_html.write(''+row[11]+'\n') - else: - self.f_html.write('\n') - if row[12]=='YES': - self.f_html.write(''+row[12]+'\n') - else: - self.f_html.write('\n') - self.f_html.write('\n') - - - def Build(self, type, mr, htmlfilename): - if type=='MR': - self.Build_MR_Table(htmlfilename) - elif type=='Tests': - self.Build_Test_Table(htmlfilename) - elif type=='singleMR': - self.Build_singleMR_Table(mr,htmlfilename) - else : - print("Undefined Dashboard Type, options : MR or Tests") - - - def Build_Test_Table(self,htmlfilename): - print("Building Tests Dashboard...") - - self.f_html=open(htmlfilename,'w') - - ###update date/time, format dd/mm/YY H:M:S - now = datetime.now() - dt_string = now.strftime("%d/%m/%Y %H:%M") - #HTML table header - self.Test_initHTML(dt_string) - - - #1 table per MR if test results exist - for x in range(len(self.git)): - mr=str(self.git[x]['iid']) - if 'PASS' not in self.db[mr]: - self.f_html.write('

'+mr+''+' '+self.git[x]['title'] + '

\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write(' \n') - self.f_html.write(' \n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - - #parsing the tests - for t in self.tests: - - row=[] - short_name= t - hyperlink= self.tests[t]['link'] - job=self.tests[t]['job'] - - - if job in self.db[mr]: - if 'PASS' in self.db[mr][job]: - row.append(self.db[mr][job]['PASS']) - else: - row.append('') - if 'FAIL' in self.db[mr][job]: - row.append(self.db[mr][job]['FAIL']) - else: - row.append('') - #2 columns for last_pass and last_fail links - if 'last_pass' in self.db[mr][job]: - lastpasshyperlink= self.db[mr][job]['last_pass'][1] - lastpasstext= self.db[mr][job]['last_pass'][0] - else: - lastpasshyperlink='' - lastpasstext='' - - if 'last_fail' in self.db[mr][job]: - lastfailhyperlink= self.db[mr][job]['last_fail'][1] - lastfailtext= self.db[mr][job]['last_fail'][0] - else: - lastfailhyperlink='' - lastfailtext='' - - - - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - if row[0]!='': - self.f_html.write('\n') - else: - self.f_html.write('\n') - if row[1]!='': - self.f_html.write('\n') - else: - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - - self.f_html.write('
Test NameBenchTest# Pass# FailLast PassLast Fail
'+short_name+''+self.tests[t]['bench']+''+self.tests[t]['test']+''+str(row[0])+''+str(row[1])+''+lastpasstext+''+lastfailtext+'
\n') - - #terminate HTML table and close file - self.Test_terminateHTML() - - - def Build_singleMR_Table(self,singlemr,htmlfilename): - print("Building single MR Tests Results...") - - self.f_html=open(htmlfilename,'w') - - ###update date/time, format dd/mm/YY H:M:S - now = datetime.now() - dt_string = now.strftime("%d/%m/%Y %H:%M") - #HTML table header - self.singleMR_initHTML(dt_string) - - - #1 table per MR if test results exist => 1 table for matching mr - for x in range(len(self.git)): - mr=str(self.git[x]['iid']) - if mr==singlemr: - #if 'PASS' not in self.db[mr]: - self.f_html.write('

'+mr+''+' '+self.git[x]['title'] + '

\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write(' \n') - self.f_html.write(' \n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - - #parsing the tests - for t in self.tests: - - row=[] - short_name= t - hyperlink= self.tests[t]['link'] - job=self.tests[t]['job'] - - - if job in self.db[mr]: - if 'PASS' in self.db[mr][job]: - row.append(self.db[mr][job]['PASS']) - else: - row.append('') - if 'FAIL' in self.db[mr][job]: - row.append(self.db[mr][job]['FAIL']) - else: - row.append('') - #2 columns for last_pass and last_fail links - if 'last_pass' in self.db[mr][job]: - lastpasshyperlink= self.db[mr][job]['last_pass'][1] - lastpasstext= self.db[mr][job]['last_pass'][0] - else: - lastpasshyperlink='' - lastpasstext='' - - if 'last_fail' in self.db[mr][job]: - lastfailhyperlink= self.db[mr][job]['last_fail'][1] - lastfailtext= self.db[mr][job]['last_fail'][0] - else: - lastfailhyperlink='' - lastfailtext='' - - - - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - if row[0]!='': - self.f_html.write('\n') - else: - self.f_html.write('\n') - if row[1]!='': - self.f_html.write('\n') - else: - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - self.f_html.write('\n') - - self.f_html.write('
Test NameBenchTest# Pass# FailLast PassLast Fail
'+short_name+''+self.tests[t]['bench']+''+self.tests[t]['test']+''+str(row[0])+''+str(row[1])+''+lastpasstext+''+lastfailtext+'
\n') - - #terminate HTML table and close file - self.Test_terminateHTML() - - - - def Build_MR_Table(self,htmlfilename): - - print("Building Merge Requests Dashboard...") - - self.f_html=open(htmlfilename,'w') - - ###update date/time, format dd/mm/YY H:M:S - now = datetime.now() - dt_string = now.strftime("%d/%m/%Y %H:%M") - - #HTML table header - self.MR_initHTML(dt_string) - - - ###MR data lines - for x in range(len(self.git)): - - - hyperlink= 'https://gitlab.eurecom.fr/oai/openairinterface5g/-/merge_requests/'+ str(self.git[x]['iid']) - text= str(self.git[x]['iid']) - - - date_time_str = self.git[x]['created_at'] - date_time_obj = datetime.strptime(date_time_str, '%Y-%m-%dT%H:%M:%S.%fZ') - - milestone1=milestone2=milestone3=milestone4="" - if self.git[x]['milestone']!=None: - if self.git[x]['milestone']['title']=="REVIEW_CAN_START": - milestone1="X" - elif self.git[x]['milestone']['title']=="REVIEW_IN_PROGRESS": - milestone2="X" - elif self.git[x]['milestone']['title']=="REVIEW_COMPLETED_AND_APPROVED": - milestone3="X" - elif self.git[x]['milestone']['title']=="OK_TO_BE_MERGED": - milestone4="X" - else: - pass - else: - pass - - #check if empty or not - if self.git[x]['assignee']!=None: - assignee = str(self.git[x]['assignee']['name']) - else: - assignee = "" - - #check if empty or not - if len(self.git[x]['reviewers'])!=0: - reviewer = str(self.git[x]['reviewers'][0]['name']) - else: - reviewer = "" - - if self.git[x]['has_conflicts']==True: - conflicts = "YES" - else: - conflicts = "" - - - #add a column flagging that the review form is present - #we use gitlab API to parse the MR notes - gl = gitlab.Gitlab.from_config('OAI') - project_id = 223 - project = gl.projects.get(project_id) - #get the opened MR in the project - mrs = project.mergerequests.list(state='opened',per_page=100) - review_form='' - for m in range (0,len(mrs)): - if mrs[m].iid==self.git[x]['iid']:#check the iid is the one we are on - mr_notes = mrs[m].notes.list(all=True) - n=0 - found=False - while found==False and n1: + if len(sys.argv) > 1: #individual MR test results + test dashboard, event based (end of slave jenkins pipeline) - if sys.argv[1]=="testevent" : - mr=sys.argv[2] - htmlDash=Dashboard() - if mr in htmlDash.mr_list: - #single MR test results - htmlDash.Build('singleMR',mr,'/tmp/MR'+mr+'_index.html') - htmlDash.CopyToS3('/tmp/MR'+mr+'_index.html','oaitestdashboard','MR'+mr+'/index.html') - #all MR test results - htmlDash.Build('Tests','0000','/tmp/Tests_index.html') - htmlDash.CopyToS3('/tmp/Tests_index.html','oaitestdashboard','index.html') + if sys.argv[1] != "gitpost": + print("error: only gitpost subcommand is supported") + exit(1) - #git post with MR test results, event based (end of master jenkins pipeline) elif sys.argv[1]=="gitpost": mr=sys.argv[2] commit=sys.argv[3] @@ -603,25 +111,15 @@ def main(): htmlDash.PostGitNote(mr,commit, args) else: print("Not a Merge Request => this build is for testing/debug purpose, no report to git") - elif sys.argv[1]=="awsclean": - mode=sys.argv[2]#report or delete - htmlDash=Dashboard() - htmlDash.AWSCleanup(mode) else: print("Wrong argument at position 1") + exit(1) #test and MR status dashboards, cron based else: - htmlDash=Dashboard() - #all MR status dashboard - htmlDash.Build('MR','0000','/tmp/MR_index.html') - htmlDash.CopyToS3('/tmp/MR_index.html','oairandashboard','index.html') - #all MR test results - htmlDash.Build('Tests','0000','/tmp/Tests_index.html') - htmlDash.CopyToS3('/tmp/Tests_index.html','oaitestdashboard','index.html') - - + print("error: only gitpost subcommand is supported") + exit(1) if __name__ == "__main__": # execute only if run as a script diff --git a/ci-scripts/ran_dashboard/mr_styles.css b/ci-scripts/ran_dashboard/mr_styles.css deleted file mode 100755 index 5d938669cb..0000000000 --- a/ci-scripts/ran_dashboard/mr_styles.css +++ /dev/null @@ -1,136 +0,0 @@ -body { - font-family: 'lato', sans-serif; - } - - -.Main { - text-align:left; - font-size: 30px; - font-weight: bold; -} - -.DashLink { - text-align:left; - font-size: 16px; -} - -.DashLink:hover { - font-weight: bold; -} - -.Date { - text-align:left; - font-size: 16px; - font-weight: bold; -} - - -a { text-decoration: none; } -a:visited { text-decoration: none; color:blue} -a:hover { text-decoration: none; font-weight: bold; color:blue} - - -.MR_Table { - border-collapse: collapse; -} - -.MR_Table tr { - font-size: 12px; - text-align:center; -} - - -.MR_Table tr:hover { - background-color:lightgray; -} - -.MR_Table td { - border: 1px solid black; - padding : 5px; -} -.MR_Table td:hover { -font-weight : bold; -} - -.MR_Table th { - font-size: 14px; - text-align:center; - padding : 5px; -} - - -.title_cell { - text-align:left; -} - - -.MR { - table-layout: fixed; - width: 50px; - background-color: rgb(143, 154, 216); -} - -.CREATED_AT { - table-layout: fixed; - width: 100px; - background-color: rgb(143, 154, 216); -} - -.AUTHOR { - table-layout: fixed; - width: 150px; - background-color: rgb(143, 154, 216); -} - -.TITLE { - table-layout: fixed; - width: 500px; - background-color: rgb(143, 154, 216); -} - -.ASSIGNEE { - table-layout: fixed; - width: 150px; - background-color: rgb(143, 154, 216); -} - -.REVIEWER { - table-layout: fixed; - width: 150px; - background-color: rgb(143, 154, 216); -} - -.CAN_START { - background-color: orange; - table-layout: fixed; - width: 150px; -} -.IN_PROGRESS { - background-color: yellow; - table-layout: fixed; - width: 150px; -} -.COMPLETED { - background-color: rgb(144, 221, 231); - table-layout: fixed; - width: 150px; -} - -.REVIEW_FORM { - table-layout: fixed; - width: 100px; - background-color: rgb(143, 154, 216); -} - -.OK_MERGE { - background-color: rgb(58, 236, 58); - table-layout: fixed; - width: 150px; -} - -.MERGE_CONFLICTS { - table-layout: fixed; - width: 100px; - background-color: rgb(143, 154, 216); -} - diff --git a/ci-scripts/ran_dashboard/ran_dashboard.py b/ci-scripts/ran_dashboard/ran_dashboard.py deleted file mode 100644 index 54a98ec878..0000000000 --- a/ci-scripts/ran_dashboard/ran_dashboard.py +++ /dev/null @@ -1,405 +0,0 @@ -#/* -# * 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 -# */ -#--------------------------------------------------------------------- -# Merge Requests Dashboard for RAN on googleSheet -# -# Required Python Version -# Python 3.x -# -#--------------------------------------------------------------------- - -#----------------------------------------------------------- -# Import -#----------------------------------------------------------- - -#author Remi - -#import google spreadsheet API -import gspread -from oauth2client.service_account import ServiceAccountCredentials - - -import subprocess -import shlex #lexical analysis -import json #json structures -import datetime #now() and date formating -from datetime import datetime -import re -import gitlab -import yaml -import os -import pickle -import time - - -from sqlconnect import SQLConnect - -#----------------------------------------------------------- -# Class Declaration -#----------------------------------------------------------- - -class gDashboard: - def __init__(self, creds_file, spreadsheet, worksheet): #"creds.json", 'OAI RAN Dashboard', 'MR Status' - self.scope = ["https://spreadsheets.google.com/feeds",'https://www.googleapis.com/auth/spreadsheets',"https://www.googleapis.com/auth/drive.file","https://www.googleapis.com/auth/drive"] - self.creds = ServiceAccountCredentials.from_json_keyfile_name(creds_file, self.scope) - self.client = gspread.authorize(self.creds) - #spreadsheet - self.ss = self.client.open(spreadsheet) - #worksheet - self.sheet = self.ss.worksheet(worksheet) - self.ss.del_worksheet(self.sheet) #start by deleting the old sheet - self.sheet = self.ss.add_worksheet(title=worksheet, rows="100", cols="30") #create a new one - - #init with data sources : git, yaml config file, test results databases - cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """ - self.git = self.__getGitData(cmd) #git data from Gitlab - self.tests = self.__loadCfg('ran_dashboard_cfg.yaml') #tests table setup from yaml - self.db = self.__loadFromDB() #test results from database - - - def __loadCfg(self,yaml_file): - with open(yaml_file,'r') as f: - tests = yaml.load(f) - return tests - - def __getGitData(self,cmd): - #cmd="""curl --silent "https://gitlab.eurecom.fr/api/v4/projects/oai%2Fopenairinterface5g/merge_requests?state=opened&per_page=100" """ - process = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE) - output = process.stdout.readline() - tmp=output.decode("utf-8") - d = json.loads(tmp) - return d - - def __loadFromDB(self): - mr_list=[] - for x in range(len(self.git)): - mr_list.append(str(self.git[x]['iid'])) - mydb=SQLConnect() - for MR in mr_list: - mydb.get(MR) - mydb.close_connection() - return mydb.data - - - - def gBuild(self, destinationSheetName): - - ###line 1 : update date/time, format dd/mm/YY H:M:S - now = datetime.now() - dt_string = "Update : " + now.strftime("%d/%m/%Y %H:%M") - row =[dt_string] - self.sheet.insert_row(row, index=1, value_input_option='RAW') - - ###line 2 is for the test short names (links to jenkins pipeline), updated at the end - - ###line 3 is for the column names - i=3 - row =["MR","Created_at","Author","Title","Assignee", "Reviewer", "CAN START","IN PROGRESS","COMPLETED","Review Form","OK MERGE","Merge conflicts",""] - - #tests - for t in range(0,len(self.tests)): - row.append("# PASS") - row.append("# FAIL") - row.append("Last Pass") - row.append("Last Fail") - - self.sheet.insert_row(row, index=i, value_input_option='RAW') - - ###line 4 onward, MR data lines - for x in range(len(self.git)): - i=i+1 - date_time_str = self.git[x]['created_at'] - date_time_obj = datetime.strptime(date_time_str, '%Y-%m-%dT%H:%M:%S.%fZ') - - milestone1=milestone2=milestone3=milestone4="" - if self.git[x]['milestone']!=None: - if self.git[x]['milestone']['title']=="REVIEW_CAN_START": - milestone1="X" - elif self.git[x]['milestone']['title']=="REVIEW_IN_PROGRESS": - milestone2="X" - elif self.git[x]['milestone']['title']=="REVIEW_COMPLETED_AND_APPROVED": - milestone3="X" - elif self.git[x]['milestone']['title']=="OK_TO_BE_MERGED": - milestone4="X" - else: - pass - else: - pass - - #check if empty or not - if self.git[x]['assignee']!=None: - assignee = str(self.git[x]['assignee']['name']) - else: - assignee = "" - - #check if empty or not - if len(self.git[x]['reviewers'])!=0: - reviewer = str(self.git[x]['reviewers'][0]['name']) - else: - reviewer = "" - - if self.git[x]['has_conflicts']==True: - conflicts = "YES" - else: - conflicts = "" - - - #add a column flagging that the review form is present - #we use gitlab API to parse the MR notes - gl = gitlab.Gitlab.from_config('OAI') - project_id = 223 - project = gl.projects.get(project_id) - #get the opened MR in the project - mrs = project.mergerequests.list(state='opened',per_page=100) - review_form='' - for m in range (0,len(mrs)): - if mrs[m].iid==self.git[x]['iid']:#check the iid is the one we are on - mr_notes = mrs[m].notes.list(all=True) - n=0 - found=False - while found==False and n Date: Thu, 2 Feb 2023 16:00:11 +0100 Subject: [PATCH 13/26] Remove unnecessary file --- ci-scripts/template.xsl | 65 ----------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 ci-scripts/template.xsl diff --git a/ci-scripts/template.xsl b/ci-scripts/template.xsl deleted file mode 100644 index 874a4c62bf..0000000000 --- a/ci-scripts/template.xsl +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - -

TEMPLATE Results Summary

- - - - - - - - - - - - - - - - - -
HostnameNb TestsFailuresTimestamp
- - - - - - - -
-

Details

- - - - - - - - - - - - - - - - - -
Test NameResultTimeDescription
- - - - - - - -
- - -
-
From a36ed24b1a7508a9e7c966b9538ee6191ba45f18 Mon Sep 17 00:00:00 2001 From: Sagar Arora Date: Thu, 2 Feb 2023 15:54:57 +0100 Subject: [PATCH 14/26] Make cluster images older than 4 weeks dangling so they are deleted --- ci-scripts/cls_cluster.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ci-scripts/cls_cluster.py b/ci-scripts/cls_cluster.py index 2096404204..487e203d36 100644 --- a/ci-scripts/cls_cluster.py +++ b/ci-scripts/cls_cluster.py @@ -230,6 +230,14 @@ class Cluster: HTML.CreateHtmlTestRow('N/A', 'KO', CONST.OC_PROJECT_FAIL) return False + # delete old images by Sagar Arora : + # 1. retrieve all images and their timestamp + # 2. awk retrieves those whose timestamp is older than 4 weeks + # 3. issue delete command on corresponding istags (the images are dangling and will be cleaned by the registry) + delete_cmd = "oc get istag -o go-template --template '{{range .items}}{{.metadata.name}} {{.metadata.creationTimestamp}}{{\"\\n\"}}{{end}}' | awk '$2 <= \"'$(date -d '-4weeks' -Ins --utc | sed 's/+0000/Z/')'\" { print $1 }' | xargs --no-run-if-empty oc delete istag" + response = self.cmd.run(delete_cmd) + logging.debug(f"deleted images:\n{response.stdout}") + self._recreate_entitlements() status = True # flag to abandon compiling if any image fails From 20f85157242122d3e9ffae71227f8264a3174bca Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 10 Feb 2023 09:26:45 +0000 Subject: [PATCH 15/26] review suggested changes --- openair2/GNB_APP/gnb_config.c | 13 +++++++------ openair2/RRC/NR/MESSAGES/asn1_msg.c | 8 +++----- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 4822c799ee..86f12d126a 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -348,13 +348,14 @@ void fix_scc(NR_ServingCellConfigCommon_t *scc,uint64_t ssbmap) { } // fix SS0 and Coreset0 - if((int)*scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero == -1) { - free(scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero); - scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero = NULL; + NR_PDCCH_ConfigCommon_t *pdcch_cc = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup; + if((int)*pdcch_cc->searchSpaceZero == -1) { + free(pdcch_cc->searchSpaceZero); + pdcch_cc->searchSpaceZero = NULL; } - if((int)*scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero == -1) { - free(scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero); - scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero = NULL; + if((int)*pdcch_cc->controlResourceSetZero == -1) { + free(pdcch_cc->controlResourceSetZero); + pdcch_cc->controlResourceSetZero = NULL; } // fix UL absolute frequency diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 54edbaa214..3616f20242 100755 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -241,12 +241,10 @@ uint8_t do_MIB_NR(gNB_RRC_INST *rrc,uint32_t frame) { * The SIB1 will be sent in this allocation (Type0-PDCCH) : 38.213, 13-4 Table and 38.213 13-11 to 13-14 tables * the reverse allocation is in nr_ue_decode_mib() */ - long cset0 = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero ? - *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->controlResourceSetZero : 0; - long ss0 = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero ? - *scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup->searchSpaceZero : 0; - + const NR_PDCCH_ConfigCommon_t *pdcch_cc = scc->downlinkConfigCommon->initialDownlinkBWP->pdcch_ConfigCommon->choice.setup; + long cset0 = pdcch_cc->controlResourceSetZero ? *pdcch_cc->controlResourceSetZero : 0; mib->message.choice.mib->pdcch_ConfigSIB1.controlResourceSetZero = cset0; + long ss0 = pdcch_cc->searchSpaceZero ? *pdcch_cc->searchSpaceZero : 0; mib->message.choice.mib->pdcch_ConfigSIB1.searchSpaceZero = ss0; switch (*scc->ssbSubcarrierSpacing) { From 986a1a80ffd9413081546ab838b8e7d84eacb447 Mon Sep 17 00:00:00 2001 From: mir Date: Tue, 4 Oct 2022 19:12:33 +0200 Subject: [PATCH 16/26] Remove nettle library from OAI, support OpenSSL 3.0 and 1.1 Add byte_array_t structure under common/ --- CMakeLists.txt | 49 ++-- cmake_targets/at_commands/CMakeLists.txt | 45 +-- cmake_targets/tools/MODULES/FindNettle.cmake | 22 -- cmake_targets/tools/build_helper | 4 +- common/CMakeLists.txt | 1 + common/utils/CMakeLists.txt | 1 + common/utils/ds/CMakeLists.txt | 5 + common/utils/ds/byte_array.c | 58 ++++ common/utils/ds/byte_array.h | 42 +++ common/utils/websrv/DOC/websrvuse.md | 8 +- docker/Dockerfile.eNB.rhel8.2 | 1 - docker/Dockerfile.eNB.ubuntu18 | 1 - docker/Dockerfile.gNB.aw2s.rhel8.2 | 1 - docker/Dockerfile.gNB.rhel8.2 | 1 - docker/Dockerfile.gNB.ubuntu18 | 1 - docker/Dockerfile.lteUE.rhel8.2 | 1 - docker/Dockerfile.lteUE.ubuntu18 | 1 - docker/Dockerfile.nrUE.rhel8.2 | 1 - docker/Dockerfile.nrUE.ubuntu18 | 1 - docker/Dockerfile.phySim.rhel8.2 | 1 - .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c | 79 +++--- .../LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h | 4 +- .../LAYER2/nr_pdcp/nr_pdcp_security_nea2.c | 65 ++--- openair2/UTIL/ALG_DS/CMakeLists.txt | 2 + openair2/UTIL/ALG_DS/README.md | 2 + openair2/UTIL/CMakeLists.txt | 2 + openair2/UTIL/OSA/README | 1 - openair2/UTIL/OSA/osa_defs.h | 2 +- openair2/UTIL/OSA/osa_key_deriver.c | 68 ++--- openair2/UTIL/OSA/osa_stream_eea.c | 158 ++++------- openair2/UTIL/OSA/osa_stream_eia.c | 225 +++++++-------- openair3/DOCS/Latex/EPC/EPC.pdf | Bin 243263 -> 0 bytes openair3/DOCS/Latex/EPC/EPC.tex | 23 -- openair3/NAS/COMMON/nr_common.c | 9 +- openair3/NAS/NR_UE/nr_nas_msg_sim.c | 48 ++-- openair3/NAS/UE/EMM/Authentication.c | 13 +- openair3/NAS/UE/EMM/SecurityModeControl.c | 11 +- openair3/SECU/aes_128.h | 88 ++++++ openair3/SECU/aes_128_cbc_cmac.c | 254 +++++++++++++++++ openair3/SECU/aes_128_cbc_cmac.h | 48 ++++ openair3/SECU/aes_128_ctr.c | 62 ++++ openair3/SECU/aes_128_ctr.h | 33 +++ openair3/SECU/kdf.c | 81 +----- openair3/SECU/kdf.h | 34 +++ openair3/SECU/key_nas_deriver.c | 5 +- openair3/SECU/nas_stream_eea1.c | 4 - openair3/SECU/nas_stream_eea2.c | 84 ++---- openair3/SECU/nas_stream_eia2.c | 104 ++----- openair3/SECU/secu_defs.c | 96 +++++++ openair3/SECU/secu_defs.h | 8 +- openair3/SECU/sha_256_hmac.c | 95 +++++++ openair3/SECU/sha_256_hmac.h | 33 +++ openair3/TEST/Makefile.am | 6 +- openair3/TEST/test_aes128_ctr.c | 265 ++++++++++++++++++ openair3/TEST/test_aes128_ctr_decrypt.c | 85 ------ openair3/TEST/test_aes128_ctr_encrypt.c | 84 ------ targets/Makerules | 8 - targets/README.txt | 2 +- 58 files changed, 1500 insertions(+), 936 deletions(-) delete mode 100755 cmake_targets/tools/MODULES/FindNettle.cmake create mode 100644 common/CMakeLists.txt create mode 100644 common/utils/CMakeLists.txt create mode 100644 common/utils/ds/CMakeLists.txt create mode 100644 common/utils/ds/byte_array.c create mode 100644 common/utils/ds/byte_array.h create mode 100644 openair2/UTIL/ALG_DS/CMakeLists.txt create mode 100644 openair2/UTIL/ALG_DS/README.md create mode 100644 openair2/UTIL/CMakeLists.txt delete mode 100644 openair3/DOCS/Latex/EPC/EPC.pdf create mode 100644 openair3/SECU/aes_128.h create mode 100644 openair3/SECU/aes_128_cbc_cmac.c create mode 100644 openair3/SECU/aes_128_cbc_cmac.h create mode 100644 openair3/SECU/aes_128_ctr.c create mode 100644 openair3/SECU/aes_128_ctr.h create mode 100644 openair3/SECU/kdf.h create mode 100644 openair3/SECU/secu_defs.c create mode 100644 openair3/SECU/sha_256_hmac.c create mode 100644 openair3/SECU/sha_256_hmac.h create mode 100644 openair3/TEST/test_aes128_ctr.c delete mode 100644 openair3/TEST/test_aes128_ctr_decrypt.c delete mode 100644 openair3/TEST/test_aes128_ctr_encrypt.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 014a67c9f8..2545a17ad0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -83,27 +83,6 @@ else() include_directories(${OPENPGM_INCLUDE_DIRS}) endif() -pkg_search_module(NETTLE nettle) -if(NOT ${NETTLE_FOUND}) - message( FATAL_ERROR "PACKAGE nettle not found: some targets will fail. Run build_oai -I again!") -else() - include_directories(${NETTLE_INCLUDE_DIRS}) -endif() - -message ("NETTLE VERSION_INSTALLED = ${NETTLE_VERSION}") - -string(REGEX REPLACE "([0-9]+).*" "\\1" NETTLE_VERSION_MAJOR ${NETTLE_VERSION}) -string(REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" NETTLE_VERSION_MINOR ${NETTLE_VERSION}) -message ("NETTLE_VERSION_MAJOR = ${NETTLE_VERSION_MAJOR}") -message ("NETTLE_VERSION_MINOR = ${NETTLE_VERSION_MINOR}") - -if ("${NETTLE_VERSION_MAJOR}" STREQUAL "" OR "${NETTLE_VERSION_MINOR}" STREQUAL "") - message( FATAL_ERROR "The nettle version not detected properly. Try to run build_oai -I again" ) -endif() - -add_definitions("-DNETTLE_VERSION_MAJOR=${NETTLE_VERSION_MAJOR}") -add_definitions("-DNETTLE_VERSION_MINOR=${NETTLE_VERSION_MINOR}") - pkg_search_module(XPM xpm) if(NOT ${XPM_FOUND}) message("PACKAGE xpm not found: some targets will fail") @@ -885,12 +864,17 @@ set(SECU_OSA_SRC ${OPENAIR2_DIR}/UTIL/OSA/osa_snow3g.c ${OPENAIR2_DIR}/UTIL/OSA/osa_stream_eea.c ${OPENAIR2_DIR}/UTIL/OSA/osa_stream_eia.c - ) -add_library(SECU_OSA ${SECU_OSA_SRC}) -target_link_libraries(SECU_OSA ${NETTLE_LIBRARIES}) + ) +add_library(SECU_OSA ${SECU_OSA_SRC} $ ) + +target_include_directories(SECU_OSA PRIVATE ${OPENAIR_DIR}/common/utils/ds/) set(SECU_CN_SRC + ${OPENAIR3_DIR}/SECU/secu_defs.c ${OPENAIR3_DIR}/SECU/kdf.c + ${OPENAIR3_DIR}/SECU/aes_128_ctr.c + ${OPENAIR3_DIR}/SECU/aes_128_cbc_cmac.c + ${OPENAIR3_DIR}/SECU/sha_256_hmac.c ${OPENAIR3_DIR}/SECU/rijndael.c ${OPENAIR3_DIR}/SECU/snow3g.c ${OPENAIR3_DIR}/SECU/key_nas_deriver.c @@ -899,9 +883,9 @@ set(SECU_CN_SRC ${OPENAIR3_DIR}/SECU/nas_stream_eea2.c ${OPENAIR3_DIR}/SECU/nas_stream_eia2.c ) -add_library(SECU_CN ${SECU_CN_SRC}) -target_link_libraries(SECU_CN ${NETTLE_LIBRARIES}) +add_library(SECU_CN ${SECU_CN_SRC} $) +target_include_directories(SECU_CN PRIVATE ${OPENAIR_DIR}/common/utils/ds/) # Physical Channel Procedures Scheduling ################################" @@ -2053,8 +2037,13 @@ add_library(LIB_NAS_SIMUE ${libnas_ue_esm_sap_OBJS} ${libnrnas_emm_msg_OBJS} ${libnrnas_ies_OBJS} + $ ) +target_include_directories(LIB_NAS_SIMUE PRIVATE ${OPENAIR_DIR}/common/utils/ds/) target_link_libraries(LIB_NAS_SIMUE PRIVATE lte_rrc) + + + set(NAS_SIM_LIB LIB_NAS_SIMUE) target_link_libraries(LIB_NAS_SIMUE PRIVATE asn1_nr_rrc asn1_lte_rrc) @@ -2074,7 +2063,9 @@ add_library(LIB_NAS_UE ${libnas_ue_emm_sap_OBJS} ${libnas_ue_esm_OBJS} ${libnas_ue_esm_sap_OBJS} + $ ) +target_include_directories(LIB_NAS_UE PRIVATE ${OPENAIR_DIR}/common/utils/ds/) target_link_libraries(LIB_NAS_UE PRIVATE lte_rrc) set(NAS_UE_LIB LIB_NAS_UE) target_link_libraries(LIB_NAS_UE PRIVATE asn1_nr_rrc asn1_lte_rrc) @@ -2106,6 +2097,8 @@ add_library(LIB_5GNAS_GNB ${NAS_SRC}/NR_UE/ue_process_nas.c ${OPENAIR3_DIR}//UICC/usim_interface.c ) + +target_include_directories(LIB_5GNAS_GNB PRIVATE ${OPENAIR_DIR}/common/utils/ds/) target_link_libraries(LIB_5GNAS_GNB PRIVATE SECU_CN ${CRYPTO_LIBRARIES}) target_link_libraries(LIB_5GNAS_GNB PRIVATE asn1_nr_rrc asn1_lte_rrc) @@ -2715,8 +2708,7 @@ endforeach(myExe) foreach(myExe s1ap secu_knas_encrypt_eia1 secu_kenb - aes128_ctr_encrypt - aes128_ctr_decrypt + aes128_ctr secu_knas_encrypt_eea2 secu_knas secu_knas_encrypt_eea1 kdf @@ -2846,6 +2838,7 @@ include (common/utils/telnetsrv/telnetsrv_CMakeLists.txt) include(${OPENAIR1_DIR}/PHY/CODING/nrLDPC_decoder/nrLDPC_tools/CMakeLists.txt) include (${OPENAIR_DIR}/common/utils/websrv/websrv_CMakeLists.txt) +add_subdirectory(common) add_subdirectory(doc) add_subdirectory(openair2) add_subdirectory(openair3) diff --git a/cmake_targets/at_commands/CMakeLists.txt b/cmake_targets/at_commands/CMakeLists.txt index a8f5932045..d2d5db704e 100755 --- a/cmake_targets/at_commands/CMakeLists.txt +++ b/cmake_targets/at_commands/CMakeLists.txt @@ -86,49 +86,8 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.2 -std=gnu99 -Wall -Wstrict-prototype set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -DMALLOC_CHECK_=3") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -ggdb -DMALLOC_CHECK_=3 -O2") -##This is to detect nettle version changes between Ubuntu 14.04/16.04 -#set ( nettle_cmd "nettle-hash" ) -#set ( nettle_arg "-V" ) -#execute_process(COMMAND ${nettle_cmd} ${nettle_arg} RESULT_VARIABLE rv OUTPUT_VARIABLE ov ERROR_VARIABLE ev) -# -#string(REGEX MATCH "[+-]?[0-9]+([.][0-9]+)?" nv ${ov}) -# -#message("NETTLE_VERSION = ${nv}") -# -## we need to remove decimal as floating point arithematic does not work properly with C preprocessor -#STRING(REGEX REPLACE "[.]" "" nv ${nv}) -# -#if ("${nv}" STREQUAL "") -# message( FATAL_ERROR "The nettle version not detected properly. Try to run build_oai -I again" ) -#endif() -# -#set (NETTLE_VERSION "${nv}") -#add_definitions("-DNETTLE_VERSION=${NETTLE_VERSION}") - include(FindPkgConfig) -pkg_search_module(NETTLE nettle) -if(NOT ${NETTLE_FOUND}) - message( FATAL_ERROR "PACKAGE nettle not found: some targets will fail. Run build_oai -I again!") -else() - include_directories(${NETTLE_INCLUDE_DIRS}) -endif() - -message ("NETTLE VERSION_INSTALLED = ${NETTLE_VERSION}") - -string(REGEX REPLACE "([0-9]+).*" "\\1" NETTLE_VERSION_MAJOR ${NETTLE_VERSION}) -string(REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" NETTLE_VERSION_MINOR ${NETTLE_VERSION}) -message ("NETTLE_VERSION_MAJOR = ${NETTLE_VERSION_MAJOR}") -message ("NETTLE_VERSION_MINOR = ${NETTLE_VERSION_MINOR}") - -if ("${NETTLE_VERSION_MAJOR}" STREQUAL "" OR "${NETTLE_VERSION_MINOR}" STREQUAL "") - message( FATAL_ERROR "The nettle version not detected properly. Try to run build_oai -I again" ) -endif() - -add_definitions("-DNETTLE_VERSION_MAJOR=${NETTLE_VERSION_MAJOR}") -add_definitions("-DNETTLE_VERSION_MINOR=${NETTLE_VERSION_MINOR}") - - pkg_search_module(OPENSSL openssl REQUIRED) include_directories(${OPENSSL_INCLUDE_DIRS}) @@ -159,6 +118,9 @@ add_boolean_option(NAS_MME False "NAS_UE and NAS_MME are incompa # SECU LIB ################################################################################ set(secu_cn_SRC + ${OPENAIR3_DIR}/SECU/aes_128_ctr.c + ${OPENAIR3_DIR}/SECU/aes_128_cbc_cmac.c + ${OPENAIR3_DIR}/SECU/sha_256_hmac.c ${OPENAIR3_DIR}/SECU/kdf.c ${OPENAIR3_DIR}/SECU/rijndael.c ${OPENAIR3_DIR}/SECU/snow3g.c @@ -719,7 +681,6 @@ target_link_libraries (at_nas_ue -Wl,-whole-archive api_network api_user api_usim emm esm ies util secu_cn -Wl,-no-whole-archive - ${NETTLE_LIBRARIES} ${OPENSSL_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} m ) diff --git a/cmake_targets/tools/MODULES/FindNettle.cmake b/cmake_targets/tools/MODULES/FindNettle.cmake deleted file mode 100755 index 4602e74997..0000000000 --- a/cmake_targets/tools/MODULES/FindNettle.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# Find the native Nettle includes, library, and flags -# -# NETTLE_INCLUDE_DIR - where to find nettle.h, etc. -# NETTLE_LIBRARIES - List of libraries when using Nettle. -# NETTLE_FOUND - True if Nettle found. -IF (NETTLE_INCLUDE_DIR) -# Already in cache, be silent - SET(NETTLE_FIND_QUIETLY TRUE) -ENDIF (NETTLE_INCLUDE_DIR) -FIND_PATH(NETTLE_INCLUDE_DIR nettle/nettle-meta.h) -SET(NETTLE_NAMES nettle) -FIND_LIBRARY(NETTLE_LIBRARY NAMES ${NETTLE_NAMES} ) -# handle the QUIETLY and REQUIRED arguments and set NETTLE_FOUND to TRUE if -# all listed variables are TRUE -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(NETTLE DEFAULT_MSG NETTLE_LIBRARY NETTLE_INCLUDE_DIR) -IF(NETTLE_FOUND) - SET(NETTLE_LIBRARIES ${NETTLE_LIBRARY}) -ELSE(NETTLE_FOUND) - SET(NETTLE_LIBRARIES ) -ENDIF(NETTLE_FOUND) -MARK_AS_ADVANCED(NETTLE_LIBRARY NETTLE_INCLUDE_DIR) diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index ed02847387..d31205343e 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -725,7 +725,6 @@ check_install_oai_software() { $SUDO update-alternatives --set "$LAPACK_LIBNAME" "$LAPACK_TARGET" - $SUDO apt-get install -y nettle-dev nettle-bin elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then if [[ "$OS_DISTRO" == "rhel" ]] || [[ "$OS_DISTRO" == "centos" ]]; then if rpm -q epel-release > /dev/null; then @@ -771,7 +770,6 @@ check_install_oai_software() { mariadb-devel \ lksctp-tools \ lksctp-tools-devel \ - openssl-devel \ libtasn1 \ libtool \ libusb-devel \ @@ -783,6 +781,7 @@ check_install_oai_software() { openssh-clients \ openssh-server \ openssl \ + openssl-devel \ patch \ psmisc \ python \ @@ -793,7 +792,6 @@ check_install_oai_software() { wget \ kernel-headers \ kernel-devel \ - nettle-devel \ gnutls-devel \ libXpm-devel \ lapack \ diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000000..512d2b1553 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(utils) diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt new file mode 100644 index 0000000000..eb6a119968 --- /dev/null +++ b/common/utils/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(ds) diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt new file mode 100644 index 0000000000..400d34ad06 --- /dev/null +++ b/common/utils/ds/CMakeLists.txt @@ -0,0 +1,5 @@ +add_library(ds OBJECT + byte_array.c +) + +target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/common/utils/ds/byte_array.c b/common/utils/ds/byte_array.c new file mode 100644 index 0000000000..04a2af0c5d --- /dev/null +++ b/common/utils/ds/byte_array.c @@ -0,0 +1,58 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "byte_array.h" + +#include "common/utils/assertions.h" +#include + +byte_array_t copy_byte_array(byte_array_t src) +{ + byte_array_t dst = {0}; + dst.buf = malloc(src.len); + DevAssert(dst.buf != NULL && "Memory exhausted"); + memcpy(dst.buf, src.buf, src.len); + dst.len = src.len; + return dst; +} + +void free_byte_array(byte_array_t ba) +{ + free(ba.buf); +} + +bool eq_byte_array(const byte_array_t* m0, const byte_array_t* m1) +{ + if (m0 == m1) + return true; + + if (m0 == NULL || m1 == NULL) + return false; + + if (m0->len != m1->len) + return false; + + const int rc = memcmp(m0->buf, m1->buf, m0->len); + if (rc != 0) + return false; + + return true; +} diff --git a/common/utils/ds/byte_array.h b/common/utils/ds/byte_array.h new file mode 100644 index 0000000000..f16fd7c01d --- /dev/null +++ b/common/utils/ds/byte_array.h @@ -0,0 +1,42 @@ +/* + * 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 + */ + +#ifndef BYTE_ARRAY_H_OAI +#define BYTE_ARRAY_H_OAI + +#include +#include +#include + +typedef struct { + size_t len; + uint8_t* buf; +} byte_array_t; + +typedef struct { + uint8_t buf[32]; +} byte_array_32_t; + +byte_array_t copy_byte_array(byte_array_t src); +void free_byte_array(byte_array_t ba); +bool eq_byte_array(const byte_array_t* m0, const byte_array_t* m1); + +#endif diff --git a/common/utils/websrv/DOC/websrvuse.md b/common/utils/websrv/DOC/websrvuse.md index aac9a40ed4..9ba2c8ade1 100644 --- a/common/utils/websrv/DOC/websrvuse.md +++ b/common/utils/websrv/DOC/websrvuse.md @@ -15,9 +15,6 @@ OPENAIR_DIR = /usr/local/oai/oai-develop/openairinterface5g FreeDiameter prefix not found, install freeDiameter if EPC, HSS 3. building the compilation directives ... running cmake ../../.. -NETTLE VERSION_INSTALLED = 3.5.1 -NETTLE_VERSION_MAJOR = 3 -NETTLE_VERSION_MINOR = 5 cuda include /usr/include cuda library -- CMAKE_BUILD_TYPE is RelWithDebInfo @@ -61,9 +58,6 @@ Call Stack (most recent call first): FreeDiameter prefix not found, install freeDiameter if EPC, HSS 3. building the compilation directives ... running cmake ../../.. - NETTLE VERSION_INSTALLED = 3.5.1 - NETTLE_VERSION_MAJOR = 3 - NETTLE_VERSION_MINOR = 5 cuda include /usr/include cuda library -- CMAKE_BUILD_TYPE is RelWithDebInfo @@ -209,4 +203,4 @@ The interface should be intuitive enough, keeping in mind the following restrict Some front-end objects, which usage are less intuitive provide a tooltip to help interface usage. -[oai web serverinterface home](websrv.md) \ No newline at end of file +[oai web serverinterface home](websrv.md) diff --git a/docker/Dockerfile.eNB.rhel8.2 b/docker/Dockerfile.eNB.rhel8.2 index 38c5d8ae49..8d9f3098d5 100644 --- a/docker/Dockerfile.eNB.rhel8.2 +++ b/docker/Dockerfile.eNB.rhel8.2 @@ -39,7 +39,6 @@ ENV TZ=Europe/Paris RUN yum update -y && \ yum install -y --enablerepo="ubi-8-codeready-builder" \ lksctp-tools \ - nettle \ tzdata \ procps-ng \ atlas \ diff --git a/docker/Dockerfile.eNB.ubuntu18 b/docker/Dockerfile.eNB.ubuntu18 index f2a6ee16ee..1c85eb1232 100644 --- a/docker/Dockerfile.eNB.ubuntu18 +++ b/docker/Dockerfile.eNB.ubuntu18 @@ -44,7 +44,6 @@ RUN apt-get update && \ tzdata \ procps \ libsctp1 \ - libnettle6 \ libblas3 \ libatlas3-base \ libconfig9 \ diff --git a/docker/Dockerfile.gNB.aw2s.rhel8.2 b/docker/Dockerfile.gNB.aw2s.rhel8.2 index 0f0c7f9c5c..bc86223130 100644 --- a/docker/Dockerfile.gNB.aw2s.rhel8.2 +++ b/docker/Dockerfile.gNB.aw2s.rhel8.2 @@ -52,7 +52,6 @@ RUN yum repolist --disablerepo=* && \ libX11 \ atlas \ lksctp-tools \ - nettle \ tzdata \ net-tools \ iputils \ diff --git a/docker/Dockerfile.gNB.rhel8.2 b/docker/Dockerfile.gNB.rhel8.2 index 33dc30bd8a..bb034dc099 100644 --- a/docker/Dockerfile.gNB.rhel8.2 +++ b/docker/Dockerfile.gNB.rhel8.2 @@ -44,7 +44,6 @@ RUN yum repolist --disablerepo=* && \ libX11 \ atlas \ lksctp-tools \ - nettle \ tzdata \ gdb \ python3 \ diff --git a/docker/Dockerfile.gNB.ubuntu18 b/docker/Dockerfile.gNB.ubuntu18 index 4ed4929891..681dd9d3d7 100644 --- a/docker/Dockerfile.gNB.ubuntu18 +++ b/docker/Dockerfile.gNB.ubuntu18 @@ -44,7 +44,6 @@ RUN apt-get update && \ procps \ libsctp1 \ tzdata \ - libnettle6 \ libblas3 \ libatlas3-base \ libconfig9 \ diff --git a/docker/Dockerfile.lteUE.rhel8.2 b/docker/Dockerfile.lteUE.rhel8.2 index de4a315ce4..e86d3d97b8 100644 --- a/docker/Dockerfile.lteUE.rhel8.2 +++ b/docker/Dockerfile.lteUE.rhel8.2 @@ -41,7 +41,6 @@ RUN yum update -y && \ yum install -y --enablerepo="ubi-8-codeready-builder" \ lksctp-tools \ procps-ng \ - nettle \ tzdata \ atlas \ gdb \ diff --git a/docker/Dockerfile.lteUE.ubuntu18 b/docker/Dockerfile.lteUE.ubuntu18 index 7f1b30c2c7..4175bab297 100644 --- a/docker/Dockerfile.lteUE.ubuntu18 +++ b/docker/Dockerfile.lteUE.ubuntu18 @@ -45,7 +45,6 @@ RUN apt-get update && \ tzdata \ procps \ libsctp1 \ - libnettle6 \ liblapacke \ libatlas3-base \ libconfig9 \ diff --git a/docker/Dockerfile.nrUE.rhel8.2 b/docker/Dockerfile.nrUE.rhel8.2 index 46281b687f..9fc3826a6d 100644 --- a/docker/Dockerfile.nrUE.rhel8.2 +++ b/docker/Dockerfile.nrUE.rhel8.2 @@ -41,7 +41,6 @@ RUN yum update -y && \ lksctp-tools \ procps-ng \ tzdata \ - nettle \ net-tools \ iputils \ iproute \ diff --git a/docker/Dockerfile.nrUE.ubuntu18 b/docker/Dockerfile.nrUE.ubuntu18 index 25379803c0..5fdf41ef2d 100644 --- a/docker/Dockerfile.nrUE.ubuntu18 +++ b/docker/Dockerfile.nrUE.ubuntu18 @@ -44,7 +44,6 @@ RUN apt-get update && \ libsctp1 \ procps \ tzdata \ - libnettle6 \ liblapacke \ libatlas3-base \ libconfig9 \ diff --git a/docker/Dockerfile.phySim.rhel8.2 b/docker/Dockerfile.phySim.rhel8.2 index c3f6d258b3..413710a08a 100644 --- a/docker/Dockerfile.phySim.rhel8.2 +++ b/docker/Dockerfile.phySim.rhel8.2 @@ -46,7 +46,6 @@ FROM registry.access.redhat.com/ubi8/ubi:latest as oai-physim RUN yum update -y && \ yum install -y --enablerepo="ubi-8-codeready-builder" \ lksctp-tools \ - nettle \ tzdata \ atlas \ hostname \ diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c index 86f0558611..eb8c7ee98e 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c @@ -21,62 +21,61 @@ #include "nr_pdcp_integrity_nia2.h" +#include #include #include #include -#include -void *nr_pdcp_integrity_nia2_init(unsigned char *integrity_key) +#include "common/utils/assertions.h" +#include "openair3/SECU/aes_128.h" +#include "openair3/SECU/aes_128_cbc_cmac.h" + +void *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]) { - CMAC_CTX *ctx; - - ctx = CMAC_CTX_new(); - if (ctx == NULL) abort(); - - CMAC_Init(ctx, integrity_key, 16, EVP_aes_128_cbc(), NULL); + // This is a hack. Reduce the 3 functions to just cipher? + // No. The overhead is x8 times more. Don't change before measuring + // return integrity_key; + cbc_cmac_ctx_t* ctx = calloc(1, sizeof(cbc_cmac_ctx_t)); + DevAssert(ctx != NULL && "Memory exhausted"); + *ctx = init_aes_128_cbc_cmac(integrity_key); return ctx; } -static void compute_t(unsigned char *t, uint32_t count, int bearer, - int direction) +void nr_pdcp_integrity_nia2_integrity(void *integrity_context, unsigned char *out, unsigned char *buffer, int length, int bearer, int count, int direction) { - t[0] = (count >> 24) & 255; - t[1] = (count >> 16) & 255; - t[2] = (count >> 8) & 255; - t[3] = (count ) & 255; - t[4] = ((bearer-1) << 3) | (direction << 2); - memset(&t[5], 0, 8-5); -} + DevAssert(integrity_context != NULL); + DevAssert(out != NULL); + DevAssert(buffer != NULL); + DevAssert(length > -1); + // Strange range: [1-32] instead of [0-31] + DevAssert(bearer > 0 && bearer < 33); + DevAssert(count > -1); -void nr_pdcp_integrity_nia2_integrity(void *integrity_context, - unsigned char *out, - unsigned char *buffer, int length, - int bearer, int count, int direction) -{ - CMAC_CTX *ctx = integrity_context; - unsigned char t[8]; - unsigned char mac[16]; - size_t maclen; + cbc_cmac_ctx_t* ctx = (cbc_cmac_ctx_t*)integrity_context; - /* see 33.401 B.2.3 for the input to 128-EIA2 - * (which is identical to 128-NIA2, see 33.501 D.3.1.3) */ - compute_t(t, count, bearer, direction); + aes_128_t k_iv = {0}; + memcpy(&k_iv.key, ctx->key, sizeof(k_iv.key)); + k_iv.type = AES_INITIALIZATION_VECTOR_8; + k_iv.iv8.d.bearer = bearer -1; + k_iv.iv8.d.direction = direction; + k_iv.iv8.d.count = htonl(count); - CMAC_Init(ctx, NULL, 0, NULL, NULL); - CMAC_Update(ctx, t, 8); - CMAC_Update(ctx, buffer, length); - CMAC_Final(ctx, mac, &maclen); + uint8_t result[16] = {0}; + byte_array_t msg = {.buf = buffer, .len = length}; + + cipher_aes_128_cbc_cmac((cbc_cmac_ctx_t*)integrity_context, &k_iv, msg, sizeof(result), result); + //aes_128_cbc_cmac(&k_iv, msg, sizeof(result), result); - /* AES CMAC (RFC 4493) outputs 128 bits but NR PDCP PDUs have a MAC-I of - * 32 bits (see 38.323 6.2). RFC 4493 2.1 says to truncate most significant - * bit first (so seems to say 33.401 B.2.3) - */ - memcpy(out, mac, 4); + // AES CMAC (RFC 4493) outputs 128 bits but NR PDCP PDUs have a MAC-I of + // 32 bits (see 38.323 6.2). RFC 4493 2.1 says to truncate most significant + // bit first (so seems to say 33.401 B.2.3) + // Precondition: out should have enough space... + memcpy(out, result, 4); } void nr_pdcp_integrity_nia2_free_integrity(void *integrity_context) { - CMAC_CTX *ctx = integrity_context; - CMAC_CTX_free(ctx); + free( ( cbc_cmac_ctx_t*) integrity_context); } + diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h index 9593a2825c..53f117aafb 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.h @@ -22,7 +22,9 @@ #ifndef _NR_PDCP_INTEGRITY_NIA2_H_ #define _NR_PDCP_INTEGRITY_NIA2_H_ -void *nr_pdcp_integrity_nia2_init(unsigned char *integrity_key); +#include + +void *nr_pdcp_integrity_nia2_init(uint8_t integrity_key[16]); void nr_pdcp_integrity_nia2_integrity(void *integrity_context, unsigned char *out, diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c index a7479a5032..c46a9d59fe 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_security_nea2.c @@ -19,55 +19,50 @@ * contact@openairinterface.org */ +#include "openair3/SECU/aes_128_ctr.h" +#include "common/utils/assertions.h" + #include "nr_pdcp_security_nea2.h" +#include +#include #include #include -#include -#include -#include - -#ifndef NETTLE_VERSION_MAJOR -/* hack: include bignum.h, not version.h because version.h does not exist - * in old versions and bignum.h includes version.h (as of today). - * May completely fail to work... maybe we should skip support of old - * versions of nettle. - */ -#include -#endif void *nr_pdcp_security_nea2_init(unsigned char *ciphering_key) { - void *ctx = calloc(1, nettle_aes128.context_size); - if (ctx == NULL) exit(1); - -#if !defined(NETTLE_VERSION_MAJOR) || NETTLE_VERSION_MAJOR < 3 - nettle_aes128.set_encrypt_key(ctx, 16, ciphering_key); -#else - nettle_aes128.set_encrypt_key(ctx, ciphering_key); -#endif - - return ctx; + // This is a hack, IMO init, cipher and free functions should be reduced to cipher. + // Test show a ~10% more processing time + return ciphering_key; } -void nr_pdcp_security_nea2_cipher(void *security_context, - unsigned char *buffer, int length, - int bearer, int count, int direction) +void nr_pdcp_security_nea2_cipher(void *security_context, unsigned char *buffer, int length, int bearer, int count, int direction) { - unsigned char t[16] = {0}; + DevAssert(security_context != NULL); + DevAssert(buffer != NULL); + DevAssert(length > 0); + DevAssert(bearer > -1 && bearer < 32); + DevAssert(direction > -1 && direction < 2); + DevAssert(count > -1); - t[0] = (count >> 24) & 255; - t[1] = (count >> 16) & 255; - t[2] = (count >> 8) & 255; - t[3] = (count ) & 255; - t[4] = ((bearer-1) << 3) | (direction << 2); + aes_128_t p = {0}; + const uint8_t *ciphering_key = (uint8_t const *)security_context; + memcpy(p.key, ciphering_key, 16); + p.type = AES_INITIALIZATION_VECTOR_16; + p.iv16.d.count = ntohl(count); + p.iv16.d.bearer = bearer; + p.iv16.d.direction = direction; - nettle_ctr_crypt(security_context, nettle_aes128.encrypt, - nettle_aes128.block_size, t, - length, buffer, buffer); + uint8_t out[length]; + memset(out, 0, length); + + byte_array_t msg = {.buf = buffer, .len = length};; + aes_128_ctr(&p, msg, length, out); + + memcpy(buffer, out, length); } void nr_pdcp_security_nea2_free_security(void *security_context) { - free(security_context); + (void)security_context; } diff --git a/openair2/UTIL/ALG_DS/CMakeLists.txt b/openair2/UTIL/ALG_DS/CMakeLists.txt new file mode 100644 index 0000000000..2b16a1b9c9 --- /dev/null +++ b/openair2/UTIL/ALG_DS/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(DS) + diff --git a/openair2/UTIL/ALG_DS/README.md b/openair2/UTIL/ALG_DS/README.md new file mode 100644 index 0000000000..dd9403dad3 --- /dev/null +++ b/openair2/UTIL/ALG_DS/README.md @@ -0,0 +1,2 @@ +Algorithms and Data Structures in C for common usage + diff --git a/openair2/UTIL/CMakeLists.txt b/openair2/UTIL/CMakeLists.txt new file mode 100644 index 0000000000..7ba707d9da --- /dev/null +++ b/openair2/UTIL/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(ALG_DS) + diff --git a/openair2/UTIL/OSA/README b/openair2/UTIL/OSA/README index 1e54944308..c605ebf379 100644 --- a/openair2/UTIL/OSA/README +++ b/openair2/UTIL/OSA/README @@ -1,7 +1,6 @@ OAISIM Security Algorithms v0.1 Dependencies: -- nettle >= 2.5 - openssl >= 1.0.1 Compilation: diff --git a/openair2/UTIL/OSA/osa_defs.h b/openair2/UTIL/OSA/osa_defs.h index 84cdf4aac3..c175f10c84 100644 --- a/openair2/UTIL/OSA/osa_defs.h +++ b/openair2/UTIL/OSA/osa_defs.h @@ -22,7 +22,7 @@ /*! * \file secu_defs.h * \brief Openair security algorithms functions implementing 3GPP TS 33.401 - * \note HMAC computations are done by nettle/openssl library + * \note HMAC computations are done by openssl library * \author Sebastien ROUX * \date 2013 * \version 0.1 diff --git a/openair2/UTIL/OSA/osa_key_deriver.c b/openair2/UTIL/OSA/osa_key_deriver.c index a685eec729..06c0a3f9b2 100644 --- a/openair2/UTIL/OSA/osa_key_deriver.c +++ b/openair2/UTIL/OSA/osa_key_deriver.c @@ -19,31 +19,15 @@ * contact@openairinterface.org */ -#include +#include "../../../openair3/SECU/kdf.h" -#include +#include +#include #include "osa_defs.h" #include "osa_internal.h" #include "common/utils/LOG/log.h" -static inline -void kdf(const uint8_t *s, const uint32_t s_length, const uint8_t *key, - const uint32_t key_length, uint8_t **out, uint32_t out_length) -{ - struct hmac_sha256_ctx ctx; - - uint8_t *buffer; - - buffer = malloc(sizeof(uint8_t) * out_length); - - hmac_sha256_set_key(&ctx, key_length, key); - hmac_sha256_update(&ctx, s_length, s); - hmac_sha256_digest(&ctx, out_length, buffer); - - *out = buffer; -} - /*! * @brief Derive the keys from key and perform truncate on the generated key to * reduce his size to 128 bits. Definition of the derivation function can @@ -58,10 +42,9 @@ void kdf(const uint8_t *s, const uint32_t s_length, const uint8_t *key, * @param[out] out Pointer to reference where output of KDF will be stored. * NOTE: knas is dynamically allocated by the KDF function */ -int derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, - const uint8_t key[32], uint8_t **out) +int derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, const uint8_t key[32], uint8_t **out) { - uint8_t string[7]; + uint8_t string[7] = {0}; /* FC */ string[0] = FC_ALG_KEY_DER; @@ -84,7 +67,7 @@ int derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, { int i; char payload[6 * sizeof(string) + 1]; - int index = 0; + int index = 0; for (i = 0; i < sizeof(string); i++) index += sprintf(&payload[index], "0x%02x ", string[i]); @@ -93,13 +76,17 @@ int derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, } #endif - kdf(string, 7, key, 32, out, 32); + byte_array_t data = {.buf = string, .len = 7}; + if (*out == NULL) { + *out = malloc(sizeof(uint8_t) * 32); + DevAssert(*out != NULL && "Memory exhausted"); + } + kdf(key, data, 32, *out); return 0; } -int nr_derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, - const uint8_t key[32], uint8_t **out) +int nr_derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, const uint8_t key[32], uint8_t **out) { uint8_t string[7]; @@ -120,17 +107,23 @@ int nr_derive_key(algorithm_type_dist_t alg_type, uint8_t alg_id, string[5] = 0x00; string[6] = 0x01; - kdf(string, 7, key, 32, out, 32); + byte_array_t data = {.buf = string, .len = 7}; + + if (*out == NULL) { + *out = malloc(sizeof(uint8_t) * 32); + DevAssert(*out != NULL && "Memory exhausted"); + } + kdf(key, data, 32, *out); + // in NR, we use the last 16 bytes, ignoring the first 16 ones - memcpy(*out, *out+16, 16); + memcpy(*out, *out + 16, 16); return 0; } int nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t key[32], uint8_t *key_ng_ran_star) { - uint8_t *out; - uint8_t s[10]; + uint8_t s[10] = {0}; /* FC */ s[0] = NR_FC_ALG_KEY_NG_RAN_STAR_DER; @@ -152,18 +145,17 @@ int nr_derive_key_ng_ran_star(uint16_t pci, uint64_t nr_arfcn_dl, const uint8_t s[8] = 0x00; s[9] = 0x03; - kdf(s, 10, key, 32, &out, 32); - memcpy(key_ng_ran_star, out, 32); - free(out); + byte_array_t data = {.buf = s, .len = 10}; + const uint32_t len_key = 32; + kdf(key, data, len_key, key_ng_ran_star); return 0; } int derive_skgNB(const uint8_t *keNB, const uint16_t sk_counter, uint8_t *skgNB) { - uint8_t *out; - uint8_t s[5]; + uint8_t s[5] = {0}; /* FC is 0x1c (see 3gpp 33.401 annex A.15) */ s[0] = 0x1c; @@ -176,10 +168,8 @@ int derive_skgNB(const uint8_t *keNB, const uint16_t sk_counter, uint8_t *skgNB) s[3] = 0x00; s[4] = 0x02; - kdf(s, 5, keNB, 32, &out, 32); - - memcpy(skgNB, out, 32); - free(out); + byte_array_t data = {.buf = s, .len = 5}; + kdf(keNB, data, 32, skgNB); return 0; } diff --git a/openair2/UTIL/OSA/osa_stream_eea.c b/openair2/UTIL/OSA/osa_stream_eea.c index 03e95008ca..61f003bc32 100644 --- a/openair2/UTIL/OSA/osa_stream_eea.c +++ b/openair2/UTIL/OSA/osa_stream_eea.c @@ -19,16 +19,15 @@ * contact@openairinterface.org */ +#include #include #include #include #include -#include -#include -#include - #include "common/utils/LOG/log.h" +#include "byte_array.h" +#include "openair3//SECU//aes_128_ctr.h" #include "assertions.h" #include "osa_defs.h" @@ -44,10 +43,13 @@ int stream_encrypt_eea0(stream_cipher_t *stream_cipher, uint8_t **out) DevAssert(stream_cipher != NULL); DevAssert(out != NULL); - LOG_D(OSA, "Entering stream_encrypt_eea0, bits length %u, bearer %u, " - "count %u, direction %s\n", stream_cipher->blength, - stream_cipher->bearer, stream_cipher->count, stream_cipher->direction == SECU_DIRECTION_DOWNLINK ? - "Downlink" : "Uplink"); + LOG_D(OSA, + "Entering stream_encrypt_eea0, bits length %u, bearer %u, " + "count %u, direction %s\n", + stream_cipher->blength, + stream_cipher->bearer, + stream_cipher->count, + stream_cipher->direction == SECU_DIRECTION_DOWNLINK ? "Downlink" : "Uplink"); byte_length = (stream_cipher->blength + 7) >> 3; @@ -59,7 +61,7 @@ int stream_encrypt_eea0(stream_cipher_t *stream_cipher, uint8_t **out) data = *out; } - memcpy (data, stream_cipher->message, byte_length); + memcpy(data, stream_cipher->message, byte_length); return 0; } @@ -67,32 +69,32 @@ int stream_encrypt_eea0(stream_cipher_t *stream_cipher, uint8_t **out) int stream_encrypt_eea1(stream_cipher_t *stream_cipher, uint8_t **out) { osa_snow_3g_context_t snow_3g_context; - int n ; - int i = 0; - uint32_t zero_bit = 0; + int n; + int i = 0; + uint32_t zero_bit = 0; uint32_t *KS; - uint32_t K[4],IV[4]; + uint32_t K[4], IV[4]; DevAssert(stream_cipher != NULL); DevAssert(stream_cipher->key != NULL); DevAssert(stream_cipher->key_length == 16); DevAssert(out != NULL); - n = ( stream_cipher->blength + 31 ) / 32; + n = (stream_cipher->blength + 31) / 32; zero_bit = stream_cipher->blength & 0x7; memset(&snow_3g_context, 0, sizeof(snow_3g_context)); /*Initialisation*/ /* Load the confidentiality key for SNOW 3G initialization as in section 3.4. */ - memcpy(K+3,stream_cipher->key+0,4); /*K[3] = key[0]; we assume - K[3]=key[0]||key[1]||...||key[31] , with key[0] the - * most important bit of key*/ - memcpy(K+2,stream_cipher->key+4,4); /*K[2] = key[1];*/ - memcpy(K+1,stream_cipher->key+8,4); /*K[1] = key[2];*/ - memcpy(K+0,stream_cipher->key+12,4); /*K[0] = key[3]; we assume - K[0]=key[96]||key[97]||...||key[127] , with key[127] the - * least important bit of key*/ + memcpy(K + 3, stream_cipher->key + 0, 4); /*K[3] = key[0]; we assume + K[3]=key[0]||key[1]||...||key[31] , with key[0] the + * most important bit of key*/ + memcpy(K + 2, stream_cipher->key + 4, 4); /*K[2] = key[1];*/ + memcpy(K + 1, stream_cipher->key + 8, 4); /*K[1] = key[2];*/ + memcpy(K + 0, stream_cipher->key + 12, 4); /*K[0] = key[3]; we assume + K[0]=key[96]||key[97]||...||key[127] , with key[127] the + * least important bit of key*/ K[3] = hton_int32(K[3]); K[2] = hton_int32(K[2]); K[1] = hton_int32(K[1]); @@ -106,26 +108,25 @@ int stream_encrypt_eea1(stream_cipher_t *stream_cipher, uint8_t **out) /* Run SNOW 3G algorithm to generate sequence of key stream bits KS*/ osa_snow3g_initialize(K, IV, &snow_3g_context); - KS = (uint32_t *)malloc(4*n); - osa_snow3g_generate_key_stream(n,(uint32_t*)KS, &snow_3g_context); + KS = (uint32_t *)malloc(4 * n); + osa_snow3g_generate_key_stream(n, (uint32_t *)KS, &snow_3g_context); if (zero_bit > 0) { - KS[n - 1] = KS[n - 1] & (uint32_t)(0xFFFFFFFF << (8 - zero_bit)); } - for (i=0; imessage[i] ^= *(((uint8_t*)KS)+i); + for (i = 0; i < n * 4; i++) { + stream_cipher->message[i] ^= *(((uint8_t *)KS) + i); } if (zero_bit > 0) { - int ceil_index = (stream_cipher->blength+7) >> 3; + int ceil_index = (stream_cipher->blength + 7) >> 3; stream_cipher->message[ceil_index - 1] = stream_cipher->message[ceil_index - 1] & (uint8_t)(0xFF << (8 - zero_bit)); } @@ -136,91 +137,32 @@ int stream_encrypt_eea1(stream_cipher_t *stream_cipher, uint8_t **out) int stream_encrypt_eea2(stream_cipher_t *stream_cipher, uint8_t **out) { - uint8_t m[16]; - uint32_t local_count; - - void *ctx; - uint8_t *data = NULL; - - uint32_t zero_bit = 0; - uint32_t byte_length; - DevAssert(stream_cipher != NULL); - DevAssert(out != NULL); - - LOG_D(OSA, "Entering stream_encrypt_eea2, bits length %u, bearer %u, " - "count %u, direction %s\n", stream_cipher->blength, - stream_cipher->bearer, stream_cipher->count, stream_cipher->direction == SECU_DIRECTION_DOWNLINK ? - "Downlink" : "Uplink"); - - zero_bit = stream_cipher->blength & 0x7; - - byte_length = stream_cipher->blength >> 3; - - if (zero_bit > 0) - byte_length += 1; - - ctx = malloc(nettle_aes128.context_size); + DevAssert(stream_cipher->key != NULL); + DevAssert(stream_cipher->key_length == 32); + DevAssert(stream_cipher->bearer < 32); + DevAssert(stream_cipher->direction < 2); + DevAssert(stream_cipher->message != NULL); + DevAssert(stream_cipher->blength > 7); if (*out == NULL) { - /* User provided output buffer */ - data = malloc(byte_length); - *out = data; - } else { - data = *out; + *out = calloc(1, (stream_cipher->blength >> 3) + 1); + DevAssert(*out != NULL && "Memory exhausted"); } - local_count = hton_int32(stream_cipher->count); - - memset(m, 0, sizeof(m)); - memcpy(&m[0], &local_count, 4); - m[4] = ((stream_cipher->bearer & 0x1F) << 3) | - ((stream_cipher->direction & 0x01) << 2); - /* Other bits are 0 */ - -#if defined(SECU_DEBUG) - { - int i; - char payload[6 * byte_length + 1]; - int index = 0; - - for (i = 0; i < byte_length; i++) - index += sprintf(&payload[index], "0x%02x ", stream_cipher->message[i]); - - LOG_D(OSA, "Original message: %s\n", payload); - } -#endif - -#if NETTLE_VERSION_MAJOR < 3 - nettle_aes128.set_encrypt_key(ctx, stream_cipher->key_length, - stream_cipher->key); -#else - nettle_aes128.set_encrypt_key(ctx, - stream_cipher->key); -#endif - - nettle_ctr_crypt(ctx, nettle_aes128.encrypt, - nettle_aes128.block_size, m, - byte_length, data, stream_cipher->message); - - if (zero_bit > 0) - data[byte_length - 1] = data[byte_length - 1] & (uint8_t)(0xFF << (8 - zero_bit)); - - free(ctx); - -#if defined(SECU_DEBUG) - { - int i; - char payload[6 * byte_length + 1]; - int index = 0; - - for (i = 0; i < byte_length; i++) - index += sprintf(&payload[index], "0x%02x ", data[i]); - - LOG_D(OSA, "Encrypted message: %s\n", payload); - } -#endif + aes_128_t p = {0}; + memcpy(p.key, stream_cipher->key, stream_cipher->key_length); + p.type = AES_INITIALIZATION_VECTOR_16; + p.iv16.d.count = htonl(stream_cipher->count); + p.iv16.d.bearer = stream_cipher->bearer; + p.iv16.d.direction = stream_cipher->direction; + DevAssert((stream_cipher->blength & 0x07) == 0 && "Cipher length must be multiple of one octet"); + const uint32_t byte_lenght = stream_cipher->blength >> 3; + // Precondition: out must have enough space, at least as much as the input + const size_t len_out = byte_lenght; + byte_array_t msg = {.buf = stream_cipher->message, .len =byte_lenght}; + aes_128_ctr(&p, msg, len_out, *out); return 0; } diff --git a/openair2/UTIL/OSA/osa_stream_eia.c b/openair2/UTIL/OSA/osa_stream_eia.c index 980a4ae3c1..da4fd23e18 100644 --- a/openair2/UTIL/OSA/osa_stream_eia.c +++ b/openair2/UTIL/OSA/osa_stream_eia.c @@ -19,6 +19,7 @@ * contact@openairinterface.org */ +#include #include #include #include @@ -26,12 +27,9 @@ #include #include "assertions.h" +#include "common/utils/LOG/log.h" -#include -#include -#include - -#include "common/utils//LOG/log.h" +#include "openair3/SECU/aes_128_cbc_cmac.h" #include "osa_defs.h" #include "osa_snow3g.h" @@ -49,7 +47,7 @@ */ uint64_t OSA_MUL64x(uint64_t V, uint64_t c) { - if ( V & 0x8000000000000000 ) + if (V & 0x8000000000000000) return (V << 1) ^ c; else return V << 1; @@ -65,10 +63,10 @@ function. */ uint64_t OSA_MUL64xPOW(uint64_t V, uint32_t i, uint64_t c) { - if ( i == 0) + if (i == 0) return V; else - return OSA_MUL64x( OSA_MUL64xPOW(V,i-1,c) , c); + return OSA_MUL64x(OSA_MUL64xPOW(V, i - 1, c), c); } /* OSA_MUL64. * Input V: a 64-bit input. @@ -84,33 +82,32 @@ uint64_t OSA_MUL64(uint64_t V, uint64_t P, uint64_t c) uint64_t result = 0; int i = 0; - for ( i=0; i<64; i++) { - if( ( P>>i ) & 0x1 ) - result ^= OSA_MUL64xPOW(V,i,c); + for (i = 0; i < 64; i++) { + if ((P >> i) & 0x1) + result ^= OSA_MUL64xPOW(V, i, c); } return result; } /* osa_mask32bit. -* Input n: an integer in 1-32. -* Output : a 32 bit mask. -* Prepares a 32 bit mask with required number of 1 bits on the MSB side. -*/ + * Input n: an integer in 1-32. + * Output : a 32 bit mask. + * Prepares a 32 bit mask with required number of 1 bits on the MSB side. + */ uint32_t osa_mask32bit(int n) { - uint32_t mask=0x0; + uint32_t mask = 0x0; - if ( n%32 == 0 ) + if (n % 32 == 0) return 0xffffffff; while (n--) - mask = (mask>>1) ^ 0x80000000; + mask = (mask >> 1) ^ 0x80000000; return mask; } - /*! * @brief Create integrity cmac t for a given message. * @param[in] stream_cipher Structure containing various variables to setup encoding @@ -119,29 +116,29 @@ uint32_t osa_mask32bit(int n) int stream_compute_integrity_eia1(stream_cipher_t *stream_cipher, uint8_t out[4]) { osa_snow_3g_context_t snow_3g_context; - uint32_t K[4],IV[4], z[5]; - int i=0,D; - uint32_t MAC_I = 0; - uint64_t EVAL; - uint64_t V; - uint64_t P; - uint64_t Q; - uint64_t c; - uint64_t M_D_2; - int rem_bits; - uint32_t mask = 0; - uint32_t *message; + uint32_t K[4], IV[4], z[5]; + int i = 0, D; + uint32_t MAC_I = 0; + uint64_t EVAL; + uint64_t V; + uint64_t P; + uint64_t Q; + uint64_t c; + uint64_t M_D_2; + int rem_bits; + uint32_t mask = 0; + uint32_t *message; - message = (uint32_t*)stream_cipher->message; /* To operate 32 bit message internally. */ + message = (uint32_t *)stream_cipher->message; /* To operate 32 bit message internally. */ /* Load the Integrity Key for SNOW3G initialization as in section 4.4. */ - memcpy(K+3,stream_cipher->key+0,4); /*K[3] = key[0]; we assume - K[3]=key[0]||key[1]||...||key[31] , with key[0] the - * most important bit of key*/ - memcpy(K+2,stream_cipher->key+4,4); /*K[2] = key[1];*/ - memcpy(K+1,stream_cipher->key+8,4); /*K[1] = key[2];*/ - memcpy(K+0,stream_cipher->key+12,4); /*K[0] = key[3]; we assume - K[0]=key[96]||key[97]||...||key[127] , with key[127] the - * least important bit of key*/ + memcpy(K + 3, stream_cipher->key + 0, 4); /*K[3] = key[0]; we assume + K[3]=key[0]||key[1]||...||key[31] , with key[0] the + * most important bit of key*/ + memcpy(K + 2, stream_cipher->key + 4, 4); /*K[2] = key[1];*/ + memcpy(K + 1, stream_cipher->key + 8, 4); /*K[1] = key[2];*/ + memcpy(K + 0, stream_cipher->key + 12, 4); /*K[0] = key[3]; we assume + K[0]=key[96]||key[97]||...||key[127] , with key[127] the + * least important bit of key*/ K[3] = hton_int32(K[3]); K[2] = hton_int32(K[2]); K[1] = hton_int32(K[1]); @@ -150,45 +147,45 @@ int stream_compute_integrity_eia1(stream_cipher_t *stream_cipher, uint8_t out[4] section 4.4. */ IV[3] = (uint32_t)stream_cipher->count; IV[2] = ((((uint32_t)stream_cipher->bearer) & 0x0000001F) << 27); - IV[1] = (uint32_t)(stream_cipher->count) ^ ( (uint32_t)(stream_cipher->direction) << 31 ) ; + IV[1] = (uint32_t)(stream_cipher->count) ^ ((uint32_t)(stream_cipher->direction) << 31); IV[0] = ((((uint32_t)stream_cipher->bearer) & 0x0000001F) << 27) ^ ((uint32_t)(stream_cipher->direction & 0x00000001) << 15); - //printf ("K:\n"); - //hexprint(K, 16); - //printf ("K[0]:%08X\n",K[0]); - //printf ("K[1]:%08X\n",K[1]); - //printf ("K[2]:%08X\n",K[2]); - //printf ("K[3]:%08X\n",K[3]); + // printf ("K:\n"); + // hexprint(K, 16); + // printf ("K[0]:%08X\n",K[0]); + // printf ("K[1]:%08X\n",K[1]); + // printf ("K[2]:%08X\n",K[2]); + // printf ("K[3]:%08X\n",K[3]); - //printf ("IV:\n"); - //hexprint(IV, 16); - //printf ("IV[0]:%08X\n",IV[0]); - //printf ("IV[1]:%08X\n",IV[1]); - //printf ("IV[2]:%08X\n",IV[2]); - //printf ("IV[3]:%08X\n",IV[3]); + // printf ("IV:\n"); + // hexprint(IV, 16); + // printf ("IV[0]:%08X\n",IV[0]); + // printf ("IV[1]:%08X\n",IV[1]); + // printf ("IV[2]:%08X\n",IV[2]); + // printf ("IV[3]:%08X\n",IV[3]); z[0] = z[1] = z[2] = z[3] = z[4] = 0; /* Run SNOW 3G to produce 5 keystream words z_1, z_2, z_3, z_4 and z_5. */ osa_snow3g_initialize(K, IV, &snow_3g_context); osa_snow3g_generate_key_stream(5, z, &snow_3g_context); - //printf ("z[0]:%08X\n",z[0]); - //printf ("z[1]:%08X\n",z[1]); - //printf ("z[2]:%08X\n",z[2]); - //printf ("z[3]:%08X\n",z[3]); - //printf ("z[4]:%08X\n",z[4]); + // printf ("z[0]:%08X\n",z[0]); + // printf ("z[1]:%08X\n",z[1]); + // printf ("z[2]:%08X\n",z[2]); + // printf ("z[3]:%08X\n",z[3]); + // printf ("z[4]:%08X\n",z[4]); P = ((uint64_t)z[0] << 32) | (uint64_t)z[1]; Q = ((uint64_t)z[2] << 32) | (uint64_t)z[3]; - //printf ("P:%16lX\n",P); - //printf ("Q:%16lX\n",Q); + // printf ("P:%16lX\n",P); + // printf ("Q:%16lX\n",Q); /* Calculation */ - D = ceil( stream_cipher->blength / 64.0 ) + 1; - //printf ("D:%d\n",D); + D = ceil(stream_cipher->blength / 64.0) + 1; + // printf ("D:%d\n",D); EVAL = 0; c = 0x1b; /* for 0 <= i <= D-3 */ - for (i=0; i 32) { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) << 32 ) | - (uint64_t) (hton_int32(message[2*(D-2)+1]) & mask); + M_D_2 = ((uint64_t)hton_int32(message[2 * (D - 2)]) << 32) | (uint64_t)(hton_int32(message[2 * (D - 2) + 1]) & mask); } else { - M_D_2 = ( (uint64_t) hton_int32(message[2*(D-2)]) & mask) << 32 ; + M_D_2 = ((uint64_t)hton_int32(message[2 * (D - 2)]) & mask) << 32; } V = EVAL ^ M_D_2; - EVAL = OSA_MUL64(V,P,c); + EVAL = OSA_MUL64(V, P, c); /* for D-1 */ EVAL ^= stream_cipher->blength; /* Multiply by Q */ - EVAL = OSA_MUL64(EVAL,Q,c); + EVAL = OSA_MUL64(EVAL, Q, c); MAC_I = (uint32_t)(EVAL >> 32) ^ z[4]; - //printf ("MAC_I:%16X\n",MAC_I); + // printf ("MAC_I:%16X\n",MAC_I); MAC_I = hton_int32(MAC_I); memcpy(out, &MAC_I, 4); return 0; @@ -221,65 +217,28 @@ int stream_compute_integrity_eia1(stream_cipher_t *stream_cipher, uint8_t out[4] int stream_compute_integrity_eia2(stream_cipher_t *stream_cipher, uint8_t out[4]) { - uint8_t *m; - uint32_t local_count; - size_t size = 4; - - uint8_t data[16]; - - CMAC_CTX *cmac_ctx; - - uint32_t zero_bit = 0; - uint32_t m_length; - DevAssert(stream_cipher != NULL); - DevAssert(stream_cipher->key != NULL); + ; + DevAssert((stream_cipher->blength & 0x7) == 0); + DevAssert(stream_cipher->key_length == 16 && "k_iv.key and stream_cipher mismatch"); + DevAssert(stream_cipher->bearer < 32); + DevAssert(stream_cipher->direction < 2); - memset(data, 0, 16); + aes_128_t k_iv = {0}; + memcpy(k_iv.key, stream_cipher->key, sizeof(k_iv.key)); + k_iv.type = AES_INITIALIZATION_VECTOR_8; - zero_bit = stream_cipher->blength & 0x7; + k_iv.iv8.d.bearer = stream_cipher->bearer; + k_iv.iv8.d.direction = stream_cipher->direction; + k_iv.iv8.d.count = htonl(stream_cipher->count); - m_length = stream_cipher->blength >> 3; + const size_t m_length = stream_cipher->blength >> 3; - if (zero_bit > 0) - m_length += 1; - - local_count = hton_int32(stream_cipher->count); - - m = calloc(m_length + 8, sizeof(uint8_t)); - - memcpy(&m[0], &local_count, 4); - m[4] = ((stream_cipher->bearer & 0x1F) << 3) | ((stream_cipher->direction & 0x01) << 2); - - memcpy(&m[8], stream_cipher->message, m_length); - -#if defined(SECU_DEBUG) - { - int i; - char payload[6 * sizeof(m) + 1]; - int index = 0; - LOG_T(OSA, "Blength: %u, Zero bits: %u\n", - stream_cipher->blength, zero_bit); - - for (i = 0; i < sizeof(m); i++) - index += sprintf(&payload[index], "0x%02x ", m[i]); - - LOG_D(OSA, "Payload: %s\n", payload); - } -#endif - - cmac_ctx = CMAC_CTX_new(); - - CMAC_Init(cmac_ctx, stream_cipher->key, stream_cipher->key_length, EVP_aes_128_cbc(), NULL); - CMAC_Update(cmac_ctx, m, m_length + 8); - - CMAC_Final(cmac_ctx, data, &size); - - CMAC_CTX_free(cmac_ctx); - - memcpy(out, data, 4); - free(m); + uint8_t result[16] = {0}; + byte_array_t msg = {.len = m_length, .buf = stream_cipher->message }; + aes_128_cbc_cmac(&k_iv, msg, sizeof(result), result); + memcpy(out, result, 4); return 0; } @@ -307,10 +266,18 @@ int stream_check_integrity(uint8_t algorithm, stream_cipher_t *stream_cipher, ui } if (memcmp(result, expected, 4) != 0) { - LOG_E(OSA, "Mismatch found in integrity for algorithm %u,\n" + LOG_E(OSA, + "Mismatch found in integrity for algorithm %u,\n" "\tgot %02x.%02x.%02x.%02x, expecting %02x.%02x.%02x.%02x\n", - algorithm, result[0], result[1], result[2], result[3], expected[0], - expected[1], expected[2], expected[3]); + algorithm, + result[0], + result[1], + result[2], + result[3], + expected[0], + expected[1], + expected[2], + expected[3]); return -1; } } diff --git a/openair3/DOCS/Latex/EPC/EPC.pdf b/openair3/DOCS/Latex/EPC/EPC.pdf deleted file mode 100644 index 598796e12838ba5d4c0f2486d16f09c301c871fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 243263 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 z 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 diff --git a/openair3/DOCS/Latex/EPC/EPC.tex b/openair3/DOCS/Latex/EPC/EPC.tex index f1df0a6a37..97e4f52565 100644 --- a/openair3/DOCS/Latex/EPC/EPC.tex +++ b/openair3/DOCS/Latex/EPC/EPC.tex @@ -544,7 +544,6 @@ Before compiling the core EPC, some packages should be installed on the platform \item flex and bison \item openssl-dev \item asn1c (see section \ref{sec:asn1c}) - \item libnettle (see section \ref{sec:libnettle}) \item freediameter (see section \ref{sec:freediameter}) and gnutls 3.1.0 (see section \ref{sec:gnutls}) \end{itemize} @@ -562,28 +561,6 @@ openssl-dev -y \subsection{ASN1c} \label{sec:asn1c} -\subsection{libnettle} -\label{sec:libnettle} - -The nettle library is used by freediameter for certificate encryption and by core -EPC for key derivation. -\begin{lstlisting} -wget ftp://ftp.lysator.liu.se/pub/security/lsh/nettle-2.5.tar.gz -gunzip nettle-2.5.tar.gz -tar -xvf nettle-2.5.tar -cd nettle-2.5/ -./configure --disable-openssl --enable-shared -make -make check -sudo make install -\end{lstlisting} -The commands provided above will download the required packages sources, configure -and install them on the system. -Note that any packages which is not in the Ubuntu repository is installed by default -in /usr/local instead of /usr. -This behaviour can be overriden at configuration time by providing --prefix=/usr to -the configuration script (configure). - \subsection{gnutls} \label{sec:gnutls} diff --git a/openair3/NAS/COMMON/nr_common.c b/openair3/NAS/COMMON/nr_common.c index 736d75f6cc..590ccf65ae 100644 --- a/openair3/NAS/COMMON/nr_common.c +++ b/openair3/NAS/COMMON/nr_common.c @@ -23,6 +23,7 @@ #include #include +#include void servingNetworkName(uint8_t *msg, char * imsiStr, int nmc_size) { //SNN-network-identifier in TS 24.501 @@ -59,8 +60,12 @@ int resToresStar(uint8_t *msg, uicc_t* uicc) { uint8_t ckik[sizeof(uicc->ck) +sizeof(uicc->ik)]; memcpy(ckik, uicc->ck, sizeof(uicc->ck)); memcpy(ckik+sizeof(uicc->ck),uicc->ik, sizeof(uicc->ik)); - uint8_t out[32]; - kdf(S, ptr-S, ckik, 32, out, sizeof(out)); + uint8_t out[32] = {0}; + assert(ptr-S == 32); + //kdf(S, ptr-S, ckik, 32, out, sizeof(out)); + byte_array_t data = {.buf = ckik, .len = 32}; + kdf(S, data, 32, out); + memcpy(msg, out+16, 16); return 16; } diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 8a600e0cf9..b24359a232 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -37,6 +37,7 @@ #include "nr_nas_msg_sim.h" #include "aka_functions.h" #include "secu_defs.h" +#include "kdf.h" #include "PduSessionEstablishRequest.h" #include "PduSessionEstablishmentAccept.h" #include "intertask_interface.h" @@ -188,18 +189,20 @@ void transferRES(uint8_t ck[16], uint8_t ik[16], uint8_t *input, uint8_t rand[16 oldS[33] = 0x08; - uint8_t key[32]; + uint8_t key[32] = {0}; memcpy(&key[0], ck, 16); memcpy(&key[16], ik, 16); //KEY - uint8_t out[32]; - kdf(key, 32, S, 31 + netNamesize, out, 32); - for (int i = 0; i < 16; i++) - output[i] = out[16 + i]; + uint8_t out[32] = {0}; + + byte_array_t data = {.buf = S, .len = 31 + netNamesize}; + kdf(key, data, 32, out); + + memcpy(output, out + 16, 16); } void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[32], uicc_t *uicc) { uint8_t S[100]={0}; - uint8_t key[32]; + uint8_t key[32] = {0}; memcpy(&key[0], ck, 16); memcpy(&key[16], ik, 16); //KEY @@ -213,7 +216,9 @@ void derive_kausf(uint8_t ck[16], uint8_t ik[16], uint8_t sqn[6], uint8_t kausf[ } S[9 + netNamesize] = 0x00; S[10 + netNamesize] = 0x06; - kdf(key, 32, S, 11 + netNamesize, kausf, 32); + + byte_array_t data = {.buf = S, .len = 11 + netNamesize}; + kdf(key, data, 32, kausf); } void derive_kseaf(uint8_t kausf[32], uint8_t kseaf[32], uicc_t *uicc) { @@ -223,12 +228,14 @@ void derive_kseaf(uint8_t kausf[32], uint8_t kseaf[32], uicc_t *uicc) { int netNamesize = strlen((char*)S+1); S[1 + netNamesize] = (uint8_t)((netNamesize & 0xff00) >> 8); S[2 + netNamesize] = (uint8_t)(netNamesize & 0x00ff); - kdf(kausf, 32, S, 3 + netNamesize, kseaf, 32); + + byte_array_t data = {.buf = S , .len = 3 + netNamesize}; + kdf(kausf, data, 32, kseaf); } void derive_kamf(uint8_t *kseaf, uint8_t *kamf, uint16_t abba, uicc_t* uicc) { int imsiLen = strlen(uicc->imsiStr); - uint8_t S[100]; + uint8_t S[100] = {0}; S[0] = 0x6D; //FC = 0x6D memcpy(&S[1], uicc->imsiStr, imsiLen ); S[1 + imsiLen] = (uint8_t)((imsiLen & 0xff00) >> 8); @@ -237,12 +244,14 @@ void derive_kamf(uint8_t *kseaf, uint8_t *kamf, uint16_t abba, uicc_t* uicc) { S[4 + imsiLen] = (abba & 0xff00) >> 8; S[5 + imsiLen] = 0x00; S[6 + imsiLen] = 0x02; - kdf(kseaf, 32, S, 7 + imsiLen, kamf, 32); + + byte_array_t data = {.buf = S, .len = 7 + imsiLen}; + kdf(kseaf, data, 32, kamf); } //------------------------------------------------------------------------------ void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t kamf[32], uint8_t *knas_int) { - uint8_t S[20]; + uint8_t S[20] = {0}; uint8_t out[32] = { 0 }; S[0] = 0x69; //FC S[1] = (uint8_t)(nas_alg_type & 0xFF); @@ -251,19 +260,20 @@ void derive_knas(algorithm_type_dist_t nas_alg_type, uint8_t nas_alg_id, uint8_t S[4] = nas_alg_id; S[5] = 0x00; S[6] = 0x01; - kdf(kamf, 32, S, 7, out, 32); - for (int i = 0; i < 16; i++) - knas_int[i] = out[16 + i]; + + byte_array_t data = {.buf = S, .len = 7}; + kdf(kamf, data, 32, out); + + memcpy(knas_int, out+16, 16); } void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb){ /* Compute the KDF input parameter * S = FC(0x6E) || UL NAS Count || 0x00 0x04 || 0x01 || 0x00 0x01 */ - uint8_t input[32]; + uint8_t input[32] = {0}; // uint16_t length = 4; // int offset = 0; - uint8_t out[32] = { 0 }; LOG_TRACE(INFO, "%s with count= %d", __FUNCTION__, count); memset(input, 0, 32); @@ -282,9 +292,9 @@ void derive_kgnb(uint8_t kamf[32], uint32_t count, uint8_t *kgnb){ input[8] = 0; input[9] = 1; - kdf(kamf, 32, input, 10, out, 32); - for (int i = 0; i < 32; i++) - kgnb[i] = out[i]; + byte_array_t data = {.buf = input, .len = 10}; + kdf(kamf, data, 32, kgnb); + printf("kgnb : "); for(int pp=0;pp<32;pp++) printf("%02x ",kgnb[pp]); diff --git a/openair3/NAS/UE/EMM/Authentication.c b/openair3/NAS/UE/EMM/Authentication.c index a387580b60..55b269f413 100644 --- a/openair3/NAS/UE/EMM/Authentication.c +++ b/openair3/NAS/UE/EMM/Authentication.c @@ -67,6 +67,7 @@ Description Defines the authentication EMM procedure executed by the #include "usim_api.h" #include "secu_defs.h" +#include "kdf.h" #include "Authentication.h" #include "executables/lte-softmodem.h" @@ -982,14 +983,10 @@ static int _authentication_kasme(const OctetString *autn, input_s[4],input_s[5],input_s[6],input_s[7], input_s[8],input_s[9],input_s[10],input_s[11], input_s[12],input_s[13]); - /* TODO !!! Compute the Kasme key */ - // todo_hmac_256(key, input_s, kasme->value); - kdf(key, - ck->length + ik->length , /*key_length*/ - input_s, - offset, - kasme->value, - kasme->length); + + assert(ck->length + ik->length == 32); + byte_array_t data = {.len = offset, .buf = input_s}; + kdf(key, data, kasme->length, kasme->value); LOG_TRACE(INFO,"EMM-PROC KASME (l=%d)%s", kasme->length, diff --git a/openair3/NAS/UE/EMM/SecurityModeControl.c b/openair3/NAS/UE/EMM/SecurityModeControl.c index fd3332669b..3386499663 100644 --- a/openair3/NAS/UE/EMM/SecurityModeControl.c +++ b/openair3/NAS/UE/EMM/SecurityModeControl.c @@ -63,6 +63,7 @@ Description Defines the security mode control EMM procedure executed by the # include "assertions.h" #include "secu_defs.h" +#include "kdf.h" #include "SecurityModeControl.h" #if defined(NAS_BUILT_IN_UE) @@ -484,7 +485,9 @@ static int _security_kenb(const OctetString *kasme, OctetString *kenb, input[5] = 0; input[6] = 4; - kdf(kasme->value, 32, input, 7, kenb->value, 32); + byte_array_t data = {.len = 7, .buf = input}; + kdf(kasme->value, data, 32, kenb->value); + kenb->length = 32; return (RETURNok); } @@ -536,8 +539,12 @@ static int _security_kdf(const OctetString *kasme, OctetString *key, input[5] = 0x00; input[6] = 0x01; + assert(kasme->length == 32); + byte_array_t data = {.len = 7, .buf=input}; /* Compute the derived key */ - kdf(kasme->value, kasme->length, input, 7, output, 32); + kdf(kasme->value, data, 32, output); + + memcpy(key->value, &output[31 - key->length + 1], key->length); return (RETURNok); } diff --git a/openair3/SECU/aes_128.h b/openair3/SECU/aes_128.h new file mode 100644 index 0000000000..7f7e550419 --- /dev/null +++ b/openair3/SECU/aes_128.h @@ -0,0 +1,88 @@ +/* + * 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 + */ + +#ifndef AES_128_OAI_H +#define AES_128_OAI_H + +#include +#include +#include + +// see 33.401 B.2.3 for the input to 128-EIA2 +// (which is identical to 128-NIA2, see 33.501 D.3.1.3) + +typedef struct { + uint32_t count; + uint8_t padding: 2; + uint8_t direction: 1; + uint8_t bearer: 5; + uint8_t pad_8; + uint16_t pad_16; + uint64_t pad_64; +} data_iv16_t; + +typedef struct{ + union{ + data_iv16_t d; + uint8_t iv[16]; + }; +} iv16_t; + +typedef struct { + uint32_t count; + uint8_t padding: 2; + uint8_t direction: 1; + uint8_t bearer: 5; + uint8_t pad_8; + uint16_t pad_16; +} data_iv8_t; + +typedef struct{ + union{ + data_iv8_t d; + uint8_t iv[8]; + }; +} iv8_t; + +typedef enum{ + AES_INITIALIZATION_VECTOR_8, + AES_INITIALIZATION_VECTOR_16, + + AES_INITIALIZATION_VECTOR_END, +} iv_e; + +#if __BYTE_ORDER == __BIG_ENDIAN +static_assert(0 != 0, "The struct iv_t needs little endianness"); +#endif + +// static_assert((16 == sizeof(iv_t)) , "Initialization Vector needs to have an AES_BLOCK_SIZE" ); + +typedef struct { + uint8_t key[16]; + iv_e type; + union{ + iv8_t iv8; + iv16_t iv16; + }; +} aes_128_t; + +#endif + diff --git a/openair3/SECU/aes_128_cbc_cmac.c b/openair3/SECU/aes_128_cbc_cmac.c new file mode 100644 index 0000000000..6b02f1673a --- /dev/null +++ b/openair3/SECU/aes_128_cbc_cmac.c @@ -0,0 +1,254 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include "aes_128_cbc_cmac.h" + +#include "common/utils/assertions.h" +#include + +#if OPENSSL_VERSION_MAJOR >= 3 +// code for version 3.0 or greater +#include + +static +const char *propq = NULL; + +void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) +{ + OSSL_LIB_CTX* library_context = OSSL_LIB_CTX_new(); + DevAssert(library_context != NULL); + + /* Fetch the CMAC implementation */ + EVP_MAC* mac = EVP_MAC_fetch(library_context, "CMAC", propq); + DevAssert(mac != NULL); + + /* Create a context for the CMAC operation */ + EVP_MAC_CTX* mctx = EVP_MAC_CTX_new(mac); + DevAssert(mctx != NULL); + + // The underlying cipher to be used + char cipher_name[] = "aes128"; + + OSSL_PARAM params[2] = {0}; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, cipher_name, + sizeof(cipher_name)); + params[1] = OSSL_PARAM_construct_end(); + + /* Initialise the CMAC operation */ + int rc = EVP_MAC_init(mctx, k_iv->key, sizeof(k_iv->key), params); + DevAssert(rc != 0); + + size_t sz_iv = 0; + uint8_t* iv = NULL; + if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ + sz_iv = 8; + iv = (uint8_t*)k_iv->iv8.iv; + } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { + sz_iv = 16; + iv = (uint8_t*)k_iv->iv16.iv; + } else { + DevAssert(0!=0 && "Unknwon Initialization vector"); + } + + /* Make one or more calls to process the data to be authenticated */ + rc = EVP_MAC_update(mctx, iv, sz_iv); + DevAssert(rc != 0); + + /* Make one or more calls to process the data to be authenticated */ + rc = EVP_MAC_update(mctx, msg.buf, msg.len); + DevAssert(rc != 0); + + /* Make a call to the final with a NULL buffer to get the length of the MAC */ + size_t out_len = 0; + rc = EVP_MAC_final(mctx, out, &out_len, len_out); + DevAssert(rc != 0); + + EVP_MAC_CTX_free(mctx); + EVP_MAC_free(mac); + OSSL_LIB_CTX_free(library_context); +} + +cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]) +{ + DevAssert(key != NULL); + + OSSL_LIB_CTX* library_context = OSSL_LIB_CTX_new(); + DevAssert(library_context != NULL); + + /* Fetch the CMAC implementation */ + EVP_MAC* mac = EVP_MAC_fetch(library_context, "CMAC", propq); + DevAssert(mac != NULL); + + cbc_cmac_ctx_t ctx = {.lib_ctx = library_context, .mac = mac }; + + assert(16 == sizeof(ctx.key)); + memcpy(ctx.key, key, 16); + + return ctx; +} + +void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) +{ + DevAssert(ctx != NULL); + DevAssert(k_iv != NULL); + /* Create a context for the CMAC operation */ + EVP_MAC_CTX* mctx = EVP_MAC_CTX_new(ctx->mac); + DevAssert(mctx != NULL); + + // The underlying cipher to be used + char cipher_name[] = "aes128"; + + OSSL_PARAM params[2] = {0}; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_CIPHER, cipher_name, + sizeof(cipher_name)); + params[1] = OSSL_PARAM_construct_end(); + + /* Initialise the CMAC operation */ + int rc = EVP_MAC_init(mctx, k_iv->key, sizeof(k_iv->key), params); + DevAssert(rc != 0); + + size_t sz_iv = 0; + uint8_t* iv = NULL; + if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ + sz_iv = 8; + iv = (uint8_t*)k_iv->iv8.iv; + } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { + sz_iv = 16; + iv = (uint8_t*)k_iv->iv16.iv; + } else { + DevAssert(0!=0 && "Unknwon Initialization vector"); + } + + /* Make one or more calls to process the data to be authenticated */ + rc = EVP_MAC_update(mctx, iv, sz_iv); + DevAssert(rc != 0); + + /* Make one or more calls to process the data to be authenticated */ + rc = EVP_MAC_update(mctx, msg.buf, msg.len); + DevAssert(rc != 0); + + /* Make a call to the final with a NULL buffer to get the length of the MAC */ + size_t out_len = 0; + rc = EVP_MAC_final(mctx, out, &out_len, len_out); + DevAssert(rc != 0); + + EVP_MAC_CTX_free(mctx); +} + +void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx) +{ + DevAssert(ctx != NULL); + EVP_MAC_free(ctx->mac); + OSSL_LIB_CTX_free(ctx->lib_ctx); +} + +#else +// code for 1.1.x or lower + +void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) +{ + DevAssert(k_iv != NULL); + + CMAC_CTX *ctx = CMAC_CTX_new(); + DevAssert(ctx != NULL); + + CMAC_Init(ctx, k_iv->key, sizeof(k_iv->key), EVP_aes_128_cbc(), NULL); + + size_t sz_iv = 0; + uint8_t* iv = NULL; + if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ + sz_iv = 8; + iv = (uint8_t*)k_iv->iv8.iv; + } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { + sz_iv = 16; + iv = (uint8_t*)k_iv->iv16.iv; + } else { + DevAssert(0!=0 && "Unknwon Initialization vector"); + } + + CMAC_Update(ctx, iv, sz_iv); + + CMAC_Update(ctx, msg.buf, msg.len); + size_t len_res = 0; + CMAC_Final(ctx, out, &len_res); + DevAssert(len_res <= len_out); + + CMAC_CTX_free(ctx); +} + +cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]) +{ + DevAssert(key != NULL); + cbc_cmac_ctx_t ctx = {.lib_ctx = NULL, .mac = NULL }; + + ctx.mac = CMAC_CTX_new(); + DevAssert(ctx.mac != NULL); + + //assert(16 == sizeof(ctx.key)); + memcpy(ctx.key, key, 16); //sizeof(ctx.key)); + + CMAC_Init(ctx.mac, ctx.key, 16, EVP_aes_128_cbc(), NULL); + return ctx; +} + +void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) +{ + DevAssert(ctx != NULL); + DevAssert(k_iv != NULL); + + //From https://man.openbsd.org/CMAC_Init.3 + //If ctx is already initialized, CMAC_Init() can be called again with key, + //cipher, and impl all set to NULL and key_len set to 0. In that case, any + //data already processed is discarded and ctx is re-initialized to start + //reading data anew. + CMAC_Init(ctx->mac, NULL, 0, NULL, NULL); + + size_t sz_iv = 0; + uint8_t* iv = NULL; + if(k_iv->type == AES_INITIALIZATION_VECTOR_8){ + sz_iv = 8; + iv = (uint8_t*)k_iv->iv8.iv; + } else if(k_iv->type == AES_INITIALIZATION_VECTOR_16) { + sz_iv = 16; + iv = (uint8_t*)k_iv->iv16.iv; + } else { + DevAssert(0!=0 && "Unknwon Initialization vector"); + } + + CMAC_Update(ctx->mac, iv, sz_iv); + + CMAC_Update(ctx->mac, msg.buf, msg.len); + size_t len_res = 0; + CMAC_Final(ctx->mac, out, &len_res); + DevAssert(len_res <= len_out); +} + +void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx) +{ + DevAssert(ctx != NULL); + CMAC_CTX_free(ctx->mac); +} + +#endif + diff --git a/openair3/SECU/aes_128_cbc_cmac.h b/openair3/SECU/aes_128_cbc_cmac.h new file mode 100644 index 0000000000..b5118118c3 --- /dev/null +++ b/openair3/SECU/aes_128_cbc_cmac.h @@ -0,0 +1,48 @@ +/* + * 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 + */ + + +#ifndef AES_128_CBC_CMAC_H +#define AES_128_CBC_CMAC_H + +#include "aes_128.h" +#include "common/utils/ds/byte_array.h" + + +#include +#include + +typedef struct { + void* lib_ctx; + void* mac; + uint8_t key[16]; +} cbc_cmac_ctx_t ; + +void aes_128_cbc_cmac(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]); + +cbc_cmac_ctx_t init_aes_128_cbc_cmac(uint8_t key[16]); + +void cipher_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx, const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]); + +void free_aes_128_cbc_cmac(cbc_cmac_ctx_t const* ctx); + +#endif + diff --git a/openair3/SECU/aes_128_ctr.c b/openair3/SECU/aes_128_ctr.c new file mode 100644 index 0000000000..4caf6ee3cf --- /dev/null +++ b/openair3/SECU/aes_128_ctr.c @@ -0,0 +1,62 @@ +/* + * 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 + */ + +// TS 133 401 pag. 88 + +#include "aes_128_ctr.h" +#include "common/utils/assertions.h" +#include +#include +#include + +void aes_128_ctr(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]) +{ + DevAssert(k_iv != NULL); + + DevAssert(sizeof(iv16_t) == AES_BLOCK_SIZE); + DevAssert(k_iv->type == AES_INITIALIZATION_VECTOR_16); + + // Create and initialise the context + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + DevAssert(ctx != NULL); + + // Set IV length if default 12 bytes (96 bits) is not appropriate + // int rc = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CTR_SET_IVLEN, 16, NULL)) + + // Initialise the encryption operation. + int rc = EVP_EncryptInit_ex(ctx, EVP_aes_128_ctr(), NULL, k_iv->key, k_iv->iv16.iv); + DevAssert(rc == 1); + + OPENSSL_assert(EVP_CIPHER_CTX_iv_length(ctx) == AES_BLOCK_SIZE); + + int len_ev = 0; + rc = EVP_EncryptUpdate(ctx, out, &len_ev, msg.buf, msg.len); + DevAssert(!(len_ev > len_out) && "Buffer overflow"); + + // Finalise the encryption. Normally ciphertext bytes may be written at + // this stage, but this does not occur in GCM mode + rc = EVP_EncryptFinal_ex(ctx, out + len_ev, &len_ev); + DevAssert(rc == 1); + + // Get the tag + // rc == EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) + EVP_CIPHER_CTX_free(ctx); +} diff --git a/openair3/SECU/aes_128_ctr.h b/openair3/SECU/aes_128_ctr.h new file mode 100644 index 0000000000..24bad85664 --- /dev/null +++ b/openair3/SECU/aes_128_ctr.h @@ -0,0 +1,33 @@ +/* + * 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 + */ + +#ifndef AES_128_CTR_OAI_H +#define AES_128_CTR_OAI_H + +#include "aes_128.h" +#include "common/utils/ds/byte_array.h" +#include +#include +#include + +void aes_128_ctr(const aes_128_t* k_iv, byte_array_t msg, size_t len_out, uint8_t out[len_out]); + +#endif diff --git a/openair3/SECU/kdf.c b/openair3/SECU/kdf.c index 1073df8ebe..8bead9158d 100644 --- a/openair3/SECU/kdf.c +++ b/openair3/SECU/kdf.c @@ -19,87 +19,18 @@ * contact@openairinterface.org */ +#include "common/utils/assertions.h" +#include "openair3/SECU/sha_256_hmac.h" #include #include #include -#include -#include "security_types.h" -#include "secu_defs.h" +#include "kdf.h" -void kdf(const uint8_t *key, uint16_t key_len, uint8_t *s, uint16_t s_len, uint8_t *out, - uint16_t out_len) +void kdf(const uint8_t key[32], byte_array_t data, size_t len, uint8_t out[len]) { - struct hmac_sha256_ctx ctx; - - memset(&ctx, 0, sizeof(ctx)); - - hmac_sha256_set_key(&ctx, key_len, key); - hmac_sha256_update(&ctx, s_len, s); - hmac_sha256_digest(&ctx, out_len, out); + // Do we still need kdf function? + sha_256_hmac(key, data, len, out); } -#ifndef NAS_UE -int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t *keNB) -{ - uint8_t s[7]; - - // FC - s[0] = FC_KENB; - // P0 = Uplink NAS count - s[1] = (nas_count & 0xff000000) >> 24; - s[2] = (nas_count & 0x00ff0000) >> 16; - s[3] = (nas_count & 0x0000ff00) >> 8; - s[4] = (nas_count & 0x000000ff); - - // Length of NAS count - s[5] = 0x00; - s[6] = 0x04; - - kdf(kasme, 32, s, 7, keNB, 32); - - return 0; -} -#endif - -int derive_keNB_star( - const uint8_t *kenb_32, - const uint16_t pci, - const uint32_t earfcn_dl, - const bool is_rel8_only, - uint8_t *kenb_star) -{ - // see 33.401 section A.5 KeNB* derivation function - uint8_t s[10] = {0}; - - // FC = 0x13 - s[0] = FC_KENB_STAR; - // P0 = PCI (target physical cell id) - s[1] = (pci & 0x0000ff00) >> 8; - s[2] = (pci & 0x000000ff); - // L0 = length of PCI (i.e. 0x00 0x02) - s[3] = 0x00; - s[4] = 0x02; - // P1 = EARFCN-DL (target physical cell downlink frequency) - if (is_rel8_only) { - s[5] = (earfcn_dl & 0x0000ff00) >> 8; - s[6] = (earfcn_dl & 0x000000ff); - s[7] = 0x00; - s[8] = 0x02; - kdf (kenb_32, 32, s, 9, kenb_star, 32); - } else { - s[5] = (earfcn_dl & 0x00ff0000) >> 16; - s[6] = (earfcn_dl & 0x0000ff00) >> 8; - s[7] = (earfcn_dl & 0x000000ff); - s[8] = 0x00; - s[9] = 0x03; - kdf (kenb_32, 32, s, 10, kenb_star, 32); - } - // L1 length of EARFCN-DL (i.e. L1 = 0x00 0x02 if EARFCN-DL is between 0 and 65535, and L1 = 0x00 0x03 if EARFCN-DL is between 65536 and 262143) - // NOTE: The length of EARFCN-DL cannot be generally set to 3 bytes for backward compatibility reasons: A Rel-8 - // entity (UE or eNB) would always assume an input parameter length of 2 bytes for the EARFCN-DL. This - // would lead to different derived keys if another entity assumed an input parameter length of 3 bytes for the - // EARFCN-DL. - return 0; -} diff --git a/openair3/SECU/kdf.h b/openair3/SECU/kdf.h new file mode 100644 index 0000000000..9567296bd2 --- /dev/null +++ b/openair3/SECU/kdf.h @@ -0,0 +1,34 @@ +/* + * 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 + */ + +#ifndef KEY_DERIVATION_FUNCTION_OSA_H +#define KEY_DERIVATION_FUNCTION_OSA_H + +#include +#include +#include "byte_array.h" + +void kdf(const uint8_t key[32], byte_array_t data, size_t len, uint8_t out[len]); + +#endif + + + diff --git a/openair3/SECU/key_nas_deriver.c b/openair3/SECU/key_nas_deriver.c index 59546be081..18cdb3d000 100644 --- a/openair3/SECU/key_nas_deriver.c +++ b/openair3/SECU/key_nas_deriver.c @@ -26,6 +26,7 @@ #include "security_types.h" #include "secu_defs.h" +#include "kdf.h" #define SECU_DEBUG 1 /*! @@ -79,7 +80,9 @@ int derive_key_nas(algorithm_type_dist_t nas_alg_type, uint8_t nas_enc_alg_id, printf("\n"); #endif - kdf(kasme, 32, s, 7, out, 32); + //kdf(kasme, 32, s, 7, out, 32); + byte_array_t data = {.len = 7, .buf = s}; + kdf(kasme, data, 32, out); memcpy(knas, &out[31-16+1], 16); diff --git a/openair3/SECU/nas_stream_eea1.c b/openair3/SECU/nas_stream_eea1.c index efe924851f..e70d49ea2c 100644 --- a/openair3/SECU/nas_stream_eea1.c +++ b/openair3/SECU/nas_stream_eea1.c @@ -24,10 +24,6 @@ #include #include -#include -#include -#include - #include "assertions.h" #include "conversions.h" #include "secu_defs.h" diff --git a/openair3/SECU/nas_stream_eea2.c b/openair3/SECU/nas_stream_eea2.c index 0cd3d91761..4eeb24b775 100644 --- a/openair3/SECU/nas_stream_eea2.c +++ b/openair3/SECU/nas_stream_eea2.c @@ -19,84 +19,40 @@ * contact@openairinterface.org */ +#include #include #include #include #include -#include -#include -#include - +#include "aes_128_ctr.h" #include "assertions.h" #include "conversions.h" #include "secu_defs.h" -// #define SECU_DEBUG - int nas_stream_encrypt_eea2(nas_stream_cipher_t *stream_cipher, uint8_t *out) { - uint8_t m[16]; - uint32_t local_count; - - void *ctx; - uint8_t *data; - - uint32_t zero_bit = 0; - uint32_t byte_length; - DevAssert(stream_cipher != NULL); - DevAssert(out != NULL); + DevAssert(stream_cipher->key != NULL); + DevAssert(stream_cipher->key_length == 32); + DevAssert(stream_cipher->bearer < 32); + DevAssert(stream_cipher->direction < 2); + DevAssert(stream_cipher->message != NULL); + DevAssert(stream_cipher->blength > 7); - zero_bit = stream_cipher->blength & 0x7; + aes_128_t p = {0}; + memcpy(p.key, stream_cipher->key, stream_cipher->key_length); + p.type = AES_INITIALIZATION_VECTOR_16; + p.iv16.d.count = htonl(stream_cipher->count); + p.iv16.d.bearer = stream_cipher->bearer; + p.iv16.d.direction = stream_cipher->direction; - byte_length = stream_cipher->blength >> 3; - - if (zero_bit > 0) - byte_length += 1; - - ctx = malloc(nettle_aes128.context_size); - data = malloc(byte_length); - - local_count = hton_int32(stream_cipher->count); - - memset(m, 0, sizeof(m)); - memcpy(&m[0], &local_count, 4); - m[4] = ((stream_cipher->bearer & 0x1F) << 3) | - ((stream_cipher->direction & 0x01) << 2); - /* Other bits are 0 */ - -#if defined(SECU_DEBUG) - { - int i; - printf("Blength: %u, Zero bits: %u\n", stream_cipher->blength, zero_bit); - - for (i = 0; i < sizeof(m); i++) - printf("0x%02x ", m[i]); - - printf("\n"); - } -#endif - -#if NETTLE_VERSION_MAJOR < 3 - nettle_aes128.set_encrypt_key(ctx, stream_cipher->key_length, - stream_cipher->key); -#else - nettle_aes128.set_encrypt_key(ctx, - stream_cipher->key); -#endif - - nettle_ctr_crypt(ctx, nettle_aes128.encrypt, - nettle_aes128.block_size, m, - byte_length, data, stream_cipher->message); - - if (zero_bit > 0) - data[byte_length - 1] = data[byte_length - 1] & (uint8_t)(0xFF << (8 - zero_bit)); - - - memcpy(out, data, byte_length); - free(data); - free(ctx); + DevAssert((stream_cipher->blength & 0x07) == 0); + const uint32_t byte_lenght = stream_cipher->blength >> 3; + // Precondition: out must have enough space, at least as much as the input + const size_t len_out = byte_lenght; + byte_array_t msg = {.buf = stream_cipher->message, .len = byte_lenght}; + aes_128_ctr(&p, msg, len_out, out); return 0; } diff --git a/openair3/SECU/nas_stream_eia2.c b/openair3/SECU/nas_stream_eia2.c index a2ec6a6dd4..4c7951a0f5 100644 --- a/openair3/SECU/nas_stream_eia2.c +++ b/openair3/SECU/nas_stream_eia2.c @@ -19,6 +19,7 @@ * contact@openairinterface.org */ +#include #include #include #include @@ -26,15 +27,8 @@ #include "secu_defs.h" -#include -#include -#include - -// test -#include -#include -#include - +#include +#include "aes_128_cbc_cmac.h" #include "assertions.h" #include "conversions.h" @@ -45,84 +39,28 @@ * @param[in] stream_cipher Structure containing various variables to setup encoding * @param[out] out For EIA2 the output string is 32 bits long */ + int nas_stream_encrypt_eia2(nas_stream_cipher_t *stream_cipher, uint8_t out[4]) { - uint8_t *m = NULL; - uint32_t local_count; - size_t size = 4; - uint8_t data[16]; - CMAC_CTX *cmac_ctx = NULL; - const EVP_CIPHER *cipher = EVP_aes_128_cbc(); - uint32_t zero_bit = 0; - uint32_t m_length; - int ret; - DevAssert(stream_cipher != NULL); - DevAssert(stream_cipher->key != NULL); - DevAssert(stream_cipher->key_length > 0); - DevAssert(out != NULL); + DevAssert(stream_cipher->message != NULL); + DevAssert(stream_cipher->bearer < 32); + DevAssert(stream_cipher->key_length == 16); + DevAssert((stream_cipher->blength & 0x7) == 0); - memset(data, 0, 16); - - zero_bit = stream_cipher->blength & 0x7; - - m_length = stream_cipher->blength >> 3; - - if (zero_bit > 0) - m_length += 1; - - local_count = hton_int32(stream_cipher->count); - - m = calloc(m_length + 8, sizeof(uint8_t)); - - memcpy(&m[0], &local_count, 4); - m[4] = ((stream_cipher->bearer & 0x1F) << 3) | ((stream_cipher->direction & 0x01) << 2); - - memcpy(&m[8], stream_cipher->message, m_length); - -#if defined(SECU_DEBUG) - { - int i; - printf("Byte length: %u, Zero bits: %u\nm: ", m_length + 8, zero_bit); - - for (i = 0; i < m_length + 8; i++) - printf("%02x", m[i]); - - printf("\nKey:"); - - for (i = 0; i < stream_cipher->key_length; i++) - printf("%02x", stream_cipher->key[i]); - - printf("\nMessage:"); - - for (i = 0; i < m_length; i++) - printf("%02x", stream_cipher->message[i]); - - printf("\n"); - } -#endif - - cmac_ctx = CMAC_CTX_new(); - ret = CMAC_Init(cmac_ctx, stream_cipher->key, stream_cipher->key_length, cipher, NULL); - ret = CMAC_Update(cmac_ctx, m, m_length + 8); - (void)ret; /* avoid gcc warning "set but not used" */ - CMAC_Final(cmac_ctx, data, &size); - CMAC_CTX_free(cmac_ctx); - -#if defined(SECU_DEBUG) - { - int i; - printf("out: "); - - for (i = 0; i < size; i++) - printf("%02x", data[i]); - - printf("\n"); - } -#endif - - memcpy(out, data, 4); - free(m); + aes_128_t k_iv = {0}; + memcpy(&k_iv.key, stream_cipher->key, sizeof(k_iv.key)); + k_iv.type = AES_INITIALIZATION_VECTOR_8; + k_iv.iv8.d.bearer = stream_cipher->bearer; + k_iv.iv8.d.direction = stream_cipher->direction; + k_iv.iv8.d.count = htonl(stream_cipher->count); + size_t const m_length = stream_cipher->blength >> 3; + uint8_t result[16] = {0}; + byte_array_t msg = {.buf = stream_cipher->message, .len = m_length }; + aes_128_cbc_cmac(&k_iv, msg, sizeof(result), result); + + memcpy(out, result, 4); return 0; } + diff --git a/openair3/SECU/secu_defs.c b/openair3/SECU/secu_defs.c new file mode 100644 index 0000000000..00cf23187e --- /dev/null +++ b/openair3/SECU/secu_defs.c @@ -0,0 +1,96 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + + +#include "secu_defs.h" +#include "kdf.h" + +#ifndef NAS_UE +int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t *keNB) +{ + uint8_t s[7] = {0}; + + // FC + s[0] = FC_KENB; + // P0 = Uplink NAS count + s[1] = (nas_count & 0xff000000) >> 24; + s[2] = (nas_count & 0x00ff0000) >> 16; + s[3] = (nas_count & 0x0000ff00) >> 8; + s[4] = (nas_count & 0x000000ff); + + // Length of NAS count + s[5] = 0x00; + s[6] = 0x04; + + // kdf(kasme, 32, s, 7, keNB, 32); + byte_array_t data = {.buf = s, .len = 7}; + kdf(kasme, data, 32, keNB); + + return 0; +} +#endif + +int derive_keNB_star( + const uint8_t *kenb_32, + const uint16_t pci, + const uint32_t earfcn_dl, + const bool is_rel8_only, + uint8_t *kenb_star) +{ + // see 33.401 section A.5 KeNB* derivation function + uint8_t s[10] = {0}; + byte_array_t data = {.buf = s}; + // FC = 0x13 + s[0] = FC_KENB_STAR; + // P0 = PCI (target physical cell id) + s[1] = (pci & 0x0000ff00) >> 8; + s[2] = (pci & 0x000000ff); + // L0 = length of PCI (i.e. 0x00 0x02) + s[3] = 0x00; + s[4] = 0x02; + // P1 = EARFCN-DL (target physical cell downlink frequency) + if (is_rel8_only) { + s[5] = (earfcn_dl & 0x0000ff00) >> 8; + s[6] = (earfcn_dl & 0x000000ff); + s[7] = 0x00; + s[8] = 0x02; +// kdf (kenb_32, 32, s, 9, kenb_star, 32); + data.len = 9; + } else { + s[5] = (earfcn_dl & 0x00ff0000) >> 16; + s[6] = (earfcn_dl & 0x0000ff00) >> 8; + s[7] = (earfcn_dl & 0x000000ff); + s[8] = 0x00; + s[9] = 0x03; + //kdf (kenb_32, 32, s, 10, kenb_star, 32); + data.len = 10; + } + + kdf (kenb_32, data, 32, kenb_star); + + // L1 length of EARFCN-DL (i.e. L1 = 0x00 0x02 if EARFCN-DL is between 0 and 65535, and L1 = 0x00 0x03 if EARFCN-DL is between 65536 and 262143) + // NOTE: The length of EARFCN-DL cannot be generally set to 3 bytes for backward compatibility reasons: A Rel-8 + // entity (UE or eNB) would always assume an input parameter length of 2 bytes for the EARFCN-DL. This + // would lead to different derived keys if another entity assumed an input parameter length of 3 bytes for the + // EARFCN-DL. + return 0; +} + diff --git a/openair3/SECU/secu_defs.h b/openair3/SECU/secu_defs.h index 8ec05f3daf..7817db30d3 100644 --- a/openair3/SECU/secu_defs.h +++ b/openair3/SECU/secu_defs.h @@ -24,6 +24,7 @@ #include "security_types.h" #include +#include #define EIA0_ALG_ID 0x00 #define EIA1_128_ALG_ID 0x01 @@ -36,13 +37,6 @@ #define SECU_DIRECTION_UPLINK 0 #define SECU_DIRECTION_DOWNLINK 1 -void kdf(const uint8_t *key, - uint16_t key_len, - uint8_t *s, - uint16_t s_len, - uint8_t *out, - uint16_t out_len); - int derive_keNB(const uint8_t kasme[32], const uint32_t nas_count, uint8_t *keNB); int derive_keNB_star(const uint8_t *kenb_32, const uint16_t pci, const uint32_t earfcn_dl, diff --git a/openair3/SECU/sha_256_hmac.c b/openair3/SECU/sha_256_hmac.c new file mode 100644 index 0000000000..ae49b1d1a2 --- /dev/null +++ b/openair3/SECU/sha_256_hmac.c @@ -0,0 +1,95 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "common/utils/assertions.h" +#include "sha_256_hmac.h" + +#include + +#if OPENSSL_VERSION_MAJOR >= 3 +// code for version 3.0 or greater + +#include + +void sha_256_hmac(const uint8_t key[32], byte_array_t data, size_t len, uint8_t out[len]) +{ + DevAssert(key != NULL); + DevAssert(data.buf != NULL); + DevAssert(data.len != 0); + DevAssert(len != 0); + + OSSL_LIB_CTX* library_context = OSSL_LIB_CTX_new(); + DevAssert(library_context != NULL); + + // A property query used for selecting the MAC implementation. + const char* propq = NULL; + // Fetch the HMAC implementation + EVP_MAC* mac = EVP_MAC_fetch(library_context, "HMAC", propq); + DevAssert(mac != NULL); + + // Create a context for the HMAC operation + EVP_MAC_CTX* mctx = EVP_MAC_CTX_new(mac); + DevAssert(mctx != NULL); + + // The underlying digest to be used + OSSL_PARAM params[2] = {0}; + char digest_name[] = "SHA2-256"; + params[0] = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, digest_name, sizeof(digest_name)); + params[1] = OSSL_PARAM_construct_end(); + + // Initialise the HMAC operation + int rc = EVP_MAC_init(mctx, key, 32, params); + DevAssert(rc == 1); + + // Make one or more calls to process the data to be authenticated + rc = EVP_MAC_update(mctx, data.buf, data.len); + DevAssert(rc == 1); + + // Make one call to the final to get the MAC + rc = EVP_MAC_final(mctx, out, &len, len); + DevAssert(rc == 1); + + // OpenSSL free functions will ignore NULL arguments + EVP_MAC_CTX_free(mctx); + EVP_MAC_free(mac); + OSSL_LIB_CTX_free(library_context); +} +#else +// code for 1.1.x or lower +void sha_256_hmac(const uint8_t key[32], byte_array_t data, size_t len, uint8_t out[len]) +{ + DevAssert(key != NULL); + DevAssert(data.buf != NULL); + DevAssert(data.len != 0); + DevAssert(len != 0); + + HMAC_CTX* ctx = HMAC_CTX_new(); + + HMAC_Init_ex(ctx, key, 32, EVP_sha256(), NULL); + + HMAC_Update(ctx, data.buf, data.len); + + HMAC_Final(ctx, out, (uint32_t*)&len); + + HMAC_CTX_free(ctx); +} + +#endif diff --git a/openair3/SECU/sha_256_hmac.h b/openair3/SECU/sha_256_hmac.h new file mode 100644 index 0000000000..79413763ea --- /dev/null +++ b/openair3/SECU/sha_256_hmac.h @@ -0,0 +1,33 @@ +/* + * 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 + */ + +#ifndef SHA_256_HMAC_OAI_H +#define SHA_256_HMAC_OAI_H + +#include +#include + +#include "byte_array.h" + +void sha_256_hmac(const uint8_t key[32], byte_array_t data, size_t len, uint8_t out[len]); + +#endif + diff --git a/openair3/TEST/Makefile.am b/openair3/TEST/Makefile.am index 211c7e62f2..1c00d33420 100644 --- a/openair3/TEST/Makefile.am +++ b/openair3/TEST/Makefile.am @@ -43,8 +43,7 @@ if HAVE_CHECK TESTS = \ test_kdf \ test_aes128_cmac_encrypt \ - test_aes128_ctr_encrypt \ - test_aes128_ctr_decrypt \ + test_aes128_ctr \ test_secu_kenb \ test_secu_knas \ test_secu_knas_encrypt_eea1 \ @@ -68,8 +67,7 @@ common_ldadd = $(top_builddir)/SECU/libsecu.la \ check_PROGRAMS = \ test_kdf \ test_aes128_cmac_encrypt \ - test_aes128_ctr_encrypt \ - test_aes128_ctr_decrypt \ + test_aes128_ctr \ test_secu_kenb \ test_secu_knas \ test_secu_knas_encrypt_eea1 \ diff --git a/openair3/TEST/test_aes128_ctr.c b/openair3/TEST/test_aes128_ctr.c new file mode 100644 index 0000000000..b29b0d2e16 --- /dev/null +++ b/openair3/TEST/test_aes128_ctr.c @@ -0,0 +1,265 @@ +/* + * 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 + */ + +// TS 133 401 pag. 88 + +#include "../../common/utils/assertions.h" +#include "../SECU/aes_128_ctr.h" +#include + +#include +#include + +void test_1(void) +{ + aes_128_ctr_t p = { + .key = {0xd3, 0xc5, 0xd5, 0x92, 0x32, 0x7f, 0xb1, 0x1c, 0x40, 0x35, 0xc6, 0x68, 0x0a, 0xf8, 0xc6, 0xd1}, + .iv_p.count = ntohl(965368244), // {0x39, 0x8a, 0x59, 0xb4}, //965368244, // + .iv_p.bearer = 21, //{0x15}, + .iv_p.direction = 1 // {0x01} + }; + + uint8_t text[32] = {0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x80, + 0x8c, 0xe3, 0x3e, 0x2c, 0xc3, 0xc0, 0xb5, 0xfc, 0x1f, 0x3d, 0xe8, 0xa6, 0xdc, 0x66, 0xb1, 0xf0}; + + uint8_t out[32] = {0}; + + // Cipher + aes_128_ctr(&p, 32, text, 32, out); + + uint8_t result[32] = {0xe9, 0xfe, 0xd8, 0xa6, 0x3d, 0x15, 0x53, 0x04, 0xd7, 0x1d, 0xf2, 0x0b, 0xf3, 0xe8, 0x22, 0x14, + 0xb2, 0x0e, 0xd7, 0xda, 0xd2, 0xf2, 0x33, 0xdc, 0x3c, 0x22, 0xd7, 0xbd, 0xee, 0xed, 0x8e, 0x78}; + + int rc = memcmp(out, result, 32); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + uint8_t text_decipher[32] = {0}; + + // Decipher + aes_128_ctr(&p, 32, out, 32, text_decipher); + + rc = memcmp(text, text_decipher, 32); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void test_2(void) +{ + aes_128_ctr_t p = { + .key = {0x2b, 0xd6, 0x45, 0x9f, 0x82, 0xc4, 0x40, 0xe0, 0x95, 0x2c, 0x49, 0x10, 0x48, 0x05, 0xff, 0x48}, + .iv_p.count = ntohl(3329599051), // c675a64b , // + .iv_p.bearer = 12, //{0x0c}, + .iv_p.direction = 1 // {0x01} + }; + + uint8_t text[100] = {0x7e, 0xc6, 0x12, 0x72, 0x74, 0x3b, 0xf1, 0x61, 0x47, 0x26, 0x44, 0x6a, 0x6c, 0x38, 0xce, 0xd1, 0x66, 0xf6, 0xca, 0x76, 0xeb, 0x54, 0x30, 0x04, 0x42, + 0x86, 0x34, 0x6c, 0xef, 0x13, 0x0f, 0x92, 0x92, 0x2b, 0x03, 0x45, 0x0d, 0x3a, 0x99, 0x75, 0xe5, 0xbd, 0x2e, 0xa0, 0xeb, 0x55, 0xad, 0x8e, 0x1b, 0x19, + 0x9e, 0x3e, 0xc4, 0x31, 0x60, 0x20, 0xe9, 0xa1, 0xb2, 0x85, 0xe7, 0x62, 0x79, 0x53, 0x59, 0xb7, 0xbd, 0xfd, 0x39, 0xbe, 0xf4, 0xb2, 0x48, 0x45, 0x83, + 0xd5, 0xaf, 0xe0, 0x82, 0xae, 0xe6, 0x38, 0xbf, 0x5f, 0xd5, 0xa6, 0x06, 0x19, 0x39, 0x01, 0xa0, 0x8f, 0x4a, 0xb4, 0x1a, 0xab, 0x9b, 0x13, 0x48, 0x80}; + + uint8_t out[100] = {0}; + aes_128_ctr(&p, 100, text, 100, out); + + uint8_t result[100] = {0x59, 0x61, 0x60, 0x53, 0x53, 0xc6, 0x4b, 0xdc, 0xa1, 0x5b, 0x19, 0x5e, 0x28, 0x85, 0x53, 0xa9, 0x10, 0x63, 0x25, 0x06, 0xd6, 0x20, 0x0a, 0xa7, 0x90, + 0xc4, 0xc8, 0x06, 0xc9, 0x99, 0x04, 0xcf, 0x24, 0x45, 0xcc, 0x50, 0xbb, 0x1c, 0xf1, 0x68, 0xa4, 0x96, 0x73, 0x73, 0x4e, 0x08, 0x1b, 0x57, 0xe3, 0x24, + 0xce, 0x52, 0x59, 0xc0, 0xe7, 0x8d, 0x4c, 0xd9, 0x7b, 0x87, 0x09, 0x76, 0x50, 0x3c, 0x09, 0x43, 0xf2, 0xcb, 0x5a, 0xe8, 0xf0, 0x52, 0xc7, 0xb7, 0xd3, + 0x92, 0x23, 0x95, 0x87, 0xb8, 0x95, 0x60, 0x86, 0xbc, 0xab, 0x18, 0x83, 0x60, 0x42, 0xe2, 0xe6, 0xce, 0x42, 0x43, 0x2a, 0x17, 0x10, 0x5c, 0x53, 0xd0}; + + int rc = memcmp(out, result, 99); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + uint8_t text_decipher[100] = {0}; + aes_128_ctr(&p, 99, out, 99, text_decipher); + + rc = memcmp(text, text_decipher, 99); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void test_3(void) +{ + aes_128_ctr_t p = {.key = {0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, 0x8b, 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, 0x77, 0xfb}, + .iv_p.count = ntohl(1414351309), // 544d49cd + .iv_p.bearer = 4, + .iv_p.direction = 0}; + + uint8_t text[40] = {0xfd, 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, 0x65, 0x74, 0x50, 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, 0x36, 0xd2, 0x34, 0x9e, + 0x23, 0xf6, 0x44, 0x39, 0x2c, 0x8e, 0xa9, 0xc4, 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, 0xf2, 0x64, 0xd0, 0xf2, 0x48, 0x00}; + + // Cipher + uint8_t out[40] = {0}; + aes_128_ctr(&p, 40, text, 40, out); + + uint8_t result[40] = {0x75, 0x75, 0x0d, 0x37, 0xb4, 0xbb, 0xa2, 0xa4, 0xde, 0xdb, 0x34, 0x23, 0x5b, 0xd6, 0x8c, 0x66, 0x45, 0xac, 0xda, 0xac, + 0xa4, 0x81, 0x38, 0xa3, 0xb0, 0xc4, 0x71, 0xe2, 0xa7, 0x04, 0x1a, 0x57, 0x64, 0x23, 0xd2, 0x92, 0x72, 0x87, 0xf0, 0x00}; + + int rc = memcmp(out, result, 39); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + // Decipher + uint8_t text_decipher[40] = {0}; + aes_128_ctr(&p, 40, out, 40, text_decipher); + + rc = memcmp(text, text_decipher, 39); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void test_4(void) +{ + aes_128_ctr_t p = {.key = {0xaa, 0x1f, 0x95, 0xae, 0xa5, 0x33, 0xbc, 0xb3, 0x2e, 0xb6, 0x3b, 0xf5, 0x2d, 0x8f, 0x83, 0x1a}, + .iv_p.count = ntohl(1926809201), // 72d8c671 + .iv_p.bearer = 16, + .iv_p.direction = 1}; + + uint8_t text[128] = {0xfb, 0x1b, 0x96, 0xc5, 0xc8, 0xba, 0xdf, 0xb2, 0xe8, 0xe8, 0xed, 0xfd, 0xe7, 0x8e, 0x57, 0xf2, 0xad, 0x81, 0xe7, 0x41, 0x03, 0xfc, 0x43, 0x0a, 0x53, 0x4d, + 0xcc, 0x37, 0xaf, 0xce, 0xc7, 0x0e, 0x15, 0x17, 0xbb, 0x06, 0xf2, 0x72, 0x19, 0xda, 0xe4, 0x90, 0x22, 0xdd, 0xc4, 0x7a, 0x06, 0x8d, 0xe4, 0xc9, 0x49, 0x6a, + 0x95, 0x1a, 0x6b, 0x09, 0xed, 0xbd, 0xc8, 0x64, 0xc7, 0xad, 0xbd, 0x74, 0x0a, 0xc5, 0x0c, 0x02, 0x2f, 0x30, 0x82, 0xba, 0xfd, 0x22, 0xd7, 0x81, 0x97, 0xc5, + 0xd5, 0x08, 0xb9, 0x77, 0xbc, 0xa1, 0x3f, 0x32, 0xe6, 0x52, 0xe7, 0x4b, 0xa7, 0x28, 0x57, 0x60, 0x77, 0xce, 0x62, 0x8c, 0x53, 0x5e, 0x87, 0xdc, 0x60, 0x77, + 0xba, 0x07, 0xd2, 0x90, 0x68, 0x59, 0x0c, 0x8c, 0xb5, 0xf1, 0x08, 0x8e, 0x08, 0x2c, 0xfa, 0x0e, 0xc9, 0x61, 0x30, 0x2d, 0x69, 0xcf, 0x3d, 0x44}; + + // Cipher + uint8_t out[128] = {0}; + aes_128_ctr(&p, 128, text, 128, out); + + uint8_t result[128] = {0xdf, 0xb4, 0x40, 0xac, 0xb3, 0x77, 0x35, 0x49, 0xef, 0xc0, 0x46, 0x28, 0xae, 0xb8, 0xd8, 0x15, 0x62, 0x75, 0x23, 0x0b, 0xdc, 0x69, 0x0d, 0x94, 0xb0, 0x0d, + 0x8d, 0x95, 0xf2, 0x8c, 0x4b, 0x56, 0x30, 0x7f, 0x60, 0xf4, 0xca, 0x55, 0xeb, 0xa6, 0x61, 0xeb, 0xba, 0x72, 0xac, 0x80, 0x8f, 0xa8, 0xc4, 0x9e, 0x26, 0x78, + 0x8e, 0xd0, 0x4a, 0x5d, 0x60, 0x6c, 0xb4, 0x18, 0xde, 0x74, 0x87, 0x8b, 0x9a, 0x22, 0xf8, 0xef, 0x29, 0x59, 0x0b, 0xc4, 0xeb, 0x57, 0xc9, 0xfa, 0xf7, 0xc4, + 0x15, 0x24, 0xa8, 0x85, 0xb8, 0x97, 0x9c, 0x42, 0x3f, 0x2f, 0x8f, 0x8e, 0x05, 0x92, 0xa9, 0x87, 0x92, 0x01, 0xbe, 0x7f, 0xf9, 0x77, 0x7a, 0x16, 0x2a, 0xb8, + 0x10, 0xfe, 0xb3, 0x24, 0xba, 0x74, 0xc4, 0xc1, 0x56, 0xe0, 0x4d, 0x39, 0x09, 0x72, 0x09, 0x65, 0x3a, 0xc3, 0x3e, 0x5a, 0x5f, 0x2d, 0x88, 0x64}; + + int rc = memcmp(out, result, 127); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + // Decipher + uint8_t text_decipher[128] = {0}; + aes_128_ctr(&p, 128, out, 128, text_decipher); + + rc = memcmp(text, text_decipher, 127); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void test_5(void) +{ + aes_128_ctr_t p = {.key = {0x96, 0x18, 0xae, 0x46, 0x89, 0x1f, 0x86, 0x57, 0x8e, 0xeb, 0xe9, 0x0e, 0xf7, 0xa1, 0x20, 0x2e}, + .iv_p.count = ntohl(3329599051), // c675a64b + .iv_p.bearer = 12, + .iv_p.direction = 1}; + + uint8_t text[156] = {0x8d, 0xaa, 0x17, 0xb1, 0xae, 0x05, 0x05, 0x29, 0xc6, 0x82, 0x7f, 0x28, 0xc0, 0xef, 0x6a, 0x12, 0x42, 0xe9, 0x3f, 0x8b, 0x31, 0x4f, 0xb1, 0x8a, 0x77, 0xf7, + 0x90, 0xae, 0x04, 0x9f, 0xed, 0xd6, 0x12, 0x26, 0x7f, 0xec, 0xae, 0xfc, 0x45, 0x01, 0x74, 0xd7, 0x6d, 0x9f, 0x9a, 0xa7, 0x75, 0x5a, 0x30, 0xcd, 0x90, 0xa9, + 0xa5, 0x87, 0x4b, 0xf4, 0x8e, 0xaf, 0x70, 0xee, 0xa3, 0xa6, 0x2a, 0x25, 0x0a, 0x8b, 0x6b, 0xd8, 0xd9, 0xb0, 0x8b, 0x08, 0xd6, 0x4e, 0x32, 0xd1, 0x81, 0x77, + 0x77, 0xfb, 0x54, 0x4d, 0x49, 0xcd, 0x49, 0x72, 0x0e, 0x21, 0x9d, 0xbf, 0x8b, 0xbe, 0xd3, 0x39, 0x04, 0xe1, 0xfd, 0x40, 0xa4, 0x1d, 0x37, 0x0a, 0x1f, 0x65, + 0x74, 0x50, 0x95, 0x68, 0x7d, 0x47, 0xba, 0x1d, 0x36, 0xd2, 0x34, 0x9e, 0x23, 0xf6, 0x44, 0x39, 0x2c, 0x8e, 0xa9, 0xc4, 0x9d, 0x40, 0xc1, 0x32, 0x71, 0xaf, + 0xf2, 0x64, 0xd0, 0xf2, 0x48, 0x41, 0xd6, 0x46, 0x5f, 0x09, 0x96, 0xff, 0x84, 0xe6, 0x5f, 0xc5, 0x17, 0xc5, 0x3e, 0xfc, 0x33, 0x63, 0xc3, 0x84, 0x92, 0xa8}; + + // Cipher + uint8_t out[156] = {0}; + aes_128_ctr(&p, 156, text, 156, out); + + uint8_t result[156] = {0x91, 0x9c, 0x8c, 0x33, 0xd6, 0x67, 0x89, 0x70, 0x3d, 0x05, 0xa0, 0xd7, 0xce, 0x82, 0xa2, 0xae, 0xac, 0x4e, 0xe7, 0x6c, 0x0f, 0x4d, 0xa0, 0x50, 0x33, 0x5e, + 0x8a, 0x84, 0xe7, 0x89, 0x7b, 0xa5, 0xdf, 0x2f, 0x36, 0xbd, 0x51, 0x3e, 0x3d, 0x0c, 0x85, 0x78, 0xc7, 0xa0, 0xfc, 0xf0, 0x43, 0xe0, 0x3a, 0xa3, 0xa3, 0x9f, + 0xba, 0xad, 0x7d, 0x15, 0xbe, 0x07, 0x4f, 0xaa, 0x5d, 0x90, 0x29, 0xf7, 0x1f, 0xb4, 0x57, 0xb6, 0x47, 0x83, 0x47, 0x14, 0xb0, 0xe1, 0x8f, 0x11, 0x7f, 0xca, + 0x10, 0x67, 0x79, 0x45, 0x09, 0x6c, 0x8c, 0x5f, 0x32, 0x6b, 0xa8, 0xd6, 0x09, 0x5e, 0xb2, 0x9c, 0x3e, 0x36, 0xcf, 0x24, 0x5d, 0x16, 0x22, 0xaa, 0xfe, 0x92, + 0x1f, 0x75, 0x66, 0xc4, 0xf5, 0xd6, 0x44, 0xf2, 0xf1, 0xfc, 0x0e, 0xc6, 0x84, 0xdd, 0xb2, 0x13, 0x49, 0x74, 0x76, 0x22, 0xe2, 0x09, 0x29, 0x5d, 0x27, 0xff, + 0x3f, 0x95, 0x62, 0x33, 0x71, 0xd4, 0x9b, 0x14, 0x7c, 0x0a, 0xf4, 0x86, 0x17, 0x1f, 0x22, 0xcd, 0x04, 0xb1, 0xcb, 0xeb, 0x26, 0x58, 0x22, 0x3e, 0x69, 0x38}; + + int rc = memcmp(out, result, 155); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + // Decipher + uint8_t text_decipher[156] = {0}; + aes_128_ctr(&p, 156, out, 156, text_decipher); + + rc = memcmp(text, text_decipher, 155); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void test_6(void) +{ + aes_128_ctr_t p = {.key = {0x54, 0xf4, 0xe2, 0xe0, 0x4c, 0x83, 0x78, 0x6e, 0xec, 0x8f, 0xb5, 0xab, 0xe8, 0xe3, 0x65, 0x66}, + .iv_p.count = ntohl(2896491791), // aca4f50f + .iv_p.bearer = 11, + .iv_p.direction = 0}; + + uint8_t text[512] = {0x40, 0x98, 0x1b, 0xa6, 0x82, 0x4c, 0x1b, 0xfb, 0x42, 0x86, 0xb2, 0x99, 0x78, 0x3d, 0xaf, 0x44, 0x2c, 0x09, 0x9f, 0x7a, 0xb0, 0xf5, 0x8d, 0x5c, 0x8e, 0x46, 0xb1, 0x04, 0xf0, + 0x8f, 0x01, 0xb4, 0x1a, 0xb4, 0x85, 0x47, 0x20, 0x29, 0xb7, 0x1d, 0x36, 0xbd, 0x1a, 0x3d, 0x90, 0xdc, 0x3a, 0x41, 0xb4, 0x6d, 0x51, 0x67, 0x2a, 0xc4, 0xc9, 0x66, 0x3a, 0x2b, + 0xe0, 0x63, 0xda, 0x4b, 0xc8, 0xd2, 0x80, 0x8c, 0xe3, 0x3e, 0x2c, 0xcc, 0xbf, 0xc6, 0x34, 0xe1, 0xb2, 0x59, 0x06, 0x08, 0x76, 0xa0, 0xfb, 0xb5, 0xa4, 0x37, 0xeb, 0xcc, 0x8d, + 0x31, 0xc1, 0x9e, 0x44, 0x54, 0x31, 0x87, 0x45, 0xe3, 0xfa, 0x16, 0xbb, 0x11, 0xad, 0xae, 0x24, 0x88, 0x79, 0xfe, 0x52, 0xdb, 0x25, 0x43, 0xe5, 0x3c, 0xf4, 0x45, 0xd3, 0xd8, + 0x28, 0xce, 0x0b, 0xf5, 0xc5, 0x60, 0x59, 0x3d, 0x97, 0x27, 0x8a, 0x59, 0x76, 0x2d, 0xd0, 0xc2, 0xc9, 0xcd, 0x68, 0xd4, 0x49, 0x6a, 0x79, 0x25, 0x08, 0x61, 0x40, 0x14, 0xb1, + 0x3b, 0x6a, 0xa5, 0x11, 0x28, 0xc1, 0x8c, 0xd6, 0xa9, 0x0b, 0x87, 0x97, 0x8c, 0x2f, 0xf1, 0xca, 0xbe, 0x7d, 0x9f, 0x89, 0x8a, 0x41, 0x1b, 0xfd, 0xb8, 0x4f, 0x68, 0xf6, 0x72, + 0x7b, 0x14, 0x99, 0xcd, 0xd3, 0x0d, 0xf0, 0x44, 0x3a, 0xb4, 0xa6, 0x66, 0x53, 0x33, 0x0b, 0xcb, 0xa1, 0x10, 0x5e, 0x4c, 0xec, 0x03, 0x4c, 0x73, 0xe6, 0x05, 0xb4, 0x31, 0x0e, + 0xaa, 0xad, 0xcf, 0xd5, 0xb0, 0xca, 0x27, 0xff, 0xd8, 0x9d, 0x14, 0x4d, 0xf4, 0x79, 0x27, 0x59, 0x42, 0x7c, 0x9c, 0xc1, 0xf8, 0xcd, 0x8c, 0x87, 0x20, 0x23, 0x64, 0xb8, 0xa6, + 0x87, 0x95, 0x4c, 0xb0, 0x5a, 0x8d, 0x4e, 0x2d, 0x99, 0xe7, 0x3d, 0xb1, 0x60, 0xde, 0xb1, 0x80, 0xad, 0x08, 0x41, 0xe9, 0x67, 0x41, 0xa5, 0xd5, 0x9f, 0xe4, 0x18, 0x9f, 0x15, + 0x42, 0x00, 0x26, 0xfe, 0x4c, 0xd1, 0x21, 0x04, 0x93, 0x2f, 0xb3, 0x8f, 0x73, 0x53, 0x40, 0x43, 0x8a, 0xaf, 0x7e, 0xca, 0x6f, 0xd5, 0xcf, 0xd3, 0xa1, 0x95, 0xce, 0x5a, 0xbe, + 0x65, 0x27, 0x2a, 0xf6, 0x07, 0xad, 0xa1, 0xbe, 0x65, 0xa6, 0xb4, 0xc9, 0xc0, 0x69, 0x32, 0x34, 0x09, 0x2c, 0x4d, 0x01, 0x8f, 0x17, 0x56, 0xc6, 0xdb, 0x9d, 0xc8, 0xa6, 0xd8, + 0x0b, 0x88, 0x81, 0x38, 0x61, 0x6b, 0x68, 0x12, 0x62, 0xf9, 0x54, 0xd0, 0xe7, 0x71, 0x17, 0x48, 0x78, 0x0d, 0x92, 0x29, 0x1d, 0x86, 0x29, 0x99, 0x72, 0xdb, 0x74, 0x1c, 0xfa, + 0x4f, 0x37, 0xb8, 0xb5, 0x6c, 0xdb, 0x18, 0xa7, 0xca, 0x82, 0x18, 0xe8, 0x6e, 0x4b, 0x4b, 0x71, 0x6a, 0x4d, 0x04, 0x37, 0x1f, 0xbe, 0xc2, 0x62, 0xfc, 0x5a, 0xd0, 0xb3, 0x81, + 0x9b, 0x18, 0x7b, 0x97, 0xe5, 0x5b, 0x1a, 0x4d, 0x7c, 0x19, 0xee, 0x24, 0xc8, 0xb4, 0xd7, 0x72, 0x3c, 0xfe, 0xdf, 0x04, 0x5b, 0x8a, 0xca, 0xe4, 0x86, 0x95, 0x17, 0xd8, 0x0e, + 0x50, 0x61, 0x5d, 0x90, 0x35, 0xd5, 0xd9, 0xc5, 0xa4, 0x0a, 0xf6, 0x02, 0x28, 0x0b, 0x54, 0x25, 0x97, 0xb0, 0xcb, 0x18, 0x61, 0x9e, 0xeb, 0x35, 0x92, 0x57, 0x59, 0xd1, 0x95, + 0xe1, 0x00, 0xe8, 0xe4, 0xaa, 0x0c, 0x38, 0xa3, 0xc2, 0xab, 0xe0, 0xf3, 0xd8, 0xff, 0x04, 0xf3, 0xc3, 0x3c, 0x29, 0x50, 0x69, 0xc2, 0x36, 0x94, 0xb5, 0xbb, 0xea, 0xcd, 0xd5, + 0x42, 0xe2, 0x8e, 0x8a, 0x94, 0xed, 0xb9, 0x11, 0x9f, 0x41, 0x2d, 0x05, 0x4b, 0xe1, 0xfa, 0x72, 0x00, 0xb0, 0x90, 0x00}; + + // Cipher + uint8_t out[512] = {0}; + aes_128_ctr(&p, 512, text, 512, out); + + uint8_t result[512] = {0x5c, 0xb7, 0x2c, 0x6e, 0xdc, 0x87, 0x8f, 0x15, 0x66, 0xe1, 0x02, 0x53, 0xaf, 0xc3, 0x64, 0xc9, 0xfa, 0x54, 0x0d, 0x91, 0x4d, 0xb9, 0x4c, 0xbe, 0xe2, 0x75, 0xd0, 0x91, 0x7c, + 0xa6, 0xaf, 0x0d, 0x77, 0xac, 0xb4, 0xef, 0x3b, 0xbe, 0x1a, 0x72, 0x2b, 0x2e, 0xf5, 0xbd, 0x1d, 0x4b, 0x8e, 0x2a, 0xa5, 0x02, 0x4e, 0xc1, 0x38, 0x8a, 0x20, 0x1e, 0x7b, 0xce, + 0x79, 0x20, 0xae, 0xc6, 0x15, 0x89, 0x5f, 0x76, 0x3a, 0x55, 0x64, 0xdc, 0xc4, 0xc4, 0x82, 0xa2, 0xee, 0x1d, 0x8b, 0xfe, 0xcc, 0x44, 0x98, 0xec, 0xa8, 0x3f, 0xbb, 0x75, 0xf9, + 0xab, 0x53, 0x0e, 0x0d, 0xaf, 0xbe, 0xde, 0x2f, 0xa5, 0x89, 0x5b, 0x82, 0x99, 0x1b, 0x62, 0x77, 0xc5, 0x29, 0xe0, 0xf2, 0x52, 0x9d, 0x7f, 0x79, 0x60, 0x6b, 0xe9, 0x67, 0x06, + 0x29, 0x6d, 0xed, 0xfa, 0x9d, 0x74, 0x12, 0xb6, 0x16, 0x95, 0x8c, 0xb5, 0x63, 0xc6, 0x78, 0xc0, 0x28, 0x25, 0xc3, 0x0d, 0x0a, 0xee, 0x77, 0xc4, 0xc1, 0x46, 0xd2, 0x76, 0x54, + 0x12, 0x42, 0x1a, 0x80, 0x8d, 0x13, 0xce, 0xc8, 0x19, 0x69, 0x4c, 0x75, 0xad, 0x57, 0x2e, 0x9b, 0x97, 0x3d, 0x94, 0x8b, 0x81, 0xa9, 0x33, 0x7c, 0x3b, 0x2a, 0x17, 0x19, 0x2e, + 0x22, 0xc2, 0x06, 0x9f, 0x7e, 0xd1, 0x16, 0x2a, 0xf4, 0x4c, 0xde, 0xa8, 0x17, 0x60, 0x36, 0x65, 0xe8, 0x07, 0xce, 0x40, 0xc8, 0xe0, 0xdd, 0x9d, 0x63, 0x94, 0xdc, 0x6e, 0x31, + 0x15, 0x3f, 0xe1, 0x95, 0x5c, 0x47, 0xaf, 0xb5, 0x1f, 0x26, 0x17, 0xee, 0x0c, 0x5e, 0x3b, 0x8e, 0xf1, 0xad, 0x75, 0x74, 0xed, 0x34, 0x3e, 0xdc, 0x27, 0x43, 0xcc, 0x94, 0xc9, + 0x90, 0xe1, 0xf1, 0xfd, 0x26, 0x42, 0x53, 0xc1, 0x78, 0xde, 0xa7, 0x39, 0xc0, 0xbe, 0xfe, 0xeb, 0xcd, 0x9f, 0x9b, 0x76, 0xd4, 0x9c, 0x10, 0x15, 0xc9, 0xfe, 0xcf, 0x50, 0xe5, + 0x3b, 0x8b, 0x52, 0x04, 0xdb, 0xcd, 0x3e, 0xed, 0x86, 0x38, 0x55, 0xda, 0xbc, 0xdc, 0xc9, 0x4b, 0x31, 0xe3, 0x18, 0x02, 0x15, 0x68, 0x85, 0x5c, 0x8b, 0x9e, 0x52, 0xa9, 0x81, + 0x95, 0x7a, 0x11, 0x28, 0x27, 0xf9, 0x78, 0xba, 0x96, 0x0f, 0x14, 0x47, 0x91, 0x1b, 0x31, 0x7b, 0x55, 0x11, 0xfb, 0xcc, 0x7f, 0xb1, 0x3a, 0xc1, 0x53, 0xdb, 0x74, 0x25, 0x11, + 0x17, 0xe4, 0x86, 0x1e, 0xb9, 0xe8, 0x3b, 0xff, 0xff, 0xc4, 0xeb, 0x77, 0x55, 0x57, 0x90, 0x38, 0xe5, 0x79, 0x24, 0xb1, 0xf7, 0x8b, 0x3e, 0x1a, 0xd9, 0x0b, 0xab, 0x2a, 0x07, + 0x87, 0x1b, 0x72, 0xdb, 0x5e, 0xef, 0x96, 0xc3, 0x34, 0x04, 0x49, 0x66, 0xdb, 0x0c, 0x37, 0xca, 0xfd, 0x1a, 0x89, 0xe5, 0x64, 0x6a, 0x35, 0x80, 0xeb, 0x64, 0x65, 0xf1, 0x21, + 0xdc, 0xe9, 0xcb, 0x88, 0xd8, 0x5b, 0x96, 0xcf, 0x23, 0xcc, 0xcc, 0xd4, 0x28, 0x07, 0x67, 0xbe, 0xe8, 0xee, 0xb2, 0x3d, 0x86, 0x52, 0x46, 0x1d, 0xb6, 0x49, 0x31, 0x03, 0x00, + 0x3b, 0xaf, 0x89, 0xf5, 0xe1, 0x82, 0x61, 0xea, 0x43, 0xc8, 0x4a, 0x92, 0xeb, 0xff, 0xff, 0xe4, 0x90, 0x9d, 0xc4, 0x6c, 0x51, 0x92, 0xf8, 0x25, 0xf7, 0x70, 0x60, 0x0b, 0x96, + 0x02, 0xc5, 0x57, 0xb5, 0xf8, 0xb4, 0x31, 0xa7, 0x9d, 0x45, 0x97, 0x7d, 0xd9, 0xc4, 0x1b, 0x86, 0x3d, 0xa9, 0xe1, 0x42, 0xe9, 0x00, 0x20, 0xcf, 0xd0, 0x74, 0xd6, 0x92, 0x7b, + 0x7a, 0xb3, 0xb6, 0x72, 0x5d, 0x1a, 0x6f, 0x3f, 0x98, 0xb9, 0xc9, 0xda, 0xa8, 0x98, 0x2a, 0xff, 0x06, 0x78, 0x28, 0x00}; + + int rc = memcmp(out, result, 483); + DevAssert(rc == 0 && "Ciphering did not produce the expected results"); + + // Decipher + uint8_t text_decipher[512] = {0}; + aes_128_ctr(&p, 512, out, 512, text_decipher); + + rc = memcmp(text, text_decipher, 483); + DevAssert(rc == 0 && "Text and deciphered text do not match"); +} + +void doit(void) +{ + test_1(); + printf("AES-128: Test 1 passed\n"); + test_2(); + printf("AES-128: Test 2 passed\n"); + test_3(); + printf("AES-128: Test 3 passed\n"); + test_4(); + printf("AES-128: Test 4 passed\n"); + test_5(); + printf("AES-128: Test 5 passed\n"); + test_6(); + printf("AES-128: Test 6 passed\n"); +} diff --git a/openair3/TEST/test_aes128_ctr_decrypt.c b/openair3/TEST/test_aes128_ctr_decrypt.c deleted file mode 100644 index 928853c9ba..0000000000 --- a/openair3/TEST/test_aes128_ctr_decrypt.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include -#include -#include -#include - -#include "test_util.h" - -#include "secu_defs.h" - -#include -#include -#include - -static -void test_uncipher_ctr(const struct nettle_cipher *cipher, const uint8_t *key, - unsigned key_length, const uint8_t *cipheredtext, - unsigned length, const uint8_t *cleartext, const uint8_t *ictr) -{ - void *ctx = malloc(cipher->context_size); - uint8_t *data = malloc(length); - uint8_t *ctr = malloc(cipher->block_size); - -#if NETTLE_VERSION_MAJOR < 3 - cipher->set_encrypt_key(ctx, key_length, key); -#else - cipher->set_encrypt_key(ctx, key); -#endif - - memcpy(ctr, ictr, cipher->block_size); - - ctr_crypt(ctx, cipher->encrypt, - cipher->block_size, ctr, - length, data, cipheredtext); - - if (compare_buffer(data, length, cleartext, length) != 0) { - fail("Fail: test_uncipher_ctr\n"); - } - - free(ctx); - free(data); - free(ctr); -} - -void doit (void) -{ - /* From NIST spec 800-38a on AES modes, - * - * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38A.pdf - * - * F.5 CTR Example Vectors - */ - /* F.5.1 CTR-AES128.Encrypt */ - test_uncipher_ctr(&nettle_aes128, - HL("2b7e151628aed2a6abf7158809cf4f3c"), - HL("6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710"), - H("874d6191b620e3261bef6864990db6ce" - "9806f66b7970fdff8617187bb9fffdff" - "5ae4df3edbd5d35e5b4f09020db03eab" - "1e031dda2fbe03d1792170a0f3009cee"), - H("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")); -} diff --git a/openair3/TEST/test_aes128_ctr_encrypt.c b/openair3/TEST/test_aes128_ctr_encrypt.c deleted file mode 100644 index 071aa603a9..0000000000 --- a/openair3/TEST/test_aes128_ctr_encrypt.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The OpenAirInterface Software Alliance licenses this file to You under - * the OAI Public License, Version 1.1 (the "License"); you may not use this file - * except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.openairinterface.org/?page_id=698 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - *------------------------------------------------------------------------------- - * For more information about the OpenAirInterface (OAI) Software Alliance: - * contact@openairinterface.org - */ - -#include -#include -#include -#include - -#include "test_util.h" - -#include "secu_defs.h" - -#include -#include -#include - -static void test_cipher_ctr(const struct nettle_cipher *cipher, const uint8_t *key, - unsigned key_length, const uint8_t *cleartext, unsigned length, - const uint8_t *ciphertext, const uint8_t *ictr) -{ - void *ctx = malloc(cipher->context_size); - uint8_t *data = malloc(length); - uint8_t *ctr = malloc(cipher->block_size); - -#if NETTLE_VERSION_MAJOR < 3 - cipher->set_encrypt_key(ctx, key_length, key); -#else - cipher->set_encrypt_key(ctx, key); -#endif - - memcpy(ctr, ictr, cipher->block_size); - - ctr_crypt(ctx, cipher->encrypt, - cipher->block_size, ctr, - length, data, cleartext); - - if (compare_buffer(data, length, ciphertext, length) != 0) { - fail("Fail: test_cipher_ctr"); - } - - free(ctx); - free(data); - free(ctr); -} - -void doit (void) -{ - /* From NIST spec 800-38a on AES modes, - * - * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38A.pdf - * - * F.5 CTR Example Vectors - */ - /* F.5.1 CTR-AES128.Encrypt */ - test_cipher_ctr(&nettle_aes128, - HL("2b7e151628aed2a6abf7158809cf4f3c"), - HL("6bc1bee22e409f96e93d7e117393172a" - "ae2d8a571e03ac9c9eb76fac45af8e51" - "30c81c46a35ce411e5fbc1191a0a52ef" - "f69f2445df4f9b17ad2b417be66c3710"), - H("874d6191b620e3261bef6864990db6ce" - "9806f66b7970fdff8617187bb9fffdff" - "5ae4df3edbd5d35e5b4f09020db03eab" - "1e031dda2fbe03d1792170a0f3009cee"), - H("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff")); -} diff --git a/targets/Makerules b/targets/Makerules index a25e2c6e8b..3488c54762 100644 --- a/targets/Makerules +++ b/targets/Makerules @@ -14,14 +14,6 @@ DATE_REV := $(shell date '+%F %T') NUM_CORES:=$(shell cat /proc/cpuinfo | grep processor | wc -l) -# Check for libraries and export cflags/linker variables -NETTLE_FOUND := $(shell if pkg-config --exists nettle; then echo "1" ; else echo "0"; fi) -ifeq ($(NETTLE_FOUND), 1) -NETTLE_LIBS := $(shell pkg-config --libs nettle) -#else -#@echo "package nettle not installed" -endif - OPENSSL_FOUND := $(shell if pkg-config --exists openssl; then echo "1" ; else echo "0"; fi) ifeq ($(OPENSSL_FOUND), 1) OPENSSL_LIBS := $(shell pkg-config --libs openssl) diff --git a/targets/README.txt b/targets/README.txt index c3a8156d9a..b32e0640e5 100644 --- a/targets/README.txt +++ b/targets/README.txt @@ -20,7 +20,7 @@ First, you must have all four openair SW directories, openair1, openair2, openai - libpgm-5.1 and libpgm-5.1-dev for distributed simulation with reliable multicast transmport - libxml2 and libxml2-dev - libforms-bin libforms-dev - - nettle-dev nettle-bin openssl libssl-dev + - openssl libssl-dev - libatlas-base-dev and libatlas-headers (for Ubuntu 11.04, libatlas-dev instead of libatlas-headers) - asn1c and the LTE ASN1 files (for more information on how to install this and generate the required files see the README file in openair2/RRC/LITE/MESSAGES) From 553f1d1a52845911be4ba61ec2bcc5c60ad42ae7 Mon Sep 17 00:00:00 2001 From: Laurent Thomas Date: Wed, 1 Feb 2023 15:22:38 +0100 Subject: [PATCH 17/26] Add basic E1 functionality With contributions from * Sakthivel Velumani * Robert Schmidt Squashed commit of the following: commit e8488acbcdaef6ee1688dc233c9e48e85b0fbfc8 Merge: f09468eb13 5d58645a85 Author: laurent Date: Sat Jan 21 15:03:40 2023 +0100 merge develop commit f09468eb13f729c49830786150918b03ae2d7524 Author: laurent Date: Fri Jan 20 10:43:51 2023 +0100 continue e1AP development commit 76545cfbfc7ec5031633b1b0ec599c3560c1e5a7 Author: laurent Date: Wed Jan 18 14:18:47 2023 +0100 fix regressions after merge develop commit 7071c480a6c433eb94501c49f4a7694ba77f4f6d Merge: 569b9fc5f3 214aa50595 Author: laurent Date: Mon Jan 16 15:51:50 2023 +0100 merge develop commit 569b9fc5f37b9947b722885e423572a211d658da Author: laurent Date: Wed Jan 4 17:07:01 2023 +0100 fix srb2 addition, simplify and remove dead code, fix one race commit 9fa89151c0f9b4a16a79d0685cc04d8cf2adfa44 Author: Sakthivel Velumani Date: Wed Jan 4 20:02:05 2023 +0530 Bearer context release decoder and encoder commit 7011cb518ed6df015ace536e9a08b2d1865188d9 Author: Sakthivel Velumani Date: Wed Jan 4 13:43:58 2023 +0530 Update documentation commit 3e8899fc962cb16fc1ded49590ca5dddede54978 Author: Sakthivel Velumani Date: Wed Jan 4 10:14:11 2023 +0530 Fix function arguments and warnings commit 989ad183a8d66961facaa6719fa7793b9d40d5b4 Author: Sakthivel Velumani Date: Tue Jan 3 20:18:31 2023 +0530 Moved E1-design.md to doc/ commit 04a8633dd509d9525755c5f3abfad976a1ba704e Author: Sakthivel Velumani Date: Tue Jan 3 20:13:46 2023 +0530 Make libraries lower case in CMakeLists commit 033ec174c1f48a0e745a2a2e1c367aa361f5ae76 Author: Sakthivel Velumani Date: Tue Jan 3 17:55:25 2023 +0530 Renamed and updated cucp cuup config files commit f76a6913352bd8b2bb73ac25f225140cf05f93f2 Author: Sakthivel Velumani Date: Tue Jan 3 13:31:19 2023 +0530 Removed CU-UP checks from nr-softmodem commit ae70afe44c84ac16b977b388c63e80e24803c43c Author: laurent Date: Mon Jan 2 13:01:45 2023 +0100 fix for gcc version hat doesnt accept declarations in switch commit 6a7a01043bf1e6e0ee9ac40b4d85ced7cdf6dcf4 Author: laurent Date: Tue Dec 27 15:59:28 2022 +0100 first functional commit standalone cu-up commit 0caa4690f9e1acbe578d4de5a82ba62f45444a12 Merge: 3619f9550e 1a0c0cd1c9 Author: Sakthivel Velumani Date: Fri Dec 23 14:21:00 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 3619f9550e4d3e78815334150ff8443db914f738 Merge: 1bcf8d5e00 cfe698eb70 Author: Sakthivel Velumani Date: Wed Dec 21 15:47:40 2022 +0530 Merge remote-tracking branch 'origin/develop' into e1-implementation-wip commit 1bcf8d5e00a9b9ce440d825f83ddc1b48086daa8 Author: Sakthivel Velumani Date: Tue Sep 13 02:10:34 2022 -0400 Fix GTP rnti to ue_id after rebase commit 23075ff8b7ef5d56df844c05e0a7100fe7bd35de Author: Sakthivel Velumani Date: Tue Sep 6 00:32:12 2022 -0400 Added documentation commit 28f6e507f74d3fb069441e05da523378dee0f6ed Author: Sakthivel Velumani Date: Mon Sep 5 15:02:02 2022 -0400 Header cleaups and unitary simulators build fixes commit e665dbab28c49327066cff72aed22e1de7797b59 Author: Sakthivel Velumani Date: Thu Sep 1 17:52:07 2022 -0400 Common interface between CUCP & CUUP for E1 and non E1 modes commit 5dcc1bf59a37ed87d7d32a617cfcd663d6415757 Author: Sakthivel Velumani Date: Thu Sep 1 13:53:49 2022 -0400 Allocate stack where ever possible Free memory after ASN encode commit 793a5379539a3a2b6c42cd327d8cef79fe21fb55 Author: Sakthivel Velumani Date: Thu Sep 1 10:41:50 2022 -0400 Fixes for build issues commit 0d3564f25a0d8746b0fc550ed371ce21e08cd05a Author: Sakthivel Velumani Date: Wed Aug 31 20:57:14 2022 -0400 Fixed uesoftmodem linkage error commit 34adf865820f09900a5efed693ada1ccf6f0f1ad Author: Sakthivel Velumani Date: Wed Aug 31 19:31:50 2022 -0400 Removed RRC thread from CUUP commit 536741910aff90a86ba3a60612f943b412c5ab40 Author: Sakthivel Velumani Date: Wed Aug 31 14:21:31 2022 -0400 Add header guard in one of E1AP header commit 22e9d9b6dea5d84d259ee2dc1aaa7cb943e55b08 Author: Sakthivel Velumani Date: Wed Aug 31 14:00:47 2022 -0400 Fix bug in PDCP ue_id commit 4c65339b07d347003ed89d5e00f49a62e9b21483 Author: Sakthivel Velumani Date: Tue Aug 30 18:04:59 2022 -0400 Linked E1AP lib to L2_NR target E1AP builds for nr-softmodem commit 014fe30ac93c84d810e0209bc7f0d3b7a1bbaee3 Author: Sakthivel Velumani Date: Tue Aug 30 15:49:27 2022 -0400 Remove xer prints from e1ap and gcc warning fixes commit 71f0c9126bbb91bc595b66d57e1e91db676d3b9d Author: Sakthivel Velumani Date: Tue Aug 30 15:42:30 2022 -0400 Fixed bug in integrity protection config commit 4e393ef9528c2b14d670a341b52fc63262abf894 Author: Sakthivel Velumani Date: Tue Aug 30 14:34:37 2022 -0400 GTP init with only local address IP traffic works both directions in Mono, F1 and E1 commit ec3bde2c5deb99f554afc2053e351b9f79525e96 Author: Sakthivel Velumani Date: Tue Aug 30 00:16:03 2022 -0400 Fix bug in pdcp config and GTP instance UL iperf still not works. Can see packets in wireshark. commit be8df4435eeae0a0755e2ac658f33caa2a3e3c94 Author: Sakthivel Velumani Date: Sat Aug 27 20:15:04 2022 -0400 Fixes in node config after rebase commit 6f42ff88de7f1615f87c46771fd6617a5fdfc48f Author: Sakthivel Velumani Date: Sat Aug 27 01:38:21 2022 -0400 Added N3 tunnel address in config params fixed bug in address length feild commit b5e132041994344ebb5c2c59ca392612a32658fd Author: Sakthivel Velumani Date: Fri Aug 26 20:38:47 2022 -0400 Send UL UP address via F1 UE cxt mod msg commit ecfd0fe260aee6aad78c291368c246f88b6bffcf Author: Sakthivel Velumani Date: Fri Aug 26 15:08:53 2022 -0400 Update GTP remote address from response message Modified GTP tunnel update function accordingly Put GTP tunnel update out of F1AP F1 split works commit 14a37e414de3d653f25ccef1a790bc5b08fc7dc2 Author: Sakthivel Velumani Date: Sat Aug 20 01:17:36 2022 -0400 Moved GTP tunnel creation out of F1AP message sending commit cdd58ab96e84f48cc8ab8f0fe6b3133081ff02f7 Author: Sakthivel Velumani Date: Fri Aug 19 17:40:43 2022 -0400 Moved DRB config out of RRCReconfig complete Monolithic and F1 split works commit 61d66e490a1b61aa2b6ce18d993bb90d112d6374 Author: Sakthivel Velumani Date: Thu Aug 11 01:30:10 2022 -0400 Allow CUUP to have RRC inst without error commit dd7ae889372bb9fdf9e96be87c28eed0d9461d7f Author: Sakthivel Velumani Date: Thu Aug 11 01:29:21 2022 -0400 Added missing IEs in bearer cxt response handler commit a6f1fe5fd254ccb36a54ede5b652161cb5e542e8 Author: Sakthivel Velumani Date: Wed Aug 10 21:02:30 2022 -0400 Unified bearer context message transfer for E1 and non-E1 mode compiles, to be tested commit f245ace285632edcc28d669123746b9bf458bc38 Author: laurent Date: Tue Aug 9 17:46:52 2022 +0200 remove log_i() of fatal errors commit d2a0f3c8620981f3b65a136f0169e3be9c549742 Author: laurent Date: Fri Aug 5 15:01:39 2022 +0200 fix rnti/ue_id for ue commit 7b52c6cd9e675c28cb9d6347d122f77234ec6542 Author: Sakthivel Velumani Date: Thu Aug 4 19:14:46 2022 -0400 Created separate functions in PDCP for E1 mode commit 46680d2b3d221e675c3aeb675137f45318e31acb Author: Sakthivel Velumani Date: Thu Aug 4 19:13:10 2022 -0400 Bug fixes in bearer context setup response commit 3d091bfd31b7aba625e4ba4b4f0f337cf2c1f6db Author: laurent Date: Thu Aug 4 15:42:11 2022 +0200 add missing file from previous commit commit 21838572196dec947da99f350f20c4d9408c3c17 Author: Sakthivel Velumani Date: Wed Aug 3 16:59:16 2022 -0400 Minor bug fixes commit 34eb73fbc6f9d33098850d62b4808e54c80bb4ca Author: Sakthivel Velumani Date: Tue Aug 2 23:16:47 2022 -0400 Added config files for testing To be renamed or removed later commit a7e9c6fb9332295ad42585eb4338a6f9ff0ae7c0 Author: Sakthivel Velumani Date: Tue Aug 2 23:15:21 2022 -0400 Made rrc_gNB_get_ue_context_from_ngap_ids() non static commit 028f0407571466ff24cf72b5c3814e42c346ae81 Author: Sakthivel Velumani Date: Tue Aug 2 23:12:16 2022 -0400 Completed bearer context setup cycle To be tested: Handling of bearer context setup response and subsequent UE context modifition msg to F1AP task commit 4481d11324b13efa73ab0b5f4f156012485661ae Author: Sakthivel Velumani Date: Tue Aug 2 23:01:50 2022 -0400 Fixed E1AP PPID (wireshark fixed) commit 7a22573f3e7e16960e3310609dea2783ddf404a2 Author: laurent Date: Fri Jul 29 13:03:31 2022 +0200 fix compile issues, simplify gtp send function commit 0209011aa75a16e4c9c1eb36ebcf4fe0ff93aba2 Author: laurent Date: Thu Jul 28 11:51:18 2022 +0200 restore compilation capability, gtp replacement of rnti by ueid commit ce3b886fd7f11322a8d37b924d1752e6a6adf777 Author: Sakthivel Velumani Date: Fri Jul 22 08:35:31 2022 -0400 Included missing IEs in bearer cxt setup message commit 1208ae0d6fe4b25341c9036dde111fd4d00fd6e9 Author: Cedric Roux Date: Tue Jul 19 10:53:35 2022 +0200 replace rnti by ue_id in pdcp some sdap and gtp files had to be touched, the work is not finished in there some changes in nr_pdcp_oai_api.c simply assume ue_id is indeed rnti (will functions in this file be used by cu-up?) commit e67d1c88c786267458194e4de516919d0b5d1002 Author: Sakthivel Velumani Date: Tue Jul 19 10:08:39 2022 +0200 Replace RNTI with UE ID in GTP files (half done. Laurent to change ctxt_t) commit 7a141e8e941e5c2624a47da7c764531c03b8f617 Author: Sakthivel Velumani Date: Tue Jul 19 10:04:15 2022 +0200 Added RRC handler for bearer cxt setup in CUUP commit fe2106676bf9144347f30969a1bfd351136f536a Author: laurent Date: Mon Jul 11 13:30:30 2022 +0200 fix race in startup procedure (config before starting threads that use the config) commit 3197baab60e53459928da30cea7a4e0514a074e3 Author: Sakthivel Velumani Date: Sun Jul 10 11:30:31 2022 +0200 Fixed bugs in bearer creation commit 26be61167dec9068e572e08a21ddc96a0f2a7731 Author: laurent Date: Sun Jul 10 10:50:57 2022 +0200 fix a race, add asn1c debug method commit 26750d1818aa206c4c56ada6dbed9bb66dfe3811 Author: laurent Date: Wed Jul 6 22:34:10 2022 +0200 e1ap bearer setup encoding invalid commit d092788aa3f8229eb5c938a41534dfb318955135 Author: laurent Date: Wed Jul 6 14:18:39 2022 +0200 fix regression with F1, some basic cleaning in NGAP commit 41a135734a33b284808d4c5049c431dbc0ff1c46 Author: laurent Date: Wed Jul 6 11:24:52 2022 +0200 fix some ngap regressions commit 460acd840dd58fd46a995219cb9da8fda103668e Author: laurent Date: Tue Jul 5 21:19:13 2022 +0200 build, run in F1 mode until UE connect but pdu session still fails commit 7c067095df287cc7a542be53e9a1471c11ec5af8 Author: Sakthivel Velumani Date: Sat Jun 25 22:45:46 2022 +0530 Populate PDU and DRB paramenters from NGAP commit c4ada56a8c31b4c07fc1498e15b0954d9b1ad2f6 Author: Sakthivel Velumani Date: Tue Jun 7 22:16:58 2022 +0530 setup resp decoding success commit a2f41597c7438934a203ccf3cced35b6b65e8cee Author: Sakthivel Velumani Date: Sat Jun 4 13:50:55 2022 +0530 Fixed bugs in E1 setup procedure E1 setup request decoding successful commit 6106207ba74c74579b5f375d572930a609abe3a9 Author: Sakthivel Velumani Date: Thu May 26 20:13:14 2022 +0530 Fixing build issues commit 67235fac01aaca4c17da13bf658144ffe6bd12df Author: Robert Schmidt Date: Mon May 23 12:04:22 2022 +0200 Make nr-softmodem depend on E1AP module commit 391cee938e8250c355b6b914e747f2004c795817 Author: Robert Schmidt Date: Mon May 23 11:57:15 2022 +0200 Fix ITTI errors in E1AP target commit db4146e4a0432b7e3759d71efbeb450754c1f0d3 Author: Sakthivel Velumani Date: Wed May 18 06:59:03 2022 +0530 CP UP task created commit 3058d2fc445acf5681a09f70409023718294d093 Author: Sakthivel Velumani Date: Tue May 10 08:12:19 2022 +0530 Bearer context setup response message commit b911e9db2a4d0a35eb9a449ef02acefff30b558f Author: Sakthivel Velumani Date: Tue May 3 16:52:49 2022 +0530 Bearer context setup c structs commit d0f4d5f3539803253b9a995a90a47b3d825ea0ca Author: Sakthivel Velumani Date: Thu Apr 28 02:21:50 2022 +0530 Bearer context setup message commit 489a5358fa0e199314254561a2d52c1a93f7803e Author: Sakthivel Velumani Date: Thu Apr 21 10:11:03 2022 +0530 Started with CUUP task commit b416061f5a237cbabbe09c6d81fe92df9e33fd72 Author: Sakthivel Velumani Date: Fri Apr 15 18:46:27 2022 +0530 Setup request and response messages commit faca4b97dba92be857bd8442193365fc2c8ed122 Author: Robert Schmidt Date: Mon Apr 11 15:01:48 2022 +0200 Fixup for cmake execution commit ecfd9f7608a5820ae85278939e9bbaf7f1245dd6 Author: Robert Schmidt Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root commit 9f41f7c52b3c7a185e5e34bb1591b0eb66459837 Author: Robert Schmidt Date: Tue Apr 12 12:07:18 2022 +0200 Remove hwlat refs in build_oai: targets don't exist anymore commit e666cf5eedc67695febd33de1397331d6b7b2320 Author: Robert Schmidt Date: Mon Apr 11 14:37:32 2022 +0200 Create CMake E1AP_RELEASE variable from E1AP_VERSION commit 1f8413361c1b99ea46ee613da035803a1387f65c Author: Robert Schmidt Date: Sun Apr 10 14:25:38 2022 +0200 Add E1AP lib commit 7465cea49243fd6eb9869035b18860dface57098 Author: Robert Schmidt Date: Sun Apr 10 14:48:40 2022 +0200 Add E1AP Grammar and build ASN.1 lib at build time commit cde22f83032a199387ae1eae6b702f44ed154f3e Author: Robert Schmidt Date: Fri Apr 22 13:42:51 2022 +0200 Accept higher vers. nums in make_version/MAKE_VERSION commit d5127b23608240374a439d1d9c2ff6d53c4b527c Author: Robert Schmidt Date: Tue Nov 24 21:39:20 2020 +0100 Move CMakeLists.txt into root --- CMakeLists.txt | 50 +- ci-scripts/conf_files/gnb-cucp.sa.conf | 248 +++ ci-scripts/conf_files/gnb-cuup.sa.conf | 100 ++ cmake_targets/build_oai | 2 +- common/ngran_types.h | 10 +- common/utils/LOG/log.c | 1 + common/utils/ocp_itti/all_msg.h | 1 + common/utils/ocp_itti/intertask_interface.h | 3 +- doc/E1-design.md | 108 ++ doc/TESTING_5GSA_setup.md | 9 +- doc/images/e1-archi.pdf | Bin 0 -> 20052 bytes doc/images/e1-archi.png | Bin 0 -> 24521 bytes doc/images/e1-archi.tex | 73 + executables/nr-cuup.c | 160 ++ executables/nr-softmodem.c | 90 +- executables/nr-uesoftmodem.c | 2 + openair1/SIMULATION/NR_PHY/dlsim.c | 71 - openair1/SIMULATION/NR_PHY/prachsim.c | 73 +- openair1/SIMULATION/NR_PHY/ulsim.c | 71 - openair2/COMMON/e1ap_messages_def.h | 33 + openair2/COMMON/e1ap_messages_types.h | 226 +++ openair2/COMMON/gtpv1_u_messages_types.h | 2 +- openair2/COMMON/messages_def.h | 1 + openair2/COMMON/ngap_messages_types.h | 15 + openair2/E1AP/CMakeLists.txt | 10 +- openair2/E1AP/e1ap.c | 1582 +++++++++++++++-- openair2/E1AP/e1ap.h | 34 +- openair2/E1AP/e1ap_api.c | 195 ++ openair2/E1AP/e1ap_api.h | 35 + openair2/E1AP/e1ap_asnc.h | 65 + openair2/E1AP/e1ap_common.c | 235 +++ openair2/E1AP/e1ap_common.h | 64 + .../E1AP/e1ap_default_values.h | 11 +- openair2/E1AP/e1ap_setup.c | 129 ++ openair2/ENB_APP/enb_app.c | 3 +- openair2/ENB_APP/enb_config.c | 2 +- openair2/ENB_APP/enb_paramdef.h | 7 +- openair2/F1AP/dummy_enb.c | 6 - openair2/F1AP/f1ap_cu_interface_management.c | 4 +- openair2/F1AP/f1ap_cu_task.c | 31 +- openair2/F1AP/f1ap_cu_ue_context_management.c | 16 +- openair2/F1AP/f1ap_decoder.c | 3 +- openair2/F1AP/f1ap_du_task.c | 29 +- openair2/F1AP/f1ap_du_ue_context_management.c | 2 +- openair2/GNB_APP/gnb_app.c | 87 +- openair2/GNB_APP/gnb_app.h | 5 +- openair2/GNB_APP/gnb_config.c | 102 +- openair2/GNB_APP/gnb_config.h | 4 +- openair2/GNB_APP/gnb_paramdef.h | 22 + openair2/LAYER2/MAC/mac_proto.h | 2 - openair2/LAYER2/NR_MAC_gNB/main.c | 5 +- openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.c | 59 + openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h | 53 + openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c | 59 +- openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c | 1 + openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h | 1 + openair2/MCE_APP/mce_app.c | 3 +- openair2/RRC/LTE/rrc_eNB.c | 1 + openair2/RRC/NR/MESSAGES/asn1_msg.c | 172 +- openair2/RRC/NR/cucp_cuup_direct.c | 229 +++ openair2/RRC/NR/cucp_cuup_e1ap.c | 61 + openair2/RRC/NR/cucp_cuup_if.h | 42 + openair2/RRC/NR/nr_rrc_common.c | 19 +- openair2/RRC/NR/nr_rrc_defs.h | 13 +- openair2/RRC/NR/nr_rrc_extern.h | 4 +- openair2/RRC/NR/nr_rrc_proto.h | 41 +- openair2/RRC/NR/rrc_gNB.c | 553 +++--- openair2/RRC/NR/rrc_gNB_GTPV1U.c | 20 +- openair2/RRC/NR/rrc_gNB_GTPV1U.h | 3 +- openair2/RRC/NR/rrc_gNB_NGAP.c | 220 ++- openair2/RRC/NR/rrc_gNB_NGAP.h | 9 +- openair2/RRC/NR/rrc_gNB_radio_bearers.c | 26 - openair2/RRC/NR/rrc_gNB_radio_bearers.h | 4 - openair2/SDAP/nr_sdap/nr_sdap_entity.c | 5 +- openair2/SDAP/nr_sdap/nr_sdap_entity.h | 3 + openair3/MME_APP/mme_app.c | 3 +- openair3/NGAP/ngap_gNB_handlers.c | 338 ++-- openair3/SCTP/sctp_eNB_task.c | 20 +- openair3/ocp-gtpu/gtp_itf.cpp | 67 +- openair3/ocp-gtpu/gtp_itf.h | 6 +- 80 files changed, 4652 insertions(+), 1422 deletions(-) create mode 100644 ci-scripts/conf_files/gnb-cucp.sa.conf create mode 100644 ci-scripts/conf_files/gnb-cuup.sa.conf create mode 100644 doc/E1-design.md create mode 100644 doc/images/e1-archi.pdf create mode 100644 doc/images/e1-archi.png create mode 100644 doc/images/e1-archi.tex create mode 100644 executables/nr-cuup.c create mode 100644 openair2/COMMON/e1ap_messages_def.h create mode 100644 openair2/COMMON/e1ap_messages_types.h create mode 100644 openair2/E1AP/e1ap_api.c create mode 100644 openair2/E1AP/e1ap_api.h create mode 100644 openair2/E1AP/e1ap_asnc.h create mode 100644 openair2/E1AP/e1ap_common.c create mode 100644 openair2/E1AP/e1ap_common.h rename executables/create_nr_tasks.h => openair2/E1AP/e1ap_default_values.h (88%) create mode 100644 openair2/E1AP/e1ap_setup.c create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.c create mode 100644 openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h create mode 100644 openair2/RRC/NR/cucp_cuup_direct.c create mode 100644 openair2/RRC/NR/cucp_cuup_e1ap.c create mode 100644 openair2/RRC/NR/cucp_cuup_if.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a781499ce..7cb2847a3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -280,7 +280,7 @@ if (SANITIZE_UNDEFINED) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") endif () -add_definitions("-DASN_DISABLE_OER_SUPPORT") +add_definitions("-DASN_DISABLE_OER_SUPPORT -DHAVE_CONFIG_H -DHAVE_CONFIG_H_") ######################### set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -ggdb2 -Wl,-rpath -Wl,${CMAKE_CURRENT_BINARY_DIR}") @@ -336,23 +336,19 @@ add_definitions("-DPACKAGE_BUGREPORT=\"openair4g-devel@lists.eurecom.fr\"") # Debug related options ######################################### -add_boolean_option(DEBUG_ASN1C False "ASN1 coder/decoder Debug traces, see common/utils/config.h, the logs are regular OAI logs, in the log group 'ASN'") -add_boolean_option(DISABLE_XER_PRINT False "print XER Format") -add_boolean_option(XER_PRINT False "print XER Format") -add_boolean_option(DEBUG_PDCP_PAYLOAD False "print PDCP PDU to stdout") # if true, make sure that global and PDCP log levels are trace +add_boolean_option(DEBUG_ASN1 False "ASN1 coder/decoder Debug") add_boolean_option(DEBUG_MAC_INTERFACE False "print MAC-RLC PDU exchange to stdout") # if true, make sure that global and PDCP log levels are trace -add_boolean_option(PRINT_STATS False "This adds the possibility to see the status") add_boolean_option(T_TRACER True "Activate the T tracer, a debugging/monitoring framework" ) add_boolean_option(UE_AUTOTEST_TRACE False "Activate UE autotest specific logs") add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace") add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace") -add_boolean_option(DEBUG_CONSOLE False "disables stdout/stderr buffering") set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti) add_library(ITTI ${OCP_ITTI}/intertask_interface.cpp ${OPENAIR_DIR}/common/utils/backtrace.c ) + target_link_libraries(ITTI PRIVATE asn1_nr_rrc asn1_lte_rrc) # asn1c skeletons have hardcoded this flag to make customized debug logs @@ -819,7 +815,6 @@ include_directories("${OPENAIR2_DIR}/UTIL/OTG") include_directories("${OPENAIR2_DIR}/UTIL/CLI") include_directories("${OPENAIR2_DIR}/UTIL/OPT") include_directories("${OPENAIR2_DIR}/UTIL/OMV") -include_directories("${OPENAIR2_DIR}/RRC/LTE/MESSAGES") include_directories("${OPENAIR_DIR}") ########### @@ -1594,6 +1589,7 @@ set (ENB_APP_SRC set (GNB_APP_SRC ${OPENAIR2_DIR}/GNB_APP/gnb_app.c + ${OPENAIR2_DIR}/E1AP/e1ap_setup.c ${OPENAIR2_DIR}/GNB_APP/gnb_config.c ) @@ -1639,7 +1635,20 @@ add_library(L2_NR ${GNB_APP_SRC} ) -target_link_libraries(L2_NR PRIVATE f1ap x2ap s1ap ngap nr_rrc) +add_library(e1_if + ${NR_RRC_DIR}/cucp_cuup_direct.c + ${NR_RRC_DIR}/cucp_cuup_e1ap.c + ) + +target_link_libraries(e1_if PRIVATE asn1_nr_rrc asn1_lte_rrc asn1_f1ap e1ap GTPV1U) + +add_library(e1_pdcp_if + ${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_e1_api.c + ) + +target_link_libraries(e1_pdcp_if PRIVATE asn1_nr_rrc asn1_lte_rrc) + +target_link_libraries(L2_NR PRIVATE f1ap x2ap s1ap ngap nr_rrc e1ap) add_library(L2_LTE_NR ${L2_RRC_SRC} @@ -2373,7 +2382,7 @@ target_link_libraries(nr-softmodem PRIVATE UTIL HASHTABLE SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU GTPV1U SECU_CN SECU_OSA ITTI ${RAL_LIB} ${NAS_UE_LIB} lte_rrc nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_COMMON_LIB NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU - x2ap f1ap m2ap m3ap + x2ap f1ap m2ap m3ap e1ap -Wl,--end-group z dl) target_link_libraries(nr-softmodem PRIVATE ${LIBXML2_LIBRARIES}) @@ -2390,8 +2399,27 @@ endif () # force the generation of ASN.1 so that we don't need to wait during the build target_link_libraries(nr-softmodem PRIVATE - asn1_lte_rrc asn1_nr_rrc asn1_s1ap asn1_ngap asn1_m2ap asn1_m3ap asn1_x2ap asn1_f1ap asn1_lpp) + asn1_lte_rrc asn1_nr_rrc asn1_s1ap asn1_ngap asn1_m2ap asn1_m3ap asn1_x2ap asn1_f1ap asn1_lpp) +add_executable(nr-cuup + executables/nr-cuup.c + executables/softmodem-common.c + ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c + ${OPENAIR2_DIR}/RRC/NAS/nas_config.c + ${NR_RRC_DIR}/rrc_gNB_UE_context.c + ${OPENAIR2_DIR}/E1AP/e1ap_setup.c + ${NR_PDCP_SRC} + ${NR_SDAP_SRC} + ${T_SOURCE} + ${SHLIB_LOADER_SOURCES} + ) + +target_link_libraries(nr-cuup PRIVATE + CONFIG_LIB ITTI SCTP_CLIENT + GTPV1U e1ap e1_pdcp_if f1ap + SECU_OSA ${OPENSSL_LIBRARIES} crypt z sctp dl pthread) +target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc asn1_nr_rrc) + # nr-uesoftmodem is UE implementation ####################################### diff --git a/ci-scripts/conf_files/gnb-cucp.sa.conf b/ci-scripts/conf_files/gnb-cucp.sa.conf new file mode 100644 index 0000000000..7932098584 --- /dev/null +++ b/ci-scripts/conf_files/gnb-cucp.sa.conf @@ -0,0 +1,248 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; +Num_Threads_PUSCH = 8; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + } + ); + + }); + + + nr_cellid = 12345678L; + + tr_s_preference = "f1"; + + local_s_if_name = "lo"; + local_s_address = "127.0.0.4"; + remote_s_address = "127.0.0.3"; + local_s_portc = 501; + local_s_portd = 2152; + remote_s_portc = 500; + remote_s_portd = 2152; + ssb_SubcarrierOffset = 0; + min_rxtxtime = 6; + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 11; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + ////////// AMF parameters: + amf_ip_address = ( { ipv4 = "192.168.70.132"; + ipv6 = "192:168:30::17"; + active = "yes"; + preference = "ipv4"; + } + ); + + E1_INTERFACE = + ( + { + type = "cp"; + ipv4_cucp = "127.0.0.4"; + port_cucp = 2152; + ipv4_cuup = "127.0.0.5"; + port_cuup = 2152; + } + ) + + NETWORK_INTERFACES : + { + + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + log_config : + { + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="debug"; + pdcp_log_level ="info"; + rrc_log_level ="info"; + f1ap_log_level ="debug"; + ngap_log_level ="debug"; + }; + diff --git a/ci-scripts/conf_files/gnb-cuup.sa.conf b/ci-scripts/conf_files/gnb-cuup.sa.conf new file mode 100644 index 0000000000..9e18459f87 --- /dev/null +++ b/ci-scripts/conf_files/gnb-cuup.sa.conf @@ -0,0 +1,100 @@ +Active_gNBs = ( "gNB-OAI"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_CU_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "gNB-OAI"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ + mcc = 208; + mnc = 99; + mnc_length = 2; + snssaiList = ( + { + sst = 1; + sd = 0x1; // 0 false, else true + } + ); + + }); + + + tr_s_preference = "f1"; + + local_s_if_name = "lo"; + local_s_address = "127.0.0.4"; + remote_s_address = "127.0.0.3"; + local_s_portc = 501; + local_s_portd = 2152; + remote_s_portc = 500; + remote_s_portd = 2152; + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + + + E1_INTERFACE = + ( + { + type = "up"; + ipv4_cucp = "127.0.0.4"; + port_cucp = 2152; + ipv4_cuup = "127.0.0.5"; + port_cuup = 2152; + } + ) + + NETWORK_INTERFACES : + { + GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_PORT_FOR_S1U = 2152; # Spec 2152 + }; + } +); + +security = { + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms = ( "nea0" ); + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms = ( "nia2", "nia0" ); + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering = "yes"; + drb_integrity = "no"; +}; + log_config : + { + global_log_level ="info"; + hw_log_level ="info"; + phy_log_level ="info"; + mac_log_level ="info"; + rlc_log_level ="debug"; + pdcp_log_level ="info"; + rrc_log_level ="info"; + f1ap_log_level ="debug"; + ngap_log_level ="debug"; + }; + diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index 720d0f0c96..7d43b33f4f 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -531,7 +531,7 @@ function main() { execlist="$execlist ocp-enb" fi if [ "$gNB" = "1" ] ; then - execlist="$execlist nr-softmodem" + execlist="$execlist nr-softmodem nr-cuup" fi if [ "$RU" = "1" ] ; then execlist="$execlist oairu" diff --git a/common/ngran_types.h b/common/ngran_types.h index 3dfa7aa01a..ddd1ec2cca 100644 --- a/common/ngran_types.h +++ b/common/ngran_types.h @@ -42,12 +42,16 @@ typedef enum { ngran_gNB_CU = 5, ngran_eNB_DU = 6, ngran_gNB_DU = 7, - ngran_eNB_MBMS_STA = 8 + ngran_eNB_MBMS_STA = 8, + ngran_gNB_CUCP = 9, + ngran_gNB_CUUP = 10 } ngran_node_t; +typedef enum { CPtype = 0, UPtype } E1_t; + #define NODE_IS_MONOLITHIC(nOdE_TyPe) ((nOdE_TyPe) == ngran_eNB || (nOdE_TyPe) == ngran_ng_eNB || (nOdE_TyPe) == ngran_gNB) -#define NODE_IS_CU(nOdE_TyPe) ((nOdE_TyPe) == ngran_eNB_CU || (nOdE_TyPe) == ngran_ng_eNB_CU || (nOdE_TyPe) == ngran_gNB_CU) +#define NODE_IS_CU(nOdE_TyPe) ((nOdE_TyPe) == ngran_eNB_CU || (nOdE_TyPe) == ngran_ng_eNB_CU || (nOdE_TyPe) == ngran_gNB_CU || (nOdE_TyPe) == ngran_gNB_CUCP || (nOdE_TyPe) == ngran_gNB_CUUP) #define NODE_IS_DU(nOdE_TyPe) ((nOdE_TyPe) == ngran_eNB_DU || (nOdE_TyPe) == ngran_gNB_DU) #define NODE_IS_MBMS(nOdE_TyPe) ((nOdE_TyPe) == ngran_eNB_MBMS_STA) - +#define GTPV1_U_PORT_NUMBER (2152) #endif diff --git a/common/utils/LOG/log.c b/common/utils/LOG/log.c index 2215e4bd40..84c70c3767 100644 --- a/common/utils/LOG/log.c +++ b/common/utils/LOG/log.c @@ -483,6 +483,7 @@ int logInit (void) register_log_component("SDAP","",SDAP); register_log_component("S1AP","",S1AP); register_log_component("F1AP","",F1AP); + register_log_component("E1AP","",E1AP); register_log_component("M2AP","",M2AP); register_log_component("M3AP","",M3AP); register_log_component("SCTP","",SCTP); diff --git a/common/utils/ocp_itti/all_msg.h b/common/utils/ocp_itti/all_msg.h index ab75eaffdd..eb484ce8f1 100644 --- a/common/utils/ocp_itti/all_msg.h +++ b/common/utils/ocp_itti/all_msg.h @@ -15,5 +15,6 @@ #include "openair2/COMMON/udp_messages_def.h" #include "openair2/COMMON/gtpv1_u_messages_def.h" #include "openair2/COMMON/f1ap_messages_def.h" +#include "openair2/COMMON/e1ap_messages_def.h" #include "openair2/COMMON/ngap_messages_def.h" diff --git a/common/utils/ocp_itti/intertask_interface.h b/common/utils/ocp_itti/intertask_interface.h index 0a7bbc3b78..2b17c7e3ef 100644 --- a/common/utils/ocp_itti/intertask_interface.h +++ b/common/utils/ocp_itti/intertask_interface.h @@ -79,6 +79,7 @@ typedef struct IttiMsgText_s { #include #include #include +#include #include #include @@ -409,7 +410,7 @@ typedef struct MessageHeader_s { } MessageHeader; typedef struct message_info_s { - int id; + MessagesIds id; message_priorities_t priority; /* Message payload size */ MessageHeaderSize size; diff --git a/doc/E1-design.md b/doc/E1-design.md new file mode 100644 index 0000000000..db4bc85f74 --- /dev/null +++ b/doc/E1-design.md @@ -0,0 +1,108 @@ +[[_TOC_]] + +# 1. Introduction + +E1 is named for the interface that lies between the nodes CU Control Plane (CUCP) and CU User Plane (CUUP). Once the nodes are configured, all user plane traffic flows through CUUP. + +The E1 design in OAI follows the 3GPP specification in TS 38.460. The code design on E1 in OAI is very similar to +F1. The ITTI message passing mechanism is used to exchange messages between E1AP thread, SCTP thread and RRC thread. + +The following sequence chart shows the current E1AP message flow. + +```mermaid +sequenceDiagram + participant c as CUCP + participant u as CUUP + u->>c: SCTP new association + c->>u: SCTP new association response + Note over u: Create UDP sockets for F1-U and N3 + u->>c: E1AP Setup Request + c->>u: E1AP Setup Response + Note over c: Receives PDU session setup request from AMF + c->>u: Bearer Context Setup Request + Note over u: Configure DRBs and create GTP Tunnels for F1-U and N3 + u->>c: Bearer Context Setup Response + Note over c: Sends F1-U UL TNL info to DU and receives DL TNL info + c->>u: Bearer Context Modification Request + Note over u: Updates GTP Tunnels with received info +``` + +_Note that the E1 bearer release procedures are currently not implemented._ + +# 2. Running the E1 Split + +## 2.1 Configuration File + +The gNB is started based on the node type that is specified in the configuration file. To start a gNB instance in CUCP or CUUP, the `tr_s_preference` should be set to "f1" and the config member `E1_INTERFACE` should be present in the config file. The `type` parameter within the `E1_INTERFACE` should be set to `cp`, and `nr-softmodem` should be used to run a CU-CP. The type should be `up` and `nr-cuup` should be used to run the CU-UP. Further, there are the parameters `ipv4_cucp` and `ipv4_cuup` to specify the IP addresses of the respective network functions. + +For CUCP, a typical `E1_INTERFACE` config looks like +``` +E1_INTERFACE = +( + { + type = "cp"; + ipv4_cucp = "127.0.0.4"; + ipv4_cuup = "127.0.0.5"; + } +) +``` + +For CUUP, it is +``` +E1_INTERFACE = +( + { + type = "up"; + ipv4_cucp = "127.0.0.4"; + ipv4_cuup = "127.0.0.5"; + } +) +``` +One could take an existing CU configuration file and add the above parameters to run the gNB as CUCP or CUUP. + +The CUUP uses the IP address specified in `local_s_address` for F1-U and `GNB_IPV4_ADDRESS_FOR_NGU` for N3 links. Note that `local_s_address` is under `gNBs` and `GNB_IPV4_ADDRESS_FOR_NGU` is part of the `NETWORK_INTERFACES` config member. + +Alternatively, you can use the config files `ci-scripts/conf_files/gnb-cucp.sa.conf` and `ci-scripts/conf_files/gnb-cuup.sa.conf` which are already in the repository. + +## 2.2 Steps to Run the Split in rfsimulator with OAI UE + +Note: A 5G core must be running at this point. Steps to start the OAI 5G core can be found [in the oai-cn5g-fed repository](https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-fed/-/blob/master/docs/DEPLOY_HOME.md) or [here](NR_SA_CN5G_gNB_USRP_COTS_UE_Tutorial.md). + +0. Open wireshark to capture the E1AP messages. You might set the capture filter + to `sctp` to limit the number of captured packages. + +1. Start the CUCP first by running the following command +``` +sudo ./nr-softmodem -O ../../../ci-scripts/conf_files/gnb-cucp.sa.conf --gNBs.[0].min_rxtxtime 6 --sa +``` + +Note that `min_rxtxtime` should be set to `6` only when you are connecting an OAI UE to the gNB. + +2. Start the CUUP and DU (in any order) + +CUUP (has its own executable): +``` +sudo ./nr-cuup -O ../../../ci-scripts/conf_files/gnb-cuup.sa.conf --sa +``` + +DU: +``` +sudo ./nr-softmodem -O ../../../ci-scripts/conf_files/gNB_SA_DU.conf --rfsim --sa +``` + +You need to use `--rfsim` if you are running the test with rfsimulator. + +3. Start OAI UE or COTS UE. + +OAI UE: +``` +sudo RFSIMULATOR=127.0.0.1 ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --nokrnmod --rfsim --sa +``` + +# 3. Configuration file IP addresses of network functions + +You can also run the nodes on different machines. If you do so please change the interface parameters accordingly and make sure the interfaces are reachable. Please refer to the following figure for an overview of all parameters. + +![E1/F1/NG parameters](images/e1-archi.png){width=1200} + +[PDF version](images/e1-archi.pdf) | [LaTeX/TikZ version](img/e1-archi.tex) if you want to modify to reflect your setup diff --git a/doc/TESTING_5GSA_setup.md b/doc/TESTING_5GSA_setup.md index 1828f967b1..18b61d30a5 100644 --- a/doc/TESTING_5GSA_setup.md +++ b/doc/TESTING_5GSA_setup.md @@ -91,7 +91,8 @@ In the first part (*amf_ip_address*) we specify the IP of the AMF and in the sec Please read [CN5G tutorial for more details](https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-fed/-/blob/master/README.md). -### **gNB configuration in CU/DU split mode** +### gNB configuration in F1 (CU/DU split mode) + For the configuration of the gNB in CU and DU blocks, the following sample configuration files are provided for the [CU](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/targets/PROJECTS/GENERIC-NR-5GC/CONF/cu_gnb.conf) and the [DU](https://gitlab.eurecom.fr/oai/openairinterface5g/-/blob/develop/targets/PROJECTS/GENERIC-NR-5GC/CONF/du_gnb.conf) entities respectively. These configuration files have to be updated with the IP addresses of the CU and the DU over the F1 interface. For example, in the following section from the DU configuration file, *local_n_address* corresponds to the DU address and *remote_n_address* corresponds to the CU address: ```bash @@ -112,9 +113,11 @@ MACRLCs = ( ); ``` - At the point of writing this document the control-plane exchanges between the CU and the DU over *F1-C* interface, as well as some IP traffic tests over *F1-U* have been validated using the OAI gNB/nrUE in RFSIMULATOR mode. - + +### gNB configuration with F1 and E1 + +Please refer to [E1-design](E1-design) for more information. ## 1.2 OAI 5G Core Network installation and configuration The instructions for the installation of OAI CN components (AMF, SMF, NRF, UPF) using `docker-compose` can be found [here](https://gitlab.eurecom.fr/oai/cn5g/oai-cn5g-fed/-/blob/master/README.md). diff --git a/doc/images/e1-archi.pdf b/doc/images/e1-archi.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ddbed79e51a30221f449c6168d990a9da2030108 GIT binary patch literal 20052 zcma&MLzFID(50ESZQHtO+qP}nHg4LsZQFj+zG>U8uX^=A=+!l-7)7jgMtcxDPQ*j5 zC@N0R%d|`QX9fqBViO9j&8itn_hEdkc-on+Ah?$Lxjp+Xg7)Eg`TURq@B1Umr zBUdv~GZP0>GZ=n;7#CM(Gb1|~&&^sbIhSQlq~7`Z1*ccT!uhljdaxwzstJ^&ePO49 zq^nhRax$>>@6XT8wDGiSr(iy{pukhH`tIkC%44egcEtnN;=H%I`*svipFUsRynXq-+ZK-t-7KrrE9H9-HPBp6*SMW4$oY(-h(I(R{d1|2(9Z4a=XE~afy05JyGMoygXgxxxux6ie({~j4{b&ZeU|l6;_vQ$d2CA{14AH7 zPbAyFoooRU9Lek-cb0Dij)}ggR@m0`K6n=KOewPfJAo#kn~63 zZCZ(QOF0lcg~$a+&+gwQM!}FPmlC~#?x$7~DeAI2n}fAYDG8(0w>Sw--50L=_JY2* z_}yH%X2OA~r)u`i#y6ky-=w!cSrATjfWCWmZBwrDZ;Yj-pOoJ>v5E*6O{`;?Xp~})`KlMAN7U}27aQ~eu#)sCz!1Z|zBogB znA%32suE{rQJY&jTLxUEjGOmM75z#(1D><;<-kC~g|**D&Bz~qAKHszU`kNZTcqsx z3Pk1Z^&ACe6zd9Kq4)96c>7>hLGy)UU`~39*(tiN2# zvdw}I9oq)!YchsU3+udNtrS=}{*(Ojd49NGW%d|dLrV~*O3Su!!Hdq$y8E!_!|Qn< z0w%6i1B?^RK(AOYs%$E9cw)I7xu&lrb|P{IZw)HM$y?j=KTX_}9iA57_|*>}-j@g6 z8|Nm51`A{L%3(vc5{tC-Oea`ffyuI;fL#|)dp_P>^fC1iO!Qp;Si@*N|1~TwedG+i zY`a3Roo>ey6QkAO44r&TQ;o`P=gOp2ZU`SqCxg=o>Um|Ig|pUx)W${_WZdF}^#j}W z4e1f&`k-c+ru0&^O2F#wl*KO z1hh<=%7(L%2G{+KI8SL{$;GokaA8R;FbmCB_dPVog6ntshpCxinw#gI5j&Z{ow@Z{ z&Dd_D7fKe=9!u6Pj-hneRu4ONYr-B2o_1Sby}`svuG26Z-B2UZY2zj1&cpJ;8iP;n zAFuX-jl)R;>B9gIABRyS|D0iBWL4+H_Lk#<5Wjy;(SekIY>8njdWi>bp-;whX^HKO zBr%*6jj(U|UGT!Q>D_f@>DQ$$`^h2)9Rh(Ls6)G&>qDcl3ffP^3r36<=GCR}=J@+| z=pFCiagcd;EuZ`8o88^weCB;~+XvUbC?6+`MNG`i^2#O;E%baTm5dW}eo}ejfaMKk5u0Tz;qxf1| zwmwbEFd~lMI%%mh+C~to3dfW12O#*t42C|@vXr>B>o>}&S`Iv&x$WblYbQfrBlVQO zkPB$jNHAvhrvGn)|1bOB=D^JQ|8oguB4&0b_W$t=79tLIE|&kP{(JpDDG?_J8|(kA znZ$X3tESkju|pLR=>gz&;C4uMb|ev)29Q_>;PjLMacoot0OB|RK7g7yS6UhhW$%pZ z_MYeVxBmI}Qp+aqbkps&_txgNH9SgQCS7I|!W>*ZPzYJKXPeg;NXt!64;cs%QGf#x zkq`qlnE-4b>_;bd)D*})M8r_B@z;##5(0uafrSeR#Ht)21VnW^7pUh8NS~Nl--wxj z0EiGV{wx5_$le%;D*g9WR4(+_e3BcNZp1VsJ^0t>D0j^ zu!VIU-WWa@WLXHxr-AiB)FuH{5YrqUn6&2?Jy>msAjlCJ6oikDuTyBw(LN(PmkgJ3 z05!M)@+@%QkOQ#+{-4Gm5PSmbTm4i=V+N4-3(}i`vt9=M5b6;G1QEDAk_{8u-zUe2 z&A=U?iDBTbDQh4(Or|GBlh%GXPT+u7h=J+ZPlb;D-T?4m!2q6+np&ed2BHcMFl)$$ zFwP+0EEwx;C`K?!Ah6bVGbrca;e;O|M5GaD(=fKTTrQ{rqFQ($V%V>4-i}t>5pdws zaFHDURH!d>hz&EZia5HN4Qx2EfkyvhN=PSA9n!5E_^(TI{6Z#$irc*b43O5wZ?(|4 zZdgS^7$-MK+Uak*U^Jue#El?iARz(*0%QVxpmlVhm!>wzADH^nYmlGXFM4tHf47$) zF2L&~F+iUpwu2LxX^ePBzz{^++k-!SzdH90p`+8V4MCcuKN zZlI2UDuxqGfW946MD7eksu9|PI(UBD)vAfQN|q*P;VqG9u0goF^2UT}SHTJCczHR1Zu{zP~0uDB5Jq)*~OHqJac% zZwu~fPRNLp2n8}SI%C)k==V#(0Xd5pL6yJ)Y2^tFV4Q?MUy3KT07?I%e20Ys3vLKV zf&ry`hKK?Sz6T^QAp%w0LEeJ|Mh76VM1K&5#unWpq6mFheG5%2J^u=UNSfYW8^`N% z{vzL#Sb#%)ngq=NcS`>n9!WV;B5nT!a6thFMg|ZiISPjM;=aYhHYEy%o*EGUu1MK< zaBv*4ywAFsX8(o+I504+!`j9@dqJ4f6I>|bZ+;a^X91FJT(C^MTK}*Nc2Ei<4f1|& zuIXP3l^uju$Ck=*Z761~>fXoPV+%;Xgb(7ojsZpDRs+3 zLett=n&tg|W|Y}oR%T4AuEtlj&c?u8(v4N}ZdiSFG2VZB4BL-%C%M4_NWjYD>i!Hc z!|LPg#pORd=F~o*mZM8N((6RS7xUOYH0*s))zwfBy~B3ow<#6HWt7ztM9l4^!_QqX z_U!$9Z_FN{uT3*~bzB4Jw`urEZ%q4jE~!S5-Le)c6qBv{_U=+)s#7#aht+{=zZ6%{ zd~icbMzd!>EB&W431=yGXX@532u_nr5E^~+e0^qp&3&oEq<^E|suU%?bFWNmA8=EX z2=wayNj>z58`orKgRjKa;kBoTsA1iHAjA*Ji}GmJ!^y1E}vo z<$=F_+ndi3jgz%*fxbBP>x+%RYSiXWxvq>U1VZ2gs_mE8fwXNZ~7MPqkz42D^>;n8= zS!CUhYnG!74b?3CDJ1U}o15w1Io3T`7?qKXk?cJWI1gM9go?8bJ!J(!2r^#hGu1am z4agthppbS{k^+|~--Ar4#iAO_OxsF7fq64clo8#x8-}S@)4n*F%S!%t1!dvU{QeNY8k}rTXCew+cjy%Y(BGagrtmu05v56fP?LH;p zM{w{852ke(2i+s~Na7)Xh3 z4~i!~{8-CDvK^vI*3-Utudo=7;ZHK*F@O$E&l$q*-4c35F&zh+3_n^F~0 z53?I@73yzkkSiY}!M8CJ1*vhZz<$63Yn*0o; zd?+x_S;;;2&+iJHg-dc=zbYEX;EL7DhwEw(@|06^ErbK~xN9yreCO(8ib}Lk*FJ>= z3LlHF^T~TC68NJ;>bf#=oi4OER=A*aUnpDTy3K{9oQbdkL@$kZ;&k!dcl}G@lPMlN zs!7ww)zW6ub3CseYu09Pr^JO5bzJ+TcjeSURK5QKl6^me@?ZilxD+b1)l8i70E{fJ zaO3-BHU4!NVjKl1*g1WpTN4zL|K|KB>jb= zodEn|{yC`M+}T!OUZ<&xaX%@|$PjGREVGMp&_qN=zA`c`J;71qLkvR)1Zqy*m;K5HsM4tn(i-Uhx%1Qvo_>sp0_n7{!wU?H^eCqke z0yC^J79AgLSyEJ)n5gB!NDmxWaVSkR=19l!^SwYjEj-;^Z&$RE+0?S1^&F#IB5lL9 z8hX7G{d)xj7C+tLR<=8W5@4rwG5=>9i*lUo ze&o%eG@l<*%h!=DRSX#+A+>1Y`0%%b{gGfIRgiY#VEPSWI1TVh?!P~HXfyTQ;DDSgX5XYg;(R$X$r9X#5|NS!oVs@tFJb`_HG0$Pw#v6%8S zf4fQq&9(}fOi;fuK>$|DMnmDnEaMtP84a}xTe=vZ4#dMtiO%8LOOk<{9{S-io(27( zSbp%B27IX08P5ICDNaUAK{j%EQYVV-QmLw$MqFqxCPtRD_iRZ>7qN^bHpjHoOy><8 zUX83T*&xQCfaU9_fsEVXbb<$bX~h=YQFo-yn6mXgQX-}-#3SPXDnl2->6M06 zq2|fh4c~=qaP(<$z)}Nm2T#>TRq(Qtp%Sv7mVH`tt-_kbm@5}(@9fIB8yHT%m6|7S zflq3s%k?vS3i!>3k&Ud(LzzDZ%<_0~FbBKO$(o`GdMKmo(VLqwGe&kNA<}i51Z|)F z*WqL{4U>dCG-&ZG@f$d=zORXrvZhQ*`css>{&82uLg@HAD5SGi!h&mW2JzK`R~M>~`1E*)7xlqGp?wzk<& zxb$yKRKlwjm=TEKoW`-69WVIaWCXSc^SRS8+RYo|i}Sx6j7xbcR+u^5yZ1^kK( zynY{HS^;%^IP&>Wk|+CL06ydhggT$;a3>9B^C{=XZWZ};5wXw0lSQ-qPxb)eT9>@h zp42cMV(BE4#2F5LHBa^P+FH2Ka}#bGW?K#Zw(k9n3{?jE!C?#O@X-iB(N#@|wQY*G z6)}&e;;&6OP_|XH*kb-4>R=VBhZ!Dnit$Z4FO!WJ1X?bKRRoz8 zLE|D})QlgRk`x01I~M1dS~foGchQ%;g9$LxeJNZef)$T>JSvRE59}nvuI|L{edu>- zMk}fLmr9*}m7A%vs(T6UKS^MO4se~ffWw9snPdtMPR`|X zYImBR)P2ekoI3j>D@u-@fjHeFZoLIK&1cCUP1AuFqKgSdFT+l`yKRxs5uqHFo)T^q z(Z-{&dZ05org_)ux7!-X?D92JU?R8=_r4PJ%k2}`aVq0zdxGOX^{Oxw!B_zaW^Nbi zXrl8=(eCjYc&6HVWp~}>1jCl>CQvY=oG9d0m2@s>AZao^5$ZDUAo`1MC~E8pI zk;=c|BI5qRDC&a3vQ;aKqLFb$6~OmlS;Je6jm+iF(9H^*jZbo}D+FZd?277g@_l%~ z+qrEB45OUC7iI4miKN_XM?1@gADs(n&yx3}!`bjc53et7^xke>IE6{MjpLA~`$ow$ z=6wwxMs$cLY#-KR*DS$d_Cbi=j^@qCd$`o!Q1-6Mw6cP^QXm?+(P9HJRj;PC+QsHL z@R`X{XN-`xjV4P|!)X5Nd=0#}GHW0&ajU{^m8BjJB3q|SMtj|qB*i*OGqNFTSa3T?2c*VxQ;wusUp?1JLDM*m1$P*2{%Rlh{oHW ztrC0fc=nQiP+jox1|G z3AJ26UjOygZ@WV5rm zj;<98&r=`n^-oj{jaEpATDSJ~)j?6SIm@`Q&SL~gKai(4+Cmh*G5agNp?x$dIGgR; z@%AqWKzB8E`k%EATjlWfR6Fo&nBP|1PxL~Me=ZO34$HJe5LuJ)eeR`8yBZF`a+cEw zChI(91>|Z!fr(iq%P+_R{rESW1Zp>t#8Um*KSfaLgATY@AB*vF2pMsk^=x?Mn;)V~ zM;c@yrFc5)`trm_`OHraQ#P@&;R^*W1OnqaLqjqMWug4Y z2%-VWxuO^?E49x;K@B~tZE;5ClsN}^TCA^g+8qVtaI{u4JXbagA!73A%3`~f6v&y zJ$rE5t}Bwe+-&N+_FWmn0$_a?j8XcR=o)&`?+bNA2U(9C|A0t8r=?%8cVN@V|IM{e zv{Nnch%H>Dk^3D+Ym`gycC9|*p?*2*G2tN?u!MpewSVUY0t=~KcqbDN(RAz}(XU!%f3xI?XYjY`|oBDl0R^JU#b zP<2OTYP+hw#-14lck-_{rEoh0UQ1&8r1b#Veefyy0dvv68IkdASmE;%tVn6w3qUU< z)zAp(mE}(MOrH7l(c8 zvYEE{!dC8~G!MoWFTXx3;mzKQ&%UiG;{B0lQ7~lMIuQAN zSFs%%-c4COaVfhbgG2w8-q6!*nO+1J-e*5ztH0VA%vM?lQ76Cl60kj@*b#G*?`ap^ zGRQoHqkpFs=`65}O6BcSR=-|jn6{ojlj46nNU)b{23!cBPP$>!! z6(`OT?uzcu9O~r)a%qe5l|NO{beJ)l44li#2JIeu;_3dT=9t%@srZT?o9KqJLBBOu zkX0DuB$v{`eQH>Z4>ciW!#j}>iN~=%l*P=VH^>ga@qKo(e%2d~1u0!Tu&N7DEN{*q zhg@M(I;iZ+Pcm;U4g*67Vr08Np(yIqhqpQ4w(}@WtOz8x9@9CFKyp~CVap>gGhUOJJ`N&*CqJ064~qb zCDZHIqRhlt<6w97?iVO-ZwWCL+9x4M4HD0kIq|O>0IyF0fAL0G8A z*>+h@&5Y;%C6MMEwS*GZ$?xp z-BsKvsI~Xs=WX)aW~o3F<`?!pB>|DK1LG#rgx`+J{AQ$I5!Tw&tJN_1^Mw4a%)hQb zx_R)6wHapADuB+m7cpRKP6ZY1Ld1$vfKF0fjkea*`0V#Ai%Ve+adtP)J}hw$_fYd( zZUw^r=1>@EpXOGPGn{zeDd~X2pWI~?Qs<4?Nk%Xx5Xp60*xp}DY!MFSo0Or{N+=Oq zrO51G_k$!HRCb#!Tvb|h#<;y-WzHsQ@S&$?@wLQi1u4Q%#i{dmoRjkS?7sfE&!nZx z8`hPN#TVJz5o^|BmoeQ`tW68g&`$hp4t8rZzXExJ40n;#^2daxOJuK?M>3^m8FP?7 zmi*USj!xiem26`IvVmSafI3@b$tPR6&~*?zh^1$!bUFxWj^VZd z2L7l47@DHx?y%{P25Z;i4EBh)I#o4fu6eIrrmUwX*$#8Fs%Ydo>o6GS?k)R9d@mtk z4I$)CBK46Ch=&)J2Q-l1%uH+eE6_LMBQ)rdt8o+ZNTG#O9I}l}TtS|Au{6P86HKBKTYe|xW3I*skx077W`3tw7euIW#-#!` zYIse{cvBn#761#TlKKw?F#ciY@5{Aai1pHx%f&eH*2(5pGbIg|YzY?vNVm4Z-OMVZ zE{BLL@t9=%k|uKCkTa;8n~r>;Lo75>Qd&YYIpW2Q4t0=>RrM2x`f7k!-9ERvYM>DRT&L^(o z5EFFGMQ_MROnXJFmQnn}?>5Gvb{p0F&36#)^2DuGR1yv5gOWQwvPxo*R}u6mOi+HT z1}y5_CoYKyZEL<^tYHnU!sA`9=0%^%+2s0I9&QQMiCUP_=lyQRbv7hsTw6jRl@8R~ z6HGV7eg!k_#&{tt_)}uu)3jB-xNX**kruJx_njh_gr1AH_9G;#9^-QwFyHt?Io7niTs1!kRDk(vIkbTSPf1cr&Pw{2I0NT z3naksN!I^Mkhy7K=bPDRVOc4okpVhi&<{t*s>JZikg*<#;R1CQC&Vo9&d-9(TBHKEX4mN<~m`r}4w3I?dLXe4bZa{}Z3)oAkz7pf~DBScI!FF-5gmtt`VYr}+hc2EifGOdhysZmO|4(q&N_4(CEe z8)@J7rsdQ9#v5c~cnqCa@@2_k_>;0=T4mMYfm=7UR1q;3^iDj)Gf`^Jbo{0`N}-La z#)y|e2{NVub^I!V6lYRut;I+f#&9`GbTRhM1NE}I!ZDNm?y zV!9xM@6NQD)Vm~Bh>HS4e36>Z6yOM<=9G%I1>f-ixD?szc>5bg-bHCIQE<98&W-Gy zj59$}ce*OkF@4;tdRh=pqMrPFHM*OvUTBiKIl*k~3>=q0mzp#2s{cAF z+LlTv!h(TjKh+RYTzW&rjR#uY;l7xYe^m+$Jb(DHw^40}raJ znWBx!ImrBxLoS3}+&Dk6`RszBEzLlPc>^e!SmO)iSd-3A(RDtIo)kL>d|#>8P&j!0 z=D;w|?I>T-NWAd?MpL?1FA;Anq@Jx|13pXX$*K1-*h;(MYW0&;5H?xOy~5-76A`Fl z0lXYNcd=O>3u9mow{l&eNJm zS3IHIdNzbxq^jvyMR80Shp*J4>GL8LS*>N3&*~w4kO|q~SEn?qJRN+DbDJh7X z&qz1EdMAfCZuIL|+?h&c2!^*M>VIF2`J2xxN6p07S>y4W@XWL8{hP3UUvhnMfPYW7lvOeia&lv{r&+Ky z3y$u2!dGcZw1Ka2JTGFtw?g4*0crKyapjdN4uxFau5#LWBO)|JHq=|2wvU~*Wo`gF zQBK}ien5Rd>d|h|LSclcoex+~Jw!~8{EYP&-%tTnv!2s{ju4ND0}VN_Fo@ChLHxC%e-&zimwT<0N(C$thY3ZmUpo~U`jrhF~6(?>o{uat@V zJ@wW4EkS9JNPzGUbGS8PycR*47Y-w$C$h!*3SPlU#Xb%ZVt88$@xLafZ{&)AXcb`l zuv6>#@Q&TtWP%T;j>jfq0=dBMc3}I3*2Wdu-(CNA4Hwk{9FGC@k4~{=2MmsZ`Y#o8 zVLPg%UBcpdn)OKqd6pDW_(4Kva{E#|*BLm&5~tBc#vL2?0W|;2rUfqb+Qnv{zXWvz zOQ|5vPot59aKVgaTCr$1xhg_C)r?>$bTB5~zz>n%WA@&;PP=hFu-6!DTjitL0;MgJ75V4$aYXnkI6 zGV?e`M9eNnVMO)|<^M@jw(?wARKOU?Vi6YMOma;!`hGZepLFGyN@8XmH{-fhGSVK%E+sW8N~U-8>ggr@%}QIv+)=Nni~^E^5O1%Doc@yC;Mu=tXx9>&D1x zlLsqVab<&)XbhnTC)@28c#XXf8(qeX+DH@D{RQ07c(9$q+Nc5Bj@ngtJd?THR+NAB zL34{1ZC*AahkTIP5s9lMhxtyz8NEX=rb#O7j`(c+`wILDs+C?1QPsV?O0__*$2hBn z3i3(>UrVgsJId~J+!lZVH^0GwXs_D+i0DRKW}kXHEWD9xt3@`i_;b)3V2OVGmh@{_ zQgm(wiV=T)b&bjBDcF{Ax|w$ba(*_~!lo=DhOp zobG;i9=MFYcJ*;_;zlmI8Wf;CkUbe#yiFxf!+Q{q>8;cBr3G)BRC;1Vnuf#P7r9&vntDb6TZeCAgHOA zbYigc=&^T^ZDJ_05eSPr9v3J)dQ>TPL5(T#V{m~6>E)Y8s#Jbyt7t+5O|3pnO zS$ME8h(I7hM#BbxGGKvDpj#ooi#VQ~!@t#GzEBQn?jD`Qc|iYlkQn+yal*YyoE*Y9 zMF2Noab)Dr??wNLn}UD<>42gAho@{1E1F_naWcdGKpr9xhTMT=gg7-*0s;Mge1Dop z;WFy5!JNAp{e1cgy}GHnxwU%!sD9PU3jtq4-|eFU-$BDbgNTNT0TrR30QLV;EQS#M zNP+n7SN;ty21W^R9{qDJ@ZR<7@-+~k9s+x}r^{`*Ju?I}5QE$f6cbq35i#)lSn%uq z{+oSkK=<2@{JR&bx}7kwXZ_FN`&RUrQm068SBNutktLu zilh8@rOJf3<4=Ob2Jx>Q>5v-uK6LYnh+*MNKjdNj+fO~Z2#F#SUkmNQS}w>xRG^Sw ziHI%Q3euZh$I;nC2Xwf0_FsG@Y;duypG`&~WTpSMGC`h+K~5%QR3spG$OH66^fQmI zV4#9uRBRZhAOmWrkb%X_ru_)2s$f7#zvLhP!6-ywuw)28)OtkFK$7nUWDpR6stgH0 zKqcNmp+H2>uw)QGRt7|P<#XUeP8UBhBThNqn3709fPO(v`4HI9>5m`qAv3!t#-xGK z5M3f&u(N(iFiiYt@q?^y!pZu)5ZmA24rxQo7tRxb)9-q)bV$7YpLA#jInGV^M@(=A zt`P$yOw@PG5%MEfU~tF%-zf+W#=wPNVWS-So96p;9L$+_Mk+X86x_0{jAziDR{{4VivSs*?!JWx| zM)>W4AIRSypaH`?CaR4iP8=Ha14ROWrzTCnY=}dlfnr9J3y*X9h^Kz$#`kA0k=S`L zeqp*2>Vty>X7OKhOATEn=bmb{ybL^vv;b}Vhu&pHkXS(w`L>lDK@mUDRWuf=d?}pY1(31 zY=m$i;+!WsshRQzK|cq1k?pfCg@WNC8HglKGu9ky}UtdqU9&9$D7#Nr&ZZHGE}RrMev zW4ks|p^^y^MCK16L?+rX*)H%UIq41>y@&2#PNS>mW<4yMvUa1Lkq6j!J9-LhHxN+AT*ZLF8q9lT^~;a+jOzfpU*vcTk9lHnLt z6Rz2^ECx-y=Ly>S_)02pEHnXr}Gt>55YfP zoqln}oKy%r7I=U4EE)IpH zhtt`ys`izB`|3JV=CFZp*TokL=(Wrls=~q(%wv*KTw5tOPXvL59ug4Df&!{neNm}a zJaKimzO)~{gEAz}h{Doi*f*&)sDtsfZvhIml@7s$lD)n^2->s0kF^V>l=6&O!KeT} zdPoL=nXO&8YvM>vm6F~cSpD+(g5kv5vVGj;Q9z~jRv6QI=b1LXP>BqC>D7Zj{0 zmvn86%NP2jhs);l*(9%8D2|b^Bogv3Mc;$8Np;cEEy976HrU9(%`~HLyD^L<6IN}l z1FGEyhXMD7E_LD$|D->8&>w!{jB0~NGyjK{zrDs;8e+JtE3IBt?c~$)CtRqveSAvG z3+=qUWcO0x!g{I-DgFhq3+!K)vk~!(y{6On2*yZjVZJndz-09L8gZO87?@8teEC6J zoXbb>=Mx)9XZl0K#0*jDX1l*8GueSMw$t{(pXF&;|ga}vy347T|@bi4iO&4>I# zzgO8P<_AEu`$l0ruyeQIscQo5cGrN!Np!DkIe~_B&&_)O?^fMP+QA`}Uc(!96rVPa z&w^2!y-Ho&mh<$km9g5JB8%o++WYp|3~MiMls@xa*31malPd*NA2``}&Vypu>|p`|B$A4GO!!*YCK6jIIAlR@M9cW2g~8`Ib9S6+aRVIO6C|~mC|lp*#dV@Z z4C-`yB%`lCbD?g(J3ry45Q-<=XeS_6rqdoafz|L=G%hL>$?0?X71?WQcHpJ1m7gpzua^0bxVX@Eyz+F%(ACR=isgNIIk((Oh3Vi3zJUIoLO(;@ z7thh}i@Ek#k79wp<1toSo}XskiuSMi0(ALDFRk5oPya2GnBPJ7jwcj#Duy-xixvm)6;EeSv(+MHvge{BiK?c zLY7s>rW<+UVU0l=8{<-98T*HVBV^7bWP4VbKA>M&F=b?>nfZcC&*eAISebcHZ**FH zn>P#^rSEvmxKxcVq-anlO2H!d0n(@r(lkjk;(B5zt$7u_RDccR*4?Pq*rq05pDIVk za9rW3WD#bD<;o{kQQAmM)&GFGB+UhW_7&rFn6N z@Is|pp+#Ju+Hu*9;phb#vBnTpu7yVM-(DyJkzbX*9c2!c~_AY~7V>}#a7 z%vqCIY>$Ipiid&uF9Ghxq;|#S=WrBPIf-^DsNo}mmKfqsscJgKNFqzp8{Qv$*IEE+7 zUrTz$bgGz@!<_JHQ5#M4}7GP2jX^rW!tv#xociE_10i%2UCTo=Xo>JL|EW8r!!o zeo`8j%rVCm)LYB3bi-w0BXWl#k(o)f(H*mc9fXb#IGJi(2J8;C*i+&un?tU*nU^xa z$WJ)ZD2soG4RvTNJL?l3vG3fBe;pz8bnMSeNXpkw-*6|xOQl6GH)*hDe$Q)YdnrM( z`Uvb!kG{-GfA{>zh3HOg>iU7pRP+l0`0lHoHkAf7y*&>ChKcUAHK-QX9J^!n=7?@&h93dql^NBx^ZDYC$v1zv2*D{?8V{eZ1U(%Ew| ztuZHSakw-IfBif4=fzU?UHMY1E;MNnw9zjtgbgE#kCEjGygVLUiiBC@DeyCvR6p!a zs*xj4Px^wkJ0wsq!Ch6HbG}WPHip~9YL4V%Hv`pNS>UTvE6K=gxW$@rBF2JGvn-n+c?SBwc3cgFsDO!(KKm zSMfG#FV|XuW8XsEJ_?w3!A42G%hq<5h{@8u24y42c;yt^hX)pC_?FmM{&g2roEwcV z`EU?|wdQk(KT6lYftG1yWlR84`?3$!*S4oN&SyUu=$^GW|R&+&~hh^$^gc`O_VD0pA(}HNqeBBb{SdWME zb-cYqrz=rI zZyedHn&=7?l%REdF$)j1)Gu{x!<~-nAJVhN(KF(5Y4D%Mcs#9_OYdQ4mi41l0aXp` zEdAHIRD8^Eh;PqkE>?C(3qMJjS%r+0DU2;dn-4( zf=>MI`o@vI-5h*gB8hE|svHhc(qB3^KGpJZD=c(Adb4bKxxSaC5f7{~M9Bh+)GjWN z>E6GE#Z&K@LPk-)WWY)*ljIDTFeD^5ejD!UwRe~DZA!bSsX|HQV^3*^HND9(FxuI8#Ez}_yn#4Q0d$J`?z%ov*nMaD;#S}Cq|Mn`4_}416U!% z*Br#9246l`WhDALb(3z~)9pS7wVFgWnI!y;}T-aI~Z~#)l8PHENh_- ztIfEc+zm;m<7%Y&S<3cvd4Q6SA(YFW3<4BU6&#j2rx21oW^0C;n`R&YT=kzB=9k<%W+3udzwI?MLa^cwhVn z-!B{sHg2-3CvA=r9u#M(e)lhv;2<+8EU^)$VS-_1z1kfhEhg_!sBZb^Ay~@mcNK@n z#1(G`Us{{HsCsJ$br7;{Xm9*!*L)RKxYE~)@U5@=PaU!6?Nl@G-;-w|NT0Y5h!wEw z+-Jf1gfg(Z-Jm38O!4gxJ45hhdGzbC>D`YF@hVUPE&^&MbBV*b3!`%S!-xznhh2PkZg~ z12=$WarSTzuSb@=6#TvY^Hf*FBv<^^(kYB;%O^deiT(NamnAoRmPumc#u?FGPHF{B z=z)wQ`qjE8jzBSVrnV`+t#0J(5%>c*uLxJuYsX`p=EhpyIGZ;nN=vk5mpod!rV znFPWy@n>gcu!XKP>Mgsqxe3jmzbQO>y(OrOA3VtcfBS};n6eKRx*LkX$evw5PU!{! zg{FsoQhn5B_hsccsf;HSVTAMFk=={e3+CAqdj#K>iqqkeJ?#d67tazQzb?RH((Qn* zMalduMZRUeg=CQ7*86der*G(BZ~Pi3l(`)=CMM8|ayxbx-eNjA zGW$tRC}Fo6Q{V3FUM}^1@a$%t-YfqULVkO)rU0Bb;3M7;2W2xX(EdAxT?2)&oVDI^ z&x=q+B*J7MEt=J(amp~RGLmj5(voguufCB@@)=nS5Kz-oS$RcR8t1Za4$khDZ|saX zui5WK@-Gj~bJ#xT)lj}cdJPrFi;~0OmA=z@gDvjKS$oZzW46|UK?}!^iIlvNhB8Bn z5-a1D9DCHwQ-7Bfd$E)c$Upq+syr6_D;*MK7=$1^$;QeoM`@_|@1nY{MjoSoTOnTc zM29ySK2Dh;1XEw$@;-?sl)o|vZ3m%JZJV8)a9G&okZ7fl_)Q7hg7+yivlGM%P`vsFS?Jo}NK~&`V_<%j>x| zxJDC2f2K=8+02Z!3l}*P8Fna9EpB^1+_if$HG{B6J0>chF93!QXOw%lHx&+AP0 z?GiE?Qn^;Hz^b{|;i=RDhS$5Y_+op?YpDhd47JWxWE#0Dkh?Ga`Eu>i(`1 zo#onOR0s{1Nvo9A8O6V2ZLZ5rJ9!6^bTp3mQTDM>Ih0bzSNH9`WcXJ$J6}2fgN%T~@^ST)24s54Zf#?;EL-5>FC6UiMO}Ex8=EH17s^3mTTV+#1?lbr>i&AO#KYcF+tugBSan4g(MYnJUA$* zw<#(+jFtQe`oiDZ2J^041%HAW9Fc?P2V<%m9ga^Q%fK1muMcRE8UcQmOY#kAZ;(yB zAs^KFP%Fc!aECJ` zpH-3p>Z1fvohtbW-||b2fC{+1QkKos8r|^G)+WElJ_w3>YBrJkWZ*}`AI)7u@m0Yi zDe@%EBWaq|Qt&iG#`D$kh|Sly;3?W$l{-gBXvNSpjlz>L-ehH;l}Vuo*{8uXJ^k@_ z4(R44@3n{?5;fn(?m5K}Qnp9BjKK-u3+vmSYqdw853H`a-*$Y}rjDGZ&411HS)*)D zS#kYNwkf`eF%=KS(riYvr~o2B0-7+Xy%)FLJbVd zx39Yfx)|dWS4-+xotAuerLC=p!w<(aJ%;-Z=fy`Je=x3lM=vWd;=N&wdf;3((06}t zHj3I&ib-!0rWulFaZ~%Ad+T6Fc1?)4$ZfmSVprP|OY+i0g@cMzK{bHDHBCt^xBfkI ze$R_wRr(-XsQAwf{U_(Qja3q}HaXnNgRUqiThz3U@MJN@MLzB-bKk!KuaNdlPzW!= zp89Cra8C4IA1Sk8^hJK*(5F?&HL5}0pAGtBr=*&l15vDQqPugA@)70MS)GtZ+S&>9 zWrNVo)-2=x#f02iim$|%@#{4g#z4ab=#@lvP9VQ4FQVWGQA01*Xj*v3weZg4*>Z2- z8;ww_c9Z!d5a{k8ujbMP!=EQsSU=!6GV5S3os4@HOdh4^Eso9QY|JVy(gwr66r}Mx z(Uo~)mPzM#;MGC+`(^9){%RTi2Nxy{PpI`}1U^=mjVyl^UwaHPvN9nylg05kZ|rA@ zb1l!ta*&n*+bwar54%`KHO<6tSHNJZuM;;9gvatUdDAyv704atgGS4kj z>D7bqP`6#nZKS8>%Ln#%V-7m7(w83jtwq?mpqmN~o%yqkUS$ZiUKC{|RVDd~+&)B& zZhL&?Iu!gdFp{78Z7gk=pRH4>bfUh~_U-hmmtYsHQ(i^7Y+6@WY=@J&o*7T-b@TZf_-RPiiXWmCu_mD&spc-`|I)vHuOP-bC%H{NxBcKC2^r;@Y1`N++Q3I2 zM0p3DpbY8xxf^y#(CQHmpT=swU5Ixr18?+lCZc7K6OVM()Zf$QoOrBv#G^2L51I#E z2XOE#!MyPB%k}6HZ@KQY&lCMyy4+np!eUaXeL871y|0a{9iIwttb7VVV>XCU7=L#%@8HIK)6yxc>oO*F_S~LN62yj7j6nw=P=f%X;{7Jp>Vwf6mE)z)v1nFLeL*WQ09I6OI!k`Ga926!4g~~AfOz`gi znS3J`pFk`CVrrph4b~6x@o{zW^ZS*Hg%>Um$Q=L9Ngim44G3h01|q=B zXbQ@3MTJXnu;Smj{$GZ`tGHk+5DtYYLv*pOI2S)zpf|yV=oN@{0S5oIT@lPYK=v0& z=`X^D@_$sq;wAwLgrHq-V2Hi|$Tc90*~p-e3kW1KVH$roF#wfNO8;(L0RA(i>Ff1r z^n~Wg$bP4pP7?wDRq(DPzxK;ZST9X8J7;rRT*_JB+>aLB&F`W#oXoudI)k}tD=v_} zG0%f7hE#PdhpPb+`spf*3@-YaJ&WW+S1JmmV|(>(7Yl|Oi`3*ZDvLfjZs5#UDrIEw zBHz7v#wB`=QT1EB5krycR~8VwaM3hY=_J9cXtu1!NAFYBOiSNpCC0~@pM1+(2wp&4 zhu&{T=EzIm2@_e1AkMQ!mxB`nDz71or3dd^3GB;|n>8zv5evB-P*9=UFUGclksyR} zj4~2XLyi%C)S)XzNmf{La&ZhNc6pn%GkDH<+tNqd z{$qn?ZPuR)8uIzgx?QN;HFdJY0hdj-YO{I78u^+GU(VZI3Z6J2nK~246#8#>Y8===oG>bz@4lwdsrGD-&TNtN2ULs@3>hy5TKQZl$yy3`e z99Jvv((zeNyw})c0$tV5Mo}jPe*fWMIGa%!im2|KD0)+`%s96`T-a_`8Zq_MYRXe# zi`DU00u>*;5r(bD89y`qFznD41{dhGj7(=RQSM}1i<=T+}jBaZG zkE`gv1!!%K^#DK&{oJvkKszW?&<+3y21oiOZ2-gyXa@trfiUKrIUY|0!jzZ-Hvi@B zfd|5V-D|%LU7(#RR6$7#0n>(|pgKx0MFkzGK3qvbUr!OHr=_E#tgEjM{AU+tl)u<) zim1O-|NoG^o3ry?e;c*?cw3KV4_jN3W)HQC+Ru`!d@D0?AW-yPFLzt9W=}_#2it4~ y;)XrjfVgH)>*7E!by2Q^g{6A=HZ+&fss5s@_#P9D1PuTHOc`lORRBN&0{}b%3akKdH{$Sz{ew4_ zdoKw*hQ;^z~kx`IB-hoHLMtiLh-6IMB26Hl!V(On3 z4%^#nwgl%YTt~G);_#NZeb=%Ge8IGH&oe}QPz*8yGJ=AB4!@WSdWP>NU1ya8XlQoA z)#bXqBdGg$Y>522{A+IbOWXjPM!#E&VIAuhuVt-2;7J|3d+qYYn7iHa7(g6_dTag~ z*$s|52xou@b`giZgk9)C|GA)SN8pH~2CEZvD)pa>G8lIEKkNQ?1OL?h z?*{(XZHBw5HKcp0wKZSdW(Ap(Evk8^s(js(`+htuMy ze4~t~lay0gu2k4RMQYGdsQuKyXh3nyt37%^KK~mdzgj;qCgV9@wO?_GL@%kzN&-?{ zHbBk!DgmMftvcqzC9R(QrcM`FMshhRsT!J7-6Dy}}1wz?f0Xn58TWS@Kl{ zAs+{rbvh6JRJdCY9{ii-+tWl+sRB&DwahpYW0=&DL_O|5f)vPkSf|A>30d(ZSA9+! z^B_pj2K7oDC$T~{W8Y~k4O%T5%HoU+ZG^FYmf7J3;mR4KN3RcUtibKovaNx*9|LG> zQMIE>=|e}D_ZJ&NUn*0cBNEhO7IzHB8If+bh!>!Hdcn`_(<~W?h;+z8c zPQPf29lud6yVlpgY;MQml2G^)GJf-Nt8zlnvW63O@qvP0XcJ7}sl z2o3PG4M=?MW##i+yl?6}Z9k^VGGp3qvO4wG*8ZF4I#=bO@7>bvoTWtNux0e(WeOIH ziup2iH)fJ7sI{e+H*QG-)AyB=S~*;-Ttex3i2TUt1Yp^c>G3230XHfjan~-lAbgpwAhQ_xl;3wP02^-&*%$6sF z9i!NBZwOa~cC$=s;XQ}T1xqN0Mab3q$@-H?kjNvNy*zi!xuhWCT+U%#5V@Jltki^Z z5Ls+8#T}O;crg1uyM%)l+F)e+TtE8TfhSpNS44|-NBJ980NLWWoOQOw9Y?x30?YHs zNAKoO(kLE;cA6E_Wo%Lq8YLtmY*gyiP+c;fzlM?mT?zX4Q}9SYDc>$|pQI%Ck|2uh z?+v-ue~P}IxYFpbJz3;Fm?Q-Po=mY)56ieG>XW6Vz@R3!x=J#f!W8f05zCoFelk4& zAN$;gEIKM7Zr3?8!zW5lJ&Jsyi4~hb?Ahuup^&Cw=MbPA{)74fU+ZLOYt-0k3!w)P z1@msa_pA2x{i1wa{GMtn~&M76eO>Dci#)afKP*1Bin_;8H-vpXm@ZTW%o!1FYAiUKH2 z5@99o%Hd9gNz4H5xTOHOOql=pBRar2F3W3Si&?BRz-+Ch70FW{x|k@q6ezg0V035S zaQ&X^5tS8n*`@lAkOe3$l7IG0ur(bp1d*%=BNGP-#wWzGb=ZcDXj*I=5;gpB_Byo?0>>etHCNTaW`oV8KpT$aeCR)EHTKxKM zYOM4`W@kznOrhUVH=}d6Oh$Y2X81tDIfs=TUI&45wVMQ(6DL=NOXBHrb@p0U_qDSv zt)J1kMYBS^hTk(lkkRe}OJKNB;0z}5rzbtl3fEbYt(4fsV_y?`@guZf?O;fpUB0**+465mi_m`j3+?Yml-t3Ko< zyrRw#!_4M|g!+X7;}a#~+epy9Q#17V`MK%KMvJ>_e=n~&tXfMx{cVN{yd0naEKOF==QN+o3$Eqw8)^C zBaTv}$-CwICX{^;!}yZKjiJc$I=|HKkY5%Dpz7jinU*RFG#Qeh(1fjrM=IJCOzNP7 z&o-nJ4~JuiF+;iHD4v4x&bcf#2Lg;a(U*`4()WfQq`$rNcvvmoIxmNjd*(xs^m!-- zU0)%@kCZWHHQRCLW%Al5`bYZRI?Q6~fJQ@>KUtdD7`3F(FQSPvOpU0&P=3+(Y^J4A zu=Rne{$xz`17tmUrsW+UG%8q`{eIx`wV2l@hbdrEXklxbK}Xl z2wF{U0S+D29b5&GC9ws7VMH+f8}Kem&Dq!1RzGW*<%$sR)Du@F^O0UeNpXrVrTV(# z1j;*S8b4fie6pe|9+@{IW@Zz#LGR&QF{rt}dU^NzpW{bGa0)}VsDsq_J)J<+;E9=x zyVpb%A%%Q2Cub_IN{Y2XyOKF%-72|OOkQCPYTgtX$&DX;`QNHuI_6%|8j?E8G-Agv zHrUwsm^tq(J7fw)f*Qf`gGyhnv_UCc8M+|UiVW;Ju%(Yr^@Z!(iY=En)H)z)gsD$E zBPR*p*1J;)aHkR$B{J~6^dy=D@3eWU{nfkA8mki4?|Nn}%BdYr{_HVdxf#zaq~eQi zxBjZFd(3f0!g=tt`@+x`PeMPqZN%GG@{S2#m6VzGC-zRJ_jEI=teHZK@`^FkxQ!f? z1AvI^CX1WMN+zFySsN}4Itk?}ypwYH_HUwoJ=k5*-NrQ@RSYdv^eEVcKeD}gRq>|- z{xLnPw(Ajk6v{q%Yes{ZoW)ZowhXl^=eL_1e=DD)1~#B{_Vw2~`2-))-@pW#;_=cg zswf7e3sMAid->+~=<6c7Imw4->-U(kJJeUUPm#luX*-rH;O;V%2H*ZTkhRG(0y;PE zYCi|WJ6|wTKAdB$4mZ#Ww5g>WEOgZn6lwK0%V+OL*dcVM8n$@@SC&6m(5rG1(`4<@)&HD{>{J@3`Q5Yq z8GYbuHA?e?R<4v7_*Qks6L$9XJLFoId%tKTbTnzxy9V11Csyu=quMWorty!A@3-CA zB>G=8y?VCZAS(yR7TCMK(e}yyoo|yB^Zh})3!kM8GS$nR+^7mZMsijW%{LJkiN|7ck)FC zs1p_wPBE~wIGkUUZiZa@*L!wPzSw>Y4}-ra61vEm2(+U=+Vx`)iC)ljWNPIPZ-LBzBc6^|}Wy{df@>MDItzVqidR1MChg0SjH7Mu+~JY%c5a_-Cq+V+Tf9W=9_ znFv4T*=AB>y^%*c>sf42hHcH0dG)4Z9y8uqFQsxY?FoR!uz}2m|NjX|@=-$HF4_y#nAfeRfh)m*o#eTDi@u?i=MHg3+qJx}xMG zX>QFepiVK{GT*l2@E=OsK7l?D5qTFEiRP)`OeYo0iM^!2;{k8;Y<&hO(qmp8ufjlv z54hjFg|JuCd+EV6jZjp-d{?CgQ8~syf%JPv894I=HFk8y`!Q)|Ln6hCIGHG&=^ShC z@w3=ixh;}TH{AK*My8N6Jd3Cwf=e5=Y0ppB)KmLa(3p{h<^4-9%T|8C_A=E4abeG_%#sDkEfPz8(v9lS|`ZC7$Scru26&yJQujKctF zIbe}q418l2An4<6J~~(+bh>I`JN3Ao{Q$pdP2r;@SErjum)}_se2yJZK^mktUdcJn zkMYtx?7U4lHN5=2hRh2y>8(9V(@T!jmP^ilw+pczlL;3jLkN)XIs3v(maQ$RN=gKA z5ck2mS{CRez`{uj4~@(EHgkv;HYscN7Uw-2V7K!=Z5X#@Bi}}=cx3y%oEE^IbTY=j zoSq61%rlt<9!+M4U#bCm&m|-+lfHO3ZraD(vZT2l1Scn9Wma@)Qqt+13(@ zLwo@!3~qO+Dn0hBdtWg=!EBkzjHT0Yx;-p*rV-NX1NQl)&i7z@_JoX3KWE=ab?7zK z&g2>@mk?N>7JIQ_sWcBrZ;BwX+(>K0voP~%Ay!|i91d%?{xcSxE@R=|JI*-u~wy= z@}gb3U_ch{#1TO`Ga7Yjjh{8!%a~(T|`7g!g zl%hTgT%k}21vyv>TZ=Yh!Z7o9+tVi)$pgyyG|f zi;}6i{-|Fj@Ock{QIDJ}poHfoywD=r{6K*vZ4PFz@)b69;mA{W_9KLYL;p#@=^-p- zky&-w!vSvFbL)(7oJD@nq4Zq3rKrju`iy}y!sg4|z>W#_Q{-Nrq$ZLOEm)vYKVt27A^Mryi;DHO#`Wr61O2J5x@HRXg_Hlc1Jw)7FOGhf5*ZH4?e{?P`)RUG2xeW+rgWoxjRLe$y zbQ*a|O~A`4%0+WYr-M&##b3bMs-|46Y^$IJC2}Qb+Fqs6xHn8f!v;>Yk=(nvDAXkP z^8>Aj)c_GRdY~Zjr!|=RgP<~dNvPwAN$a$2(ZDAJVr!~}0tVZ!7X*}l+%?UKqS)wn zyB3@I)>81^s_=|J4DBpiz7&=+>miz+C`>UM8FOiB>F_YwwXd>LImJ9sT>A_OLw#V=r(Pw2r_O*gEp zQW+k8FKbalOXAq6m(b0{C$hy|CbemN4pN=okr0}E)}2dWwI5ZF6wLSib!45M0ctHb zlig6&Dxm<(EWqN|zg?ei;#Hob{&8J@8wGF3V^DQS$b3dl(3$BK<;KLBWVAT12Y0ROL-iGvb&H zybf+0sQYHS*z9zDwpdlI@D&&mdVe&aCEXQ}sRSi$eD`_U*ZzJDu&< z+VSQHNk=2#E#oJcx%}agyx}Bw`B-E0S|BSa)V|u5FEETplSwqm@zxfFT7qx6f$TDZ z!#}x^1skXDb96>3-H+J#;4wK_YZfZH=g|wxqmw~)l#a9E+R)HVkBmwk`45)T&L~Dp zr?Fze1sgq!$)WcRV^2yv9;Cc4finMT(aE{VQ_e&yv6BJgXi`>*CxO}<2j`abm1uis zgP9JfviY+*e&Yv&#gmqBzo=S)$okcx-y}oU!IgM`)yKEQP4RS6LbVFGo$V&%8nX?4 z7fIK?e4=b^QsE3L^oCivv zJI2)^*}?I}`N7er+DXM#duwv9f`VBJE`cwM;li1rzw)CmL$af7m|hoLzw6FAy=dc_ z-e3pcAFOUdMoEe0(SgLT)s6LF#D){GO5Z%qZLp}m##dUfOZ1$3-W1?m)*wE)!2}=U< z|17t*r8)!>+A*JE0BC96G2tyszK0e}@_xE37pL`Kj)@+&rJ)bF_(X1=ngO{S-OuaaC|h;glH!r`kAAq3 z!#_!^(4WrQ6i}Qy-#$(bY7B3-6Pi+Wyif5Py0x9D?v6;&^^EH3R`(%5LkH|kViA-X z+yq3Vn;r%q&7>0G{O8+!Qq~oD!Yo{uKU@y`Pgwc3`6hRYt$EjtopB~J)bHkwb5`P0bIEAfDET+upj9scU8`mx4@i$ng$-Y{ax#>l*`Nb*$mSCfl0w=mzRm&B+DY zK2;Ztn@IFzXU>`}#L)nG_55@NKwa{;5sGsc_ZuCr${zR2o2EhtlE=^9IMT{R4^i&Q zTAkuV_4Gzc90e6V_jvPrS85V0k26a6fRPSaUn=Vg5V z+cH+MYt|HRi@3rBS6)O>x}Tc;jpXQwEaX$2d0cb)=hYwN#~_=biIs$t)$Zwn?)%z` zSie{&+rL5h4`fKcvoZjC{oKpbT6bTs);aiZ&kT|5{41$Sd)$evw-Xv7-fV zkO?zAOjd}VZN+$zN>*Y?L=m9BM^xj)(6Oy(S0lSCASe@v8rFYlNsH>1Gl)YCVo?D&k!gR`%i?EfjLdG|)pgAWO+24vos zRh*e@!A!Da)qipWtqRqq7jVH-A{EgzvEmB2-I&j}NA=R`DOmr1mT^?^VJgdvlhRFKS zZ+}PZp?N+&<=TiYN3%!CYex+E?!&n)E8H6Xc(wz?hbN@c@D0d2c`xBic$0+sB zTxjL3;%Ru6>ZvmQsCfM}Ia=wUOIGMtCn3fahM7>Df!W6RO5GSy#~B?+i5{qP#O!M1 zz7>As{7pSqeLarORjZ{G+O z=%CE&+ET~3%MKt-_zYva>bfckE08_AXdovkRcE>=aXZ0vwOk~Vk;Qs-+I)x5x|UR4 z$pj{?Sh4ke@MMZa2GUH2_nJoE6lE!GJ&auFnu1)IWi_a+#CgrTdIAN9Gg6*57DlUg zPCgB~l>=(hMrFkYFm^&%YWSOIKVA2)Zmr*+kCALEIwVzF&J{@)O70@^I{_H6FiUF9 z?QhH7ohbXZqAz7%O+$K+eh}-A)Lr81kJb^9jTtbpcnw$5tVUHkp%x@=q=kZdvo;Xb za_=~8%Y%<7yaj>dG4Qymj9JS5rkk`ZApN~BL`lxd_TvB%OuerLXXo4l2QJ(lu>enj zSwQ@UF3OwhR#?pK-c>-)<-%b`T!z3op=MaJQjeXr;@2f*!Z#fK;Tfc!CgCI^l_B*& z%Rtv|InqH=Md1`tgH*DbW!u$uS~B#;R7z_1T$ORX@#rY#OpyLz`vAIpVo=FHGbhsKGzG`GBndVlyw^ zriOdJv)w3YhR8#tvm-}(K@Z#5$MsA1ZQvGh7lV}bFZu)BL1udq3!~|q0a)xFGmGGh^xyS`D%l@=*4myjRj=qx zsdS^^2;lui)7W%G=MYIWI9ckQCD|zb;ix;_n?;YEfi=is;#HQ_l~=#S?QGrs5({}W zBmn_un<2BNA9qV8OO6VbU$Ro9m+K@{+*tmKnp9Aw&C&v zSd(oxLidG8K@e{cPM4VIvB_s)mZ>7iBGu5idRJ9B1-dPX_0~!vle=v6$MMJL$I}k8 zN`$gaTVN>&q-+C0SBWqU$0UXfNj7hEtT>ya zLD8Euk>hO-rM5RW>?crTFFe6`at3%+=tNBgKKfvsEOg_g#)FaH8AzF#)9(@9?mesi zwt)5OH7?BFLI&Q1l{;!y^krET#BjCSYcptx05b->4@`-P0?Q(%{C?~E+@kYg%znvP;usNb|{ljoh1zWz&!xuTt#a!`lvA&z>HN36h^|HBux@4>GkMF#5IU+p1hE?^lNF@Q! zz{E5|W6Mro%Z;<{kI|q80R6`m{nO?#uWh~;gNY*p-KVaBO{81Tg|Mm9ankd?Ws8QG zo))Z3D!H3d2MOg~p3quQ2T{cZ;Y6b?gDdwC<$lrZhDsysN}y1axwf7$A=XT?8>!~t zaKmi4rKyXUVAwTZP)GuL-lkx4u^ywoT7@01%)A-)OOV8DgxxDbfB|s4G*P1NF*sZn zU!o3jcEKPQM=1zHa%1w55$OH3RS*_U2wH1mvSa&3y4A9Naupy@ZVi$+9aAc zw#~iG(L8tyKfXL_0VR}O)T2Al=$#F1>KjBI51mNChHa8mmhqjri0BEH2*x&cE4Y|NH`_YJ2kph4Q?GQk?i8Pfl4J<{1AbpsB!6 z56m|73?M#X8km<%U`SC9Z--^1C9*NX@+Dv;$VT6}{;i(mq%HSPn@ftr(CG!oC+p0A#Zi(t$p6oae|x0|J@rbR zSXu2P_D{PPYpGwHWC}i6qr&8HvD@bH^tLw}^yUPu{F+YbsaGA4Qm@Euyv9~tkr79M+MOnCjV?c{UAKJ=OK<8Cse)zG!AAA}%@Bdf?62VdrU1qj^(-EN zlC#86pt^UjTG!%&jd%RzLDJVBqWJUEIc$M*`IzFM8riJ znbTQ;wseQUT zx1j>Z_OK{N0%e^lS8=k1z7M7WEw_tsVXh;v#08RXkf+3jA;q0KQO;Z=Hk?zLwp{P5 zbXvspLaS&;-((mhteqFkvMl$P2V`AU>3}R6c*`z^IN&Q^KH}xNcL4kpLy+2#m{ocG zN+|oV(NE#@&&oRRz1pvwM9tF(v#_%9sW!CEjl0?|C%?wPk69H;-A3*0%%LJ|N43u< z)(o!aQw>U3a*kjAp?)x92GLcs2dUCOG{IoHGWaOIlGiqKXJ+Ixjx-?{?RBpMzU9WV zN-W0(!Xg|_Doi?2GI5FsUbJFZ9R7J0JohH*$N28=mD5IMEo|SyGbc0`E%!<;Yjc@%t* zPMBs~vKf--7CZr<_G%R9^SN5EPG>K>|89x=LW}s#)WlBH?~*u71GEOV4z~ucAHQ2u zyg9U4oA5NHi&kPEFh57blJb>BnLWDuYhPtm@+%3H7%oUjgawpIiKtnV zv!yC7){@a0*B)JD7dn_XGD&J3^4`1G=RAEW8^2g}Ifg&ymRydm7-l!-$_;*3Nur$z zT<1gSlyH#Rm%|=}hR!bYtpStY__ft@&WoJXTWTH`KA_73i6TOi^MY<`mB#eVujp9^ z;JI|s9X}=$Lb;6J4qV{kW%h5T>CQFuo6Y8R+y zxkqB(eza(RF9|MXQ7^e3eA-n%gylOG`uVuf%#;(ZxZ*Ww*SQAq$fV5kR)QX=?E8$S zuTO~)xzrn?Lt=QCP-mGs$|rFBRrhrDx6ekiKDy#+;N$GgMU&=4PIyJx$a8T+-|5ts zcRI8O#j>qU>+{4D`yu$3Q&liq6%_0EbuF8>I*BEQWLd9D)#KH>jt{h^cWZDBHB7rz zFG#IM%XD1~2&mgx(kqJehCjlXdfi0Cd?eg!mhrbkwIu2bf$iY99PWqs!N(evCWyaHseTU`;6q#i-@rnrP3SC1QIuW+$iWC)#OTQ*m-5})NhW+162bmJ@~!;C3={RA|g(8g0@D=dBYv5yr- zRT`#zHecCAQsWaXlq(SuA1?@ba`Tjw7N-eQsGZ9U?T7dl29|Zu4Z7u zrjZIiBA=tVZ?XIKTpXINQV6UxIf?c4sv=lneDl^w%YP>57YM7`=xfz!Ki?hX{dxqD zS-LPg)VPNiePTs#FVOwhPpPRXWv1;uC^~fS=h@Ok!ceAuA3gvg!#n+PS`grN@hAK& zR!>l&ClDzjhp>3|HzQdhs%f^pXV_3 z+In|7UZfweA)&NX3O6s8tM2x@joNEb{;baIN#z8QMu7c0H--7Mvyh>AIoQ~q6AvZJ zp->+ez$+_VdMae`pg`Yx|86k*#BPd7P1!TSx@{fDg96LxFo4L_#-KupFPudAyeV83 z2S-R@#&o630E@_EW~?YX_BN7@Z>Ds>AfipI4|19o5sK(v=j^8%7V!lSx>3d=bXQ8&!MR&m<&ep)Xfcl)SFCc_@^jYoVHO8l2T5s&?hCS5w}K=ZUb%>CH80v<4To z=fexG`aU0+eQ&%XNEOQAShLWL;cMyJJ4x1eWlrJX6+}QAJ%e9JP<4a&;As1bh z5QpCI?hgRU!($EcP{O&(h$~-{fXqrTO4^_frS>M7vB?id1Ym;Pkj(ErOmG$*&HA?j z)+v%THf@}##^^S-v~bs+I{}$WQ9(;RpEuvpfr^P4!(sjgg^Pb*+M*V7uw>3tJt^@d zPgS|S@2a5r%pO`J`Ncsk8urC@60%v$EEcalwY-{|lxT*VHpF1ZZH@G8@wOexJm0x=|471Xj+&PYm<&N&F;GzrSXl@ztsOoANs{?0oYA`0m;H!0xa7 z$1QcVDqq_yB_jKRih8S{ugiH-K7A}w*KN1(qYsIn65x;0iw|sZz~V(5iUp@QUC%`s z;T-#b{KFKilg|hE8w5fgUO%WppECf7X#mxL$1BA{GS(}7*+}hp#@$~*!3#sadM9chK>36{$0gv1F~wFNA}vuwGq`;|ERP z@{W+iA#e`X?b5C$z)RkR8_1{5`s$pk0Q|#smK_}TWCH7g%Oc8tE`C!wF0y=#R+{s{ zUoC{BVT<$hV(pZmC`xhAbDAIJH980}-h>y8+wrqjDDT-9HIx^zA$U|+)G<TzldBik}}-s;O+0&=M{zM3xKTlnHAbT42XFD>$I zBV4`@BS0*$h}FNnGWfV1Q|-kr;SlJzYbfmm+{O2x9~PvpAvn zB!j=8D~LIz5F&#$36A!gk~_1~c5}-3Wpl<#NuG>rUY|oy-fAbaDN>lzDm8dOeTxvA zh4E!l^{Iz&gZ-x-Du0%FQOv|Tg`rQ4I*(nn9 zygCmo6V}X;wEsZePxv}ox7uBGmuFw`@D~RFdI|739~jwM{2}&!JO?Yjd)Ar;ld;ud zGlf4AM|`xK?#Ea68ntqZ1ydw)gNT+a5^_QR%hr#(eKTg}L&jEYgOCk3rlx@?K1>Bm-ndR+eq~I;g`A=AA1e(?=S!v_BX4VO^NpF0|d% z*U!6cQYXSklJ9U1y0yc$I%jnaBm7#&1Ezatuda|oel^#}Ptm|86Baml0mH81t^9$% z_a}zvL)41Am9WYhW_ucYbAC;(PrF=d@W}U0evKLdKImpNe$%^-ysf<}eIVYTvS-|T zz{8+JUk619fh&iQ+xeNVf!=uPT%#iSH0$l`Mo%bFJgmZsnMw|gH}}T z@~KX*uSU#tsrZ5uK7SaXHtjYz+MnkO&#@OnfiFd+=5870L#R>XHQQYRTcFl1D1~E@ zaEz+8dKlh#wAezKZX`gvG3IlWaQ>F!mg?Up#Q#ySU3UHvye=P53@Zp3(CP7DMxd%s z?WN`Kh}d(-&WdkRMlPO|0dSB}spb(>?xIWYz4+(sF}@g(gz`mhL2~LLws~)SuuP!* zVMbM8<|De~EColD+8~cZI;lq0JW2Qdzp0|L7oo5m8!jmP7PLV@R;T!&ZdV&y6}Ew7 zFqm|eC?LR4-$0CobnznR&tGUg7=vctFsr9yt)hN;=%``%XpvXr3&%ttu#?-0;1kMc z*tEAxxx=L2?ao_T!f2o8(!xan$ZGH+XU$6KJ}|GVZg1im9euQi%gUF_`KIidPzai( zw3tr23QcXB98l&9%WtVfM(q2E3d%(pZ&77i z_Jr2|#;4G{+fC#eGP?t_Pwn>!Ol|kAUdir=BuDAAFjSS|9alt|%I2U3OKb6dDOeHz zpI8zU?IBKIlmZ)}GouwW`IlY0lWHLFFU6&ELwpMGWh0Y!2aa9;r7gMQ{#4K3rZ#aVU$`0gDVPU?VH5fM5gmV$cTaJ*qL&DU2g=>~@j9q0puZtW zUY+-t&PYI}#7PZ_IZ-A{aLcQ}0XR<8082tPbB#`C%0(Cbp3U0j25=S4WzphJ(Cc2iz0$|Vn*h| z5i(%7-je|nAxM=i&*-B^-f`o0>c1~$*3WnXE2*>NReb8O<96GSf>=%{1xaC0V*iYT zDIRQbPF2ypbdY|!TYcA^o1`8d81OgX`PFEIAIJ@P3dlm5xrcNmFkd}IO>3#^3jZk# z=9Xu75c(UdRPFwB4U2uijL*An+R#eD{E+$KKM4aKkiSIeGm_uUTxm~x4|hvq>>sdg z$iOVu$2k)+FsA`)em*X&Yg4K2B?)pW+Fg|=*~RH{GpB<_-BAjiCpDi`0g`bNiOi?f zosjZkBIPMs8iL6!FYLAE*?TJsd*6o%vzEf&ImYXHpvyCl(?4gB2*K=0SyrXHDeeId zp;@*s^UXsI3bapM^Q@y{|B!D0_Dxewf!A$muri`I^7I8;CKbJ3>flTBbY39jF9|&{ z-zwb{H&i>{FWEH~{4NDVP1^#W8gQRwznVi1>-R>t8Q)7;{Xn%-_9WT}lFJmVA4|X1 zGy!AD8}scJd|g9pF4WFh0qiXXd(tP3{Ns-0)ZjyP!}NSlt@@&vkzq6G^ZwHoJC2Fu z5+}40hQn@IzpAG@Zo^=d-kl+SUS*f==AlR9G=gPnWjU9!Dh+<65-cCuC{YFmlG=mF zFO@?L(O8L}WVrP~_WpjeceHW;yZSHlO!mqcwznd_cMJ7_ zGj@LW*C$@qpNje|Bu2a6=ZT170KyYkWtqP->O1n)(X&IR2~R&2<=ap#&)z9FX33Y( zMuR;UOXC@*WqnFDo2K$?onJFFM~lMZ|Jds3Jm_w7Ih3!o2}m!GHXR-_j~cH_Z9O!C z^HJARQAv@bJkDwagC%GWGC&ohZV(L*GzJ@jX{Nsi*7y$kEb#iijaz@nJaTCed604Q zH_mw==gdu`NuEDh(NiCTopV+2=Y*~K$r(NItc_g!K0=5Zyw&0ibCB9CLlABsAADbt z_-qt%UH3a=zQ;W{WJT8Wf@!5DH|nJ;YE$5 z-U>c@Q?4+~g#oJ$^)$u24U=GJQBv_GGmflyC@6#r`rL@~@sB$L9OM?_*2gO3$7}t3 zG>Pk^%mHicgLEpvEN0w;i4*?UviXJL7qdmDFT>M5E)TSXMfc zQ(0LI<2q_SeG0o$&bG%?hQ(?JVc=MwKRQnklp+>f)$*yyUg}+rv7Lgh^n#fPtZkwe zvmb5&e}-i0lcw%Bj2gf5mc$&);?tkswI5IID#7(9n=ODCDAI&^MNy>4`mNBxT`g?W#u)x{bQeVla~r zX8zSCJR_#n^QriC26GfYs}7zH$kZ|vGug#OlVZ2|#HE6C{?B`4D!vd0*>N2XwA&fZ z!NpPS5A|S9Dj_L}qR3{m^5Oi}gI?6y4V_oEhLMMxYYSJu)yW)NZ2R(shoQs!KZ68} zjZ>b(Ok(Szkvj#|yV0ok9VK4C*K^k-RN%`SRA5RPFtpfic?_(KO$K67;09bCDi>pyVStCmn7nwk1f zE?9Xxt^4`XCmWsR{&h&xwWb$Puf@qatv`3C3$>$WQ&`a^1IB~aopObP9T*E1e||9U zV64YpS2u1wLBDwJ&szxp(%vuvcGR2sa;ZUHm z*G|c-dGTJ8TA`sk^1@(3zYj|Te_)KV-7N4hTx6DE=(gP^jN5MEqcaUvZkGi+(Im1t zg@z1cf%0~GYL^rwg%dkh!$0Nwg-wrc8^EDX6taw>LY;=4@Ks#%cgs*jK%LMa1V=| z{TESEY~RLtESit|y<9LoN4`DV>A_}h(d?&s6WdAjEeM(mNyf7!fE@T~^s+=9g zj|O`o8^juPZ$u`V4u8oyExrZA_}_0xT4<)yw4nVDzMS!%o|L6^a?9aH%uVNVMVq;0)|e*%DNp#9YVxJe|3v@sXC&L zEG7>&;g*lcFaYH=A-Wl%G(idQ`N{sgk{27b959D~-Q02QUSfpJ0@9`h;;=S8yq!cL zJp7!UI;aIxEB^y{OmkfSzKP3yZCG>2%UQ6jd&>oyU-^~pkia-HLI|EQ3U=MPNYCjn z2R4{?TMe>B-A9=|dLZ*93^Hdm2>wQ{@&1jtdnn{ZPOCMV$s2+SV~u+w|4$+39o1B` zXz@@40Vyg{r77x1?>$uMQWfb%s`Su{grb542t`18Q+f-65~)GyU_b~E2u->WYA|4^ z@1Xa-_3m5it@jUML1yM;&YaB5p1pru=AP}`&rbMXG)wf(MdiJO3)w|ozs~J~oh*wo z!M9SrN=|fn!qsVGDcWc`lq*ofC}Y1;&JRzx>VEE|Np#tn2BT}0<;hdd*APn&s^P;O zzg}`Y)A_Ry2xaLdTGcF*b(YyjF7AV7wTW3>m+^hT2q=cZ=jvrcZ$6k4$V>WWYf*Y|4se12Y>tegZF*j{7asI3I9g8ps~3*!6)8kkjQ6PvsEs?fDzbYFP6 zRNu7t`2s7EUM>qenb_}PbbhyikjqZQWAN21AXuENF$4=VrTbF!BH64MpO6b^cZOb2 z8?>mL9C=C-eOR|Rs6cCR;31k3RQoBAF6yl=yzNWnb#AmT);n+12KH;#5?C$}*@#b8 z!wzjba$u&{na-QIxl{>?I+VSVKw6J!csPHk0azFZoww5p zY%H5*+p`<3zp@&zq*1!5FZ{>He<#s`YBZmC2yJ95&}6!?>;a!oDJ^lY(R^5xqUH{l zn&lT}X1y#2N|m>4prX+ojb-IWK4#jTuurw@^hK{~ zo5s$SWaU^ABeFQHrL`%_UNhKIx8(%vCH5ZZhQ2gYgKjA1R0&u_a0k1Yn{ui>aPi&! zKGt=fNyy#ZV8ur`QU7+>ga^^6!07$nqVm(-bM(BcV#rO<9Oz7vvJf}b4pU7p}mL>0PU8eEXSD$zT4!0lj|J~mWRb|chrjij6ORB>b_A|ZIzn~KSwX%e* z9yP@5E~YIRa@yY0{0xX*8Fhq2v4p~jXNP(2IBKS`iN?Z#C1I`i#cbHFm;HS<`V+0I%NJK`?Z%;Z3jum86qT7BA z4$pNTer8#zVa2@aP8MbSo|6n1ApEq8^8!onh|W=)zQc!ymk8_c39}|NAkkbd15Ls^ zAc#?D4_1kvjvlAj8hB@sk%lpo83h)TfK*A&`Ao-K1{o{z1|IrxcFMzel=ZGAaOx-( zZ*%ua$DL}X1i{OTQBDnKzqRrx2NKwEpFkiEP=RpO6!r}Q$fmV5zu4Wm<|hrUh`D($ z_4O9JP;&%a<#)=via@*LF3TL9HwdiRC_9qOR{r5>Ord1u#vBOF@BlUr)`C?KmQ%&1vs)%{aow()T&+R-t1f5WBp2WkC?x=%8(<^6FEup1vP2UlN*E#U?&Cs3k2%yMEe zK;7wX6Y%_O8-3a^-@0-F`72G5eksLrD#i&zq)H){5^Xx|k-gdZasqK{DcmNd-hS z_x2}O>9(4y)r?RZSrthr0T??=Otkm%cDNtoZTCD>^VDk)ba(UBJ_xJ+m5?{BDqcTn zJbc@c-Sf%lWwq)|SoAB!IVdo+8q-P@dNW(DR;Pax2(QwgnY?=+caJ*tr_E;btNUvK z1LiRGSsR*36Ah$8rpxs}`Y(y?O58&laQj`=-zSuEkbh%QhN}_hSd@3{hgf;CTVvK0 z5sl&awh<3pPO5w;$u9PqYaX}-)&2eO#*oKLIZDo@<7y+Bo@E<5s}{!U1KqXj_Vg5h zX9*Z_ATd7~sqk4qP&f_@xp=#g`42M%Kf!jJ^>dyet6ko#5|G*Cv_9XtWoeS*GI+f1%g1vig6UeTu za|h%3uV?Lf+)J;C0APz)!uEFL5YT1M$yrc7YcTJUd_%(^i5gXTGwf~}*%EKmEHD@O zFCX^PjsPO=S-yjG@6X$3yVmRn>6Cb4!Q{NgcJ;kK&=n2reFVR2SYxft#uU7E zxE5sjaMq}vrYk&*joVSADv1Pj6e-QzbF%!j<)eo+62W=iV(c;@OaYn~9c=etV?W(~ zHD&ee1Ujp-vcSWWj?rxZabK;D2Iipl)!Y}8LP4w$cX|Y0=x54MO=0?_HHZ1;NF$(k zYbt>APEZoSyc%0Yrj2Oh-+>yyIk8}g0Wu3Iu2e+;YtvX_yB>17`}+9zEwO<=r+BlZ z_)+MJN(uCGUBVwC$?O%ofC*?8N!t%aZo|Ph^dHmYHNF_J!2W{T%rknakks>dPkAKBPr+c4L22AM7k{zQumW4nS%%P{Ozfvf~mvI!M zU&tmVk(nJVGkW6Lw}2|)=9Wlb28{fM%OXysKYh=4CMKzL1%3YJk~L;f=N!sjoWoE8 z%`jI+Oy?q8uWG~EBa|f9k<4K}2f<#-YMfARbl~%(u~JQ_%PW|Ni4?c^>;?Q1(B~xt z!d5v-R*|k`qbg?IcH3O$w~wO4U(e4QF)>shXz>jt)$EOkouHkX9t(jqOA+sKXyum2 zSs9UQG=l8n1xSy*3x&o|7Vf&IWH!ZIX5a+JIL40Mu9*!HqIjB38yPo{6LmIR$NrWW zdAg#~@LsI)^oKd9+qO-yeV03ez{7txX#K(dCh&a%@Hqso`ysRLW;jzk4cJG78f1c^ zObEVNT2z3W=#~AAzegr3JoExx08E9!sFct@gjIGw^*h33A!kn+K@OiKErgjAy|#R1 z8}`!Zn&lS2K76jkJL)-x*>A^n@b2G3f8@(vOF1Hz4#8xRN=d6*4-QWV1q6cd24FRG zq(OVR&6qOi76z5{NA;`Rt{rb(#=u+EeKm?45eil+rmM|&UC zFh>lllsqKR;c)*HBmmfffJ8dhq&&5Yq0sODEWSVEyO3apN@UcLv{?H3yjQZ6H$tgm ztK9i50~<&?Vg8@*AJY`084RcwKAKEB5Z8W9Mw$~Xj_#oByw9=-fgesj@Xe<-1buJ8 z-9gb7vMQPn9<2u9?m<5hq%0rIhuVR+aZ*D^_fbh?)o^(6PTghDuL#Eq#;AYoZ$a9Q z1&qmYs2MXNN*uDJr*iL>Wp%z#3z(18wSlNJmQ~{EzSZrVk*CPwH199~~WW zMb~+k1D%=caOTwUn)Js;*!X6pebi=q{v|*RqwG95V>QTPr!QoD@#b zhV0ug$|^1YBfjk^6cfk<%bU>@Z0LV~rcrLTidS!#)BqvK4A4K2fUtgE{SZNv&b=Im zArjCt-<8vs?{U9qu}~X?H7iM$4^mD8n(fe6GMHmOrkS&UQPrq@u**!~b-T1deX}fc z@Plokp;I~_)o1Wg?v{_Q^yX~!w<~Y?r()HrrmBF^vpJN{9_ax*%x65Ca6!;-n_n(j zfhX^LFM`HHgYTVL9~YN^*3*O>br^?}IbPFznb~U1OVV0O?iLGHX>pvx=%_C7lQLR`=wF1n3~YzneD~p zyrA>dah3U^Mhmgz!!icO@}yJn9Mpx}N{4P*>%N{Fp3d$k}Hi+H%M*qaRatTn;6<4|-RP`usCy<%Lm$V%>J6=X<*5du;|4 zn+K}Bso@V$ysKiBBF8W6?`|DXu~ZTY13V-}60)^FGLE zQtYw>QTOgeivxMxbRG@P1MxLkUb>UHU*J*(T`CnWanJ%o302%4QHk1o(g4WMtx$cq z5>7#b%11;_AL~>KDo$aw)Yn_;8i87XHoz|1z;3T>e?!EQu zFenC}?F=Nl1>OIArFdt7FeGSKN|SumAQjG>0vA|3rCG*N9u&1OD=V0|=PVqfcE@l1 z*gOpNM(4QP8mbf@>R20Fs7Z@-Ji0^0g+Ar##s3TlF$?Q4tFlIlAw2u=&Fh|=-J87@ zt1x3PlA=uVAGln~n&^<0fRk=eQ=>n_srm2Nau1OdPctqj@J*A)pawdXis0PD`VpFN z2SA}J6`UVpt|SLp&x4Z7LnylCOn%oy$qJc$_vB6>1x*ho0#kHAm(C<7*hiQPoYUQ)j+eEiNYFeZ1+5yoF8>y2JpVXi_?}2tGF_ zJB^}|zCX20=ah17es@2v75LtQOp04P#j8YMMD0+ohj^BfiQn$HhA-|wy3yGzc@hkc zWC+{b1^0}vG?>U#LK=ZiP~Y|DjtoyHpT8vJ=L2)1@Pa(fen*F+sdQ`W!g}ah2?_6b zK7LALTJ<_bptUZ`sLK_7uQeW*?ZEq%Z zrxu?O-BM+t&ZBO7F%WPbAux2>AK>QoB*Pi9KIRiml(H-gR*~F_v$fwEo}5tjnbGmS ztTo$&M!I7CUFqaZ{4eR#E$O2-9;l!^Fp~Q3lNz3y(CdQ_Zx~|J+4ZT1bJQ;O0Kukf z9VzN1zV-=yO$OvyC4Ev38$T`7Qpoh{hnx_M(`j>(`T^hjqBa;3ie0T_3|Ryt&t%#o zrz0$Q;_jz&sQn_tiZIF??ibCKaD;i9WOE9HTAI8nqOzYxQ;NNT3wv(*p0LyN*`;?Y zF2^Nly@4})E$plFM2hO?_M4FL_a(}l?rgW5DiP&B`tO#@XcgQt;EBF%i&b!!_7GWr zp;TgXAXAMVY}sS)|I9hkyJxg2II(%o~GeQ6yBD2YmudWwvX#)r2qFFXNu~>?%aIO&@4*vN(bu zoHstc841n1^MnT)WGT&aeSZ?RO}E{8IvC96_0c4mPG)_S#>_oXOI;|pa33Uz^;&G0 zlk}#pdO0Sp@@(zJuH;Lx^eehjM|9-mf=RgtizIu|!!i*IbF5w$0&JWvig_R2_pOhx zTAzr^xu+eNJsk8PBoHIRnK8uCgHR>jNE`^NC#gU|_B*R((YYd`6B=0#zKfrtkcx|v zgEc3ekV9@&1*i3A&_3H;itVhWB26^86dXG*D(Jit=rc+CzX=qh1*%YqnG*M0X=b$# zBGBj7(PH#{i|?%FM1dAYPkvQgnImMIjQ8Ct4tt5qY@h(eZ@0D7^CKlQJ@{HOnNqjh zMTB`-;!oFMZ^RmIHw=%U<$m4&nkDKAT6xwh%o!+pj+?M?-R`d32l+j0Dra}kvaRSE zIY(N8`KZYFXFS4l6Kf{v4~GZoX#*ifh@N8gu?zl!Ef1sq&?H8N4JfWWi3m!d=niA; zrkc`GseSiKI7M85Ki&Ol1j;9?w&)*Zdd}fX{yLjDW$B-Y<%C>g;GhCWJAOLgv0pC6 zl$4u)EHuXrY)ouT+TL!w3NWLWa=8P7B+vR574Q`}S4wEoCKylMK&Quxxb`-76_((~ z?y!VdH3z-F1{w{jHnlR zVH^TTgghesBa*^Ed`$BlsY-eFZ6^rGkyag-UkK|Wj$sD-H{R`D7-RKBL({J=K=bl) zRW(;l}NT~5U z>Q1AxaPE{MX)o3&zGx*8bI9XLCvI5XW9c=d%ebdiG8mf;MQ$U~5?bed9UP5?;p7#<;@Xz<%eb#H zy)1yc0x#bi7EkO6xlvdG*j;$5+PAOW{M_(YTloQmU9FJrFesB&5=s2x&h#b>! z`FSUD>U<0)VQ#4AK+v}-@c~J;BGz#So)!SnJ?PwMDZsst_ z;1Cu)B`U+?KRo5IfZwK-Z}nfNC?ck%xX7vUgEYvwF|S3~&8tD_`5z)eb05?-jxDpD zd!nFaGbZjzgre=sN{Yrt@4f083hWy0EA05%W2)~Lj&%cKNz2J=E1MMC7Nggp&&FF~ zn`#y)=Bv4=g6*a)zThnYAd#ty29NmfTvpF%VXz*;Ytp-Aq6g*KT>N}FwJvQO6idqA zyv*)I?UEv^ZV@1sKm;@NsgHH+A;&cY}C?jMO!S Y#Dt}|FIdya0sjZ-Xc(y1s6Bf2U+!?HxBvhE literal 0 HcmV?d00001 diff --git a/doc/images/e1-archi.tex b/doc/images/e1-archi.tex new file mode 100644 index 0000000000..7a18d63c53 --- /dev/null +++ b/doc/images/e1-archi.tex @@ -0,0 +1,73 @@ +\documentclass{standalone} +%\usepackage[tt=false]{libertine} +\renewcommand{\familydefault}{\sfdefault} +% improve for some encodings +\usepackage{textcomp} +\usepackage{tikz} +\usetikzlibrary{positioning} +\usetikzlibrary{shapes.symbols} +\usetikzlibrary{decorations.markings} +\usetikzlibrary{fit} + +\tikzset{splitpos/.store in=\splitpos,splitpos=0.5} +\newcommand{\ip}[1]{\texttt{#1}} + +\begin{document} +\begin{tikzpicture} + [ + font=\scriptsize, + node distance=1.5cm and 3.2cm, + entity/.style={ + rectangle, + draw, + inner sep=0pt, + minimum height=6mm,minimum width=11mm, + align=center + }, + split/.style={postaction={% + decorate, + decoration={ + markings,mark={ + at position \splitpos + with { + \draw (0pt,2.5pt) -- (0pt,-2.5pt); + } + } + } + } + }, + ip/.style={font=\tiny,outer sep=1pt,inner sep=1pt}, + ] + \node[entity] (cu-cp) {CU-CP}; + \node[entity,below=of cu-cp] (cu-up) {CU-UP}; + \node[entity,below left=0.45cm and 3.2cm of cu-cp] (du) {DU}; + \node[entity,right=of cu-cp] (amf) {AMF}; + \node[entity,right=of cu-up] (upf) {UPF}; + + \draw[split,splitpos=0.5] (cu-cp) -- + node[ip,at start, below right, align=left] {\ip{GNB\_IPV4\_ADDRESS\_FOR\_NG\_AMF:38412}} + node[above] {NG-C/N2} + %node[ip,at end, below left] {127.0.1.10/24} + (amf); + \draw[split,splitpos=0.5] (cu-up) -- + node[ip, at start, below right,align=left] {\ip{GNB\_IPV4\_ADDRESS\_FOR\_NGU:2152}} + node[above] {NG-U/N3} + %node[ip, at end, below left] {127.0.1.10/24} + (upf); + \draw[split,splitpos=0.5] (cu-cp) -- + node[ip, at start, below right] {\ip{ipv4\_cucp:38462}} + node[right] {E1AP} + node[ip, at end, above right] {\ip{ipv4\_cuup:38462}} + (cu-up); + \draw[split] (du.north east) -- + node[ip, at start, below right] {to \ip{remote\_n\_address:38472}} + node[above] {F1-C} + node[ip, at end, above left, align=right] {\ip{local\_s\_address:38472}} + (cu-cp); + \draw[split] (du.south east) -- + node[ip, at start, above right] {to \ip{remote\_n\_address:remote\_n\_portd}} + node[above] {F1-U} + node[ip, at end, below left, align=right] {\ip{local\_s\_address:local\_s\_portd}} + (cu-up); +\end{tikzpicture} +\end{document} diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c new file mode 100644 index 0000000000..e2b2bdb50b --- /dev/null +++ b/executables/nr-cuup.c @@ -0,0 +1,160 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "common/utils/simple_executable.h" +#include "executables/softmodem-common.h" +#include "common/utils/ocp_itti/intertask_interface.h" +#include "openair3/ocp-gtpu/gtp_itf.h" +#include "openair2/E1AP/e1ap.h" +#include "common/ran_context.h" +#include "nfapi/oai_integration/vendor_ext.h" +#include "openair2/F1AP/f1ap_common.h" +#include "openair2/GNB_APP/gnb_config.h" + +RAN_CONTEXT_t RC; +THREAD_STRUCT thread_struct; +uint64_t downlink_frequency[MAX_NUM_CCs][4]; +int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; +int asn1_xer_print; +int oai_exit = 0; +instance_t CUuniqInstance = 0; +RRC_release_list_t rrc_release_info; + +void exit_function(const char *file, const char *function, const int line, const char *s) +{ +} + +nfapi_mode_t nfapi_mod = -1; +void nfapi_setmode(nfapi_mode_t nfapi_mode) +{ + nfapi_mod = nfapi_mode; +} +nfapi_mode_t nfapi_getmode(void) +{ + return nfapi_mod; +} + +ngran_node_t get_node_type() +{ + return ngran_gNB_CUUP; +} + +rlc_op_status_t rlc_data_req(const protocol_ctxt_t *const pc, + const srb_flag_t sf, + const MBMS_flag_t mf, + const rb_id_t rb_id, + const mui_t mui, + const confirm_t c, + const sdu_size_t size, + mem_block_t *const buf, + const uint32_t *const a, + const uint32_t *const b) +{ + abort(); + return 0; +} + +int nr_rlc_get_available_tx_space(const rnti_t rntiP, const logical_chan_id_t channel_idP) +{ + abort(); + return 0; +} + +void nr_rlc_bearer_init(NR_RLC_BearerConfig_t *RLC_BearerConfig, NR_RLC_BearerConfig__servedRadioBearer_PR rb_type) +{ + abort(); +} + +void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP, NR_CellGroupConfig_t *cell_groupConfig_from_DU) +{ + abort(); +} + +void nr_rlc_bearer_init_ul_spec(struct NR_LogicalChannelConfig *mac_LogicalChannelConfig) +{ + abort(); +} + +rlc_op_status_t nr_rrc_rlc_config_asn1_req(const protocol_ctxt_t *const ctxt_pP, + const NR_SRB_ToAddModList_t *const srb2add_listP, + const NR_DRB_ToAddModList_t *const drb2add_listP, + const NR_DRB_ToReleaseList_t *const drb2release_listP, + struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list) +{ + abort(); + return 0; +} + +int nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP(const protocol_ctxt_t *const ctxt_pP, const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP) +{ + abort(); + return 0; +} + +void nr_drb_config(struct NR_RLC_Config *rlc_Config, NR_RLC_Config_PR rlc_config_pr) +{ + abort(); +} + +void prepare_and_send_ue_context_modification_f1(rrc_gNB_ue_context_t *ue_context_p, e1ap_bearer_setup_resp_t *e1ap_resp) +{ + abort(); +} + +f1ap_cudu_inst_t *getCxt(F1_t isCU, instance_t instanceP) +{ + abort(); + return NULL; +} + +void fill_DRB_configList(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP) +{ + abort(); +} + +int main(int argc, char **argv) +{ + /// static configuration for NR at the moment + if (load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY) == NULL) { + exit_fun("[SOFTMODEM] Error, configuration module init failed\n"); + } + logInit(); + set_softmodem_sighandler(); + itti_init(TASK_MAX, tasks_info); + int rc; + rc = itti_create_task(TASK_SCTP, sctp_eNB_task, NULL); + AssertFatal(rc >= 0, "Create task for SCTP failed\n"); + rc = itti_create_task(TASK_GTPV1_U, gtpv1uTask, NULL); + AssertFatal(rc >= 0, "Create task for GTPV1U failed\n"); + rc = itti_create_task(TASK_CUUP_E1, E1AP_CUUP_task, NULL); + AssertFatal(rc >= 0, "Create task for CUUP E1 failed\n"); + pdcp_layer_init(); + MessageDef *msg = RCconfig_NR_CU_E1(true); + AssertFatal(msg != NULL, "Send init to task for E1AP UP failed\n"); + itti_send_msg_to_task(TASK_CUUP_E1, 0, msg); + + printf("TYPE TO TERMINATE\n"); + itti_wait_tasks_end(); + + logClean(); + printf("Bye.\n"); + return 0; +} diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 2cbea144cf..68eb294a34 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -78,6 +78,9 @@ unsigned short config_frames[4] = {2,9,11,13}; #include "gnb_paramdef.h" #include #include "nfapi/oai_integration/vendor_ext.h" +#include "gnb_config.h" +#include "openair2/E1AP/e1ap_common.h" +#include "openair2/E1AP/e1ap_api.h" pthread_cond_t nfapi_sync_cond; pthread_mutex_t nfapi_sync_mutex; @@ -289,16 +292,60 @@ void exit_function(const char *file, const char *function, const int line, const -int create_gNB_tasks(uint32_t gnb_nb) { +static int create_gNB_tasks(void) { + uint32_t gnb_nb = RC.nb_nr_inst; + uint32_t gnb_id_start = 0; + uint32_t gnb_id_end = gnb_id_start + gnb_nb; LOG_D(GNB_APP, "%s(gnb_nb:%d)\n", __FUNCTION__, gnb_nb); itti_wait_ready(1); + LOG_I(PHY, "%s() Task ready initialize structures\n", __FUNCTION__); + + RCconfig_NR_L1(); + RCconfig_nr_prs(); + + if (RC.nb_nr_macrlc_inst>0) RCconfig_nr_macrlc(); + + LOG_I(PHY, "%s() RC.nb_nr_L1_inst:%d\n", __FUNCTION__, RC.nb_nr_L1_inst); + + if (RC.nb_nr_L1_inst>0) AssertFatal(l1_north_init_gNB()==0,"could not initialize L1 north interface\n"); + + AssertFatal (gnb_nb <= RC.nb_nr_inst, + "Number of gNB is greater than gNB defined in configuration file (%d/%d)!", + gnb_nb, RC.nb_nr_inst); + + LOG_I(GNB_APP,"Allocating gNB_RRC_INST for %d instances\n",RC.nb_nr_inst); + + RC.nrrrc = (gNB_RRC_INST **)malloc(RC.nb_nr_inst*sizeof(gNB_RRC_INST *)); + LOG_I(PHY, "%s() RC.nb_nr_inst:%d RC.nrrrc:%p\n", __FUNCTION__, RC.nb_nr_inst, RC.nrrrc); + ngran_node_t node_type = get_node_type(); + for (int gnb_id = gnb_id_start; (gnb_id < gnb_id_end) ; gnb_id++) { + RC.nrrrc[gnb_id] = (gNB_RRC_INST*)calloc(1,sizeof(gNB_RRC_INST)); + LOG_I(PHY, "%s() Creating RRC instance RC.nrrrc[%d]:%p (%d of %d)\n", __FUNCTION__, gnb_id, RC.nrrrc[gnb_id], gnb_id+1, gnb_id_end); + configure_nr_rrc(gnb_id); + } + + if (RC.nb_nr_inst > 0 && + !get_softmodem_params()->nsa && + !(node_type == ngran_gNB_DU)) { + // we start pdcp in both cuup (for drb) and cucp (for srb) + init_pdcp(); + } + + if (is_x2ap_enabled() ) { //&& !NODE_IS_DU(node_type) + LOG_I(X2AP, "X2AP enabled \n"); + __attribute__((unused)) uint32_t x2_register_gnb_pending = gNB_app_register_x2 (gnb_id_start, gnb_id_end); + } + + /* For the CU case the gNB registration with the AMF might have to take place after the F1 setup, as the PLMN info + * can originate from the DU. Add check on whether x2ap is enabled to account for ENDC NSA scenario.*/ + if ((get_softmodem_params()->sa || is_x2ap_enabled()) && + !NODE_IS_DU(node_type)) { + /* Try to register each gNB */ + //registered_gnb = 0; + __attribute__((unused)) uint32_t register_gnb_pending = gNB_app_register (gnb_id_start, gnb_id_end); + } if (gnb_nb > 0) { - /* Last task to create, others task must be ready before its start */ - /*if (itti_create_task (TASK_GNB_APP, gNB_app_task, NULL) < 0) { - LOG_E(GNB_APP, "Create task for gNB APP failed\n"); - return -1; - }*/ if(itti_create_task(TASK_SCTP, sctp_eNB_task, NULL) < 0) { LOG_E(SCTP, "Create task for SCTP failed\n"); return -1; @@ -313,7 +360,8 @@ int create_gNB_tasks(uint32_t gnb_nb) { } } - if (get_softmodem_params()->sa) { + if (get_softmodem_params()->sa && + !NODE_IS_DU(node_type)) { char* gnb_ipv4_address_for_NGU = NULL; uint32_t gnb_port_for_NGU = 0; @@ -326,9 +374,10 @@ int create_gNB_tasks(uint32_t gnb_nb) { for(int i = GNB_INTERFACE_NAME_FOR_NG_AMF_IDX; i <= GNB_IPV4_ADDRESS_FOR_NG_AMF_IDX; i++) { if( NETParams[i].strptr == NULL) { - LOG_E(NGAP, "No AMF configuration in the file.\n"); + LOG_E(NGAP, "No AMF configuration in the file.\n"); + exit(1); } else { - LOG_D(NGAP, "Configuration in the file: %s.\n",*NETParams[i].strptr); + LOG_D(NGAP, "Configuration in the file: %s.\n",*NETParams[i].strptr); } } @@ -346,15 +395,26 @@ int create_gNB_tasks(uint32_t gnb_nb) { return -1; } - LOG_I(NR_RRC,"Creating NR RRC gNB Task\n"); - + LOG_I(NR_RRC, "Creating NR RRC gNB Task, that will also create TASKS\n"); if (itti_create_task (TASK_RRC_GNB, rrc_gnb_task, NULL) < 0) { LOG_E(NR_RRC, "Create task for NR RRC gNB failed\n"); return -1; } - //Use check on x2ap to consider the NSA scenario and check for SA scenario - if(is_x2ap_enabled() || get_softmodem_params()->sa) { + // If CU + if (node_type == ngran_gNB_CU || node_type == ngran_gNB) { + MessageDef *msg = RCconfig_NR_CU_E1(false); + instance_t inst = 0; + createE1inst(UPtype, inst, &E1AP_SETUP_REQ(msg)); + cuup_init_n3(inst); + itti_free(TASK_UNKNOWN, msg); + getCxtE1(inst)->same_process = true; + ; + RC.nrrrc[gnb_id_start]->e1_inst = inst; // stupid instance !!!*/ + } + + //Use check on x2ap to consider the NSA scenario + if((is_x2ap_enabled() || get_softmodem_params()->sa) && (node_type != ngran_gNB_CUCP)) { if (itti_create_task (TASK_GTPV1_U, >pv1uTask, NULL) < 0) { LOG_E(GTPU, "Create task for GTPV1U failed\n"); return -1; @@ -504,7 +564,7 @@ void init_pdcp(void) { LINK_ENB_PDCP_TO_GTPV1U_BIT; if (!get_softmodem_params()->nsa) { - if (!NODE_IS_DU(RC.nrrrc[0]->node_type)) { + if (!NODE_IS_DU(get_node_type())) { pdcp_layer_init(); nr_pdcp_module_init(pdcp_initmask, 0); } @@ -582,7 +642,7 @@ int main( int argc, char **argv ) { // don't create if node doesn't connect to RRC/S1/GTP if (NFAPI_MODE != NFAPI_MODE_PNF) { - int ret = create_gNB_tasks(1); + int ret = create_gNB_tasks(); AssertFatal(ret == 0, "cannot create ITTI tasks\n"); } diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 4aef4e56a3..1630ef8ef0 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -102,6 +102,8 @@ int config_sync_var=-1; instance_t CUuniqInstance=0; instance_t DUuniqInstance=0; +int get_node_type() {return -1;} + RAN_CONTEXT_t RC; int oai_exit = 0; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index da5bbbffdf..9b6db46aa8 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -81,8 +81,6 @@ const char *__asan_default_options() return "detect_leaks=0"; } -LCHAN_DESC DCCH_LCHAN_DESC,DTCH_DL_LCHAN_DESC,DTCH_UL_LCHAN_DESC; - PHY_VARS_gNB *gNB; PHY_VARS_NR_UE *UE; RAN_CONTEXT_t RC; @@ -100,22 +98,6 @@ nfapi_ue_release_request_body_t release_rntis; //Fixme: Uniq dirty DU instance, by global var, datamodel need better management instance_t DUuniqInstance=0; instance_t CUuniqInstance=0; -teid_t newGtpuCreateTunnel(instance_t instance, - ue_id_t ue_id, - int incoming_bearer_id, - int outgoing_bearer_id, - teid_t outgoing_teid, - int qfi, - transport_layer_addr_t remoteAddr, - int port, - gtpCallback callBack, - gtpCallbackSDAP callBackSDAP) { - return 0; -} - -int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { - return 0; -} // dummy functions int dummy_nr_ue_ul_indication(nr_uplink_indication_t *ul_info) { return(0); } @@ -158,59 +140,6 @@ rrc_data_ind( { } -int -gtpv1u_create_s1u_tunnel( - const instance_t instanceP, - const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req_pP, - gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP -) { - return 0; -} - -int -rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -) { - return 0; -} - -int -gtpv1u_create_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t * const create_tunnel_req_pP, - gtpv1u_gnb_create_tunnel_resp_t * const create_tunnel_resp_pP){ - return 0; -} - -int -gtpv1u_update_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req_pP, - const ue_id_t prior_ue_id -){ - return 0; -} - -int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { - return 0; -} - -int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req) { - return 0; -} - -int -nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -){ - return 0; -} - int nr_derive_key(int alg_type, uint8_t alg_id, const uint8_t key[32], uint8_t **out) { diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index f0ac036926..9489b1aeb3 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -58,7 +58,9 @@ #define NR_PRACH_DEBUG 1 #define PRACH_WRITE_OUTPUT_DEBUG 1 -LCHAN_DESC DCCH_LCHAN_DESC,DTCH_DL_LCHAN_DESC,DTCH_UL_LCHAN_DESC; +THREAD_STRUCT thread_struct; +char *parallel_config = NULL; +char *worker_config = NULL; char *uecap_file; PHY_VARS_gNB *gNB; @@ -91,22 +93,6 @@ int oai_nfapi_nr_rach_indication(nfapi_nr_rach_indication_t *ind) { return(0); //Fixme: Uniq dirty DU instance, by global var, datamodel need better management instance_t DUuniqInstance=0; instance_t CUuniqInstance=0; -teid_t newGtpuCreateTunnel(instance_t instance, - ue_id_t ue_id, - int incoming_bearer_id, - int outgoing_bearer_id, - teid_t outgoing_teid, - int qfi, - transport_layer_addr_t remoteAddr, - int port, - gtpCallback callBack, - gtpCallbackSDAP callBackSDAP) { - return 0; -} - -int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { - return 0; -} void rrc_data_ind( @@ -118,59 +104,6 @@ rrc_data_ind( { } -int -gtpv1u_create_s1u_tunnel( - const instance_t instanceP, - const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req_pP, - gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP -) { - return 0; -} - -int -rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -) { - return 0; -} - -int -gtpv1u_create_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t * const create_tunnel_req_pP, - gtpv1u_gnb_create_tunnel_resp_t * const create_tunnel_resp_pP){ - return 0; -} - -int -gtpv1u_update_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req_pP, - const ue_id_t prior_ue_id -){ - return 0; -} - -int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { - return 0; -} - -int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req) { - return 0; -} - -int -nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -){ - return 0; -} - int8_t nr_mac_rrc_data_ind_ue(const module_id_t module_id, const int CC_id, const uint8_t gNB_index, diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 11c131a5c2..b407b90113 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -76,8 +76,6 @@ const char *__asan_default_options() return "detect_leaks=0"; } -LCHAN_DESC DCCH_LCHAN_DESC,DTCH_DL_LCHAN_DESC,DTCH_UL_LCHAN_DESC; - PHY_VARS_gNB *gNB; PHY_VARS_NR_UE *UE; RAN_CONTEXT_t RC; @@ -96,22 +94,6 @@ nfapi_ue_release_request_body_t release_rntis; //Fixme: Uniq dirty DU instance, by global var, datamodel need better management instance_t DUuniqInstance=0; instance_t CUuniqInstance=0; -teid_t newGtpuCreateTunnel(instance_t instance, - ue_id_t ue_id, - int incoming_bearer_id, - int outgoing_bearer_id, - teid_t outgoing_teid, - int qfi, - transport_layer_addr_t remoteAddr, - int port, - gtpCallback callBack, - gtpCallbackSDAP callBackSDAP) { - return 0; -} - -int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { - return 0; -} extern void fix_scd(NR_ServingCellConfig_t *scd);// forward declaration @@ -149,59 +131,6 @@ rrc_data_ind( } -int -gtpv1u_create_s1u_tunnel( - const instance_t instanceP, - const gtpv1u_enb_create_tunnel_req_t *const create_tunnel_req_pP, - gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP -) { - return 0; -} - -int gtpv1u_delete_s1u_tunnel(const instance_t instance, const gtpv1u_enb_delete_tunnel_req_t *const req_pP) { - return 0; -} - -int gtpv1u_delete_ngu_tunnel( const instance_t instance, - gtpv1u_gnb_delete_tunnel_req_t *req) { - return 0; -} - -int -rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -) { - return 0; -} - -int -gtpv1u_create_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t * const create_tunnel_req_pP, - gtpv1u_gnb_create_tunnel_resp_t * const create_tunnel_resp_pP){ - return 0; -} - -int -gtpv1u_update_ngu_tunnel( - const instance_t instanceP, - const gtpv1u_gnb_create_tunnel_req_t *const create_tunnel_req_pP, - const ue_id_t prior_ue_id -){ - return 0; -} - -int -nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list -){ - return 0; -} - // Dummy function to avoid linking error at compilation of nr-ulsim int is_x2ap_enabled(void) { diff --git a/openair2/COMMON/e1ap_messages_def.h b/openair2/COMMON/e1ap_messages_def.h new file mode 100644 index 0000000000..70533f8add --- /dev/null +++ b/openair2/COMMON/e1ap_messages_def.h @@ -0,0 +1,33 @@ +/* + * 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 + */ + +/* gNB_CUUP application layer -> E1AP messages */ +MESSAGE_DEF(E1AP_SETUP_REQ , MESSAGE_PRIORITY_MED , e1ap_setup_req_t , e1ap_setup_req) + +/* E1AP -> eNB_DU or eNB_CU_RRC -> E1AP application layer messages */ +MESSAGE_DEF(E1AP_SETUP_RESP , MESSAGE_PRIORITY_MED, e1ap_setup_resp_t , e1ap_setup_resp) + +MESSAGE_DEF(E1AP_BEARER_CONTEXT_SETUP_REQ , MESSAGE_PRIORITY_MED , e1ap_bearer_setup_req_t , e1ap_bearer_setup_req) + +MESSAGE_DEF(E1AP_BEARER_CONTEXT_SETUP_RESP , MESSAGE_PRIORITY_MED , e1ap_bearer_setup_resp_t , e1ap_bearer_setup_resp) + +MESSAGE_DEF(E1AP_BEARER_CONTEXT_MODIFICATION_REQ , MESSAGE_PRIORITY_MED , e1ap_bearer_setup_req_t , e1ap_bearer_mod_req) + diff --git a/openair2/COMMON/e1ap_messages_types.h b/openair2/COMMON/e1ap_messages_types.h new file mode 100644 index 0000000000..941d95b044 --- /dev/null +++ b/openair2/COMMON/e1ap_messages_types.h @@ -0,0 +1,226 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * 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 + */ + +#ifndef E1AP_MESSAGES_TYPES_H +#define E1AP_MESSAGES_TYPES_H + +#include "f1ap_messages_types.h" +#include "ngap_messages_types.h" + +#define E1AP_MAX_NUM_TRANSAC_IDS 4 +#define E1AP_MAX_NUM_PLMNS 4 +#define E1AP_MAX_NUM_CELL_GROUPS 4 +#define E1AP_MAX_NUM_QOS_FLOWS 4 +#define E1AP_MAX_NUM_NGRAN_DRB 4 +#define E1AP_MAX_NUM_PDU_SESSIONS 4 +#define E1AP_MAX_NUM_DRBS 4 +#define E1AP_MAX_NUM_DRBS 4 +#define E1AP_MAX_NUM_UP_PARAM 4 + +#define E1AP_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.e1ap_setup_req +#define E1AP_SETUP_RESP(mSGpTR) (mSGpTR)->ittiMsg.e1ap_setup_resp +#define E1AP_BEARER_CONTEXT_SETUP_REQ(mSGpTR) (mSGpTR)->ittiMsg.e1ap_bearer_setup_req +#define E1AP_BEARER_CONTEXT_SETUP_RESP(mSGpTR) (mSGpTR)->ittiMsg.e1ap_bearer_setup_resp +#define E1AP_BEARER_CONTEXT_MODIFICATION_REQ(mSGpTR) (mSGpTR)->ittiMsg.e1ap_bearer_setup_req + +typedef f1ap_net_ip_address_t e1ap_net_ip_address_t; + +typedef struct PLMN_ID_s { + int mcc; + int mnc; + int mnc_digit_length; +} PLMN_ID_t; + +typedef struct e1ap_setup_req_s { + uint64_t gNB_cu_up_id; + char * gNB_cu_up_name; + int assoc_id; + uint64_t transac_id; + int supported_plmns; + PLMN_ID_t plmns[E1AP_MAX_NUM_PLMNS]; + uint16_t sctp_in_streams; + uint16_t sctp_out_streams; + uint16_t default_sctp_stream_id; + net_ip_address_t CUUP_e1_ip_address; + net_ip_address_t CUCP_e1_ip_address; + long cn_support; + uint16_t remotePortF1U; + char* localAddressF1U; + uint16_t localPortF1U; + char* localAddressN3; + uint16_t localPortN3; + uint16_t remotePortN3; +} e1ap_setup_req_t; + +typedef struct e1ap_setup_resp_s { + long transac_id; +} e1ap_setup_resp_t; + +typedef struct cell_group_s { + long id; +} cell_group_t; + +typedef struct up_params_s { + in_addr_t tlAddress; + long teId; + int cell_group_id; +} up_params_t; + +typedef struct drb_to_setup_s { + long drbId; + long pDCP_SN_Size_UL; + long pDCP_SN_Size_DL; + long rLC_Mode; + long qci; + long qosPriorityLevel; + long pre_emptionCapability; + long pre_emptionVulnerability; + in_addr_t tlAddress; + long teId; + int numCellGroups; + cell_group_t cellGroupList[E1AP_MAX_NUM_CELL_GROUPS]; +} drb_to_setup_t; + +typedef struct qos_flow_to_setup_s { + long id; + fiveQI_type_t fiveQI_type; + long fiveQI; + long qoSPriorityLevel; + long packetDelayBudget; + long packetError_scalar; + long packetError_exponent; + long priorityLevel; + long pre_emptionCapability; + long pre_emptionVulnerability; +} qos_flow_to_setup_t; + +typedef struct DRB_nGRAN_to_setup_s { + long id; + long defaultDRB; + long sDAP_Header_UL; + long sDAP_Header_DL; + long pDCP_SN_Size_UL; + long pDCP_SN_Size_DL; + long discardTimer; + long reorderingTimer; + long rLC_Mode; + in_addr_t tlAddress; + int teId; + int numDlUpParam; + up_params_t DlUpParamList[E1AP_MAX_NUM_UP_PARAM]; + int numCellGroups; + cell_group_t cellGroupList[E1AP_MAX_NUM_CELL_GROUPS]; + int numQosFlow2Setup; + qos_flow_to_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; +} DRB_nGRAN_to_setup_t; + +typedef struct pdu_session_to_setup_s { + long sessionId; + long sessionType; + int8_t sst; + long integrityProtectionIndication; + long confidentialityProtectionIndication; + in_addr_t tlAddress; + in_addr_t tlAddress_dl; + int32_t teId; + int32_t teId_dl; + int tl_port; + int tl_port_dl; + long numDRB2Setup; + DRB_nGRAN_to_setup_t DRBnGRanList[E1AP_MAX_NUM_NGRAN_DRB]; + long numDRB2Modify; + DRB_nGRAN_to_setup_t DRBnGRanModList[E1AP_MAX_NUM_NGRAN_DRB]; +} pdu_session_to_setup_t; + +typedef struct e1ap_bearer_setup_req_s { + uint32_t gNB_cu_cp_ue_id; + uint32_t gNB_cu_up_ue_id; + rnti_t rnti; + uint64_t cipheringAlgorithm; + uint64_t integrityProtectionAlgorithm; + char encryptionKey[128]; + char integrityProtectionKey[128]; + long ueDlAggMaxBitRate; + PLMN_ID_t servingPLMNid; + long activityNotificationLevel; + int numDRBs; + drb_to_setup_t DRBList[E1AP_MAX_NUM_DRBS]; + int numPDUSessions; + pdu_session_to_setup_t pduSession[E1AP_MAX_NUM_PDU_SESSIONS]; + int numPDUSessionsMod; + pdu_session_to_setup_t pduSessionMod[E1AP_MAX_NUM_PDU_SESSIONS]; +} e1ap_bearer_setup_req_t; + +typedef struct e1ap_bearer_release_cmd_s { + uint32_t gNB_cu_cp_ue_id; + uint32_t gNB_cu_up_ue_id; + long cause_type; + long cause; +} e1ap_bearer_release_cmd_t; + +typedef struct drb_setup_s { + int drbId; + in_addr_t tlAddress; + int teId; + int numUpParam; + up_params_t UpParamList[E1AP_MAX_NUM_UP_PARAM]; +} drb_setup_t; + +typedef struct qos_flow_setup_s { + long id; +} qos_flow_setup_t; + +typedef struct DRB_nGRAN_setup_s { + long id; + int numUpParam; + up_params_t UpParamList[E1AP_MAX_NUM_UP_PARAM]; + int numQosFlowSetup; + qos_flow_setup_t qosFlows[E1AP_MAX_NUM_QOS_FLOWS]; +} DRB_nGRAN_setup_t; + +typedef struct DRB_nGRAN_failed_s { + long id; + long cause_type; + long cause; +} DRB_nGRAN_failed_t; + +typedef struct pdu_session_setup_s { + long id; + in_addr_t tlAddress; + long teId; + int numDRBSetup; + DRB_nGRAN_setup_t DRBnGRanList[E1AP_MAX_NUM_NGRAN_DRB]; + int numDRBFailed; + DRB_nGRAN_failed_t DRBnGRanFailedList[E1AP_MAX_NUM_NGRAN_DRB]; +} pdu_session_setup_t; + +typedef struct e1ap_bearer_setup_resp_s { + uint64_t gNB_cu_cp_ue_id; + uint64_t gNB_cu_up_ue_id; + int numDRBs; + drb_setup_t DRBList[E1AP_MAX_NUM_DRBS]; + int numPDUSessions; + pdu_session_setup_t pduSession[E1AP_MAX_NUM_PDU_SESSIONS]; +} e1ap_bearer_setup_resp_t; + +#endif /* E1AP_MESSAGES_TYPES_H */ diff --git a/openair2/COMMON/gtpv1_u_messages_types.h b/openair2/COMMON/gtpv1_u_messages_types.h index 7c784a0c35..77e92f39bb 100644 --- a/openair2/COMMON/gtpv1_u_messages_types.h +++ b/openair2/COMMON/gtpv1_u_messages_types.h @@ -185,7 +185,7 @@ typedef struct gtpv1u_gnb_create_tunnel_resp_s { ue_id_t ue_id; int num_tunnels; teid_t gnb_NGu_teid[NR_GTPV1U_MAX_BEARERS_PER_UE]; ///< Tunnel Endpoint Identifier - pdusessionid_t pdusession_id[NR_GTPV1U_MAX_BEARERS_PER_UE]; + pdusessionid_t pdusession_id[NR_GTPV1U_MAX_BEARERS_PER_UE]; transport_layer_addr_t gnb_addr; } gtpv1u_gnb_create_tunnel_resp_t; typedef struct gtpv1u_gnb_delete_tunnel_req_s { diff --git a/openair2/COMMON/messages_def.h b/openair2/COMMON/messages_def.h index 394b40eeed..8334700dd0 100644 --- a/openair2/COMMON/messages_def.h +++ b/openair2/COMMON/messages_def.h @@ -41,3 +41,4 @@ #include "sctp_messages_def.h" #include "udp_messages_def.h" #include "gtpv1_u_messages_def.h" +#include "e1ap_messages_def.h" diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index 961599a9ed..70412e8368 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -274,9 +274,15 @@ typedef struct ngap_transport_layer_addr_s { dEST.length = sOURCE.length; \ } while (0) +typedef enum { + non_dynamic, + dynamic +} fiveQI_type_t; + typedef struct pdusession_level_qos_parameter_s { uint8_t qfi; uint64_t fiveQI; + fiveQI_type_t fiveQI_type; ngap_allocation_retention_priority_t allocation_retention_priority; } pdusession_level_qos_parameter_t; @@ -706,12 +712,21 @@ typedef struct ngap_pdusession_setup_req_s { /* gNB ue ngap id as initialized by NGAP layer */ uint32_t gNB_ue_ngap_id; + /* S-NSSAI */ + ngap_allowed_NSSAI_t allowed_nssai[8]; + /* Number of pdusession to be setup in the list */ uint8_t nb_pdusessions_tosetup; /* E RAB setup request */ pdusession_t pdusession_setup_params[NGAP_MAX_PDUSESSION]; + /* UE Uplink Aggregated Max Bitrates */ + uint64_t ueAggMaxBitRateUplink; + + /* UE Downlink Aggregated Max Bitrates */ + uint64_t ueAggMaxBitRateDownlink; + } ngap_pdusession_setup_req_t; typedef struct ngap_pdusession_setup_resp_s { diff --git a/openair2/E1AP/CMakeLists.txt b/openair2/E1AP/CMakeLists.txt index 89d87fe555..a8bacdd3fa 100644 --- a/openair2/E1AP/CMakeLists.txt +++ b/openair2/E1AP/CMakeLists.txt @@ -1,7 +1,7 @@ add_subdirectory(MESSAGES) -add_library(E1AP e1ap.c) -target_link_libraries(E1AP - PUBLIC asn1_e1ap - PRIVATE UTIL asn1_nr_rrc asn1_lte_rrc) -target_include_directories(E1AP PUBLIC ${CMAKE_CURRENT_DIR}) +add_library(e1ap e1ap.c e1ap_common.c e1ap_api.c) +target_link_libraries(e1ap + PUBLIC asn1_e1ap f1ap + PRIVATE asn1_nr_rrc asn1_lte_rrc asn1_f1ap UTIL e1_pdcp_if e1_if) +target_include_directories(e1ap PUBLIC ${CMAKE_CURRENT_DIR}) diff --git a/openair2/E1AP/e1ap.c b/openair2/E1AP/e1ap.c index b49f4d4ca1..52043602e2 100644 --- a/openair2/E1AP/e1ap.c +++ b/openair2/E1AP/e1ap.c @@ -22,48 +22,87 @@ */ #include "e1ap.h" +#include "e1ap_common.h" +#include "e1ap_api.h" +#include "gnb_config.h" +#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h" +#include "openair3/UTILS/conversions.h" +#include "openair2/RRC/NR/MESSAGES/asn1_msg.h" +#include "common/openairinterface5g_limits.h" +#include "common/utils/LOG/log.h" +#include "openair2/F1AP/f1ap_common.h" +#include "e1ap_default_values.h" -int asn1_encoder_xer_print = 1; +#define E1AP_NUM_MSG_HANDLERS 14 +typedef int (*e1ap_message_processing_t)(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *message_p); +e1ap_message_processing_t e1ap_message_processing[E1AP_NUM_MSG_HANDLERS][3] = { -int e1ap_assoc_id(bool isCu, instance_t instance) { - return 0; + { 0, 0, 0 }, /* Reset */ + { 0, 0, 0 }, /* ErrorIndication */ + { 0, 0, 0 }, /* privateMessage */ + { e1apCUCP_handle_SETUP_REQUEST, e1apCUUP_handle_SETUP_RESPONSE, e1apCUUP_handle_SETUP_FAILURE }, /* gNBCUUPE1Setup */ + { 0, 0, 0 }, /* gNBCUCPE1Setup */ + { 0, 0, 0 }, /* gNBCUUPConfigurationUpdate */ + { 0, 0, 0 }, /* gNBCUCPConfigurationUpdate */ + { 0, 0, 0 }, /* E1Release */ + { e1apCUUP_handle_BEARER_CONTEXT_SETUP_REQUEST, e1apCUCP_handle_BEARER_CONTEXT_SETUP_RESPONSE, e1apCUCP_handle_BEARER_CONTEXT_SETUP_FAILURE }, /* bearerContextSetup */ + { e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_REQUEST, 0, 0 }, /* bearerContextModification */ + { 0, 0, 0 }, /* bearerContextModificationRequired */ + { e1apCUUP_handle_BEARER_CONTEXT_RELEASE_COMMAND, e1apCUCP_handle_BEARER_CONTEXT_RELEASE_COMPLETE, 0 }, /* bearerContextRelease */ + { 0, 0, 0 }, /* bearerContextReleaseRequired */ + { 0, 0, 0 } /* bearerContextInactivityNotification */ +}; + +const char *e1ap_direction2String(int e1ap_dir) { + static const char *e1ap_direction_String[] = { + "", /* Nothing */ + "Initiating message", /* initiating message */ + "Successfull outcome", /* successfull outcome */ + "UnSuccessfull outcome", /* successfull outcome */ + }; + return(e1ap_direction_String[e1ap_dir]); } -int e1ap_encode_send(bool isCu, instance_t instance, E1AP_E1AP_PDU_t *pdu, uint16_t stream, const char *func) { - DevAssert(pdu != NULL); +int e1ap_handle_message(instance_t instance, uint32_t assoc_id, + const uint8_t *const data, const uint32_t data_length) { + E1AP_E1AP_PDU_t pdu= {0}; + int ret; + DevAssert(data != NULL); - if (asn1_encoder_xer_print) { - LOG_E(E1AP, "----------------- ASN1 ENCODER PRINT START ----------------- \n"); - xer_fprint(stdout, &asn_DEF_E1AP_E1AP_PDU, pdu); - LOG_E(E1AP, "----------------- ASN1 ENCODER PRINT END----------------- \n"); + if (e1ap_decode_pdu(&pdu, data, data_length) < 0) { + LOG_E(E1AP, "Failed to decode PDU\n"); + return -1; } - - char errbuf[2048]; /* Buffer for error message */ - size_t errlen = sizeof(errbuf); /* Size of the buffer */ - int ret = asn_check_constraints(&asn_DEF_E1AP_E1AP_PDU, pdu, errbuf, &errlen); - - if(ret) { - fprintf(stderr, "%s: Constraint validation failed: %s\n", func, errbuf); - } - - void *buffer = NULL; - ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_E1AP_E1AP_PDU, 0, pdu, buffer); - - if (encoded < 0) { - LOG_E(E1AP, "%s: Failed to encode E1AP message\n", func); + const E1AP_ProcedureCode_t procedureCode = pdu.choice.initiatingMessage->procedureCode; + /* Checking procedure Code and direction of message */ + if ((procedureCode >= E1AP_NUM_MSG_HANDLERS) || (pdu.present > E1AP_E1AP_PDU_PR_unsuccessfulOutcome) || (pdu.present <= E1AP_E1AP_PDU_PR_NOTHING)) { + LOG_E(E1AP, "[SCTP %d] Either procedureCode %ld or direction %d exceed expected\n", assoc_id, procedureCode, pdu.present); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, &pdu); return -1; - } else { - MessageDef *message = itti_alloc_new_message(isCu?TASK_CUCP_E1:TASK_CUUP_E1, 0, SCTP_DATA_REQ); - sctp_data_req_t *s = &message->ittiMsg.sctp_data_req; - s->assoc_id = e1ap_assoc_id(isCu,instance); - s->buffer = buffer; - s->buffer_length = encoded; - s->stream = stream; - LOG_I(E1AP, "%s: Sending ITTI message to SCTP Task\n", func); - itti_send_msg_to_task(TASK_SCTP, instance, message); } - return encoded; + if (e1ap_message_processing[procedureCode][pdu.present - 1] == NULL) { + // No handler present. This can mean not implemented or no procedure for eNB (wrong direction). + LOG_E(E1AP, "[SCTP %d] No handler for procedureCode %ld in %s\n", assoc_id, procedureCode, e1ap_direction2String(pdu.present - 1)); + ret=-1; + } else { + /* Calling the right handler */ + LOG_I(E1AP, "Calling handler with instance %ld\n",instance); + ret = (*e1ap_message_processing[procedureCode][pdu.present - 1])(getCxtE1(instance), &pdu); + } + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, &pdu); + return ret; +} + +void e1_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) +{ + int result; + DevAssert(sctp_data_ind != NULL); + e1ap_handle_message(instance, sctp_data_ind->assoc_id, + sctp_data_ind->buffer, sctp_data_ind->buffer_length); + result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer); + AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); } void e1ap_itti_send_sctp_close_association(bool isCu, instance_t instance) { @@ -73,14 +112,16 @@ void e1ap_itti_send_sctp_close_association(bool isCu, instance_t instance) { itti_send_msg_to_task(TASK_SCTP, instance, message); } -int e1ap_send_RESET(bool isCu, instance_t instance, E1AP_Reset_t *Reset) { +int e1ap_send_RESET(bool isCu, e1ap_setup_req_t *setupReq, E1AP_Reset_t *Reset) +{ AssertFatal(false,"Not implemented yet\n"); E1AP_E1AP_PDU_t pdu= {0}; - return e1ap_encode_send(isCu, instance, &pdu,0, __func__); + return e1ap_encode_send(isCu, setupReq, &pdu, 0, __func__); } int e1ap_send_RESET_ACKNOWLEDGE(instance_t instance, E1AP_Reset_t *Reset) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_handle_RESET(instance_t instance, @@ -88,6 +129,7 @@ int e1ap_handle_RESET(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_handle_RESET_ACKNOWLEDGE(instance_t instance, @@ -95,6 +137,7 @@ int e1ap_handle_RESET_ACKNOWLEDGE(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } /* @@ -105,10 +148,12 @@ int e1ap_handle_ERROR_INDICATION(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_send_ERROR_INDICATION(instance_t instance, E1AP_ErrorIndication_t *ErrorIndication) { AssertFatal(false,"Not implemented yet\n"); + return -1; } @@ -116,74 +161,316 @@ int e1ap_send_ERROR_INDICATION(instance_t instance, E1AP_ErrorIndication_t *Erro E1 Setup: can be sent on both ways, to be refined */ -int e1apCUUP_send_SETUP_REQUEST(instance_t instance, E1AP_Reset_t *Reset) { - AssertFatal(false,"Not implemented yet\n"); +static void fill_SETUP_REQUEST(e1ap_setup_req_t *setup, E1AP_E1AP_PDU_t *pdu) +{ + /* Create */ + /* 0. pdu Type */ + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, initMsg); + initMsg->procedureCode = E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup; + initMsg->criticality = E1AP_Criticality_reject; + initMsg->value.present = E1AP_InitiatingMessage__value_PR_GNB_CU_UP_E1SetupRequest; + E1AP_GNB_CU_UP_E1SetupRequest_t *e1SetupUP = &initMsg->value.choice.GNB_CU_UP_E1SetupRequest; + /* mandatory */ + /* c1. Transaction ID (integer value) */ + asn1cSequenceAdd(e1SetupUP->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_GNB_CU_UP_E1SetupRequestIEs__value_PR_TransactionID; + setup->transac_id = E1AP_get_next_transaction_identifier(); + ieC1->value.choice.TransactionID = setup->transac_id; + LOG_I(E1AP, "Transaction ID of setup request %ld\n", setup->transac_id); + /* mandatory */ + /* c2. GNB_CU_ID (integer value) */ + asn1cSequenceAdd(e1SetupUP->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_ID; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_GNB_CU_UP_E1SetupRequestIEs__value_PR_GNB_CU_UP_ID; + asn_int642INTEGER(&ieC2->value.choice.GNB_CU_UP_ID, setup->gNB_cu_up_id); + /* mandatory */ + /* c4. CN Support */ + asn1cSequenceAdd(e1SetupUP->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ieC4); + ieC4->id = E1AP_ProtocolIE_ID_id_CNSupport; + ieC4->criticality = E1AP_Criticality_reject; + ieC4->value.present = E1AP_GNB_CU_UP_E1SetupRequestIEs__value_PR_CNSupport; + ieC4->value.choice.CNSupport = setup->cn_support; + + /* mandatory */ + /* c5. Supported PLMNs */ + asn1cSequenceAdd(e1SetupUP->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ieC5); + ieC5->id = E1AP_ProtocolIE_ID_id_SupportedPLMNs; + ieC5->criticality = E1AP_Criticality_reject; + ieC5->value.present = E1AP_GNB_CU_UP_E1SetupRequestIEs__value_PR_SupportedPLMNs_List; + + int numSupportedPLMNs = setup->supported_plmns; + + for (int i=0; i < numSupportedPLMNs; i++) { + asn1cSequenceAdd(ieC5->value.choice.SupportedPLMNs_List.list, E1AP_SupportedPLMNs_Item_t, supportedPLMN); + /* 5.1 PLMN Identity */ + MCC_MNC_TO_PLMNID(setup->plmns[i].mcc, setup->plmns[i].mnc, setup->plmns[i].mnc_digit_length, &supportedPLMN->pLMN_Identity); + } } -int e1apCUCP_send_SETUP_RESPONSE(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +static void fill_SETUP_RESPONSE(const e1ap_setup_resp_t *e1ap_setup_resp, E1AP_E1AP_PDU_t *pdu) +{ + /* Create */ + /* 0. pdu Type */ + pdu->present = E1AP_E1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, initMsg); + initMsg->procedureCode = E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup; + initMsg->criticality = E1AP_Criticality_reject; + initMsg->value.present = E1AP_SuccessfulOutcome__value_PR_GNB_CU_UP_E1SetupResponse; + E1AP_GNB_CU_UP_E1SetupResponse_t *out = &pdu->choice.successfulOutcome->value.choice.GNB_CU_UP_E1SetupResponse; + /* mandatory */ + /* c1. Transaction ID (integer value) */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupResponseIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_GNB_CU_UP_E1SetupResponseIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = e1ap_setup_resp->transac_id; } -int e1apCUCP_send_SETUP_FAILURE() { - AssertFatal(false,"Not implemented yet\n"); +void e1ap_send_SETUP_RESPONSE(instance_t inst, const e1ap_setup_resp_t *e1ap_setup_resp) +{ + AssertFatal(getCxtE1(inst), ""); + e1ap_setup_req_t *setupReq = &getCxtE1(inst)->setupReq; + E1AP_E1AP_PDU_t pdu = {0}; + fill_SETUP_RESPONSE(e1ap_setup_resp, &pdu); + e1ap_encode_send(getCxtE1(inst)->type, setupReq, &pdu, 0, __func__); } -int e1apCUCP_handle_SETUP_REQUEST(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +static void fill_SETUP_FAILURE(long transac_id, E1AP_E1AP_PDU_t *pdu) +{ + /* Create */ + /* 0. pdu Type */ + pdu->present = E1AP_E1AP_PDU_PR_unsuccessfulOutcome; + asn1cCalloc(pdu->choice.unsuccessfulOutcome, initMsg); + initMsg->procedureCode = E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup; + initMsg->criticality = E1AP_Criticality_reject; + initMsg->value.present = E1AP_UnsuccessfulOutcome__value_PR_GNB_CU_UP_E1SetupFailure; + E1AP_GNB_CU_UP_E1SetupFailure_t *out = &pdu->choice.unsuccessfulOutcome->value.choice.GNB_CU_UP_E1SetupFailure; + /* mandatory */ + /* c1. Transaction ID (integer value) */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_GNB_CU_UP_E1SetupResponseIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = transac_id; + /* mandatory */ + /* c2. cause (integer value) */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_Cause; + ieC2->criticality = E1AP_Criticality_ignore; + ieC2->value.present = E1AP_GNB_CU_UP_E1SetupFailureIEs__value_PR_Cause; + ieC2->value.choice.Cause.present = E1AP_Cause_PR_radioNetwork; //choose this accordingly + ieC2->value.choice.Cause.choice.radioNetwork = E1AP_CauseRadioNetwork_unspecified; } -int e1apCUUP_handle_SETUP_RESPONSE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +void e1apCUCP_send_SETUP_FAILURE(e1ap_setup_req_t *setupReq, long transac_id) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_SETUP_FAILURE(transac_id, &pdu); + e1ap_encode_send(CPtype, setupReq, &pdu, 0, __func__); } -int e1apCUUP_handle_SETUP_FAILURE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +void extract_SETUP_REQUEST(const E1AP_E1AP_PDU_t *pdu, + e1ap_setup_req_t *req) { + E1AP_GNB_CU_UP_E1SetupRequestIEs_t *ie; + E1AP_GNB_CU_UP_E1SetupRequest_t *in = &pdu->choice.initiatingMessage->value.choice.GNB_CU_UP_E1SetupRequest; + /* assoc_id */ + /* req->assoc_id = assoc_id; */ + + /* transac_id */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_TransactionID, true); + req->transac_id = ie->value.choice.TransactionID; + LOG_D(E1AP, "gNB CU UP E1 setup request transaction ID: %ld\n", req->transac_id); + + /* gNB CU UP ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_gNB_CU_UP_ID, true); + asn_INTEGER2ulong(&ie->value.choice.GNB_CU_UP_ID, &req->gNB_cu_up_id); + LOG_D(E1AP, "gNB CU UP ID: %ld\n", req->gNB_cu_up_id); + + /* CN Support */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_CNSupport, true); + req->cn_support = ie->value.choice.CNSupport; + LOG_D(E1AP, "E1ap CN support: %ld\n", req->cn_support); + + /* Supported PLMNs */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupRequestIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_SupportedPLMNs, true); + req->supported_plmns = ie->value.choice.SupportedPLMNs_List.list.count; + LOG_D(E1AP, "Number of supported PLMNs: %d\n", req->supported_plmns); + + for (int i=0; i < req->supported_plmns; i++) { + E1AP_SupportedPLMNs_Item_t *supported_plmn_item = (E1AP_SupportedPLMNs_Item_t *)(ie->value.choice.SupportedPLMNs_List.list.array[i]); + + /* PLMN Identity */ + PLMNID_TO_MCC_MNC(&supported_plmn_item->pLMN_Identity, + req->plmns[i].mcc, + req->plmns[i].mnc, + req->plmns[i].mnc_digit_length); + LOG_D(E1AP, "MCC: %d\nMNC: %d\n", req->plmns[i].mcc, req->plmns[i].mnc); + } +} + +int e1apCUCP_handle_SETUP_REQUEST(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + extract_SETUP_REQUEST(pdu, &inst->setupReq); + /* Create ITTI message and send to queue */ + MessageDef *msg_p = itti_alloc_new_message(TASK_CUCP_E1, 0 /*unused by callee*/, E1AP_SETUP_REQ); + memcpy(&E1AP_SETUP_REQ(msg_p), &inst->setupReq, sizeof(e1ap_setup_req_t)); + + if (inst->setupReq.supported_plmns > 0) { + itti_send_msg_to_task(TASK_RRC_GNB, 0 /*unused by callee*/, msg_p); + } else { + e1apCUCP_send_SETUP_FAILURE(&inst->setupReq, inst->setupReq.transac_id); + itti_free(TASK_CUCP_E1, msg_p); + return -1; + } + + return 0; +} + +int e1apCUUP_handle_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu) +{ + LOG_D(E1AP, "%s\n", __func__); + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_successfulOutcome); + DevAssert(pdu->choice.successfulOutcome->procedureCode == E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup); + DevAssert(pdu->choice.successfulOutcome->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.successfulOutcome->value.present == E1AP_SuccessfulOutcome__value_PR_GNB_CU_UP_E1SetupResponse); + const E1AP_GNB_CU_UP_E1SetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.GNB_CU_UP_E1SetupResponse; + E1AP_GNB_CU_UP_E1SetupResponseIEs_t *ie; + + /* transac_id */ + long transaction_id; + long old_transaction_id = inst->setupReq.transac_id; + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupResponseIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_TransactionID, true); + transaction_id = ie->value.choice.TransactionID; + LOG_D(E1AP, "gNB CU UP E1 setup response transaction ID: %ld\n", transaction_id); + + if (old_transaction_id != transaction_id) + LOG_E(E1AP, "Transaction IDs do not match %ld != %ld\n", old_transaction_id, transaction_id); + + E1AP_free_transaction_identifier(transaction_id); + + /* do the required processing */ + + return 0; +} + +int e1apCUUP_handle_SETUP_FAILURE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu) +{ + E1AP_GNB_CU_UP_E1SetupFailureIEs_t *ie; + DevAssert(pdu != NULL); + E1AP_GNB_CU_UP_E1SetupFailure_t *in = &pdu->choice.unsuccessfulOutcome->value.choice.GNB_CU_UP_E1SetupFailure; + /* Transaction ID */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_TransactionID, true); + /* Cause */ + F1AP_FIND_PROTOCOLIE_BY_ID(E1AP_GNB_CU_UP_E1SetupFailureIEs_t, ie, in, + E1AP_ProtocolIE_ID_id_Cause, true); + + return 0; +} + +static void fill_CONFIGURATION_UPDATE(E1AP_E1AP_PDU_t *pdu) +{ + /* Create */ + /* 0. pdu Type */ + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, msg); + msg->procedureCode = E1AP_ProcedureCode_id_gNB_CU_UP_ConfigurationUpdate; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_InitiatingMessage__value_PR_GNB_CU_UP_ConfigurationUpdate; + E1AP_GNB_CU_UP_ConfigurationUpdate_t *out = &pdu->choice.initiatingMessage->value.choice.GNB_CU_UP_ConfigurationUpdate; + /* mandatory */ + /* c1. Transaction ID (integer value) */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_ConfigurationUpdateIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_TransactionID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_GNB_CU_UP_ConfigurationUpdateIEs__value_PR_TransactionID; + ieC1->value.choice.TransactionID = 0;//get this from stored transaction IDs in CU + /* mandatory */ + /* c2. Supported PLMNs */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_ConfigurationUpdateIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_SupportedPLMNs; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_GNB_CU_UP_ConfigurationUpdateIEs__value_PR_SupportedPLMNs_List; + + int numSupportedPLMNs = 1; + + for (int i=0; i < numSupportedPLMNs; i++) { + asn1cSequenceAdd(ieC2->value.choice.SupportedPLMNs_List.list, E1AP_SupportedPLMNs_Item_t, supportedPLMN); + /* 5.1 PLMN Identity */ + OCTET_STRING_fromBuf(&supportedPLMN->pLMN_Identity, "OAI", strlen("OAI")); + } + + /* mandatory */ + /* c3. TNLA to remove list */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_GNB_CU_UP_ConfigurationUpdateIEs_t, ieC3); + ieC3->id = E1AP_ProtocolIE_ID_id_GNB_CU_UP_TNLA_To_Remove_List; + ieC3->criticality = E1AP_Criticality_reject; + ieC3->value.present = E1AP_GNB_CU_UP_ConfigurationUpdateIEs__value_PR_GNB_CU_UP_TNLA_To_Remove_List; + + int numTNLAtoRemoveList = 1; + + for (int i=0; i < numTNLAtoRemoveList; i++) { + asn1cSequenceAdd(ieC2->value.choice.GNB_CU_UP_TNLA_To_Remove_List.list, E1AP_GNB_CU_UP_TNLA_To_Remove_Item_t, TNLAtoRemove); + TNLAtoRemove->tNLAssociationTransportLayerAddress.present = E1AP_CP_TNL_Information_PR_endpoint_IP_Address; + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(1234, &TNLAtoRemove->tNLAssociationTransportLayerAddress.choice.endpoint_IP_Address); // TODO: correct me + } } /* E1 configuration update: can be sent in both ways, to be refined */ -int e1apCUUP_send_gNB_DU_CONFIGURATION_UPDATE(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +void e1apCUUP_send_CONFIGURATION_UPDATE(e1ap_setup_req_t *setupReq) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_CONFIGURATION_UPDATE(&pdu); + e1ap_encode_send(UPtype, setupReq, &pdu, 0, __func__); } -int e1apCUCP_send_gNB_DU_CONFIGURATION_FAILURE(instance_t instance) { +int e1apCUCP_send_gNB_DU_CONFIGURATION_FAILURE(e1ap_setup_req_t *setupReq) +{ AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUCP_send_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance) { +int e1apCUCP_send_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(e1ap_setup_req_t *setupReq) +{ AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUCP_handle_gNB_DU_CONFIGURATION_UPDATE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { +int e1apCUCP_handle_CONFIGURATION_UPDATE(e1ap_setup_req_t *setupReq, E1AP_E1AP_PDU_t *pdu) +{ + /* + E1AP_GNB_CU_UP_E1SetupRequestIEs_t *ie; + DevAssert(pdu != NULL); + E1AP_GNB_CU_UP_E1SetupRequest_t *in = &pdu->choice.initiatingMessage->value.choice.GNB_CU_UP_E1SetupRequest; + */ + AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUUP_handle_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { +int e1apCUUP_handle_gNB_DU_CONFIGURATION_UPDATE_ACKNOWLEDGE(e1ap_setup_req_t *setupReq, uint32_t assoc_id, uint32_t stream, E1AP_E1AP_PDU_t *pdu) +{ AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUUP_handle_gNB_DU_CONFIGURATION_FAILURE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { +int e1apCUUP_handle_gNB_DU_CONFIGURATION_FAILURE(e1ap_setup_req_t *setupReq, uint32_t assoc_id, uint32_t stream, E1AP_E1AP_PDU_t *pdu) +{ AssertFatal(false,"Not implemented yet\n"); + return -1; } /* @@ -192,10 +479,12 @@ int e1apCUUP_handle_gNB_DU_CONFIGURATION_FAILURE(instance_t instance, int e1ap_send_RELEASE_REQUEST(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_send_RELEASE_ACKNOWLEDGE(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_handle_RELEASE_REQUEST(instance_t instance, @@ -203,6 +492,7 @@ int e1ap_handle_RELEASE_REQUEST(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1ap_handle_RELEASE_ACKNOWLEDGE(instance_t instance, @@ -210,66 +500,750 @@ int e1ap_handle_RELEASE_ACKNOWLEDGE(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } /* BEARER CONTEXT SETUP REQUEST */ -int e1apCUCP_send_BEARER_CONTEXT_SETUP_REQUEST(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +static int fill_BEARER_CONTEXT_SETUP_REQUEST(e1ap_setup_req_t *setup, e1ap_bearer_setup_req_t *const bearerCxt, E1AP_E1AP_PDU_t *pdu) +{ + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextSetup; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_InitiatingMessage__value_PR_BearerContextSetupRequest; + E1AP_BearerContextSetupRequest_t *out = &pdu->choice.initiatingMessage->value.choice.BearerContextSetupRequest; + /* mandatory */ + /* c1. gNB-CU-UP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ieC1->value.choice.GNB_CU_CP_UE_E1AP_ID = bearerCxt->gNB_cu_cp_ue_id; + /* mandatory */ + /* c2. Security Information */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_SecurityInformation; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_SecurityInformation; + ieC2->value.choice.SecurityInformation.securityAlgorithm.cipheringAlgorithm = bearerCxt->cipheringAlgorithm; + OCTET_STRING_fromBuf(&ieC2->value.choice.SecurityInformation.uPSecuritykey.encryptionKey, + bearerCxt->encryptionKey, strlen(bearerCxt->encryptionKey)); + + asn1cCallocOne(ieC2->value.choice.SecurityInformation.securityAlgorithm.integrityProtectionAlgorithm, bearerCxt->integrityProtectionAlgorithm); + asn1cCalloc(ieC2->value.choice.SecurityInformation.uPSecuritykey.integrityProtectionKey, protKey); + OCTET_STRING_fromBuf(protKey, bearerCxt->integrityProtectionKey, strlen(bearerCxt->integrityProtectionKey)); + /* mandatory */ + /* c3. UE DL Aggregate Maximum Bit Rate */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC3); + ieC3->id = E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate; + ieC3->criticality = E1AP_Criticality_reject; + ieC3->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_BitRate; + asn_long2INTEGER(&ieC3->value.choice.BitRate, bearerCxt->ueDlAggMaxBitRate); + /* mandatory */ + /* c4. Serving PLMN */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC4); + ieC4->id = E1AP_ProtocolIE_ID_id_Serving_PLMN; + ieC4->criticality = E1AP_Criticality_ignore; + ieC4->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_PLMN_Identity; + PLMN_ID_t *servingPLMN = setup->plmns; // First PLMN is serving PLMN. TODO: Remove hard coding here + MCC_MNC_TO_PLMNID(servingPLMN->mcc, servingPLMN->mnc, servingPLMN->mnc_digit_length, &ieC4->value.choice.PLMN_Identity); + /* mandatory */ + /* Activity Notification Level */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC5); + ieC5->id = E1AP_ProtocolIE_ID_id_ActivityNotificationLevel; + ieC5->criticality = E1AP_Criticality_reject; + ieC5->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_ActivityNotificationLevel; + ieC5->value.choice.ActivityNotificationLevel = E1AP_ActivityNotificationLevel_pdu_session;// TODO: Remove hard coding + /* mandatory */ + /* */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupRequestIEs_t, ieC6); + ieC6->id = E1AP_ProtocolIE_ID_id_System_BearerContextSetupRequest; + ieC6->criticality = E1AP_Criticality_reject; + ieC6->value.present = E1AP_BearerContextSetupRequestIEs__value_PR_System_BearerContextSetupRequest; + ieC6->value.choice.System_BearerContextSetupRequest.present = E1AP_System_BearerContextSetupRequest_PR_nG_RAN_BearerContextSetupRequest; + E1AP_ProtocolIE_Container_4932P19_t *msgNGRAN_list = calloc(1, sizeof(E1AP_ProtocolIE_Container_4932P19_t)); + ieC6->value.choice.System_BearerContextSetupRequest.choice.nG_RAN_BearerContextSetupRequest = (struct E1AP_ProtocolIE_Container *) msgNGRAN_list; + asn1cSequenceAdd(msgNGRAN_list->list, E1AP_NG_RAN_BearerContextSetupRequest_t, msgNGRAN); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextSetupRequest__value_PR_PDU_Session_Resource_To_Setup_List; + E1AP_PDU_Session_Resource_To_Setup_List_t *pdu2Setup = &msgNGRAN->value.choice.PDU_Session_Resource_To_Setup_List; + for(pdu_session_to_setup_t *i=bearerCxt->pduSession; i < bearerCxt->pduSession+bearerCxt->numPDUSessions; i++) { + asn1cSequenceAdd(pdu2Setup->list, E1AP_PDU_Session_Resource_To_Setup_Item_t, ieC6_1); + ieC6_1->pDU_Session_ID = i->sessionId; + ieC6_1->pDU_Session_Type = i->sessionType; + + INT8_TO_OCTET_STRING(i->sst, &ieC6_1->sNSSAI.sST); + + ieC6_1->securityIndication.integrityProtectionIndication = i->integrityProtectionIndication; + ieC6_1->securityIndication.confidentialityProtectionIndication = i->confidentialityProtectionIndication; + + ieC6_1->nG_UL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(ieC6_1->nG_UL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(i->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(i->teId, &gTPTunnel->gTP_TEID); + + for (DRB_nGRAN_to_setup_t *j=i->DRBnGRanList; j < i->DRBnGRanList+i->numDRB2Setup; j++) { + asn1cSequenceAdd(ieC6_1->dRB_To_Setup_List_NG_RAN.list, E1AP_DRB_To_Setup_Item_NG_RAN_t, ieC6_1_1); + ieC6_1_1->dRB_ID = j->id; + + ieC6_1_1->sDAP_Configuration.defaultDRB = j->defaultDRB; + ieC6_1_1->sDAP_Configuration.sDAP_Header_UL = j->sDAP_Header_UL; + ieC6_1_1->sDAP_Configuration.sDAP_Header_DL = j->sDAP_Header_DL; + + ieC6_1_1->pDCP_Configuration.pDCP_SN_Size_UL = j->pDCP_SN_Size_UL; + ieC6_1_1->pDCP_Configuration.pDCP_SN_Size_DL = j->pDCP_SN_Size_DL; + asn1cCallocOne(ieC6_1_1->pDCP_Configuration.discardTimer, j->discardTimer); + E1AP_T_ReorderingTimer_t *roTimer = calloc(1, sizeof(E1AP_T_ReorderingTimer_t)); + ieC6_1_1->pDCP_Configuration.t_ReorderingTimer = roTimer; + roTimer->t_Reordering = j->reorderingTimer; + ieC6_1_1->pDCP_Configuration.rLC_Mode = j->rLC_Mode; + + for (cell_group_t *k=j->cellGroupList; k < j->cellGroupList+j->numCellGroups; k++) { + asn1cSequenceAdd(ieC6_1_1->cell_Group_Information.list, E1AP_Cell_Group_Information_Item_t, ieC6_1_1_1); + ieC6_1_1_1->cell_Group_ID = k->id; + } + + for (qos_flow_to_setup_t *k=j->qosFlows; k < j->qosFlows+j->numQosFlow2Setup; k++) { + asn1cSequenceAdd(ieC6_1_1->qos_flow_Information_To_Be_Setup, E1AP_QoS_Flow_QoS_Parameter_Item_t, ieC6_1_1_1); + ieC6_1_1_1->qoS_Flow_Identifier = k->id; + + if (k->fiveQI_type == non_dynamic) { // non Dynamic 5QI + ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_non_Dynamic_5QI; + asn1cCalloc(ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.non_Dynamic_5QI, non_Dynamic_5QI); + non_Dynamic_5QI->fiveQI = k->fiveQI; + } else { // dynamic 5QI + ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.present = E1AP_QoS_Characteristics_PR_dynamic_5QI; + asn1cCalloc(ieC6_1_1_1->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI, dynamic_5QI); + dynamic_5QI->qoSPriorityLevel = k->qoSPriorityLevel; + dynamic_5QI->packetDelayBudget = k->packetDelayBudget; + dynamic_5QI->packetErrorRate.pER_Scalar = k->packetError_scalar; + dynamic_5QI->packetErrorRate.pER_Exponent = k->packetError_exponent; + } + + ieC6_1_1_1->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.priorityLevel = k->priorityLevel; + ieC6_1_1_1->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionCapability = k->pre_emptionCapability; + ieC6_1_1_1->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionVulnerability = k->pre_emptionVulnerability; + } + } + } + return 0; } -int e1apCUUP_send_BEARER_CONTEXT_SETUP_RESPONSE(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +void e1apCUCP_send_BEARER_CONTEXT_SETUP_REQUEST(instance_t instance, e1ap_bearer_setup_req_t *const bearerCxt) +{ + if (!getCxtE1(instance)) { + LOG_E(E1AP, "Received a UE bearer to establish while no CU-UP is connected, response on NGAP to send failure is not developped\n"); + // Fixme: add response on NGAP to send failure + return; + } + + E1AP_E1AP_PDU_t pdu = {0}; + e1ap_setup_req_t *setupReq = &getCxtE1(instance)->setupReq; + fill_BEARER_CONTEXT_SETUP_REQUEST(setupReq, bearerCxt, &pdu); + e1ap_encode_send(CPtype, setupReq, &pdu, 0, __func__); +} + +static void fill_BEARER_CONTEXT_SETUP_RESPONSE(e1ap_bearer_setup_resp_t *const resp, E1AP_E1AP_PDU_t *pdu) +{ + /* Create */ + /* 0. pdu Type */ + pdu->present = E1AP_E1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextSetup; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_SuccessfulOutcome__value_PR_BearerContextSetupResponse; + E1AP_BearerContextSetupResponse_t *out = &pdu->choice.successfulOutcome->value.choice.BearerContextSetupResponse; + /* mandatory */ + /* c1. gNB-CU-CP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupResponseIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_BearerContextSetupResponseIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ieC1->value.choice.GNB_CU_CP_UE_E1AP_ID = resp->gNB_cu_cp_ue_id; + /* mandatory */ + /* c2. gNB-CU-UP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupResponseIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_BearerContextSetupResponseIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; + ieC2->value.choice.GNB_CU_CP_UE_E1AP_ID = resp->gNB_cu_up_ue_id; + + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextSetupResponseIEs_t, ieC3); + ieC3->id = E1AP_ProtocolIE_ID_id_System_BearerContextSetupResponse; + ieC3->criticality = E1AP_Criticality_reject; + ieC3->value.present = E1AP_BearerContextSetupResponseIEs__value_PR_System_BearerContextSetupResponse; + if (0) { // EUTRAN + ieC3->value.choice.System_BearerContextSetupResponse.present = E1AP_System_BearerContextSetupResponse_PR_e_UTRAN_BearerContextSetupResponse; + E1AP_ProtocolIE_Container_4932P21_t *msgEUTRAN_list = calloc(1, sizeof(E1AP_ProtocolIE_Container_4932P21_t)); + ieC3->value.choice.System_BearerContextSetupResponse.choice.e_UTRAN_BearerContextSetupResponse = (struct E1AP_ProtocolIE_Container *) msgEUTRAN_list; + asn1cSequenceAdd(msgEUTRAN_list->list, E1AP_EUTRAN_BearerContextSetupResponse_t, msgEUTRAN); + msgEUTRAN->id = E1AP_ProtocolIE_ID_id_DRB_Setup_List_EUTRAN; + msgEUTRAN->criticality = E1AP_Criticality_reject; + msgEUTRAN->value.present = E1AP_EUTRAN_BearerContextSetupResponse__value_PR_DRB_Setup_List_EUTRAN; + E1AP_DRB_Setup_List_EUTRAN_t *drbSetup = &msgEUTRAN->value.choice.DRB_Setup_List_EUTRAN; + + for (drb_setup_t *i=resp->DRBList; i < resp->DRBList+resp->numDRBs; i++) { + asn1cSequenceAdd(drbSetup->list, E1AP_DRB_Setup_Item_EUTRAN_t, ieC3_1); + ieC3_1->dRB_ID = i->drbId; + + ieC3_1->s1_DL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(ieC3_1->s1_DL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(i->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(i->teId, &gTPTunnel->gTP_TEID); + + for (up_params_t *j=i->UpParamList; j < i->UpParamList+i->numUpParam; j++) { + asn1cSequenceAdd(ieC3_1->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, ieC3_1_1); + ieC3_1_1->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(ieC3_1_1->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(j->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(j->teId, &gTPTunnel->gTP_TEID); + } + } + } else { + ieC3->value.choice.System_BearerContextSetupResponse.present = E1AP_System_BearerContextSetupResponse_PR_nG_RAN_BearerContextSetupResponse; + E1AP_ProtocolIE_Container_4932P22_t *msgNGRAN_list = calloc(1, sizeof(E1AP_ProtocolIE_Container_4932P22_t)); + ieC3->value.choice.System_BearerContextSetupResponse.choice.nG_RAN_BearerContextSetupResponse = (struct E1AP_ProtocolIE_Container *) msgNGRAN_list; + asn1cSequenceAdd(msgNGRAN_list->list, E1AP_NG_RAN_BearerContextSetupResponse_t, msgNGRAN); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Setup_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextSetupResponse__value_PR_PDU_Session_Resource_Setup_List; + E1AP_PDU_Session_Resource_Setup_List_t *pduSetup = &msgNGRAN->value.choice.PDU_Session_Resource_Setup_List; + + for (pdu_session_setup_t *i=resp->pduSession; i < resp->pduSession+resp->numPDUSessions; i++) { + asn1cSequenceAdd(pduSetup->list, E1AP_PDU_Session_Resource_Setup_Item_t, ieC3_1); + ieC3_1->pDU_Session_ID = i->id; + + ieC3_1->nG_DL_UP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(ieC3_1->nG_DL_UP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(i->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(i->teId, &gTPTunnel->gTP_TEID); + + for (DRB_nGRAN_setup_t *j=i->DRBnGRanList; j < i->DRBnGRanList+i->numDRBSetup; j++) { + asn1cSequenceAdd(ieC3_1->dRB_Setup_List_NG_RAN.list, E1AP_DRB_Setup_Item_NG_RAN_t, ieC3_1_1); + ieC3_1_1->dRB_ID = j->id; + + for (up_params_t *k=j->UpParamList; k < j->UpParamList+j->numUpParam; k++) { + asn1cSequenceAdd(ieC3_1_1->uL_UP_Transport_Parameters.list, E1AP_UP_Parameters_Item_t, ieC3_1_1_1); + ieC3_1_1_1->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(ieC3_1_1_1->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(k->teId, &gTPTunnel->gTP_TEID); + } + + for (qos_flow_setup_t *k=j->qosFlows; k < j->qosFlows+j->numQosFlowSetup; k++) { + asn1cSequenceAdd(ieC3_1_1->flow_Setup_List.list, E1AP_QoS_Flow_Item_t, ieC3_1_1_1); + ieC3_1_1_1->qoS_Flow_Identifier = k->id; + } + } + + if (i->numDRBFailed > 0) + ieC3_1->dRB_Failed_List_NG_RAN = calloc(1, sizeof(E1AP_DRB_Failed_List_NG_RAN_t)); + + for (DRB_nGRAN_failed_t *j=i->DRBnGRanFailedList; j < i->DRBnGRanFailedList+i->numDRBFailed; j++) { + asn1cSequenceAdd(ieC3_1->dRB_Failed_List_NG_RAN->list, E1AP_DRB_Failed_Item_NG_RAN_t, ieC3_1_1); + ieC3_1_1->dRB_ID = j->id; + + ieC3_1_1->cause.present = j->cause_type; + switch (ieC3_1_1->cause.present) { + case E1AP_Cause_PR_radioNetwork: + ieC3_1_1->cause.choice.radioNetwork = j->cause; + break; + + case E1AP_Cause_PR_transport: + ieC3_1_1->cause.choice.transport = j->cause; + break; + + case E1AP_Cause_PR_protocol: + ieC3_1_1->cause.choice.protocol = j->cause; + break; + + case E1AP_Cause_PR_misc: + ieC3_1_1->cause.choice.misc = j->cause; + break; + + default: + LOG_E(E1AP, "DRB setup failure cause out of expected range\n"); + break; + } + } + } + } +} + +void e1apCUUP_send_BEARER_CONTEXT_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, e1ap_bearer_setup_resp_t *const resp) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_BEARER_CONTEXT_SETUP_RESPONSE(resp, &pdu); + e1ap_encode_send(UPtype, &inst->setupReq, &pdu, 0, __func__); } int e1apCUUP_send_BEARER_CONTEXT_SETUP_FAILURE(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUUP_handle_BEARER_CONTEXT_SETUP_REQUEST(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +void extract_BEARER_CONTEXT_SETUP_REQUEST(const E1AP_E1AP_PDU_t *pdu, + e1ap_bearer_setup_req_t *bearerCxt) { + const E1AP_BearerContextSetupRequest_t *in = &pdu->choice.initiatingMessage->value.choice.BearerContextSetupRequest; + E1AP_BearerContextSetupRequestIEs_t *ie; + + LOG_I(E1AP, "Bearer context setup number of IEs %d\n", in->protocolIEs.list.count); + + for (int i=0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch(ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + bearerCxt->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_SecurityInformation: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_SecurityInformation); + bearerCxt->cipheringAlgorithm = ie->value.choice.SecurityInformation.securityAlgorithm.cipheringAlgorithm; + memcpy(bearerCxt->encryptionKey, + ie->value.choice.SecurityInformation.uPSecuritykey.encryptionKey.buf, + ie->value.choice.SecurityInformation.uPSecuritykey.encryptionKey.size); + if (ie->value.choice.SecurityInformation.securityAlgorithm.integrityProtectionAlgorithm) { + bearerCxt->integrityProtectionAlgorithm = *ie->value.choice.SecurityInformation.securityAlgorithm.integrityProtectionAlgorithm; + } + if (ie->value.choice.SecurityInformation.uPSecuritykey.integrityProtectionKey) { + memcpy(bearerCxt->integrityProtectionKey, + ie->value.choice.SecurityInformation.uPSecuritykey.integrityProtectionKey->buf, + ie->value.choice.SecurityInformation.uPSecuritykey.integrityProtectionKey->size); + } + break; + + case E1AP_ProtocolIE_ID_id_UEDLAggregateMaximumBitRate: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_BitRate); + asn_INTEGER2long(&ie->value.choice.BitRate, &bearerCxt->ueDlAggMaxBitRate); + break; + + case E1AP_ProtocolIE_ID_id_Serving_PLMN: + DevAssert(ie->criticality == E1AP_Criticality_ignore); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_PLMN_Identity); + PLMNID_TO_MCC_MNC(&ie->value.choice.PLMN_Identity, + bearerCxt->servingPLMNid.mcc, + bearerCxt->servingPLMNid.mnc, + bearerCxt->servingPLMNid.mnc_digit_length); + break; + + case E1AP_ProtocolIE_ID_id_ActivityNotificationLevel: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_ActivityNotificationLevel); + bearerCxt->activityNotificationLevel = ie->value.choice.ActivityNotificationLevel; + break; + + case E1AP_ProtocolIE_ID_id_System_BearerContextSetupRequest: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupRequestIEs__value_PR_System_BearerContextSetupRequest); + DevAssert(ie->value.choice.System_BearerContextSetupRequest.present == + E1AP_System_BearerContextSetupRequest_PR_nG_RAN_BearerContextSetupRequest); + DevAssert(ie->value.choice.System_BearerContextSetupRequest.choice.nG_RAN_BearerContextSetupRequest); + E1AP_ProtocolIE_Container_4932P19_t *msgNGRAN_list = (E1AP_ProtocolIE_Container_4932P19_t *) ie->value.choice.System_BearerContextSetupRequest.choice.nG_RAN_BearerContextSetupRequest; + E1AP_NG_RAN_BearerContextSetupRequest_t *msgNGRAN = msgNGRAN_list->list.array[0]; + DevAssert(msgNGRAN_list->list.count == 1); + DevAssert(msgNGRAN->id == E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Setup_List); + DevAssert(msgNGRAN->value.present == E1AP_NG_RAN_BearerContextSetupRequest__value_PR_PDU_Session_Resource_To_Setup_List); + + E1AP_PDU_Session_Resource_To_Setup_List_t *pdu2SetupList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Setup_List; + bearerCxt->numPDUSessions = pdu2SetupList->list.count; + for (int i=0; i < pdu2SetupList->list.count; i++) { + pdu_session_to_setup_t *pdu_session = bearerCxt->pduSession + i; + E1AP_PDU_Session_Resource_To_Setup_Item_t *pdu2Setup = pdu2SetupList->list.array[i]; + + pdu_session->sessionId = pdu2Setup->pDU_Session_ID; + pdu_session->sessionType = pdu2Setup->pDU_Session_Type; + + OCTET_STRING_TO_INT8(&pdu2Setup->sNSSAI.sST, pdu_session->sst); + + pdu_session->integrityProtectionIndication = pdu2Setup->securityIndication.integrityProtectionIndication; + pdu_session->confidentialityProtectionIndication = pdu2Setup->securityIndication.confidentialityProtectionIndication; + + if (pdu2Setup->nG_UL_UP_TNL_Information.choice.gTPTunnel) { // Optional IE + DevAssert(pdu2Setup->nG_UL_UP_TNL_Information.present == + E1AP_UP_TNL_Information_PR_gTPTunnel); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&pdu2Setup->nG_UL_UP_TNL_Information.choice.gTPTunnel->transportLayerAddress, + pdu_session->tlAddress); + OCTET_STRING_TO_INT32(&pdu2Setup->nG_UL_UP_TNL_Information.choice.gTPTunnel->gTP_TEID, pdu_session->teId); + } + + E1AP_DRB_To_Setup_List_NG_RAN_t *drb2SetupList = &pdu2Setup->dRB_To_Setup_List_NG_RAN; + pdu_session->numDRB2Setup = drb2SetupList->list.count; + for (int j=0; j < drb2SetupList->list.count; j++) { + DRB_nGRAN_to_setup_t *drb = pdu_session->DRBnGRanList + j; + E1AP_DRB_To_Setup_Item_NG_RAN_t *drb2Setup = drb2SetupList->list.array[j]; + + drb->id = drb2Setup->dRB_ID; + + drb->defaultDRB = drb2Setup->sDAP_Configuration.defaultDRB; + drb->sDAP_Header_UL = drb2Setup->sDAP_Configuration.sDAP_Header_UL; + drb->sDAP_Header_DL = drb2Setup->sDAP_Configuration.sDAP_Header_DL; + + drb->pDCP_SN_Size_UL = drb2Setup->pDCP_Configuration.pDCP_SN_Size_UL; + drb->pDCP_SN_Size_DL = drb2Setup->pDCP_Configuration.pDCP_SN_Size_DL; + + if (drb2Setup->pDCP_Configuration.discardTimer) { + drb->discardTimer = *drb2Setup->pDCP_Configuration.discardTimer; + } + + if (drb2Setup->pDCP_Configuration.t_ReorderingTimer) { + drb->reorderingTimer = drb2Setup->pDCP_Configuration.t_ReorderingTimer->t_Reordering; + } + + drb->rLC_Mode = drb2Setup->pDCP_Configuration.rLC_Mode; + + E1AP_Cell_Group_Information_t *cellGroupList = &drb2Setup->cell_Group_Information; + drb->numCellGroups = cellGroupList->list.count; + for (int k=0; k < cellGroupList->list.count; k++) { + E1AP_Cell_Group_Information_Item_t *cg2Setup = cellGroupList->list.array[k]; + + drb->cellGroupList[k].id = cg2Setup->cell_Group_ID; + } + + E1AP_QoS_Flow_QoS_Parameter_List_t *qos2SetupList = &drb2Setup->qos_flow_Information_To_Be_Setup; + drb->numQosFlow2Setup = qos2SetupList->list.count; + for (int k=0; k < qos2SetupList->list.count; k++) { + qos_flow_to_setup_t *qos = drb->qosFlows + k; + E1AP_QoS_Flow_QoS_Parameter_Item_t *qos2Setup = qos2SetupList->list.array[k]; + + qos->id = qos2Setup->qoS_Flow_Identifier; + + if (qos2Setup->qoSFlowLevelQoSParameters.qoS_Characteristics.present == + E1AP_QoS_Characteristics_PR_non_Dynamic_5QI) { + qos->fiveQI_type = non_dynamic; + qos->fiveQI = qos2Setup->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.non_Dynamic_5QI->fiveQI; + } else { + E1AP_Dynamic5QIDescriptor_t *dynamic5QI = qos2Setup->qoSFlowLevelQoSParameters.qoS_Characteristics.choice.dynamic_5QI; + qos->fiveQI_type = dynamic; + qos->qoSPriorityLevel = dynamic5QI->qoSPriorityLevel; + qos->packetDelayBudget = dynamic5QI->packetDelayBudget; + qos->packetError_scalar = dynamic5QI->packetErrorRate.pER_Scalar; + qos->packetError_exponent = dynamic5QI->packetErrorRate.pER_Exponent; + } + + qos->priorityLevel = qos2Setup->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.priorityLevel; + qos->pre_emptionCapability = qos2Setup->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionCapability; + qos->pre_emptionVulnerability = qos2Setup->qoSFlowLevelQoSParameters.nGRANallocationRetentionPriority.pre_emptionVulnerability; + } + } + } + break; + + default: + LOG_E(E1AP, "Handle for this IE is not implemented (or) invalid IE detected\n"); + break; + } + } + + } -int e1apCUCP_handle_BEARER_CONTEXT_SETUP_RESPONSE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +int e1apCUUP_handle_BEARER_CONTEXT_SETUP_REQUEST(e1ap_upcp_inst_t *e1_inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_initiatingMessage); + DevAssert(pdu->choice.initiatingMessage->procedureCode == E1AP_ProcedureCode_id_bearerContextSetup); + DevAssert(pdu->choice.initiatingMessage->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.initiatingMessage->value.present == E1AP_InitiatingMessage__value_PR_BearerContextSetupRequest); + + e1ap_bearer_setup_req_t bearerCxt = {0}; + extract_BEARER_CONTEXT_SETUP_REQUEST(pdu, &bearerCxt); + process_e1_bearer_context_setup_req(e1_inst->instance, &bearerCxt); + return 0; } -int e1apCUCP_handle_BEARER_CONTEXT_SETUP_FAILURE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { +void extract_BEARER_CONTEXT_SETUP_RESPONSE(const E1AP_E1AP_PDU_t *pdu, + e1ap_bearer_setup_resp_t *bearerCxt) { + const E1AP_BearerContextSetupResponse_t *in = &pdu->choice.successfulOutcome->value.choice.BearerContextSetupResponse; + E1AP_BearerContextSetupResponseIEs_t *ie; + + LOG_I(E1AP, "Bearer context setup response number of IEs %d\n", in->protocolIEs.list.count); + + for (int i=0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch(ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupResponseIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + bearerCxt->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupResponseIEs__value_PR_GNB_CU_UP_UE_E1AP_ID); + bearerCxt->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_System_BearerContextSetupResponse: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextSetupResponseIEs__value_PR_System_BearerContextSetupResponse); + DevAssert(ie->value.choice.System_BearerContextSetupResponse.present == + E1AP_System_BearerContextSetupResponse_PR_nG_RAN_BearerContextSetupResponse); + E1AP_ProtocolIE_Container_4932P22_t *msgNGRAN_list = (E1AP_ProtocolIE_Container_4932P22_t *) ie->value.choice.System_BearerContextSetupResponse.choice.nG_RAN_BearerContextSetupResponse; + DevAssert(msgNGRAN_list->list.count == 1); + E1AP_NG_RAN_BearerContextSetupResponse_t *msgNGRAN = msgNGRAN_list->list.array[0]; + DevAssert(msgNGRAN->id == E1AP_ProtocolIE_ID_id_PDU_Session_Resource_Setup_List); + DevAssert(msgNGRAN->criticality == E1AP_Criticality_reject); + DevAssert(msgNGRAN->value.present == E1AP_NG_RAN_BearerContextSetupResponse__value_PR_PDU_Session_Resource_Setup_List); + E1AP_PDU_Session_Resource_Setup_List_t *pduSetupList = &msgNGRAN->value.choice.PDU_Session_Resource_Setup_List; + bearerCxt->numPDUSessions = pduSetupList->list.count; + + for (int i=0; i < pduSetupList->list.count; i++) { + pdu_session_setup_t *pduSetup = bearerCxt->pduSession + i; + E1AP_PDU_Session_Resource_Setup_Item_t *pdu_session = pduSetupList->list.array[i]; + pduSetup->id = pdu_session->pDU_Session_ID; + + if (pdu_session->nG_DL_UP_TNL_Information.choice.gTPTunnel) { + DevAssert(pdu_session->nG_DL_UP_TNL_Information.present == E1AP_UP_TNL_Information_PR_gTPTunnel); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&pdu_session->nG_DL_UP_TNL_Information.choice.gTPTunnel->transportLayerAddress, + pduSetup->tlAddress); + OCTET_STRING_TO_INT32(&pdu_session->nG_DL_UP_TNL_Information.choice.gTPTunnel->gTP_TEID, + pduSetup->teId); + } + + pduSetup->numDRBSetup = pdu_session->dRB_Setup_List_NG_RAN.list.count; + for (int j=0; j < pdu_session->dRB_Setup_List_NG_RAN.list.count; j++) { + DRB_nGRAN_setup_t *drbSetup = pduSetup->DRBnGRanList + j; + E1AP_DRB_Setup_Item_NG_RAN_t *drb = pdu_session->dRB_Setup_List_NG_RAN.list.array[j]; + + drbSetup->id = drb->dRB_ID; + + drbSetup->numUpParam = drb->uL_UP_Transport_Parameters.list.count; + for (int k=0; k < drb->uL_UP_Transport_Parameters.list.count; k++) { + up_params_t *UL_UP_param = drbSetup->UpParamList + k; + E1AP_UP_Parameters_Item_t *in_UL_UP_param = drb->uL_UP_Transport_Parameters.list.array[k]; + + DevAssert(in_UL_UP_param->uP_TNL_Information.present == E1AP_UP_TNL_Information_PR_gTPTunnel); + E1AP_GTPTunnel_t *gTPTunnel = in_UL_UP_param->uP_TNL_Information.choice.gTPTunnel; + if (gTPTunnel) { + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&gTPTunnel->transportLayerAddress, + UL_UP_param->tlAddress); + OCTET_STRING_TO_INT32(&gTPTunnel->gTP_TEID, + UL_UP_param->teId); + } else { + AssertFatal(false, "gTPTunnel information in required\n"); + } + } + } + } + break; + + // TODO: remaining IE handlers + + default: + LOG_E(E1AP, "Handle for this IE is not implemented (or) invalid IE detected\n"); + break; + } + } +} + +int e1apCUCP_handle_BEARER_CONTEXT_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_successfulOutcome); + DevAssert(pdu->choice.successfulOutcome->procedureCode == E1AP_ProcedureCode_id_bearerContextSetup); + DevAssert(pdu->choice.successfulOutcome->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.successfulOutcome->value.present == E1AP_SuccessfulOutcome__value_PR_BearerContextSetupResponse); + + MessageDef *msg = itti_alloc_new_message(TASK_CUCP_E1, 0, E1AP_BEARER_CONTEXT_SETUP_RESP); + e1ap_bearer_setup_resp_t *bearerCxt = &E1AP_BEARER_CONTEXT_SETUP_RESP(msg); + extract_BEARER_CONTEXT_SETUP_RESPONSE(pdu, bearerCxt); + // Fixme: instance is the NGAP instance, no good way to set it here + instance_t instance = 0; + itti_send_msg_to_task(TASK_RRC_GNB, instance, msg); + + return 0; +} + +int e1apCUCP_handle_BEARER_CONTEXT_SETUP_FAILURE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu) +{ AssertFatal(false,"Not implemented yet\n"); + return -1; } /* BEARER CONTEXT MODIFICATION REQUEST */ -int e1apCUCP_send_BEARER_CONTEXT_MODIFICATION_REQUEST(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +static int fill_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_setup_req_t *setupReq, e1ap_bearer_setup_req_t *const bearerCxt, E1AP_E1AP_PDU_t *pdu) +{ + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextModification; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest; + E1AP_BearerContextModificationRequest_t *out = &pdu->choice.initiatingMessage->value.choice.BearerContextModificationRequest; + /* mandatory */ + /* c1. gNB-CU-CP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ieC1->value.choice.GNB_CU_CP_UE_E1AP_ID = bearerCxt->gNB_cu_cp_ue_id; + /* mandatory */ + /* c2. gNB-CU-UP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; + ieC2->value.choice.GNB_CU_UP_UE_E1AP_ID = bearerCxt->gNB_cu_cp_ue_id; + /* optional */ + /* */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextModificationRequestIEs_t, ieC3); + ieC3->id = E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest; + ieC3->criticality = E1AP_Criticality_reject; + ieC3->value.present = E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest; + ieC3->value.choice.System_BearerContextModificationRequest.present = E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest; + E1AP_ProtocolIE_Container_4932P26_t *msgNGRAN_list = calloc(1, sizeof(E1AP_ProtocolIE_Container_4932P26_t)); + ieC3->value.choice.System_BearerContextModificationRequest.choice.nG_RAN_BearerContextModificationRequest = (struct E1AP_ProtocolIE_Container *) msgNGRAN_list; + asn1cSequenceAdd(msgNGRAN_list->list, E1AP_NG_RAN_BearerContextModificationRequest_t, msgNGRAN); + msgNGRAN->id = E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List; + msgNGRAN->criticality = E1AP_Criticality_reject; + msgNGRAN->value.present = E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List; + E1AP_PDU_Session_Resource_To_Modify_List_t *pdu2Setup = &msgNGRAN->value.choice.PDU_Session_Resource_To_Modify_List; + for(pdu_session_to_setup_t *i=bearerCxt->pduSessionMod; i < bearerCxt->pduSessionMod+bearerCxt->numPDUSessionsMod; i++) { + asn1cSequenceAdd(pdu2Setup->list, E1AP_PDU_Session_Resource_To_Modify_Item_t, ieC3_1); + ieC3_1->pDU_Session_ID = i->sessionId; + + for (DRB_nGRAN_to_setup_t *j=i->DRBnGRanModList; j < i->DRBnGRanModList+i->numDRB2Modify; j++) { + asn1cCalloc(ieC3_1->dRB_To_Modify_List_NG_RAN, drb2Mod_List); + asn1cSequenceAdd(drb2Mod_List->list, E1AP_DRB_To_Modify_Item_NG_RAN_t, drb2Mod); + drb2Mod->dRB_ID = j->id; + + if (j->numDlUpParam > 0) { + asn1cCalloc(drb2Mod->dL_UP_Parameters, DL_UP_Param_List); + for (up_params_t *k=j->DlUpParamList; k < j->DlUpParamList+j->numDlUpParam; k++) { + asn1cSequenceAdd(DL_UP_Param_List->list, E1AP_UP_Parameters_Item_t, DL_UP_Param); + DL_UP_Param->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel; + asn1cCalloc(DL_UP_Param->uP_TNL_Information.choice.gTPTunnel, gTPTunnel); + TRANSPORT_LAYER_ADDRESS_IPv4_TO_BIT_STRING(k->tlAddress, &gTPTunnel->transportLayerAddress); + INT32_TO_OCTET_STRING(k->teId, &gTPTunnel->gTP_TEID); + + DL_UP_Param->cell_Group_ID = k->cell_group_id; + } + } + } + } + return 0; +} + +static void e1apCUCP_send_BEARER_CONTEXT_MODIFICATION_REQUEST(instance_t inst, e1ap_bearer_setup_req_t *const bearerCxt) +{ + AssertFatal(getCxtE1(inst), ""); + e1ap_setup_req_t *setupReq = &getCxtE1(inst)->setupReq; + E1AP_E1AP_PDU_t pdu = {0}; + fill_BEARER_CONTEXT_MODIFICATION_REQUEST(setupReq, bearerCxt, &pdu); + e1ap_encode_send(CPtype, setupReq, &pdu, 0, __func__); } int e1apCUUP_send_BEARER_CONTEXT_MODIFICATION_RESPONSE(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUUP_send_BEARER_CONTEXT_MODIFICATION_FAILURE(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_REQUEST(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +void extract_BEARER_CONTEXT_MODIFICATION_REQUEST(const E1AP_E1AP_PDU_t *pdu, + e1ap_bearer_setup_req_t *bearerCxt) { + const E1AP_BearerContextModificationRequest_t *in = &pdu->choice.initiatingMessage->value.choice.BearerContextModificationRequest; + E1AP_BearerContextModificationRequestIEs_t *ie; + + LOG_I(E1AP, "Bearer context setup number of IEs %d\n", in->protocolIEs.list.count); + + for (int i=0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch(ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + bearerCxt->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_GNB_CU_UP_UE_E1AP_ID); + bearerCxt->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_System_BearerContextModificationRequest: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextModificationRequestIEs__value_PR_System_BearerContextModificationRequest); + DevAssert(ie->value.choice.System_BearerContextModificationRequest.present == + E1AP_System_BearerContextModificationRequest_PR_nG_RAN_BearerContextModificationRequest); + DevAssert(ie->value.choice.System_BearerContextModificationRequest.choice.nG_RAN_BearerContextModificationRequest != NULL); + E1AP_ProtocolIE_Container_4932P26_t *msgNGRAN_list = (E1AP_ProtocolIE_Container_4932P26_t *) ie->value.choice.System_BearerContextModificationRequest.choice.nG_RAN_BearerContextModificationRequest; + E1AP_NG_RAN_BearerContextModificationRequest_t *msgNGRAN = msgNGRAN_list->list.array[0]; + DevAssert(msgNGRAN_list->list.count == 1); + DevAssert(msgNGRAN->id == E1AP_ProtocolIE_ID_id_PDU_Session_Resource_To_Modify_List); + DevAssert(msgNGRAN->value.present = + E1AP_NG_RAN_BearerContextModificationRequest__value_PR_PDU_Session_Resource_To_Modify_List); + + E1AP_PDU_Session_Resource_To_Modify_List_t *pdu2ModList = &msgNGRAN->value.choice.PDU_Session_Resource_To_Modify_List; + bearerCxt->numPDUSessionsMod = pdu2ModList->list.count; + for (int i=0; i < pdu2ModList->list.count; i++) { + pdu_session_to_setup_t *pdu_session = bearerCxt->pduSessionMod + i; + E1AP_PDU_Session_Resource_To_Modify_Item_t *pdu2Mod = pdu2ModList->list.array[i]; + + pdu_session->sessionId = pdu2Mod->pDU_Session_ID; + + E1AP_DRB_To_Modify_List_NG_RAN_t *drb2ModList = pdu2Mod->dRB_To_Modify_List_NG_RAN; + pdu_session->numDRB2Modify = drb2ModList->list.count; + for (int j=0; j < drb2ModList->list.count; j++) { + DRB_nGRAN_to_setup_t *drb = pdu_session->DRBnGRanModList + j; + E1AP_DRB_To_Modify_Item_NG_RAN_t *drb2Mod = drb2ModList->list.array[j]; + + drb->id = drb2Mod->dRB_ID; + + E1AP_UP_Parameters_t *dl_up_paramList = drb2Mod->dL_UP_Parameters; + drb->numDlUpParam = dl_up_paramList->list.count; + for (int k=0; k < dl_up_paramList->list.count; k++) { + up_params_t *dl_up_param = drb->DlUpParamList + k; + E1AP_UP_Parameters_Item_t *dl_up_param_in = dl_up_paramList->list.array[k]; + if (dl_up_param_in->uP_TNL_Information.choice.gTPTunnel) { // Optional IE + DevAssert(dl_up_param_in->uP_TNL_Information.present = E1AP_UP_TNL_Information_PR_gTPTunnel); + BIT_STRING_TO_TRANSPORT_LAYER_ADDRESS_IPv4(&dl_up_param_in->uP_TNL_Information.choice.gTPTunnel->transportLayerAddress, + dl_up_param->tlAddress); + OCTET_STRING_TO_INT32(&dl_up_param_in->uP_TNL_Information.choice.gTPTunnel->gTP_TEID, dl_up_param->teId); + } else { + AssertFatal(false, "gTPTunnel IE is missing. It is mandatory at this point\n"); + } + dl_up_param->cell_group_id = dl_up_param_in->cell_Group_ID; + } + } + } + break; + + default: + LOG_E(E1AP, "Handle for this IE is not implemented (or) invalid IE detected\n"); + break; + } + } +} + +int e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_upcp_inst_t *e1_inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_initiatingMessage); + DevAssert(pdu->choice.initiatingMessage->procedureCode == E1AP_ProcedureCode_id_bearerContextModification); + DevAssert(pdu->choice.initiatingMessage->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.initiatingMessage->value.present == E1AP_InitiatingMessage__value_PR_BearerContextModificationRequest); + + e1ap_bearer_setup_req_t bearerCxt = {0}; + extract_BEARER_CONTEXT_MODIFICATION_REQUEST(pdu, &bearerCxt); + CUUP_process_bearer_context_mod_req(e1_inst->instance, &bearerCxt); + return 0; } int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE(instance_t instance, @@ -277,6 +1251,7 @@ int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_FAILURE(instance_t instance, @@ -284,14 +1259,17 @@ int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_FAILURE(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUUP_send_BEARER_CONTEXT_MODIFICATION_REQUIRED(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_send_BEARER_CONTEXT_MODIFICATION_CONFIRM(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_REQUIRED(instance_t instance, @@ -299,6 +1277,7 @@ int e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_REQUIRED(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_CONFIRM(instance_t instance, @@ -306,35 +1285,178 @@ int e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_CONFIRM(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } /* BEARER CONTEXT RELEASE */ -int e1apCUCP_send_BEARER_CONTEXT_RELEASE_COMMAND(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +static int fill_BEARER_CONTEXT_RELEASE_COMMAND(e1ap_setup_req_t *setupReq, e1ap_bearer_release_cmd_t *const cmd, E1AP_E1AP_PDU_t *pdu) +{ + pdu->present = E1AP_E1AP_PDU_PR_initiatingMessage; + asn1cCalloc(pdu->choice.initiatingMessage, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextRelease; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_InitiatingMessage__value_PR_BearerContextReleaseCommand; + E1AP_BearerContextReleaseCommand_t *out = &pdu->choice.initiatingMessage->value.choice.BearerContextReleaseCommand; + /* mandatory */ + /* c1. gNB-CU-CP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextReleaseCommandIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_BearerContextReleaseCommandIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ieC1->value.choice.GNB_CU_CP_UE_E1AP_ID = cmd->gNB_cu_cp_ue_id; + /* mandatory */ + /* c2. gNB-CU-UP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextReleaseCommandIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_BearerContextReleaseCommandIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; + ieC2->value.choice.GNB_CU_UP_UE_E1AP_ID = cmd->gNB_cu_cp_ue_id; + + return 0; } -int e1apCUUP_send_BEARER_CONTEXT_RELEASE_COMPLETE(instance_t instance) { - AssertFatal(false,"Not implemented yet\n"); +int e1apCUCP_send_BEARER_CONTEXT_RELEASE_COMMAND(e1ap_setup_req_t *setupReq, e1ap_bearer_release_cmd_t *const cmd) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_BEARER_CONTEXT_RELEASE_COMMAND(setupReq, cmd, &pdu); + return e1ap_encode_send(CPtype, setupReq, &pdu, 0, __func__); +} + +int fill_BEARER_CONTEXT_RELEASE_COMPLETE(e1ap_setup_req_t *setupReq, e1ap_bearer_release_cmd_t *const cmd, E1AP_E1AP_PDU_t *pdu) +{ + pdu->present = E1AP_E1AP_PDU_PR_successfulOutcome; + asn1cCalloc(pdu->choice.successfulOutcome, msg); + msg->procedureCode = E1AP_ProcedureCode_id_bearerContextRelease; + msg->criticality = E1AP_Criticality_reject; + msg->value.present = E1AP_SuccessfulOutcome__value_PR_BearerContextReleaseComplete; + E1AP_BearerContextReleaseComplete_t *out = &pdu->choice.successfulOutcome->value.choice.BearerContextReleaseComplete; + /* mandatory */ + /* c1. gNB-CU-CP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextReleaseCompleteIEs_t, ieC1); + ieC1->id = E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID; + ieC1->criticality = E1AP_Criticality_reject; + ieC1->value.present = E1AP_BearerContextReleaseCompleteIEs__value_PR_GNB_CU_CP_UE_E1AP_ID; + ieC1->value.choice.GNB_CU_CP_UE_E1AP_ID = cmd->gNB_cu_cp_ue_id; + /* mandatory */ + /* c2. gNB-CU-UP UE E1AP ID */ + asn1cSequenceAdd(out->protocolIEs.list, E1AP_BearerContextReleaseCompleteIEs_t, ieC2); + ieC2->id = E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID; + ieC2->criticality = E1AP_Criticality_reject; + ieC2->value.present = E1AP_BearerContextReleaseCompleteIEs__value_PR_GNB_CU_UP_UE_E1AP_ID; + ieC2->value.choice.GNB_CU_UP_UE_E1AP_ID = cmd->gNB_cu_cp_ue_id; + + return 0; +} + +int e1apCUUP_send_BEARER_CONTEXT_RELEASE_COMPLETE(e1ap_upcp_inst_t *inst, e1ap_bearer_release_cmd_t *const cmd) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_BEARER_CONTEXT_RELEASE_COMPLETE(&inst->setupReq, cmd, &pdu); + return e1ap_encode_send(CPtype, &inst->setupReq, &pdu, 0, __func__); } int e1apCUUP_send_BEARER_CONTEXT_RELEASE_REQUEST(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } -int e1apCUUP_handle_BEARER_CONTEXT_RELEASE_COMMAND(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +void extract_BEARER_CONTEXT_RELEASE_COMMAND(const E1AP_E1AP_PDU_t *pdu, + e1ap_bearer_release_cmd_t *bearerCxt) { + const E1AP_BearerContextReleaseCommand_t *in = &pdu->choice.initiatingMessage->value.choice.BearerContextReleaseCommand; + E1AP_BearerContextReleaseCommandIEs_t *ie; + + LOG_I(E1AP, "Bearer context setup number of IEs %d\n", in->protocolIEs.list.count); + + for (int i=0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch(ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextReleaseCommandIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + bearerCxt->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextReleaseCommandIEs__value_PR_GNB_CU_UP_UE_E1AP_ID); + bearerCxt->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_Cause: + DevAssert(ie->criticality == E1AP_Criticality_ignore); + DevAssert(ie->value.present == E1AP_BearerContextReleaseCommandIEs__value_PR_Cause); + bearerCxt->cause_type = ie->value.choice.Cause.present; + if ((ie->value.choice.Cause.present != E1AP_Cause_PR_NOTHING) && + (ie->value.choice.Cause.present != E1AP_Cause_PR_choice_extension)) + bearerCxt->cause = ie->value.choice.Cause.choice.radioNetwork; + + + default: + LOG_E(E1AP, "Handle for this IE is not implemented (or) invalid IE detected\n"); + break; + } + } } -int e1apCUCP_handle_BEARER_CONTEXT_RELEASE_COMPLETE(instance_t instance, - uint32_t assoc_id, - uint32_t stream, - E1AP_E1AP_PDU_t *pdu) { - AssertFatal(false,"Not implemented yet\n"); +int e1apCUUP_handle_BEARER_CONTEXT_RELEASE_COMMAND(e1ap_upcp_inst_t *e1_inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_initiatingMessage); + DevAssert(pdu->choice.initiatingMessage->procedureCode == E1AP_ProcedureCode_id_bearerContextRelease); + DevAssert(pdu->choice.initiatingMessage->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.initiatingMessage->value.present == E1AP_InitiatingMessage__value_PR_BearerContextReleaseCommand); + + e1ap_bearer_release_cmd_t bearerCxt = {0}; + extract_BEARER_CONTEXT_RELEASE_COMMAND(pdu, &bearerCxt); + CUUP_process_bearer_release_command(e1_inst->instance, &bearerCxt); + return 0; +} + +void extract_BEARER_CONTEXT_RELEASE_COMPLETE(const E1AP_E1AP_PDU_t *pdu, + e1ap_bearer_release_cmd_t *bearerCxt) { + const E1AP_BearerContextReleaseComplete_t *in = &pdu->choice.successfulOutcome->value.choice.BearerContextReleaseComplete; + E1AP_BearerContextReleaseCompleteIEs_t *ie; + + LOG_I(E1AP, "Bearer context setup number of IEs %d\n", in->protocolIEs.list.count); + + for (int i=0; i < in->protocolIEs.list.count; i++) { + ie = in->protocolIEs.list.array[i]; + + switch(ie->id) { + case E1AP_ProtocolIE_ID_id_gNB_CU_CP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextReleaseCompleteIEs__value_PR_GNB_CU_CP_UE_E1AP_ID); + bearerCxt->gNB_cu_cp_ue_id = ie->value.choice.GNB_CU_CP_UE_E1AP_ID; + break; + + case E1AP_ProtocolIE_ID_id_gNB_CU_UP_UE_E1AP_ID: + DevAssert(ie->criticality == E1AP_Criticality_reject); + DevAssert(ie->value.present == E1AP_BearerContextReleaseCompleteIEs__value_PR_GNB_CU_UP_UE_E1AP_ID); + bearerCxt->gNB_cu_up_ue_id = ie->value.choice.GNB_CU_UP_UE_E1AP_ID; + break; + + default: + LOG_E(E1AP, "Handle for this IE is not implemented (or) invalid IE detected\n"); + break; + } + } +} + +int e1apCUCP_handle_BEARER_CONTEXT_RELEASE_COMPLETE(e1ap_upcp_inst_t *e1_inst, const E1AP_E1AP_PDU_t *pdu) +{ + DevAssert(pdu != NULL); + DevAssert(pdu->present == E1AP_E1AP_PDU_PR_successfulOutcome); + DevAssert(pdu->choice.successfulOutcome->procedureCode == E1AP_ProcedureCode_id_bearerContextRelease); + DevAssert(pdu->choice.successfulOutcome->criticality == E1AP_Criticality_reject); + DevAssert(pdu->choice.successfulOutcome->value.present == E1AP_SuccessfulOutcome__value_PR_BearerContextReleaseComplete); + + e1ap_bearer_release_cmd_t bearerCxt = {0}; + extract_BEARER_CONTEXT_RELEASE_COMPLETE(pdu, &bearerCxt); + //TODO: CUCP_process_bearer_release_complete(&beareCxt, instance); + return 0; } int e1apCUCP_handle_BEARER_CONTEXT_RELEASE_REQUEST(instance_t instance, @@ -342,6 +1464,7 @@ int e1apCUCP_handle_BEARER_CONTEXT_RELEASE_REQUEST(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } /* @@ -350,6 +1473,7 @@ BEARER CONTEXT INACTIVITY NOTIFICATION int e1apCUUP_send_BEARER_CONTEXT_INACTIVITY_NOTIFICATION(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_handle_BEARER_CONTEXT_INACTIVITY_NOTIFICATION(instance_t instance, @@ -357,6 +1481,7 @@ int e1apCUCP_handle_BEARER_CONTEXT_INACTIVITY_NOTIFICATION(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } /* DL DATA @@ -364,10 +1489,12 @@ int e1apCUCP_handle_BEARER_CONTEXT_INACTIVITY_NOTIFICATION(instance_t instance, int e1apCUUP_send_DL_DATA_NOTIFICATION(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUUP_send_DATA_USAGE_REPORT(instance_t instance) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_handle_DL_DATA_NOTIFICATION(instance_t instance, @@ -375,6 +1502,7 @@ int e1apCUCP_handle_DL_DATA_NOTIFICATION(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } int e1apCUCP_handle_send_DATA_USAGE_REPORT(instance_t instance, @@ -382,4 +1510,236 @@ int e1apCUCP_handle_send_DATA_USAGE_REPORT(instance_t instance, uint32_t stream, E1AP_E1AP_PDU_t *pdu) { AssertFatal(false,"Not implemented yet\n"); + return -1; } + +static instance_t cuup_task_create_gtpu_instance_to_du(eth_params_t *IPaddrs) { + openAddr_t tmp= {0}; + strncpy(tmp.originHost, IPaddrs->my_addr, sizeof(tmp.originHost)-1); + sprintf(tmp.originService, "%d", IPaddrs->my_portd); + return gtpv1Init(tmp); +} + +static void e1_task_send_sctp_association_req(long task_id, instance_t instance, e1ap_setup_req_t *e1ap_setup_req) +{ + DevAssert(e1ap_setup_req != NULL); + getCxtE1(instance)->sockState = SCTP_STATE_CLOSED; + MessageDef *message_p = itti_alloc_new_message(task_id, 0, SCTP_NEW_ASSOCIATION_REQ); + sctp_new_association_req_t *sctp_new_req = &message_p->ittiMsg.sctp_new_association_req; + sctp_new_req->ulp_cnx_id = instance; + sctp_new_req->port = E1AP_PORT_NUMBER; + sctp_new_req->ppid = E1AP_SCTP_PPID; + sctp_new_req->in_streams = e1ap_setup_req->sctp_in_streams; + sctp_new_req->out_streams = e1ap_setup_req->sctp_out_streams; + // remote + sctp_new_req->remote_address = e1ap_setup_req->CUCP_e1_ip_address; + // local + sctp_new_req->local_address = e1ap_setup_req->CUUP_e1_ip_address; + itti_send_msg_to_task(TASK_SCTP, instance, message_p); +} + +static void e1apCUUP_send_SETUP_REQUEST(e1ap_setup_req_t *setup) +{ + E1AP_E1AP_PDU_t pdu = {0}; + fill_SETUP_REQUEST(setup, &pdu); + e1ap_encode_send(UPtype, setup, &pdu, 0, __func__); +} + +static void e1_task_handle_sctp_association_resp(E1_t type, instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp) +{ + DevAssert(sctp_new_association_resp != NULL); + getCxtE1(instance)->sockState = sctp_new_association_resp->sctp_state; + if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) { + LOG_W(E1AP, "Received unsuccessful result for SCTP association (%u), instance %ld, cnx_id %u\n", + sctp_new_association_resp->sctp_state, + instance, + sctp_new_association_resp->ulp_cnx_id); + long timer_id; // if we want to cancel timer + timer_setup(1, 0, TASK_CUUP_E1, 0, TIMER_ONE_SHOT, NULL, &timer_id); + return; + } + + if (type == UPtype) { + e1ap_setup_req_t *e1ap_cuup_setup_req = &getCxtE1(instance)->setupReq; + e1ap_cuup_setup_req->assoc_id = sctp_new_association_resp->assoc_id; + e1ap_cuup_setup_req->sctp_in_streams = sctp_new_association_resp->in_streams; + e1ap_cuup_setup_req->sctp_out_streams = sctp_new_association_resp->out_streams; + e1ap_cuup_setup_req->default_sctp_stream_id = 0; + + eth_params_t IPaddr; + IPaddr.my_addr = e1ap_cuup_setup_req->localAddressF1U; + IPaddr.my_portd = e1ap_cuup_setup_req->localPortF1U; + if (getCxtE1(instance)->gtpInstF1U < 0) + getCxtE1(instance)->gtpInstF1U = cuup_task_create_gtpu_instance_to_du(&IPaddr); + if (getCxtE1(instance)->gtpInstF1U < 0) + LOG_E(E1AP, "Failed to create CUUP F1-U UDP listener"); + extern instance_t CUuniqInstance; + CUuniqInstance = getCxtE1(instance)->gtpInstF1U; + cuup_init_n3(instance); + e1apCUUP_send_SETUP_REQUEST(&getCxtE1(instance)->setupReq); + } +} + +void cuup_init_n3(instance_t instance) +{ + if (getCxtE1(instance)->gtpInstN3 < 0) { + e1ap_setup_req_t *setup = &getCxtE1(instance)->setupReq; + openAddr_t tmp = {0}; + strcpy(tmp.originHost, setup->localAddressN3); + sprintf(tmp.originService, "%d", setup->localPortN3); + sprintf(tmp.destinationService, "%d", setup->remotePortN3); + LOG_I(GTPU, "Configuring GTPu address : %s, port : %s\n", tmp.originHost, tmp.originService); + // Fixme: fully inconsistent instances management + // dirty global var is a bad fix + extern instance_t legacyInstanceMapping; + legacyInstanceMapping = getCxtE1(instance)->gtpInstN3 = gtpv1Init(tmp); + } + if (getCxtE1(instance)->gtpInstN3 < 0) + LOG_E(E1AP, "Failed to create CUUP N3 UDP listener"); + extern instance_t *N3GTPUInst; + N3GTPUInst = &getCxtE1(instance)->gtpInstN3; +} + +void cucp_task_send_sctp_init_req(instance_t instance, char *my_addr) { + LOG_I(E1AP, "E1AP_CUCP_SCTP_REQ(create socket)\n"); + MessageDef *message_p = NULL; + message_p = itti_alloc_new_message (TASK_CUCP_E1, 0, SCTP_INIT_MSG); + message_p->ittiMsg.sctp_init.port = E1AP_PORT_NUMBER; + message_p->ittiMsg.sctp_init.ppid = E1AP_SCTP_PPID; + message_p->ittiMsg.sctp_init.ipv4 = 1; + message_p->ittiMsg.sctp_init.ipv6 = 0; + message_p->ittiMsg.sctp_init.nb_ipv4_addr = 1; + message_p->ittiMsg.sctp_init.ipv4_address[0] = inet_addr(my_addr); + /* + * SR WARNING: ipv6 multi-homing fails sometimes for localhost. + * * * * Disable it for now. + */ + message_p->ittiMsg.sctp_init.nb_ipv6_addr = 0; + message_p->ittiMsg.sctp_init.ipv6_address[0] = "0:0:0:0:0:0:0:1"; + itti_send_msg_to_task(TASK_SCTP, instance, message_p); +} + +void e1_task_handle_sctp_association_ind(E1_t type, instance_t instance, sctp_new_association_ind_t *sctp_new_ind) +{ + if (getCxtE1(instance)) + LOG_W(E1AP, "CUCP incoming call, re-use older socket context, finish implementation required\n"); + else + createE1inst(type, instance, NULL); + getCxtE1(instance)->sockState = SCTP_STATE_ESTABLISHED; + getCxtE1(instance)->incoming_sock = true; + e1ap_setup_req_t *setup_req = &getCxtE1(instance)->setupReq; + setup_req->assoc_id = sctp_new_ind->assoc_id; + setup_req->sctp_in_streams = sctp_new_ind->in_streams; + setup_req->sctp_out_streams = sctp_new_ind->out_streams; + setup_req->default_sctp_stream_id = 0; +} + +void e1apHandleTimer(instance_t myInstance) +{ + LOG_W(E1AP, "Try to reconnect to CP\n"); + if (getCxtE1(myInstance)->sockState != SCTP_STATE_ESTABLISHED) + e1_task_send_sctp_association_req(TASK_CUUP_E1, myInstance, &getCxtE1(myInstance)->setupReq); +} + +void *E1AP_CUCP_task(void *arg) { + LOG_I(E1AP, "Starting E1AP at CU CP\n"); + MessageDef *msg = NULL; + e1ap_common_init(); + int result; + + while (1) { + itti_receive_msg(TASK_CUCP_E1, &msg); + instance_t myInstance=ITTI_MSG_DESTINATION_INSTANCE(msg); + const int msgType = ITTI_MSG_ID(msg); + LOG_I(E1AP, "CUCP received %s for instance %ld\n", messages_info[msgType].name, myInstance); + + switch (ITTI_MSG_ID(msg)) { + case SCTP_NEW_ASSOCIATION_IND: + e1_task_handle_sctp_association_ind(CPtype, ITTI_MSG_ORIGIN_INSTANCE(msg), &msg->ittiMsg.sctp_new_association_ind); + break; + + case SCTP_NEW_ASSOCIATION_RESP: + e1_task_handle_sctp_association_resp(CPtype, ITTI_MSG_ORIGIN_INSTANCE(msg), &msg->ittiMsg.sctp_new_association_resp); + break; + + case E1AP_SETUP_REQ: { + e1ap_setup_req_t *req = &E1AP_SETUP_REQ(msg); + if (req->CUCP_e1_ip_address.ipv4 == 0) { + LOG_E(E1AP, "No IPv4 address configured\n"); + return NULL; + } + cucp_task_send_sctp_init_req(0, req->CUCP_e1_ip_address.ipv4_address); + } break; + + case SCTP_DATA_IND: + e1_task_handle_sctp_data_ind(myInstance, &msg->ittiMsg.sctp_data_ind); + break; + + case E1AP_SETUP_RESP: + e1ap_send_SETUP_RESPONSE(myInstance, &E1AP_SETUP_RESP(msg)); + break; + + case E1AP_BEARER_CONTEXT_SETUP_REQ: + e1apCUCP_send_BEARER_CONTEXT_SETUP_REQUEST(myInstance, &E1AP_BEARER_CONTEXT_SETUP_REQ(msg)); + break; + + case E1AP_BEARER_CONTEXT_MODIFICATION_REQ: + e1apCUCP_send_BEARER_CONTEXT_MODIFICATION_REQUEST(myInstance, &E1AP_BEARER_CONTEXT_SETUP_REQ(msg)); + break; + + default: + LOG_E(E1AP, "Unknown message received in TASK_CUCP_E1\n"); + break; + } + + result = itti_free(ITTI_MSG_ORIGIN_ID(msg), msg); + AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d) in E1AP_CUCP_task!\n", result); + msg = NULL; + + } +} + +void *E1AP_CUUP_task(void *arg) { + LOG_I(E1AP, "Starting E1AP at CU UP\n"); + e1ap_common_init(); + int result; + + // SCTP + while (1) { + MessageDef *msg = NULL; + itti_receive_msg(TASK_CUUP_E1, &msg); + const instance_t myInstance = ITTI_MSG_DESTINATION_INSTANCE(msg); + const int msgType = ITTI_MSG_ID(msg); + LOG_I(E1AP, "CUUP received %s for instance %ld\n", messages_info[msgType].name, myInstance); + switch (msgType) { + case E1AP_SETUP_REQ: { + e1ap_setup_req_t *msgSetup = &E1AP_SETUP_REQ(msg); + createE1inst(UPtype, myInstance, msgSetup); + + e1_task_send_sctp_association_req(TASK_CUUP_E1, myInstance, msgSetup); + } break; + + case SCTP_NEW_ASSOCIATION_RESP: + e1_task_handle_sctp_association_resp(UPtype, myInstance, &msg->ittiMsg.sctp_new_association_resp); + break; + + case SCTP_DATA_IND: + e1_task_handle_sctp_data_ind(myInstance, &msg->ittiMsg.sctp_data_ind); + break; + + case TIMER_HAS_EXPIRED: + e1apHandleTimer(myInstance); + break; + + default: + LOG_E(E1AP, "Unknown message received in TASK_CUUP_E1\n"); + break; + } + + result = itti_free(ITTI_MSG_ORIGIN_ID(msg), msg); + AssertFatal(result == EXIT_SUCCESS, "Failed to free memory (%d) in E1AP_CUUP_task!\n", result); + msg = NULL; + + } +} + diff --git a/openair2/E1AP/e1ap.h b/openair2/E1AP/e1ap.h index ef22152648..af1dcae363 100644 --- a/openair2/E1AP/e1ap.h +++ b/openair2/E1AP/e1ap.h @@ -24,11 +24,33 @@ #ifndef __E1AP_H_ #define __E1AP_H_ -#include -#include "common/openairinterface5g_limits.h" -#include +#include "openair2/COMMON/e1ap_messages_types.h" +#include "e1ap_asnc.h" +#include "openair2/E1AP/e1ap_common.h" -#include -#include -#include +int e1apCUCP_handle_SETUP_REQUEST(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUUP_handle_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUUP_handle_SETUP_FAILURE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUUP_handle_BEARER_CONTEXT_SETUP_REQUEST(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUCP_handle_BEARER_CONTEXT_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUCP_handle_BEARER_CONTEXT_SETUP_FAILURE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUUP_handle_BEARER_CONTEXT_MODIFICATION_REQUEST(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +void e1apCUUP_send_BEARER_CONTEXT_SETUP_RESPONSE(e1ap_upcp_inst_t *inst, e1ap_bearer_setup_resp_t *const resp); + +int e1apCUUP_handle_BEARER_CONTEXT_RELEASE_COMMAND(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUCP_handle_BEARER_CONTEXT_RELEASE_COMPLETE(e1ap_upcp_inst_t *inst, const E1AP_E1AP_PDU_t *pdu); + +int e1apCUUP_send_BEARER_CONTEXT_RELEASE_COMPLETE(e1ap_upcp_inst_t *inst, e1ap_bearer_release_cmd_t *const cmd); + +void *E1AP_CUUP_task(void *arg); + +void *E1AP_CUCP_task(void *arg); #endif diff --git a/openair2/E1AP/e1ap_api.c b/openair2/E1AP/e1ap_api.c new file mode 100644 index 0000000000..23a9fcd1ea --- /dev/null +++ b/openair2/E1AP/e1ap_api.c @@ -0,0 +1,195 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include "e1ap_api.h" +#include "UTIL/OSA/osa_defs.h" +#include "nr_pdcp/nr_pdcp_entity.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h" +#include "openair2/RRC/NR/cucp_cuup_if.h" +#include "openair2/RRC/LTE/MESSAGES/asn1_msg.h" +#include "openair3/ocp-gtpu/gtp_itf.h" +#include "e1ap_asnc.h" +#include "e1ap_common.h" +#include "e1ap.h" + +struct NR_DRB_ToAddMod; +static void fill_DRB_configList_e1(NR_DRB_ToAddModList_t *DRB_configList, pdu_session_to_setup_t *pdu) { + + for (int i=0; i < pdu->numDRB2Setup; i++) { + DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + i; + asn1cSequenceAdd(DRB_configList->list, struct NR_DRB_ToAddMod, ie); + ie->drb_Identity = drb->id; + ie->cnAssociation = CALLOC(1, sizeof(*ie->cnAssociation)); + ie->cnAssociation->present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config; + + // sdap_Config + asn1cCalloc(ie->cnAssociation->choice.sdap_Config, sdap_config); + sdap_config->pdu_Session = pdu->sessionId; + sdap_config->sdap_HeaderDL = drb->sDAP_Header_DL; + sdap_config->sdap_HeaderUL = drb->sDAP_Header_UL; + sdap_config->defaultDRB = drb->defaultDRB; + + asn1cCalloc(sdap_config->mappedQoS_FlowsToAdd, FlowsToAdd); + for (int j=0; j < drb->numQosFlow2Setup; j++) { + asn1cSequenceAdd(FlowsToAdd->list, NR_QFI_t, qfi); + *qfi = drb->qosFlows[j].fiveQI; + } + sdap_config->mappedQoS_FlowsToRelease = NULL; + + // pdcp_Config + ie->reestablishPDCP = NULL; + ie->recoverPDCP = NULL; + asn1cCalloc(ie->pdcp_Config, pdcp_config); + asn1cCalloc(pdcp_config->drb, drbCfg); + asn1cCallocOne(drbCfg->discardTimer, drb->discardTimer); + asn1cCallocOne(drbCfg->pdcp_SN_SizeUL, drb->pDCP_SN_Size_UL); + asn1cCallocOne(drbCfg->pdcp_SN_SizeDL, drb->pDCP_SN_Size_DL); + drbCfg->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed; + drbCfg->headerCompression.choice.notUsed = 0; + + drbCfg->integrityProtection = NULL; + drbCfg->statusReportRequired = NULL; + drbCfg->outOfOrderDelivery = NULL; + pdcp_config->moreThanOneRLC = NULL; + + pdcp_config->t_Reordering = calloc(1, sizeof(*pdcp_config->t_Reordering)); + *pdcp_config->t_Reordering = drb->reorderingTimer; + pdcp_config->ext1 = NULL; + + if (pdu->integrityProtectionIndication == 0 || // Required + pdu->integrityProtectionIndication == 1) { // Preferred + asn1cCallocOne(drbCfg->integrityProtection, NR_PDCP_Config__drb__integrityProtection_enabled); + } + + if (pdu->confidentialityProtectionIndication == 0 || // Required + pdu->confidentialityProtectionIndication == 1) { // Preferred + asn1cCalloc(pdcp_config->ext1, ext1); + asn1cCallocOne(ext1->cipheringDisabled, NR_PDCP_Config__ext1__cipheringDisabled_true); + } + } +} + +static int drb_config_N3gtpu_create(e1ap_bearer_setup_req_t * const req, + gtpv1u_gnb_create_tunnel_resp_t *create_tunnel_resp, + instance_t instance) { + + gtpv1u_gnb_create_tunnel_req_t create_tunnel_req={0}; + + NR_DRB_ToAddModList_t DRB_configList = {0}; + for (int i=0; i < req->numPDUSessions; i++) { + pdu_session_to_setup_t *const pdu = &req->pduSession[i]; + create_tunnel_req.pdusession_id[i] = pdu->sessionId; + create_tunnel_req.incoming_rb_id[i] = pdu->DRBnGRanList[0].id; // taking only the first DRB. TODO:change this + memcpy(&create_tunnel_req.dst_addr[i].buffer, + &pdu->tlAddress, + sizeof(uint8_t)*4); + create_tunnel_req.dst_addr[i].length = 32; // 8bits * 4bytes + create_tunnel_req.outgoing_teid[i] = pdu->teId; + fill_DRB_configList_e1(&DRB_configList, pdu); + } + create_tunnel_req.num_tunnels = req->numPDUSessions; + create_tunnel_req.ue_id = (req->gNB_cu_cp_ue_id & 0xFFFF); + + // Create N3 tunnel + int ret = gtpv1u_create_ngu_tunnel(instance, + &create_tunnel_req, + create_tunnel_resp); + if (ret != 0) { + LOG_E(NR_RRC,"rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ : gtpv1u_create_ngu_tunnel failed,start to release UE id %ld\n", + create_tunnel_req.ue_id); + return ret; + } + + // Configure DRBs + uint8_t *kUPenc = NULL; + uint8_t *kUPint = NULL; + + nr_derive_key(UP_ENC_ALG, req->cipheringAlgorithm, (uint8_t *)req->encryptionKey, &kUPenc); + + nr_derive_key(UP_INT_ALG, req->integrityProtectionAlgorithm, (uint8_t *)req->integrityProtectionKey, &kUPint); + + nr_pdcp_e1_add_drbs(true, // set this to notify PDCP that his not UE + create_tunnel_req.ue_id, + &DRB_configList, + (req->integrityProtectionAlgorithm << 4) | req->cipheringAlgorithm, + kUPenc, + kUPint); + return ret; +} + +void process_e1_bearer_context_setup_req(instance_t instance, e1ap_bearer_setup_req_t *const req) +{ + e1ap_upcp_inst_t *inst = getCxtE1(instance); + AssertFatal(inst, ""); + gtpv1u_gnb_create_tunnel_resp_t create_tunnel_resp_N3={0}; + + // GTP tunnel for UL + drb_config_N3gtpu_create(req, &create_tunnel_resp_N3, inst->gtpInstN3); + + MessageDef *msg = itti_alloc_new_message(TASK_CUCP_E1, 0, E1AP_BEARER_CONTEXT_SETUP_RESP); + e1ap_bearer_setup_resp_t *resp = &E1AP_BEARER_CONTEXT_SETUP_RESP(msg); + + in_addr_t my_addr; + if (inet_pton(AF_INET, inst->setupReq.localAddressF1U, &my_addr) != 1) + LOG_E(E1AP, "can't use the F1-U local interface: %s\n", inst->setupReq.localAddressF1U); + fill_e1ap_bearer_setup_resp(resp, req, inst->gtpInstF1U, req->gNB_cu_cp_ue_id, inst->setupReq.remotePortF1U, my_addr); + + resp->gNB_cu_cp_ue_id = req->gNB_cu_cp_ue_id; + resp->numPDUSessions = req->numPDUSessions; + for (int i=0; i < req->numPDUSessions; i++) { + pdu_session_setup_t *pduSetup = resp->pduSession + i; + pdu_session_to_setup_t *pdu2Setup = req->pduSession + i; + + pduSetup->id = pdu2Setup->sessionId; + if (inet_pton(AF_INET, inst->setupReq.localAddressN3, &pduSetup->tlAddress) != 1) + LOG_E(E1AP, "can't use the N3 local interface: %s\n", inst->setupReq.localAddressN3); + pduSetup->teId = create_tunnel_resp_N3.gnb_NGu_teid[i]; + pduSetup->numDRBSetup = pdu2Setup->numDRB2Setup; + + // At this point we don't have a way to know the DRBs that failed to setup + // We assume all DRBs to setup have are setup successfully so we always send successful outcome in response + // TODO: Modify nr_pdcp_add_drbs() to return DRB list that failed to setup to support E1AP + pduSetup->numDRBFailed = 0; + } + + e1apCUUP_send_BEARER_CONTEXT_SETUP_RESPONSE(inst, resp); +} + +void CUUP_process_bearer_context_mod_req(instance_t instance, e1ap_bearer_setup_req_t *const req) +{ + e1ap_upcp_inst_t *inst = getCxtE1(instance); + AssertFatal(inst, ""); + // assume we receive modification of F1-U but it is wrong, we can also get modification of N3 when HO will occur + CU_update_UP_DL_tunnel(req, inst->gtpInstF1U, req->gNB_cu_cp_ue_id); + // TODO: send bearer cxt mod response +} + +void CUUP_process_bearer_release_command(instance_t instance, e1ap_bearer_release_cmd_t *const cmd) +{ + e1ap_upcp_inst_t *inst = getCxtE1(instance); + AssertFatal(inst, ""); + newGtpuDeleteAllTunnels(inst->gtpInstN3, cmd->gNB_cu_up_ue_id); + newGtpuDeleteAllTunnels(inst->gtpInstF1U, cmd->gNB_cu_up_ue_id); + e1apCUUP_send_BEARER_CONTEXT_RELEASE_COMPLETE(inst, cmd); +} diff --git a/openair2/E1AP/e1ap_api.h b/openair2/E1AP/e1ap_api.h new file mode 100644 index 0000000000..1b9f219fcc --- /dev/null +++ b/openair2/E1AP/e1ap_api.h @@ -0,0 +1,35 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * 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 + */ + +#ifndef E1AP_API_H +#define E1AP_API_H + +#include "platform_types.h" +#include "openair2/COMMON/e1ap_messages_types.h" +#include "openair2/E1AP/e1ap_common.h" +void cuup_init_n3(instance_t instance); +void process_e1_bearer_context_setup_req(instance_t, e1ap_bearer_setup_req_t *const req); +void CUUP_process_bearer_context_mod_req(instance_t, e1ap_bearer_setup_req_t *const req); + +void CUUP_process_bearer_release_command(instance_t, e1ap_bearer_release_cmd_t *const cmd); +#endif diff --git a/openair2/E1AP/e1ap_asnc.h b/openair2/E1AP/e1ap_asnc.h new file mode 100644 index 0000000000..00dbcc13f3 --- /dev/null +++ b/openair2/E1AP/e1ap_asnc.h @@ -0,0 +1,65 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * 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 + */ + +#ifndef E1AP_ASNC_H +#define E1AP_ASNC_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/openair2/E1AP/e1ap_common.c b/openair2/E1AP/e1ap_common.c new file mode 100644 index 0000000000..2db1ecef65 --- /dev/null +++ b/openair2/E1AP/e1ap_common.c @@ -0,0 +1,235 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include "e1ap_common.h" +#include "e1ap_default_values.h" +#include "e1ap_asnc.h" +#include "common/openairinterface5g_limits.h" +#include "common/utils/ocp_itti/intertask_interface.h" + +static e1ap_upcp_inst_t *e1ap_inst[NUMBER_OF_gNB_MAX] = {0}; + +e1ap_upcp_inst_t *getCxtE1(instance_t instance) +{ + AssertFatal(instance < sizeofArray(e1ap_inst), "instance exceeds limit\n"); + return e1ap_inst[instance]; +} + +int e1ap_assoc_id(E1_t type, instance_t instance) { + AssertFatal(e1ap_inst[instance] != NULL, "Trying to access uninitiated instance of CUCP\n"); + return e1ap_inst[instance]->setupReq.assoc_id; +} + +void createE1inst(E1_t type, instance_t instance, e1ap_setup_req_t *req) { + AssertFatal(e1ap_inst[instance] == NULL, "Double call to E1 instance %d\n", (int)instance); + e1ap_inst[instance] = calloc(1, sizeof(e1ap_upcp_inst_t)); + e1ap_inst[instance]->type = type; + e1ap_inst[instance]->instance = instance; + if (req) + memcpy(&e1ap_inst[instance]->setupReq, req, sizeof(*req)); + e1ap_inst[instance]->gtpInstN3 = -1; + e1ap_inst[instance]->gtpInstF1U = -1; +} + +E1AP_TransactionID_t transacID[E1AP_MAX_NUM_TRANSAC_IDS] = {0}; + +void e1ap_common_init() { + srand(time(NULL)); +} + +bool check_transac_id(E1AP_TransactionID_t id, int *freeIdx) { + + bool isFreeIdxSet = false; + for (int i=0; i < E1AP_MAX_NUM_TRANSAC_IDS; i++) { + if (id == transacID[i]) + return false; + else if (!isFreeIdxSet && (transacID[i] == 0)) { + *freeIdx = i; + isFreeIdxSet = true; + } + } + + return true; +} + +E1AP_TransactionID_t E1AP_get_next_transaction_identifier() { + E1AP_TransactionID_t genTransacId; + bool isTransacIdValid = false; + int freeIdx; + + while (!isTransacIdValid) { + genTransacId = rand() & 255; + isTransacIdValid = check_transac_id(genTransacId, &freeIdx); + } + + AssertFatal(freeIdx < E1AP_MAX_NUM_TRANSAC_IDS, "Free Index exceeds array length\n"); + transacID[freeIdx] = genTransacId; + return genTransacId; +} + +void E1AP_free_transaction_identifier(E1AP_TransactionID_t id) { + + for (int i=0; i < E1AP_MAX_NUM_TRANSAC_IDS; i++) { + if (id == transacID[i]) { + transacID[i] = 0; + return; + } + } + LOG_E(E1AP, "Couldn't find transaction ID %ld in list\n", id); +} + +int e1ap_decode_initiating_message(E1AP_E1AP_PDU_t *pdu) { + DevAssert(pdu != NULL); + + switch(pdu->choice.initiatingMessage->procedureCode) { + case E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup: + break; + + case E1AP_ProcedureCode_id_gNB_CU_UP_ConfigurationUpdate: + break; + + case E1AP_ProcedureCode_id_bearerContextSetup: + break; + + case E1AP_ProcedureCode_id_bearerContextModification: + break; + + default: + LOG_E(E1AP, "Unsupported procedure code (%d) for initiating message\n", + (int)pdu->choice.initiatingMessage->procedureCode); + return -1; + } + return 0; +} + +int e1ap_decode_successful_outcome(E1AP_E1AP_PDU_t *pdu) { + DevAssert(pdu != NULL); + switch(pdu->choice.successfulOutcome->procedureCode) { + case E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup: + break; + + case E1AP_ProcedureCode_id_bearerContextSetup: + break; + + default: + LOG_E(E1AP, "Unsupported procedure code (%d) for successful message\n", + (int)pdu->choice.successfulOutcome->procedureCode); + return -1; + } + return 0; +} + +int e1ap_decode_unsuccessful_outcome(E1AP_E1AP_PDU_t *pdu) { + DevAssert(pdu != NULL); + switch(pdu->choice.unsuccessfulOutcome->procedureCode) { + case E1AP_ProcedureCode_id_gNB_CU_UP_E1Setup: + break; + + default: + LOG_E(E1AP, "Unsupported procedure code (%d) for unsuccessful message\n", + (int)pdu->choice.unsuccessfulOutcome->procedureCode); + return -1; + } + return 0; +} + +int e1ap_decode_pdu(E1AP_E1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length) { + asn_dec_rval_t dec_ret; + DevAssert(buffer != NULL); + dec_ret = aper_decode(NULL, + &asn_DEF_E1AP_E1AP_PDU, + (void **)&pdu, + buffer, + length, + 0, + 0); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + LOG_E(E1AP, "----------------- ASN1 DECODER PRINT START----------------- \n"); + xer_fprint(stdout, &asn_DEF_E1AP_E1AP_PDU, pdu); + LOG_E(E1AP, "----------------- ASN1 DECODER PRINT END ----------------- \n"); + } + + if (dec_ret.code != RC_OK) { + AssertFatal(1==0,"Failed to decode pdu\n"); + return -1; + } + + switch(pdu->present) { + case E1AP_E1AP_PDU_PR_initiatingMessage: + return e1ap_decode_initiating_message(pdu); + + case E1AP_E1AP_PDU_PR_successfulOutcome: + return e1ap_decode_successful_outcome(pdu); + + case E1AP_E1AP_PDU_PR_unsuccessfulOutcome: + return e1ap_decode_unsuccessful_outcome(pdu); + + default: + LOG_E(E1AP, "Unknown presence (%d) or not implemented\n", (int)pdu->present); + break; + } + + return -1; +} + +int e1ap_encode_send(E1_t type, e1ap_setup_req_t *setupReq, E1AP_E1AP_PDU_t *pdu, uint16_t stream, const char *func) +{ + DevAssert(pdu != NULL); + + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { + LOG_E(E1AP, "----------------- ASN1 ENCODER PRINT START ----------------- \n"); + xer_fprint(stdout, &asn_DEF_E1AP_E1AP_PDU, pdu); + LOG_E(E1AP, "----------------- ASN1 ENCODER PRINT END----------------- \n"); + } + + char errbuf[2048]; /* Buffer for error message */ + size_t errlen = sizeof(errbuf); /* Size of the buffer */ + int ret = asn_check_constraints(&asn_DEF_E1AP_E1AP_PDU, pdu, errbuf, &errlen); + + if(ret) { + LOG_E(E1AP, "%s: Constraint validation failed: %s\n", func, errbuf); + } + + void *buffer = NULL; + ssize_t encoded = aper_encode_to_new_buffer(&asn_DEF_E1AP_E1AP_PDU, 0, pdu, &buffer); + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_E1AP_E1AP_PDU, pdu); + + if (encoded < 0) { + LOG_E(E1AP, "%s: Failed to encode E1AP message\n", func); + return -1; + } else { + MessageDef *message = itti_alloc_new_message((type==CPtype)?TASK_CUCP_E1:TASK_CUUP_E1, 0, SCTP_DATA_REQ); + sctp_data_req_t *s = &message->ittiMsg.sctp_data_req; + s->assoc_id = setupReq->assoc_id; + s->buffer = buffer; + s->buffer_length = encoded; + s->stream = stream; + LOG_I(E1AP, "%s: Sending ITTI message to SCTP Task\n", func); + itti_send_msg_to_task(TASK_SCTP, 0 /*unused by callee*/, message); + } + + return encoded; +} diff --git a/openair2/E1AP/e1ap_common.h b/openair2/E1AP/e1ap_common.h new file mode 100644 index 0000000000..80a2b925e1 --- /dev/null +++ b/openair2/E1AP/e1ap_common.h @@ -0,0 +1,64 @@ +/* + * 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 + * + * Author and copyright: Laurent Thomas, open-cells.com + * + * 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 + */ + +#ifndef E1AP_COMMON_H_ +#define E1AP_COMMON_H_ +#include "openair2/COMMON/e1ap_messages_types.h" +#include "e1ap_asnc.h" +#include "openair2/COMMON/sctp_messages_types.h" +#include "common/ngran_types.h" + +typedef struct e1ap_upcp_inst_s { + bool incoming_sock; + instance_t instance; + bool same_process; + E1_t type; + enum sctp_state_e sockState; + uint32_t assoc_id; + instance_t gtpInstN3; + instance_t gtpInstF1U; + e1ap_setup_req_t setupReq; + e1ap_bearer_setup_req_t bearerSetupReq; + e1ap_bearer_setup_resp_t bearerSetupResp; +} e1ap_upcp_inst_t; + +extern int asn1_xer_print; + +int e1ap_decode_pdu(E1AP_E1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length); + +e1ap_upcp_inst_t *getCxtE1(instance_t instance); + +E1AP_TransactionID_t E1AP_get_next_transaction_identifier(); + +void createE1inst(E1_t type, instance_t instance, e1ap_setup_req_t *req); + +bool check_transac_id(E1AP_TransactionID_t id, int *freeIdx); + +int e1ap_assoc_id(E1_t type, instance_t instance); + +int e1ap_encode_send(E1_t type, e1ap_setup_req_t *setupReq, E1AP_E1AP_PDU_t *pdu, uint16_t stream, const char *func); + +void e1ap_common_init(); + +void E1AP_free_transaction_identifier(E1AP_TransactionID_t id); +#endif /* E1AP_COMMON_H_ */ diff --git a/executables/create_nr_tasks.h b/openair2/E1AP/e1ap_default_values.h similarity index 88% rename from executables/create_nr_tasks.h rename to openair2/E1AP/e1ap_default_values.h index 23dd23e7db..f5980031df 100644 --- a/executables/create_nr_tasks.h +++ b/openair2/E1AP/e1ap_default_values.h @@ -19,11 +19,10 @@ * contact@openairinterface.org */ -#ifndef CREATE_NR_TASKS_H_ -#define CREATE_NR_TASKS_H_ +#ifndef E1AP_DEFAULT_VALUES_H +#define E1AP_DEFAULT_VALUES_H +#define E1AP_PORT_NUMBER (38462) +#define E1AP_SCTP_PPID (64) -int create_gNB_tasks(uint32_t gnb_nb); - - -#endif /* CREATE_TASKS_H_ */ +#endif diff --git a/openair2/E1AP/e1ap_setup.c b/openair2/E1AP/e1ap_setup.c new file mode 100644 index 0000000000..349e3dcab5 --- /dev/null +++ b/openair2/E1AP/e1ap_setup.c @@ -0,0 +1,129 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include +#include +#include +#include "common/utils/LOG/log.h" +#include "common/utils/nr/nr_common.h" +#include "common/utils/LOG/log_extern.h" +#include "assertions.h" +#include "common/utils/ocp_itti/intertask_interface.h" +#include "openair2/GNB_APP/gnb_paramdef.h" +#include "openair3/ocp-gtpu/gtp_itf.h" + +static void get_NGU_S1U_addr(char **addr, uint16_t *port) +{ + int num_gnbs = 0; + char *gnb_ipv4_address_for_NGU = NULL; + uint32_t gnb_port_for_NGU = 0; + char *gnb_ipv4_address_for_S1U = NULL; + uint32_t gnb_port_for_S1U = 0; + char gtpupath[MAX_OPTNAME_SIZE * 2 + 8]; + + paramdef_t GNBSParams[] = GNBSPARAMS_DESC; + paramdef_t NETParams[] = GNBNETPARAMS_DESC; + LOG_I(GTPU, "Configuring GTPu\n"); + + /* get number of active eNodeBs */ + config_get(GNBSParams, sizeof(GNBSParams) / sizeof(paramdef_t), NULL); + num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt; + AssertFatal(num_gnbs > 0, "Failed to parse config file no active gNodeBs in %s \n", GNB_CONFIG_STRING_ACTIVE_GNBS); + + sprintf(gtpupath, "%s.[%i].%s", GNB_CONFIG_STRING_GNB_LIST, 0, GNB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG); + config_get(NETParams, sizeof(NETParams) / sizeof(paramdef_t), gtpupath); + char *address; + if (NETParams[1].strptr != NULL) { + LOG_I(GTPU, "SA mode \n"); + address = strdup(gnb_ipv4_address_for_NGU); + *port = gnb_port_for_NGU; + } else { + LOG_I(GTPU, "NSA mode \n"); + address = strdup(gnb_ipv4_address_for_S1U); + *port = gnb_port_for_S1U; + } + if (strchr(address, '/')) + *strchr(address, '/') = 0; + *addr = address; + return; +} + +MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process) +{ + MessageDef *msgConfig = itti_alloc_new_message(TASK_GNB_APP, 0, E1AP_SETUP_REQ); + if (!msgConfig) + return NULL; + + paramdef_t GNBSParams[] = GNBSPARAMS_DESC; + paramdef_t GNBParams[] = GNBPARAMS_DESC; + paramlist_def_t GNBParamList = {GNB_CONFIG_STRING_GNB_LIST, NULL, 0}; + config_get(GNBSParams, sizeofArray(GNBSParams), NULL); + char aprefix[MAX_OPTNAME_SIZE * 2 + 8]; + sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); + int num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt; + AssertFatal(num_gnbs == 1, "Support only one gNB per process\n"); + + if (num_gnbs > 0) { + config_getlist(&GNBParamList, GNBParams, sizeofArray(GNBParams), NULL); + paramdef_t *gnbParms = GNBParamList.paramarray[0]; + AssertFatal(gnbParms[GNB_GNB_ID_IDX].uptr != NULL, "gNB id %u is not defined in configuration file\n", 0); + e1ap_setup_req_t *e1Setup = &E1AP_SETUP_REQ(msgConfig); + msgConfig->ittiMsgHeader.destinationInstance = 0; + e1Setup->gNB_cu_up_id = *(gnbParms[GNB_GNB_ID_IDX].uptr); + + paramdef_t PLMNParams[] = GNBPLMNPARAMS_DESC; + paramlist_def_t PLMNParamList = {GNB_CONFIG_STRING_PLMN_LIST, NULL, 0}; + /* map parameter checking array instances to parameter definition array instances */ + checkedparam_t config_check_PLMNParams[] = PLMNPARAMS_CHECK; + + for (int I = 0; I < sizeofArray(PLMNParams); ++I) + PLMNParams[I].chkPptr = &(config_check_PLMNParams[I]); + + config_getlist(&PLMNParamList, PLMNParams, sizeofArray(PLMNParams), aprefix); + int numPLMNs = PLMNParamList.numelt; + e1Setup->supported_plmns = numPLMNs; + + for (int I = 0; I < numPLMNs; I++) { + e1Setup->plmns[I].mcc = *PLMNParamList.paramarray[I][GNB_MOBILE_COUNTRY_CODE_IDX].uptr; + e1Setup->plmns[I].mnc = *PLMNParamList.paramarray[I][GNB_MOBILE_NETWORK_CODE_IDX].uptr; + e1Setup->plmns[I].mnc = *PLMNParamList.paramarray[I][GNB_MNC_DIGIT_LENGTH].u8ptr; + } + + e1Setup->remotePortF1U = *(GNBParamList.paramarray[0][GNB_REMOTE_S_PORTD_IDX].uptr); + e1Setup->localAddressF1U = strdup(*(GNBParamList.paramarray[0][GNB_LOCAL_S_ADDRESS_IDX].strptr)); + e1Setup->localPortF1U = *(GNBParamList.paramarray[0][GNB_LOCAL_S_PORTD_IDX].uptr); + get_NGU_S1U_addr(&e1Setup->localAddressN3, &e1Setup->localPortN3); + e1Setup->remotePortN3 = GTPV1_U_PORT_NUMBER; + + if (separate_CUUP_process) { + paramlist_def_t GNBE1ParamList = {GNB_CONFIG_STRING_E1_PARAMETERS, NULL, 0}; + paramdef_t GNBE1Params[] = GNBE1PARAMS_DESC; + config_getlist(&GNBE1ParamList, GNBE1Params, sizeofArray(GNBE1Params), aprefix); + paramdef_t *e1Parms = GNBE1ParamList.paramarray[0]; + strcpy(e1Setup->CUCP_e1_ip_address.ipv4_address, *(e1Parms[GNB_CONFIG_E1_IPV4_ADDRESS_CUCP].strptr)); + e1Setup->CUCP_e1_ip_address.ipv4 = 1; + strcpy(e1Setup->CUUP_e1_ip_address.ipv4_address, *(e1Parms[GNB_CONFIG_E1_IPV4_ADDRESS_CUUP].strptr)); + e1Setup->CUUP_e1_ip_address.ipv4 = 1; + e1Setup->cn_support = *e1Parms[GNB_CONFIG_E1_CN_SUPPORT].uptr; + } + } + return msgConfig; +} diff --git a/openair2/ENB_APP/enb_app.c b/openair2/ENB_APP/enb_app.c index 98ddaebfdc..9a0ed3f04a 100644 --- a/openair2/ENB_APP/enb_app.c +++ b/openair2/ENB_APP/enb_app.c @@ -40,7 +40,8 @@ # include "intertask_interface.h" # include "s1ap_eNB.h" -#include "sctp_eNB_task.h" +# include "sctp_eNB_task.h" +# include "openair3/ocp-gtpu/gtp_itf.h" # include "x2ap_eNB.h" # include "x2ap_messages_types.h" diff --git a/openair2/ENB_APP/enb_config.c b/openair2/ENB_APP/enb_config.c index fbb5fd0225..776c89c02b 100644 --- a/openair2/ENB_APP/enb_config.c +++ b/openair2/ENB_APP/enb_config.c @@ -2015,7 +2015,7 @@ int RCconfig_gtpu(void ) { int num_enbs = 0; char *enb_interface_name_for_S1U = NULL; char *enb_ipv4_address_for_S1U = NULL; - uint32_t enb_port_for_S1U = 0; + uint16_t enb_port_for_S1U = 0; char *address = NULL; char *cidr = NULL; char gtpupath[MAX_OPTNAME_SIZE*2 + 8]; diff --git a/openair2/ENB_APP/enb_paramdef.h b/openair2/ENB_APP/enb_paramdef.h index 1f2ca6392f..647ac96c18 100644 --- a/openair2/ENB_APP/enb_paramdef.h +++ b/openair2/ENB_APP/enb_paramdef.h @@ -1123,12 +1123,13 @@ typedef struct srb1_params_s { /* S1/X2 interface configuration parameters */ /* optname helpstr paramflags XXXptr defXXXval type numelt */ /*--------------------------------------------------------------------------------------------------------------------------------------------------*/ +// clang-format off #define NETPARAMS_DESC { \ {ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1_MME, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {ENB_CONFIG_STRING_ENB_IPV4_ADDRESS_FOR_S1_MME, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1U, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_S1U, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ - {ENB_CONFIG_STRING_ENB_PORT_FOR_S1U, NULL, 0, uptr:NULL, defintval:2152L, TYPE_UINT, 0}, \ + {ENB_CONFIG_STRING_ENB_PORT_FOR_S1U, NULL, 0, u16ptr:NULL, defuintval:GTPV1_U_PORT_NUMBER, TYPE_UINT16, 0}, \ {ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_X2C, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ {ENB_CONFIG_STRING_ENB_PORT_FOR_X2C, NULL, 0, uptr:NULL, defintval:0L, TYPE_UINT, 0}, \ {ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_M2C, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ @@ -1144,8 +1145,10 @@ typedef struct srb1_params_s { #define GTPUPARAMS_DESC { \ {ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1U, NULL, 0, strptr:&enb_interface_name_for_S1U, defstrval:"lo", TYPE_STRING, 0}, \ {ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_S1U, NULL, 0, strptr:&enb_ipv4_address_for_S1U, defstrval:"127.0.0.1", TYPE_STRING, 0}, \ - {ENB_CONFIG_STRING_ENB_PORT_FOR_S1U, NULL, 0, uptr:&enb_port_for_S1U, defintval:2152, TYPE_UINT, 0} \ + {ENB_CONFIG_STRING_ENB_PORT_FOR_S1U, NULL, 0, u16ptr:&enb_port_for_S1U, defuintval:GTPV1_U_PORT_NUMBER, TYPE_UINT16, 0} \ } +// clang-format on + /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ diff --git a/openair2/F1AP/dummy_enb.c b/openair2/F1AP/dummy_enb.c index afb4a1048a..942e652213 100644 --- a/openair2/F1AP/dummy_enb.c +++ b/openair2/F1AP/dummy_enb.c @@ -26,12 +26,6 @@ #include "NR_ServingCellConfigCommon.h" #include "NR_MIB.h" -void apply_macrlc_config(gNB_RRC_INST *rrc, - rrc_gNB_ue_context_t *const ue_context_pP, - const protocol_ctxt_t *const ctxt_pP ) { - abort(); -} - bool sdap_data_req(protocol_ctxt_t *ctxt_p, const ue_id_t ue_id, const srb_flag_t srb_flag, diff --git a/openair2/F1AP/f1ap_cu_interface_management.c b/openair2/F1AP/f1ap_cu_interface_management.c index 39274cdf38..0dfcefaef3 100644 --- a/openair2/F1AP/f1ap_cu_interface_management.c +++ b/openair2/F1AP/f1ap_cu_interface_management.c @@ -144,7 +144,9 @@ int CU_handle_F1_SETUP_REQUEST(instance_t instance, // LTS: FIXME data model failure: we don't KNOW if we receive a 4G or a 5G cell // Furthermore, cell_type is not a attribute of a cell in the data structure !!!!!!!!!! - if (RC.nrrrc && RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU) + if (RC.nrrrc && + ( RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CU || + RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type == ngran_gNB_CUCP) ) f1ap_req(true, instance)->cell_type=CELL_MACRO_GNB; else f1ap_req(true, instance)->cell_type=CELL_MACRO_ENB; diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c index b4e0c1ad2a..09b3651035 100644 --- a/openair2/F1AP/f1ap_cu_task.c +++ b/openair2/F1AP/f1ap_cu_task.c @@ -57,8 +57,11 @@ static void cu_task_handle_sctp_association_ind(instance_t instance, sctp_new_as f1ap_cu_data->sctp_in_streams = sctp_new_association_ind->in_streams; f1ap_cu_data->sctp_out_streams = sctp_new_association_ind->out_streams; f1ap_cu_data->default_sctp_stream_id = 0; - getCxt(CUtype, instance)->gtpInst=cu_task_create_gtpu_instance(IPaddrs); - AssertFatal(getCxt(CUtype, instance)->gtpInst>0,"Failed to create CU F1-U UDP listener"); + if (RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)]->node_type != ngran_gNB_CUCP) { + getCxt(CUtype, instance)->gtpInst = cu_task_create_gtpu_instance(IPaddrs); + AssertFatal(getCxt(CUtype, instance)->gtpInst > 0, "Failed to create CU F1-U UDP listener"); + } else + LOG_I(F1AP, "In F1AP connection, don't start GTP-U, as we have also E1AP\n"); // Fixme: fully inconsistent instances management // dirty global var is a bad fix CUuniqInstance=getCxt(CUtype, instance)->gtpInst; @@ -120,7 +123,9 @@ void *F1AP_CU_task(void *arg) { eth_params_t *IPaddrs; // Hardcoded instance id! - if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_CU) + if (RC.nrrrc && + (RC.nrrrc[0]->node_type == ngran_gNB_CU || + RC.nrrrc[0]->node_type == ngran_gNB_CUCP ) ) IPaddrs=&RC.nrrrc[0]->eth_params_s; else IPaddrs=&RC.rrc[0]->eth_params_s; @@ -129,71 +134,57 @@ void *F1AP_CU_task(void *arg) { while (1) { itti_receive_msg(TASK_CU_F1, &received_msg); - + LOG_I(F1AP, "CU Task Received %s for instance %ld\n", + ITTI_MSG_NAME(received_msg), ITTI_MSG_DESTINATION_INSTANCE(received_msg)); switch (ITTI_MSG_ID(received_msg)) { case SCTP_NEW_ASSOCIATION_IND: - LOG_I(F1AP, "CU Task Received SCTP_NEW_ASSOCIATION_IND for instance %ld\n", - ITTI_MSG_DESTINATION_INSTANCE(received_msg)); cu_task_handle_sctp_association_ind(ITTI_MSG_ORIGIN_INSTANCE(received_msg), &received_msg->ittiMsg.sctp_new_association_ind, IPaddrs); break; case SCTP_NEW_ASSOCIATION_RESP: - LOG_I(F1AP, "CU Task Received SCTP_NEW_ASSOCIATION_RESP for instance %ld\n", - ITTI_MSG_DESTINATION_INSTANCE(received_msg)); cu_task_handle_sctp_association_resp(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &received_msg->ittiMsg.sctp_new_association_resp); break; case SCTP_DATA_IND: - LOG_I(F1AP, "CU Task Received SCTP_DATA_IND for Instance %ld\n", - ITTI_MSG_DESTINATION_INSTANCE(received_msg)); cu_task_handle_sctp_data_ind(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &received_msg->ittiMsg.sctp_data_ind); break; case F1AP_SETUP_RESP: // from rrc - LOG_I(F1AP, "CU Task Received F1AP_SETUP_RESP\n"); CU_send_F1_SETUP_RESPONSE(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_SETUP_RESP(received_msg)); break; case F1AP_GNB_CU_CONFIGURATION_UPDATE: // from rrc - LOG_I(F1AP, "CU Task Received F1AP_GNB_CU_CONFIGURAITON_UPDATE\n"); - // CU_send_f1setup_resp(ITTI_MSG_DESTINATION_INSTANCE(received_msg), - // &F1AP_SETUP_RESP(received_msg)); CU_send_gNB_CU_CONFIGURATION_UPDATE(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_GNB_CU_CONFIGURATION_UPDATE(received_msg)); break; case F1AP_DL_RRC_MESSAGE: // from rrc - LOG_I(F1AP, "CU Task Received F1AP_DL_RRC_MESSAGE\n"); CU_send_DL_RRC_MESSAGE_TRANSFER(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_DL_RRC_MESSAGE(received_msg)); free(F1AP_DL_RRC_MESSAGE(received_msg).rrc_container); break; case F1AP_UE_CONTEXT_SETUP_REQ: // from rrc - LOG_I(F1AP, "CU Task Received F1AP_UE_CONTEXT_SETUP_REQ\n"); CU_send_UE_CONTEXT_SETUP_REQUEST(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_UE_CONTEXT_SETUP_REQ(received_msg)); break; case F1AP_UE_CONTEXT_MODIFICATION_REQ: - LOG_I(F1AP, "CU Task received F1AP_UE_CONTEXT_MODIFICATION_REQ\n"); CU_send_UE_CONTEXT_MODIFICATION_REQUEST(ITTI_MSG_DESTINATION_INSTANCE(received_msg), - &F1AP_UE_CONTEXT_MODIFICATION_REQ(received_msg)); + &F1AP_UE_CONTEXT_SETUP_REQ(received_msg)); break; case F1AP_UE_CONTEXT_RELEASE_CMD: // from rrc - LOG_I(F1AP, "CU Task Received F1AP_UE_CONTEXT_RELEASE_CMD\n"); CU_send_UE_CONTEXT_RELEASE_COMMAND(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_UE_CONTEXT_RELEASE_CMD(received_msg)); break; case F1AP_PAGING_IND: - LOG_I(F1AP, "CU Task Received F1AP_PAGING_IND\n"); CU_send_Paging(ITTI_MSG_DESTINATION_INSTANCE(received_msg), &F1AP_PAGING_IND(received_msg)); break; diff --git a/openair2/F1AP/f1ap_cu_ue_context_management.c b/openair2/F1AP/f1ap_cu_ue_context_management.c index 597887b841..d609cb98cb 100644 --- a/openair2/F1AP/f1ap_cu_ue_context_management.c +++ b/openair2/F1AP/f1ap_cu_ue_context_management.c @@ -510,6 +510,7 @@ int CU_send_UE_CONTEXT_SETUP_REQUEST(instance_t instance, /* 12.1.3 uLUPTNLInformation_ToBeSetup_List */ for (int j = 0; j < f1ap_ue_context_setup_req->drbs_to_be_setup[i].up_ul_tnl_length; j++) { + /*Use a dummy teid for the outgoing GTP-U tunnel (DU) which will be updated once we get the UE context setup response from the DU*/ /* Use a dummy address and teid for the outgoing GTP-U tunnel (DU) which will be updated once we get the UE context setup response from the DU */ transport_layer_addr_t addr = { length: 32, buffer: { 0 } }; f1ap_ue_context_setup_req->drbs_to_be_setup[i].up_ul_tnl[j].teid = newGtpuCreateTunnel(getCxt(CUtype, instance)->gtpInst, @@ -1009,7 +1010,7 @@ int CU_handle_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, f1ap_remove_ue(CUtype, instance, rnti); return 0; } -//void CU_send_UE_CONTEXT_MODIFICATION_REQUEST(F1AP_UEContextModificationRequest_t *UEContextModificationRequest) { + int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance, f1ap_ue_context_setup_t *f1ap_ue_context_modification_req) { F1AP_F1AP_PDU_t pdu= {0}; F1AP_UEContextModificationRequest_t *out; @@ -1477,19 +1478,6 @@ int CU_send_UE_CONTEXT_MODIFICATION_REQUEST(instance_t instance, f1ap_ue_context /* 12.1.3 uLUPTNLInformation_ToBeSetup_List */ for (int j = 0; j < f1ap_ue_context_modification_req->drbs_to_be_setup[i].up_ul_tnl_length; j++) { /* Use a dummy address and teid for the outgoing GTP-U tunnel (DU) which will be updated once we get the UE context modification response from the DU */ - transport_layer_addr_t addr = { length: 32, buffer: { 0 } }; - - f1ap_ue_context_modification_req->drbs_to_be_setup[i].up_ul_tnl[j].teid = newGtpuCreateTunnel(getCxt(CUtype, instance)->gtpInst, - f1ap_ue_context_modification_req->rnti, - f1ap_ue_context_modification_req->drbs_to_be_setup[i].drb_id, - f1ap_ue_context_modification_req->drbs_to_be_setup[i].drb_id, - 0xFFFF, // We will set the right value from DU answer - -1, // no qfi - addr, // We will set the right value from DU answer - f1ap_ue_context_modification_req->drbs_to_be_setup[i].up_dl_tnl[0].port, - cu_f1u_data_req, - NULL); - /* 12.3.1 ULTunnels_ToBeSetup_Item */ asn1cSequenceAdd(drbs_toBeSetupMod_item->uLUPTNLInformation_ToBeSetup_List.list, F1AP_ULUPTNLInformation_ToBeSetup_Item_t, uLUPTNLInformation_ToBeSetup_Item); uLUPTNLInformation_ToBeSetup_Item->uLUPTNLInformation.present = F1AP_UPTransportLayerInformation_PR_gTPTunnel; diff --git a/openair2/F1AP/f1ap_decoder.c b/openair2/F1AP/f1ap_decoder.c index 6d1c60c67d..0085add838 100644 --- a/openair2/F1AP/f1ap_decoder.c +++ b/openair2/F1AP/f1ap_decoder.c @@ -166,7 +166,8 @@ static int f1ap_decode_unsuccessful_outcome(F1AP_F1AP_PDU_t *pdu) { int f1ap_decode_pdu(F1AP_F1AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length) { asn_dec_rval_t dec_ret; DevAssert(buffer != NULL); - dec_ret = aper_decode(NULL, + asn_codec_ctx_t st={.max_stack_size = 100 * 1000}; + dec_ret = aper_decode(&st, &asn_DEF_F1AP_F1AP_PDU, (void **)&pdu, buffer, diff --git a/openair2/F1AP/f1ap_du_task.c b/openair2/F1AP/f1ap_du_task.c index ac952e0498..c11316e19b 100644 --- a/openair2/F1AP/f1ap_du_task.c +++ b/openair2/F1AP/f1ap_du_task.c @@ -107,18 +107,18 @@ void *F1AP_DU_task(void *arg) { MessageDef *msg = NULL; itti_receive_msg(TASK_DU_F1, &msg); instance_t myInstance=ITTI_MSG_DESTINATION_INSTANCE(msg); - + LOG_I(F1AP, "DU Task Received %s for instance %ld\n", + ITTI_MSG_NAME(msg),myInstance); switch (ITTI_MSG_ID(msg)) { - case F1AP_SETUP_REQ: + case F1AP_SETUP_REQ: { // this is not a true F1 message, but rather an ITTI message sent by enb_app // 1. save the itti msg so that you can use it to sen f1ap_setup_req, fill the f1ap_setup_req message, // 2. store the message in f1ap context, that is also stored in RC // 2. send a sctp_association req - LOG_I(F1AP, "DU Task Received F1AP_SETUP_REQ\n"); - f1ap_setup_req_t *msgSetup=&F1AP_SETUP_REQ(msg); + f1ap_setup_req_t *msgSetup = &F1AP_SETUP_REQ(msg); createF1inst(false, myInstance, msgSetup); - du_task_send_sctp_association_req(myInstance,msgSetup); - break; + du_task_send_sctp_association_req(myInstance, msgSetup); + } break; case F1AP_GNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE: DU_send_gNB_CU_CONFIGURATION_UPDATE_ACKNOWLEDGE(ITTI_MSG_ORIGIN_INSTANCE(msg), @@ -133,32 +133,23 @@ void *F1AP_DU_task(void *arg) { case SCTP_NEW_ASSOCIATION_RESP: // 1. store the respon // 2. send the f1setup_req - LOG_I(F1AP, "DU Task Received SCTP_NEW_ASSOCIATION_RESP\n"); du_task_handle_sctp_association_resp(myInstance, &msg->ittiMsg.sctp_new_association_resp); break; case SCTP_DATA_IND: // ex: any F1 incoming message for DU ends here - LOG_I(F1AP, "DU Task Received SCTP_DATA_IND\n"); du_task_handle_sctp_data_ind(myInstance, &msg->ittiMsg.sctp_data_ind); break; case F1AP_INITIAL_UL_RRC_MESSAGE: // from rrc - LOG_I(F1AP, "DU Task Received F1AP_INITIAL_UL_RRC_MESSAGE\n"); + { f1ap_initial_ul_rrc_message_t *msgRrc = &F1AP_INITIAL_UL_RRC_MESSAGE(msg); - DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(0,0,0,msgRrc->crnti, - msgRrc->rrc_container, - msgRrc->rrc_container_length, - msgRrc->du2cu_rrc_container, - msgRrc->du2cu_rrc_container_length - ); - break; + DU_send_INITIAL_UL_RRC_MESSAGE_TRANSFER(0, 0, 0, msgRrc->crnti, msgRrc->rrc_container, msgRrc->rrc_container_length, msgRrc->du2cu_rrc_container, msgRrc->du2cu_rrc_container_length); + } break; case F1AP_UL_RRC_MESSAGE: // to rrc - LOG_D(F1AP, "DU Task Received F1AP_UL_RRC_MESSAGE\n"); - if (RC.nrrrc && RC.nrrrc[0]->node_type == ngran_gNB_DU) { DU_send_UL_NR_RRC_MESSAGE_TRANSFER(myInstance, &F1AP_UL_RRC_MESSAGE(msg)); @@ -174,12 +165,10 @@ void *F1AP_DU_task(void *arg) { break; case F1AP_UE_CONTEXT_MODIFICATION_RESP: - LOG_I(F1AP, "DU task received itti message from RRC for F1AP_UE_CONTEXT_MODIFICATION_RESP message generation \n"); DU_send_UE_CONTEXT_MODIFICATION_RESPONSE(myInstance, &F1AP_UE_CONTEXT_MODIFICATION_RESP(msg)); break; case F1AP_UE_CONTEXT_RELEASE_REQ: // from MAC - LOG_I(F1AP, "DU Task Received F1AP_UE_CONTEXT_RELEASE_REQ\n"); DU_send_UE_CONTEXT_RELEASE_REQUEST(myInstance, &F1AP_UE_CONTEXT_RELEASE_REQ(msg)); break; diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index 4be90a9c15..23ab58c6ed 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -1078,7 +1078,7 @@ int DU_send_UE_CONTEXT_RELEASE_COMPLETE(instance_t instance, static instance_t du_create_gtpu_instance_to_cu(char *CUaddr, uint16_t CUport, char *DUaddr, uint16_t DUport) { - openAddr_t tmp={0}; + openAddr_t tmp = {0}; strncpy(tmp.originHost, DUaddr, sizeof(tmp.originHost)-1); strncpy(tmp.destinationHost, CUaddr, sizeof(tmp.destinationHost)-1); sprintf(tmp.originService, "%d", DUport); diff --git a/openair2/GNB_APP/gnb_app.c b/openair2/GNB_APP/gnb_app.c index 862cf9dddc..680b566eec 100644 --- a/openair2/GNB_APP/gnb_app.c +++ b/openair2/GNB_APP/gnb_app.c @@ -35,7 +35,6 @@ #include #include "gnb_app.h" -#include "gnb_config.h" #include "assertions.h" #include "common/ran_context.h" @@ -52,6 +51,8 @@ #include "nfapi/oai_integration/vendor_ext.h" #include #include "openair2/LAYER2/PDCP_v10.1.0/pdcp.h" +#include "openair2/E1AP/e1ap.h" +#include "gnb_config.h" extern unsigned char NB_gNB_INST; extern RAN_CONTEXT_t RC; @@ -59,7 +60,7 @@ extern RAN_CONTEXT_t RC; #define GNB_REGISTER_RETRY_DELAY 10 /*------------------------------------------------------------------------------*/ -static void configure_nr_rrc(uint32_t gnb_id) +void configure_nr_rrc(uint32_t gnb_id) { MessageDef *msg_p = NULL; // int CC_id; @@ -80,7 +81,7 @@ static void configure_nr_rrc(uint32_t gnb_id) /*------------------------------------------------------------------------------*/ -static uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end)//, const Enb_properties_array_t *enb_properties) +uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end)//, const Enb_properties_array_t *enb_properties) { uint32_t gnb_id; MessageDef *msg_p; @@ -102,7 +103,6 @@ static uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end)//, itti_send_msg_to_task (TASK_NGAP, GNB_MODULE_ID_TO_INSTANCE(gnb_id), msg_p); } - if (gnb_id == 0) RCconfig_nr_gtpu(); } LOG_I(GNB_APP,"[gNB %d] gNB_app_register for instance %d\n", gnb_id, GNB_MODULE_ID_TO_INSTANCE(gnb_id)); @@ -115,7 +115,7 @@ static uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end)//, /*------------------------------------------------------------------------------*/ -static uint32_t gNB_app_register_x2(uint32_t gnb_id_start, uint32_t gnb_id_end) { +uint32_t gNB_app_register_x2(uint32_t gnb_id_start, uint32_t gnb_id_end) { uint32_t gnb_id; MessageDef *msg_p; uint32_t register_gnb_x2_pending = 0; @@ -138,10 +138,6 @@ static uint32_t gNB_app_register_x2(uint32_t gnb_id_start, uint32_t gnb_id_end) void *gNB_app_task(void *args_p) { - uint32_t gnb_nb = RC.nb_nr_inst; - uint32_t gnb_id_start = 0; - uint32_t gnb_id_end = gnb_id_start + gnb_nb; - uint32_t gnb_id; MessageDef *msg_p = NULL; const char *msg_name = NULL; instance_t instance; @@ -151,60 +147,35 @@ void *gNB_app_task(void *args_p) int cell_to_activate = 0; itti_mark_task_ready (TASK_GNB_APP); + ngran_node_t node_type = get_node_type(); - LOG_I(PHY, "%s() Task ready initialize structures\n", __FUNCTION__); - - RCconfig_NR_L1(); - RCconfig_nr_prs(); - - if (RC.nb_nr_macrlc_inst>0) RCconfig_nr_macrlc(); - - LOG_I(PHY, "%s() RC.nb_nr_L1_inst:%d\n", __FUNCTION__, RC.nb_nr_L1_inst); - - if (RC.nb_nr_L1_inst>0) AssertFatal(l1_north_init_gNB()==0,"could not initialize L1 north interface\n"); - - AssertFatal (gnb_nb <= RC.nb_nr_inst, - "Number of gNB is greater than gNB defined in configuration file (%d/%d)!", - gnb_nb, RC.nb_nr_inst); - - LOG_I(GNB_APP,"Allocating gNB_RRC_INST for %d instances\n",RC.nb_nr_inst); - - RC.nrrrc = (gNB_RRC_INST **)malloc(RC.nb_nr_inst*sizeof(gNB_RRC_INST *)); - LOG_I(PHY, "%s() RC.nb_nr_inst:%d RC.nrrrc:%p\n", __FUNCTION__, RC.nb_nr_inst, RC.nrrrc); - for (gnb_id = gnb_id_start; (gnb_id < gnb_id_end) ; gnb_id++) { - RC.nrrrc[gnb_id] = (gNB_RRC_INST*)calloc(1,sizeof(gNB_RRC_INST)); - LOG_I(PHY, "%s() Creating RRC instance RC.nrrrc[%d]:%p (%d of %d)\n", __FUNCTION__, gnb_id, RC.nrrrc[gnb_id], gnb_id+1, gnb_id_end); - configure_nr_rrc(gnb_id); - } - - if (RC.nb_nr_inst > 0 && !get_softmodem_params()->nsa) { - init_pdcp(); - } - - if (is_x2ap_enabled() ) { //&& !NODE_IS_DU(RC.rrc[0]->node_type) - LOG_I(X2AP, "X2AP enabled \n"); - __attribute__((unused)) uint32_t x2_register_gnb_pending = gNB_app_register_x2 (gnb_id_start, gnb_id_end); - } - - /* For the CU case the gNB registration with the AMF might have to take place after the F1 setup, as the PLMN info - * can originate from the DU. Add check on whether x2ap is enabled to account for ENDC NSA scenario.*/ - if ((get_softmodem_params()->sa || is_x2ap_enabled()) && !NODE_IS_DU(RC.nrrrc[0]->node_type) ) { //&& !NODE_IS_CU(RC.nrrrc[0]->node_type)) { - /* Try to register each gNB */ - //registered_gnb = 0; - __attribute__((unused)) uint32_t register_gnb_pending = gNB_app_register (gnb_id_start, gnb_id_end); - } - if (RC.nb_nr_inst > 0) { - if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + if (node_type == ngran_gNB_CUCP || + node_type == ngran_gNB_CU || + node_type == ngran_eNB_CU || + node_type == ngran_ng_eNB_CU) { if (itti_create_task(TASK_CU_F1, F1AP_CU_task, NULL) < 0) { - LOG_E(F1AP, "Create task for F1AP CU failed\n"); - AssertFatal(1==0,"exiting"); + LOG_E(F1AP, "Create task for F1AP CU failed\n"); + AssertFatal(1==0,"exiting"); } } - if (NODE_IS_DU(RC.nrrrc[0]->node_type)) { + if (node_type == ngran_gNB_CUCP) { + if (itti_create_task(TASK_CUCP_E1, E1AP_CUCP_task, NULL) < 0) + AssertFatal(false, "Create task for E1AP CP failed\n"); + MessageDef *msg = RCconfig_NR_CU_E1(true); + if (msg) + itti_send_msg_to_task(TASK_CUCP_E1, 0, msg); + else + AssertFatal(false, "Send inti to task for E1AP CP failed\n"); + } + if (node_type == ngran_gNB_CUUP) { + AssertFatal(false, "To run CU-UP use executable nr-cuup\n"); + } + + if (NODE_IS_DU(node_type)) { if (itti_create_task(TASK_DU_F1, F1AP_DU_task, NULL) < 0) { LOG_E(F1AP, "Create task for F1AP DU failed\n"); AssertFatal(1==0,"exiting"); @@ -213,7 +184,7 @@ void *gNB_app_task(void *args_p) LOG_I(GNB_APP,"ngran_gNB_DU: Allocating ITTI message for F1AP_SETUP_REQ\n"); msg_p = itti_alloc_new_message (TASK_GNB_APP, 0, F1AP_SETUP_REQ); RCconfig_NR_DU_F1(msg_p, 0); - + itti_send_msg_to_task (TASK_DU_F1, GNB_MODULE_ID_TO_INSTANCE(0), msg_p); } } @@ -279,7 +250,7 @@ void *gNB_app_task(void *args_p) break; case F1AP_SETUP_RESP: - AssertFatal(NODE_IS_DU(RC.nrrrc[0]->node_type), "Should not have received F1AP_SETUP_RESP in CU/gNB\n"); + AssertFatal(NODE_IS_DU(node_type), "Should not have received F1AP_SETUP_RESP in CU/gNB\n"); LOG_I(GNB_APP, "Received %s: associated ngran_gNB_CU %s with %d cells to activate\n", ITTI_MSG_NAME (msg_p), F1AP_SETUP_RESP(msg_p).gNB_CU_name,F1AP_SETUP_RESP(msg_p).num_cells_to_activate); @@ -289,7 +260,7 @@ void *gNB_app_task(void *args_p) break; case F1AP_GNB_CU_CONFIGURATION_UPDATE: - AssertFatal(NODE_IS_DU(RC.nrrrc[0]->node_type), "Should not have received F1AP_GNB_CU_CONFIGURATION_UPDATE in CU/gNB\n"); + AssertFatal(NODE_IS_DU(node_type), "Should not have received F1AP_GNB_CU_CONFIGURATION_UPDATE in CU/gNB\n"); LOG_I(GNB_APP, "Received %s: associated ngran_gNB_CU %s with %d cells to activate\n", ITTI_MSG_NAME (msg_p), F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p).gNB_CU_name,F1AP_GNB_CU_CONFIGURATION_UPDATE(msg_p).num_cells_to_activate); diff --git a/openair2/GNB_APP/gnb_app.h b/openair2/GNB_APP/gnb_app.h index b0b422aae1..44c4374fc3 100644 --- a/openair2/GNB_APP/gnb_app.h +++ b/openair2/GNB_APP/gnb_app.h @@ -33,6 +33,7 @@ #include void *gNB_app_task(void *args_p); - - +void configure_nr_rrc(uint32_t gnb_id); +uint32_t gNB_app_register(uint32_t gnb_id_start, uint32_t gnb_id_end); +uint32_t gNB_app_register_x2(uint32_t gnb_id_start, uint32_t gnb_id_end); #endif /* GNB_APP_H_ */ diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index e4a855acda..dd4cb32bd7 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -91,8 +91,6 @@ extern uint16_t sf_ahead; int macrlc_has_f1 = 0; -static ngran_node_t get_node_type(void); - extern int config_check_band_frequencies(int ind, int16_t band, uint64_t downlink_frequency, int32_t uplink_frequency_offset, uint32_t frame_type); @@ -1154,7 +1152,8 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { printf("NRRRC %d: Southbound Transport %s\n",i,*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr)); - if (strcmp(*(GNBParamList.paramarray[i][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "f1") == 0) { + rrc->node_type = get_node_type(); + if (NODE_IS_CU(rrc->node_type)) { paramdef_t SCTPParams[] = GNBSCTPPARAMS_DESC; char aprefix[MAX_OPTNAME_SIZE*2 + 8]; sprintf(aprefix,"%s.[%u].%s",GNB_CONFIG_STRING_GNB_LIST,i,GNB_CONFIG_STRING_SCTP_CONFIG); @@ -1171,21 +1170,12 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { rrc->eth_params_s.my_portd = *(GNBParamList.paramarray[i][GNB_LOCAL_S_PORTD_IDX].uptr); rrc->eth_params_s.remote_portd = *(GNBParamList.paramarray[i][GNB_REMOTE_S_PORTD_IDX].uptr); rrc->eth_params_s.transp_preference = ETH_UDP_MODE; - rrc->node_type = ngran_gNB_CU; rrc->sctp_in_streams = (uint16_t)*(SCTPParams[GNB_SCTP_INSTREAMS_IDX].uptr); rrc->sctp_out_streams = (uint16_t)*(SCTPParams[GNB_SCTP_OUTSTREAMS_IDX].uptr); - } else { - // set to ngran_gNB for now, it will get set to ngran_gNB_DU if macrlc entity which uses F1 is present - // Note: we will have to handle the case of ngran_ng_gNB_DU - if (macrlc_has_f1 == 0) { - rrc->node_type = ngran_gNB; - LOG_I(NR_RRC,"Setting node_type to ngran_gNB\n"); - } else { - rrc->node_type = ngran_gNB_DU; - LOG_I(NR_RRC,"Setting node_type to ngran_gNB_DU\n"); - } } + + rrc->nr_cellid = (uint64_t)*(GNBParamList.paramarray[i][GNB_NRCELLID_IDX].u64ptr); rrc->um_on_default_drb = *(GNBParamList.paramarray[i][GNB_UMONDEFAULTDRB_IDX].uptr); @@ -1282,55 +1272,6 @@ void RCconfig_NRRRC(MessageDef *msg_p, uint32_t i, gNB_RRC_INST *rrc) { config_security(rrc); }//End RCconfig_NRRRC function -int RCconfig_nr_gtpu(void ) { - - int num_gnbs = 0; - char* gnb_ipv4_address_for_NGU = NULL; - uint32_t gnb_port_for_NGU = 0; - char* gnb_ipv4_address_for_S1U = NULL; - uint32_t gnb_port_for_S1U = 0; - char gtpupath[MAX_OPTNAME_SIZE*2 + 8]; - - paramdef_t GNBSParams[] = GNBSPARAMS_DESC; - paramdef_t NETParams[] = GNBNETPARAMS_DESC; - LOG_I(GTPU,"Configuring GTPu\n"); - -/* get number of active eNodeBs */ - config_get( GNBSParams,sizeof(GNBSParams)/sizeof(paramdef_t),NULL); - num_gnbs = GNBSParams[GNB_ACTIVE_GNBS_IDX].numelt; - AssertFatal (num_gnbs >0, - "Failed to parse config file no active gNodeBs in %s \n", GNB_CONFIG_STRING_ACTIVE_GNBS); - - sprintf(gtpupath,"%s.[%i].%s",GNB_CONFIG_STRING_GNB_LIST,0,GNB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG); - config_get(NETParams,sizeof(NETParams)/sizeof(paramdef_t),gtpupath); - char *cidr=NULL, *address = NULL; - int port; - if (NETParams[1].strptr != NULL) { - LOG_I(GTPU, "SA mode \n"); - address = strtok_r(gnb_ipv4_address_for_NGU, "/", &cidr); - port=gnb_port_for_NGU; - } else { - LOG_I(GTPU, "NSA mode \n"); - address = strtok_r(gnb_ipv4_address_for_S1U, "/", &cidr); - port=gnb_port_for_S1U; - } - - if (address) { - MessageDef *message; - message = itti_alloc_new_message(TASK_GNB_APP, 0, GTPV1U_REQ); - AssertFatal(message!=NULL,""); - IPV4_STR_ADDR_TO_INT_NWBO (address, GTPV1U_REQ(message).localAddr, "BAD IP ADDRESS FORMAT FOR gNB NG_U !\n" ); - LOG_I(GTPU,"Configuring GTPu address : %s -> %x\n",address,GTPV1U_REQ(message).localAddr); - GTPV1U_REQ(message).localPort = port; - strcpy(GTPV1U_REQ(message).localAddrStr,address); - sprintf(GTPV1U_REQ(message).localPortStr,"%d", port); - itti_send_msg_to_task (TASK_GTPV1_U, 0, message); // data model is wrong: gtpu doesn't have enb_id (or module_id) - } else - LOG_E(GTPU,"invalid address for NGU or S1U\n"); - -return 0; -} - int RCconfig_NR_NG(MessageDef *msg_p, uint32_t i) { int j,k = 0; @@ -1957,8 +1898,8 @@ int RCconfig_NR_DU_F1(MessageDef *msg_p, uint32_t i) { f1Setup->mib_length[k] = rrc->carrier.sizeof_MIB; NR_BCCH_DL_SCH_Message_t *bcch_message = NULL; - - asn_dec_rval_t dec_rval = uper_decode_complete( NULL, + asn_codec_ctx_t st={100*1000}; + asn_dec_rval_t dec_rval = uper_decode_complete( &st, &asn_DEF_NR_BCCH_DL_SCH_Message, (void **)&bcch_message, (const void *)rrc->carrier.SIB1, @@ -2252,16 +2193,24 @@ int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_up return(ret); } -static ngran_node_t get_node_type(void) +ngran_node_t get_node_type(void) { paramdef_t MacRLC_Params[] = MACRLCPARAMS_DESC; paramlist_def_t MacRLC_ParamList = {CONFIG_STRING_MACRLC_LIST,NULL,0}; paramdef_t GNBParams[] = GNBPARAMS_DESC; paramlist_def_t GNBParamList = {GNB_CONFIG_STRING_GNB_LIST,NULL,0}; + paramdef_t GNBE1Params[] = GNBE1PARAMS_DESC; + paramlist_def_t GNBE1ParamList = {GNB_CONFIG_STRING_E1_PARAMETERS, NULL, 0}; + config_getlist( &GNBParamList,GNBParams,sizeof(GNBParams)/sizeof(paramdef_t),NULL); + + if (GNBParamList.numelt == 0) // We have no valid configuration, let's return a default + return ngran_gNB; + config_getlist( &MacRLC_ParamList,MacRLC_Params,sizeof(MacRLC_Params)/sizeof(paramdef_t), NULL); - config_getlist( &GNBParamList,GNBParams,sizeof(GNBParams)/sizeof(paramdef_t),NULL); - + char aprefix[MAX_OPTNAME_SIZE*2 + 8]; + sprintf(aprefix, "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, 0); + config_getlist( &GNBE1ParamList, GNBE1Params, sizeof(GNBE1Params)/sizeof(paramdef_t), aprefix); if ( MacRLC_ParamList.numelt > 0) { RC.nb_nr_macrlc_inst = MacRLC_ParamList.numelt; for (int j = 0; j < RC.nb_nr_macrlc_inst; j++) { @@ -2271,9 +2220,16 @@ static ngran_node_t get_node_type(void) } } - if (strcmp(*(GNBParamList.paramarray[0][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "f1") == 0) - return ngran_gNB_CU; - else if (macrlc_has_f1 == 0) + if (strcmp(*(GNBParamList.paramarray[0][GNB_TRANSPORT_S_PREFERENCE_IDX].strptr), "f1") == 0) { + if ( GNBE1ParamList.paramarray == NULL || GNBE1ParamList.numelt == 0 ) + return ngran_gNB_CU; + else if (strcmp(*(GNBE1ParamList.paramarray[0][GNB_CONFIG_E1_CU_TYPE_IDX].strptr), "cp") == 0) + return ngran_gNB_CUCP; + else if (strcmp(*(GNBE1ParamList.paramarray[0][GNB_CONFIG_E1_CU_TYPE_IDX].strptr), "up") == 0) + return ngran_gNB_CUUP; + else + return ngran_gNB_CU; + } else if (macrlc_has_f1 == 0) return ngran_gNB; else return ngran_gNB_DU; @@ -2309,9 +2265,7 @@ void nr_read_config_and_init(void) { RCconfig_NRRRC(msg_p,gnb_id, RC.nrrrc[gnb_id]); } - if (NODE_IS_CU(RC.nrrrc[0]->node_type)) { + if (NODE_IS_CU(RC.nrrrc[0]->node_type) && RC.nrrrc[0]->node_type != ngran_gNB_CUCP) { pdcp_layer_init(); -// nr_DRB_preconfiguration(0x1234); - rrc_init_nr_global_param(); } } diff --git a/openair2/GNB_APP/gnb_config.h b/openair2/GNB_APP/gnb_config.h index b2a9ece078..3089753cff 100644 --- a/openair2/GNB_APP/gnb_config.h +++ b/openair2/GNB_APP/gnb_config.h @@ -41,6 +41,7 @@ #include "s1ap_messages_types.h" #include "ngap_messages_types.h" #include "f1ap_messages_types.h" +#include "e1ap_messages_types.h" #include "rrc_messages_types.h" #include "intertask_interface.h" @@ -94,7 +95,6 @@ extern void NRRCconfig_RU(void); extern void RCconfig_nr_prs(void); extern void RCconfig_NR_L1(void); extern void RCconfig_nr_macrlc(void); -extern int RCconfig_nr_gtpu(void ); extern void NRRCConfig(void); //void enb_config_display(void); @@ -107,6 +107,8 @@ int RCconfig_NR_DU_F1(MessageDef *msg_p, uint32_t i); int gNB_app_handle_f1ap_setup_resp(f1ap_setup_resp_t *resp); int gNB_app_handle_f1ap_gnb_cu_configuration_update(f1ap_gnb_cu_configuration_update_t *gnb_cu_cfg_update); void nr_read_config_and_init(void); +MessageDef *RCconfig_NR_CU_E1(bool separate_CUUP_process); +ngran_node_t get_node_type(void); #endif /* GNB_CONFIG_H_ */ /** @} */ diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index bf5e9f9660..8b62b7720b 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -34,6 +34,7 @@ #define __GNB_APP_GNB_PARAMDEF__H__ #include "common/config/config_paramdesc.h" +#include "common/ngran_types.h" #include "RRC_nr_paramsvalues.h" @@ -369,6 +370,27 @@ typedef enum { /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/ +/* E1 configuration section */ +#define GNB_CONFIG_STRING_E1_PARAMETERS "E1_INTERFACE" + +#define GNB_CONFIG_E1_CU_TYPE_IDX 0 +#define GNB_CONFIG_E1_IPV4_ADDRESS_CUCP 1 +#define GNB_CONFIG_E1_IPV4_ADDRESS_CUUP 2 +#define GNB_CONFIG_E1_CN_SUPPORT 3 + +#define GNB_CONFIG_STRING_E1_CU_TYPE "type" +#define GNB_CONFIG_STRING_E1_IPV4_ADDRESS_CUCP "ipv4_cucp" +#define GNB_CONFIG_STRING_E1_IPV4_ADDRESS_CUUP "ipv4_cuup" +#define GNB_CONFIG_STRING_E1_CN_SUPPORT "cn_support" + +// clang-format off +#define GNBE1PARAMS_DESC { \ + {GNB_CONFIG_STRING_E1_CU_TYPE, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ + {GNB_CONFIG_STRING_E1_IPV4_ADDRESS_CUCP, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ + {GNB_CONFIG_STRING_E1_IPV4_ADDRESS_CUUP, NULL, 0, strptr:NULL, defstrval:NULL, TYPE_STRING, 0}, \ + {GNB_CONFIG_STRING_E1_CN_SUPPORT, NULL, 0, uptr :NULL, defintval:1L, TYPE_UINT, 0} \ + } +// clang-format on /* L1 configuration section names */ #define CONFIG_STRING_L1_LIST "L1s" #define CONFIG_STRING_L1_CONFIG "l1_config" diff --git a/openair2/LAYER2/MAC/mac_proto.h b/openair2/LAYER2/MAC/mac_proto.h index af94ef3ae0..abb6ba0ec3 100644 --- a/openair2/LAYER2/MAC/mac_proto.h +++ b/openair2/LAYER2/MAC/mac_proto.h @@ -664,8 +664,6 @@ int remove_ue_list(UE_list_t *listP, int UE_id); void dump_ue_list(UE_list_t *listP); void init_ue_list(UE_list_t *listP); int UE_num_active_CC(UE_info_t *listP, int ue_idP); -int UE_PCCID(module_id_t mod_idP, int ue_idP); -rnti_t UE_RNTI(module_id_t mod_idP, int ue_idP); uint8_t find_rb_table_index(uint8_t average_rbs); diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index 6d592b797f..e9d41986c5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -249,14 +249,11 @@ void mac_top_init_gNB(ngran_node_t node_type) AssertFatal(rlc_module_init(1) == 0,"Could not initialize RLC layer\n"); // These should be out of here later - pdcp_layer_init(); + if (get_softmodem_params()->usim_test == 0 ) pdcp_layer_init(); if(IS_SOFTMODEM_NOS1 && get_softmodem_params()->phy_test) nr_DRB_preconfiguration(0x1234); - rrc_init_nr_global_param(); - - } else { RC.nrmac = NULL; } diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.c new file mode 100644 index 0000000000..9a584a3b27 --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.c @@ -0,0 +1,59 @@ +/* + * 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 + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "nr_pdcp_e1_api.h" + +void e1_add_drb(int is_gnb, + uint64_t ue_id, + struct NR_DRB_ToAddMod *s, + int ciphering_algorithm, + int integrity_algorithm, + unsigned char *ciphering_key, + unsigned char *integrity_key) +{ + add_drb_am(is_gnb, ue_id, s, ciphering_algorithm, integrity_algorithm, + ciphering_key, integrity_key); + LOG_I(PDCP, "%s:%s:%d: added DRB for UE ID %ld\n", __FILE__, __FUNCTION__, __LINE__, ue_id); +} + +void nr_pdcp_e1_add_drbs(eNB_flag_t enb_flag, + uint64_t ue_id, + NR_DRB_ToAddModList_t *const drb2add_list, + const uint8_t security_modeP, + uint8_t *const kUPenc, + uint8_t *const kUPint) { + + if (drb2add_list != NULL) { + for (int i = 0; i < drb2add_list->list.count; i++) { + e1_add_drb(enb_flag, ue_id, drb2add_list->list.array[i], + security_modeP & 0x0f, (security_modeP >> 4) & 0x0f, + kUPenc, kUPint); + } + } else + LOG_W(PDCP, "%s with void list\n", __FUNCTION__); + if (kUPenc) + free(kUPenc); + if (kUPint) + free(kUPint); +} diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h b/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h new file mode 100644 index 0000000000..a4b44fb74a --- /dev/null +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h @@ -0,0 +1,53 @@ +/* + * 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 + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include "common/utils/oai_asn1.h" +#include "NR_RadioBearerConfig.h" + +#include "pdcp.h" +#include "LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include +#include "openair2/SDAP/nr_sdap/nr_sdap.h" + +void e1_add_drb(int is_gnb, + uint64_t ue_id, + struct NR_DRB_ToAddMod *s, + int ciphering_algorithm, + int integrity_algorithm, + unsigned char *ciphering_key, + unsigned char *integrity_key); + +void nr_pdcp_e1_add_drbs(eNB_flag_t enb_flag, + uint64_t ue_id, + NR_DRB_ToAddModList_t *const drb2add_list, + const uint8_t security_modeP, + uint8_t *const kUPenc, + uint8_t *const kUPint); + +void add_drb_am(int is_gnb, ue_id_t rntiMaybeUEid, struct NR_DRB_ToAddMod *s, + int ciphering_algorithm, + int integrity_algorithm, + unsigned char *ciphering_key, + unsigned char *integrity_key); + diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index fd1430420c..190757d74c 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -38,6 +38,8 @@ #include "LAYER2/nr_rlc/nr_rlc_oai_api.h" #include #include "openair2/SDAP/nr_sdap/nr_sdap.h" +#include "nr_pdcp_e1_api.h" +#include "gnb_config.h" #include "executables/softmodem-common.h" #define TODO do { \ @@ -56,10 +58,11 @@ static int nr_pdcp_current_time_last_subframe; hash_table_t *pdcp_coll_p; static uint64_t pdcp_optmask; -ngran_node_t node_type = ngran_gNB; uint8_t first_dcch = 0; uint8_t proto_agent_flag = 0; +static ngran_node_t node_type; + /****************************************************************************/ /* rlc_data_req queue - begin */ /****************************************************************************/ @@ -524,6 +527,10 @@ int pdcp_fifo_flush_sdus(const protocol_ctxt_t *const ctxt_pP) return 0; } +static void set_node_type() { + node_type = get_node_type(); +} + void pdcp_layer_init(void) { /* hack: be sure to initialize only once */ @@ -539,7 +546,9 @@ void pdcp_layer_init(void) nr_pdcp_ue_manager = new_nr_pdcp_ue_manager(1); - if ((RC.nrrrc == NULL) || (!NODE_IS_CU(RC.nrrrc[0]->node_type))) { + set_node_type(); + + if ((RC.nrrrc == NULL) || (!NODE_IS_CU(node_type))) { init_nr_rlc_data_req_queue(); } @@ -667,7 +676,7 @@ rb_found: ctxt.brOption = 0; ctxt.rntiMaybeUEid = ue->rntiMaybeUEid; - if (RC.nrrrc != NULL && NODE_IS_CU(RC.nrrrc[0]->node_type)) { + if (NODE_IS_CU(node_type)) { MessageDef *message_p = itti_alloc_new_message_sized(TASK_PDCP_ENB, 0, GTPV1U_TUNNEL_DATA_REQ, sizeof(gtpv1u_tunnel_data_req_t) @@ -688,7 +697,6 @@ rb_found: extern instance_t CUuniqInstance; itti_send_msg_to_task(TASK_GTPV1_U, CUuniqInstance, message_p); } else { - memblock = get_free_mem_block(size, __FUNCTION__); memcpy(memblock->data, buf, size); LOG_D(PDCP, "%s(): (drb %d) calling rlc_data_req size %d\n", __func__, rb_id, size); @@ -764,7 +772,7 @@ srb_found: LOG_D(PDCP, "%s(): (srb %d) calling rlc_data_req size %d\n", __func__, srb_id, size); //for (i = 0; i < size; i++) printf(" %2.2x", (unsigned char)memblock->data[i]); //printf("\n"); - if ((RC.nrrrc == NULL) || (!NODE_IS_CU(RC.nrrrc[0]->node_type))) { + if ((RC.nrrrc == NULL) || (!NODE_IS_CU(node_type))) { if (entity->is_gnb) { f1ap_dl_rrc_message_t dl_rrc = {.old_gNB_DU_ue_id = 0xFFFFFF, .rrc_container = (uint8_t *)buf, .rrc_container_length = size, .rnti = ue->rntiMaybeUEid, .srb_id = DCCH}; gNB_RRC_INST *rrc = RC.nrrrc[0]; @@ -1056,35 +1064,30 @@ void nr_DRB_preconfiguration(ue_id_t crntiMaybeUEid) drb_ToAddMod->reestablishPDCP = NULL; drb_ToAddMod->recoverPDCP = NULL; drb_ToAddMod->pdcp_Config = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config)); - drb_ToAddMod->pdcp_Config->drb = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config->drb)); - drb_ToAddMod->pdcp_Config->drb->discardTimer = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config->drb->discardTimer)); - *drb_ToAddMod->pdcp_Config->drb->discardTimer=NR_PDCP_Config__drb__discardTimer_infinity; - drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeUL = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeUL)); - *drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeUL = NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits; - drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeDL = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeDL)); - *drb_ToAddMod->pdcp_Config->drb->pdcp_SN_SizeDL = NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits; - drb_ToAddMod->pdcp_Config->drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed; - drb_ToAddMod->pdcp_Config->drb->headerCompression.choice.notUsed = 0; + asn1cCalloc(drb_ToAddMod->pdcp_Config->drb, drb); + asn1cCallocOne(drb->discardTimer, NR_PDCP_Config__drb__discardTimer_infinity); + asn1cCallocOne(drb->pdcp_SN_SizeUL, NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits); + asn1cCallocOne(drb->pdcp_SN_SizeDL, NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits); + drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed; + drb->headerCompression.choice.notUsed = 0; - drb_ToAddMod->pdcp_Config->drb->integrityProtection=NULL; - drb_ToAddMod->pdcp_Config->drb->statusReportRequired=NULL; - drb_ToAddMod->pdcp_Config->drb->outOfOrderDelivery=NULL; + drb->integrityProtection = NULL; + drb->statusReportRequired = NULL; + drb->outOfOrderDelivery = NULL; drb_ToAddMod->pdcp_Config->moreThanOneRLC = NULL; - drb_ToAddMod->pdcp_Config->t_Reordering = calloc(1,sizeof(*drb_ToAddMod->pdcp_Config->t_Reordering)); - *drb_ToAddMod->pdcp_Config->t_Reordering = NR_PDCP_Config__t_Reordering_ms0; + asn1cCallocOne(drb_ToAddMod->pdcp_Config->t_Reordering, NR_PDCP_Config__t_Reordering_ms0); drb_ToAddMod->pdcp_Config->ext1 = NULL; asn1cSeqAdd(&rbconfig->drb_ToAddModList->list,drb_ToAddMod); rbconfig->drb_ToReleaseList = NULL; - rbconfig->securityConfig = calloc(1,sizeof(*rbconfig->securityConfig)); - rbconfig->securityConfig->securityAlgorithmConfig = calloc(1,sizeof(*rbconfig->securityConfig->securityAlgorithmConfig)); - rbconfig->securityConfig->securityAlgorithmConfig->cipheringAlgorithm = NR_CipheringAlgorithm_nea0; - rbconfig->securityConfig->securityAlgorithmConfig->integrityProtAlgorithm=NULL; - rbconfig->securityConfig->keyToUse = calloc(1,sizeof(*rbconfig->securityConfig->keyToUse)); - *rbconfig->securityConfig->keyToUse = NR_SecurityConfig__keyToUse_master; + asn1cCalloc(rbconfig->securityConfig, secConf); + asn1cCalloc(secConf->securityAlgorithmConfig, secConfAlgo); + secConfAlgo->cipheringAlgorithm = NR_CipheringAlgorithm_nea0; + secConfAlgo->integrityProtAlgorithm = NULL; + asn1cCallocOne(secConf->keyToUse, NR_SecurityConfig__keyToUse_master); if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_RadioBearerConfig, (const void*)rbconfig); @@ -1108,11 +1111,7 @@ void nr_DRB_preconfiguration(ue_id_t crntiMaybeUEid) nr_pdcp_add_drbs(ctxt.enb_flag, ctxt.rntiMaybeUEid, rbconfig->drb_ToAddModList, 0, NULL, NULL, Rlc_Bearer_ToAdd_list); - nr_rrc_rlc_config_asn1_req (&ctxt, - (NR_SRB_ToAddModList_t *) NULL, - rbconfig->drb_ToAddModList, - rbconfig->drb_ToReleaseList, - Rlc_Bearer_ToAdd_list); + nr_rrc_rlc_config_asn1_req(&ctxt, (NR_SRB_ToAddModList_t *)NULL, rbconfig->drb_ToAddModList, rbconfig->drb_ToReleaseList, Rlc_Bearer_ToAdd_list); LOG_D(PDCP, "%s:%d: done RRC PDCP/RLC ASN1 request for UE ID/RNTI %ld\n", __FUNCTION__, __LINE__, ctxt.rntiMaybeUEid); } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c index 29aab4ab5a..60c002537a 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c +++ b/openair2/LAYER2/nr_rlc/nr_rlc_oai_api.c @@ -985,6 +985,7 @@ rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt if(rlc_bearer2add_list->list.array[j]->servedRadioBearer->present == NR_RLC_BearerConfig__servedRadioBearer_PR_srb_Identity){ if(srb2add_listP->list.array[i]->srb_Identity == rlc_bearer2add_list->list.array[j]->servedRadioBearer->choice.srb_Identity){ add_rlc_srb(rnti, srb2add_listP->list.array[i], rlc_bearer2add_list->list.array[j]); + LOG_D(RLC, "Add srb %ld\n", srb2add_listP->list.array[i]->srb_Identity); } } } diff --git a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h index 92d20d1aca..c7e42a0a0d 100644 --- a/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h +++ b/openair2/LAYER2/nr_rlc/nr_rlc_ue_manager.h @@ -28,6 +28,7 @@ typedef void nr_rlc_ue_manager_t; typedef struct nr_rlc_ue_t { int rnti; + ue_id_t ue_id; nr_rlc_entity_t *srb0; nr_rlc_entity_t *srb[3]; nr_rlc_entity_t *drb[MAX_DRBS_PER_UE]; diff --git a/openair2/MCE_APP/mce_app.c b/openair2/MCE_APP/mce_app.c index 37b461e819..e626a09a98 100644 --- a/openair2/MCE_APP/mce_app.c +++ b/openair2/MCE_APP/mce_app.c @@ -40,7 +40,8 @@ # include "intertask_interface.h" # include "s1ap_eNB.h" -#include "sctp_eNB_task.h" +# include "sctp_eNB_task.h" +# include "openair3/ocp-gtpu/gtp_itf.h" # include "x2ap_eNB.h" # include "x2ap_messages_types.h" diff --git a/openair2/RRC/LTE/rrc_eNB.c b/openair2/RRC/LTE/rrc_eNB.c index b4f7eece01..171e69ea59 100644 --- a/openair2/RRC/LTE/rrc_eNB.c +++ b/openair2/RRC/LTE/rrc_eNB.c @@ -5629,6 +5629,7 @@ rrc_eNB_generate_RRCConnectionSetup( case ngran_eNB_CU : case ngran_ng_eNB_CU : case ngran_gNB_CU : + case ngran_gNB_CUCP : // create an ITTI message /* TODO: F1 IDs ar missing in RRC */ message_p = itti_alloc_new_message (TASK_RRC_ENB, 0, F1AP_DL_RRC_MESSAGE); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 299e0acaa9..8dbc078c2c 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -295,11 +295,6 @@ uint8_t do_MIB_NR(gNB_RRC_INST *rrc,uint32_t frame) { 24); AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); - - if (enc_rval.encoded==-1) { - return(-1); - } - return((enc_rval.encoded+7)/8); } @@ -638,11 +633,6 @@ uint8_t do_SIB23_NR(rrc_gNB_carrier_data_t *carrier, 100); AssertFatal (enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); - - if (enc_rval.encoded==-1) { - return(-1); - } - return((enc_rval.encoded+7)/8); } @@ -690,11 +680,8 @@ int do_RRCReject(uint8_t Mod_id, buffer, 100); - if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); LOG_D(NR_RRC,"RRCReject Encoded %zd bits (%zd bytes)\n", enc_rval.encoded,(enc_rval.encoded+7)/8); @@ -1297,13 +1284,11 @@ int do_RRCSetup(rrc_gNB_ue_context_t *const ue_context_pP, (void *)&dl_ccch_msg, buffer, 1000); + - if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", + AssertFatal(enc_rval.encoded >0, "ASN1 message encoding failed (%s, %lu)!\n", enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } - + LOG_D(NR_RRC,"RRCSetup Encoded %zd bits (%zd bytes)\n", enc_rval.encoded,(enc_rval.encoded+7)/8); return((enc_rval.encoded+7)/8); @@ -1346,19 +1331,11 @@ uint8_t do_NR_SecurityModeCommand( buffer, 100); - if(enc_rval.encoded == -1) { - LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } + AssertFatal(enc_rval.encoded >0 , "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); LOG_D(NR_RRC, "[gNB %d] securityModeCommand for UE %lx Encoded %zd bits (%zd bytes)\n", ctxt_pP->module_id, ctxt_pP->rntiMaybeUEid, enc_rval.encoded, (enc_rval.encoded + 7) / 8); - if (enc_rval.encoded==-1) { - LOG_E(NR_RRC, "[gNB %d] ASN1 : securityModeCommand encoding failed for UE %lx\n", ctxt_pP->module_id, ctxt_pP->rntiMaybeUEid); - return(-1); - } - // rrc_ue_process_ueCapabilityEnquiry(0,1000,&dl_dcch_msg.message.choice.c1.choice.ueCapabilityEnquiry,0); // exit(-1); return((enc_rval.encoded+7)/8); @@ -1411,6 +1388,9 @@ uint8_t do_NR_SA_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP, (void *)sa_band_filter, req_freq_buf, 1024); + AssertFatal(enc_rval.encoded >0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_UE_CapabilityRequestFilterNR, (void *)sa_band_filter); @@ -1435,19 +1415,11 @@ uint8_t do_NR_SA_UECapabilityEnquiry( const protocol_ctxt_t *const ctxt_pP, buffer, 100); - if(enc_rval.encoded == -1) { - LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } - + AssertFatal(enc_rval.encoded >0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + LOG_D(NR_RRC, "[gNB %d] NR UECapabilityRequest for UE %lx Encoded %zd bits (%zd bytes)\n", ctxt_pP->module_id, ctxt_pP->rntiMaybeUEid, enc_rval.encoded, (enc_rval.encoded + 7) / 8); - if (enc_rval.encoded==-1) { - LOG_E(NR_RRC, "[gNB %d] ASN1 : NR UECapabilityRequest encoding failed for UE %lx\n", ctxt_pP->module_id, ctxt_pP->rntiMaybeUEid); - return(-1); - } - return((enc_rval.encoded+7)/8); } @@ -1480,11 +1452,8 @@ uint8_t do_NR_RRCRelease(uint8_t *buffer, (void *)&dl_dcch_msg, buffer, buffer_size); - if(enc_rval.encoded == -1) { - LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); return((enc_rval.encoded+7)/8); } @@ -1508,19 +1477,18 @@ int16_t do_RRCReconfiguration( NR_CellGroupConfig_t *cellGroupConfig) //------------------------------------------------------------------------------ { - NR_DL_DCCH_Message_t dl_dcch_msg; + NR_DL_DCCH_Message_t dl_dcch_msg={0}; asn_enc_rval_t enc_rval; NR_RRCReconfiguration_IEs_t *ie; unsigned char masterCellGroup_buf[1000]; - memset(&dl_dcch_msg, 0, sizeof(NR_DL_DCCH_Message_t)); dl_dcch_msg.message.present = NR_DL_DCCH_MessageType_PR_c1; - dl_dcch_msg.message.choice.c1 = CALLOC(1, sizeof(struct NR_DL_DCCH_MessageType__c1)); - dl_dcch_msg.message.choice.c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; + asn1cCalloc(dl_dcch_msg.message.choice.c1, c1);// = CALLOC(1, sizeof(struct NR_DL_DCCH_MessageType__c1)); + c1->present = NR_DL_DCCH_MessageType__c1_PR_rrcReconfiguration; - dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration = calloc(1, sizeof(NR_RRCReconfiguration_t)); - dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->rrc_TransactionIdentifier = Transaction_id; - dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration; + asn1cCalloc(c1->choice.rrcReconfiguration, rrcReconf); // = calloc(1, sizeof(NR_RRCReconfiguration_t)); + rrcReconf->rrc_TransactionIdentifier = Transaction_id; + rrcReconf->criticalExtensions.present = NR_RRCReconfiguration__criticalExtensions_PR_rrcReconfiguration; /******************** Radio Bearer Config ********************/ /* Configure Security */ @@ -1532,7 +1500,8 @@ int16_t do_RRCReconfiguration( // *security_config->keyToUse = NR_SecurityConfig__keyToUse_master; ie = calloc(1, sizeof(NR_RRCReconfiguration_IEs_t)); - if (SRB_configList || DRB_configList) { + if ((SRB_configList && SRB_configList->list.size) || + (DRB_configList && DRB_configList->list.size)) { ie->radioBearerConfig = calloc(1, sizeof(NR_RadioBearerConfig_t)); ie->radioBearerConfig->srb_ToAddModList = SRB_configList; ie->radioBearerConfig->drb_ToAddModList = DRB_configList; @@ -1561,42 +1530,42 @@ int16_t do_RRCReconfiguration( configuration); enc_rval = uper_encode_to_buffer(&asn_DEF_NR_CellGroupConfig, - NULL, - (void *)cellGroupConfig, - masterCellGroup_buf, - 1000); - if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "ASN1 message CellGroupConfig encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } + NULL, + (void *)cellGroupConfig, + masterCellGroup_buf, + 1000); + AssertFatal(enc_rval.encoded >0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, (const void *) cellGroupConfig); } ie->nonCriticalExtension->masterCellGroup = calloc(1,sizeof(OCTET_STRING_t)); - + ie->nonCriticalExtension->masterCellGroup->buf = masterCellGroup_buf; ie->nonCriticalExtension->masterCellGroup->size = (enc_rval.encoded+7)/8; } - + dl_dcch_msg.message.choice.c1->choice.rrcReconfiguration->criticalExtensions.choice.rrcReconfiguration = ie; - + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_DL_DCCH_Message, (void *)&dl_dcch_msg); } - + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_DL_DCCH_Message, - NULL, - (void *)&dl_dcch_msg, - buffer, - buffer_size); - - if(enc_rval.encoded == -1) { - LOG_I(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } - + NULL, + (void *)&dl_dcch_msg, + buffer, + buffer_size); + + AssertFatal(enc_rval.encoded >0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); + + LOG_D(NR_RRC,"[gNB %d] RRCReconfiguration for UE %lx Encoded %zd bits (%zd bytes)\n", + ctxt_pP->module_id, + ctxt_pP->rntiMaybeUEid, + enc_rval.encoded, + (enc_rval.encoded+7)/8); + return((enc_rval.encoded+7)/8); } @@ -1731,9 +1700,9 @@ uint8_t do_RRCSetupComplete(uint8_t Mod_id, uint8_t *buffer, size_t buffer_size, NR_UL_DCCH_Message_t ul_dcch_msg; NR_RRCSetupComplete_t *RrcSetupComplete; memset((void *)&ul_dcch_msg,0,sizeof(NR_UL_DCCH_Message_t)); - + uint8_t buf[6]; - + ul_dcch_msg.message.present = NR_UL_DCCH_MessageType_PR_c1; ul_dcch_msg.message.choice.c1 = CALLOC(1,sizeof(struct NR_UL_DCCH_MessageType__c1)); ul_dcch_msg.message.choice.c1->present = NR_UL_DCCH_MessageType__c1_PR_rrcSetupComplete; @@ -1746,7 +1715,7 @@ uint8_t do_RRCSetupComplete(uint8_t Mod_id, uint8_t *buffer, size_t buffer_size, // sizeof(*RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->nonCriticalExtension)); RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->selectedPLMN_Identity = sel_plmn_id; RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->registeredAMF = NULL; - + RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value = CALLOC(1, sizeof(struct NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value)); RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->present = NR_RRCSetupComplete_IEs__ng_5G_S_TMSI_Value_PR_ng_5G_S_TMSI; RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.size = 6; @@ -1757,23 +1726,23 @@ uint8_t do_RRCSetupComplete(uint8_t Mod_id, uint8_t *buffer, size_t buffer_size, RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.buf[3] = 0x78; RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.buf[4] = 0x9A; RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->ng_5G_S_TMSI_Value->choice.ng_5G_S_TMSI.buf[5] = 0xBC; - - memset(&RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->dedicatedNAS_Message,0,sizeof(OCTET_STRING_t)); - OCTET_STRING_fromBuf(&RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->dedicatedNAS_Message,dedicatedInfoNAS,dedicatedInfoNASLength); -if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { - xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); -} - -enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, - NULL, - (void *)&ul_dcch_msg, - buffer, - buffer_size); -AssertFatal(enc_rval.encoded > 0,"ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name,enc_rval.encoded); -LOG_D(NR_RRC,"RRCSetupComplete Encoded %zd bits (%zd bytes)\n",enc_rval.encoded,(enc_rval.encoded+7)/8); - -return((enc_rval.encoded+7)/8); + + memset(&RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->dedicatedNAS_Message,0,sizeof(OCTET_STRING_t)); + OCTET_STRING_fromBuf(&RrcSetupComplete->criticalExtensions.choice.rrcSetupComplete->dedicatedNAS_Message,dedicatedInfoNAS,dedicatedInfoNASLength); + if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { + xer_fprint(stdout, &asn_DEF_NR_UL_DCCH_Message, (void *)&ul_dcch_msg); + } + + enc_rval = uper_encode_to_buffer(&asn_DEF_NR_UL_DCCH_Message, + NULL, + (void *)&ul_dcch_msg, + buffer, + buffer_size); + AssertFatal(enc_rval.encoded > 0,"ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name,enc_rval.encoded); + LOG_D(NR_RRC,"RRCSetupComplete Encoded %zd bits (%zd bytes)\n",enc_rval.encoded,(enc_rval.encoded+7)/8); + + return((enc_rval.encoded+7)/8); } //------------------------------------------------------------------------------ @@ -1993,11 +1962,8 @@ NR_SRB_ToAddModList_t **SRB_configList buffer, 100); - if(enc_rval.encoded == -1) { - LOG_E(NR_RRC, "[gNB AssertFatal]ASN1 message encoding failed (%s, %lu)!\n", - enc_rval.failed_type->name, enc_rval.encoded); - return -1; - } + AssertFatal(enc_rval.encoded > 0, "ASN1 message encoding failed (%s, %lu)!\n", + enc_rval.failed_type->name, enc_rval.encoded); LOG_D(NR_RRC,"RRCReestablishment Encoded %u bits (%u bytes)\n", (uint32_t)enc_rval.encoded, (uint32_t)(enc_rval.encoded+7)/8); diff --git a/openair2/RRC/NR/cucp_cuup_direct.c b/openair2/RRC/NR/cucp_cuup_direct.c new file mode 100644 index 0000000000..12530c7b82 --- /dev/null +++ b/openair2/RRC/NR/cucp_cuup_direct.c @@ -0,0 +1,229 @@ +/* + * 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: + * conmnc_digit_lengtht@openairinterface.org + */ + +#include + +#include "cucp_cuup_if.h" +#include "platform_types.h" +#include "nr_rrc_defs.h" + +#include "softmodem-common.h" +#include "nr_rrc_proto.h" +#include "nr_rrc_extern.h" +#include "openair2/COMMON/e1ap_messages_types.h" +#include "UTIL/OSA/osa_defs.h" +#include "nr_pdcp/nr_pdcp_entity.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h" +#include +#include "openair3/ocp-gtpu/gtp_itf.h" +#include "rrc_gNB_GTPV1U.h" +#include "common/ran_context.h" +#include "openair2/F1AP/f1ap_common.h" +#include "openair2/E1AP/e1ap_common.h" +extern RAN_CONTEXT_t RC; + +void fill_e1ap_bearer_setup_resp(e1ap_bearer_setup_resp_t *resp, + e1ap_bearer_setup_req_t *const req, + instance_t gtpInst, + ue_id_t ue_id, + int remote_port, + in_addr_t my_addr) { + + resp->numPDUSessions = req->numPDUSessions; + transport_layer_addr_t dummy_address = {0}; + dummy_address.length = 32; // IPv4 + for (int i=0; i < req->numPDUSessions; i++) { + resp->pduSession[i].numDRBSetup = req->pduSession[i].numDRB2Setup; + for (int j=0; j < req->pduSession[i].numDRB2Setup; j++) { + DRB_nGRAN_to_setup_t *drb2Setup = req->pduSession[i].DRBnGRanList + j; + DRB_nGRAN_setup_t *drbSetup = resp->pduSession[i].DRBnGRanList + j; + + drbSetup->numUpParam = 1; + drbSetup->UpParamList[0].tlAddress = my_addr; + drbSetup->UpParamList[0].teId = newGtpuCreateTunnel(gtpInst, + (ue_id & 0xFFFF), + drb2Setup->id, + drb2Setup->id, + 0xFFFF, // We will set the right value from DU answer + -1, // no qfi + dummy_address, // We will set the right value from DU answer + remote_port, + cu_f1u_data_req, + NULL); + drbSetup->id = drb2Setup->id; + + drbSetup->numQosFlowSetup = drb2Setup->numQosFlow2Setup; + for (int k=0; k < drbSetup->numQosFlowSetup; k++) { + drbSetup->qosFlows[k].id = drb2Setup->qosFlows[k].id; + } + } + } +} + +void CU_update_UP_DL_tunnel(e1ap_bearer_setup_req_t *const req, instance_t instance, ue_id_t ue_id) { + for (int i=0; i < req->numPDUSessionsMod; i++) { + for (int j=0; j < req->pduSessionMod[i].numDRB2Modify; j++) { + DRB_nGRAN_to_setup_t *drb_p = req->pduSessionMod[i].DRBnGRanModList + j; + + transport_layer_addr_t newRemoteAddr; + newRemoteAddr.length = 32; // IPv4 + memcpy(newRemoteAddr.buffer, + &drb_p->DlUpParamList[0].tlAddress, + sizeof(in_addr_t)); + + GtpuUpdateTunnelOutgoingAddressAndTeid(instance, + (ue_id & 0xFFFF), + (ebi_t)drb_p->id, + *(in_addr_t*)&newRemoteAddr.buffer, + drb_p->DlUpParamList[0].teId); + } + } +} + +static int drb_config_gtpu_create(const protocol_ctxt_t *const ctxt_p, + rrc_gNB_ue_context_t *ue_context_p, + e1ap_bearer_setup_req_t *const req, + NR_DRB_ToAddModList_t *DRB_configList, + NR_SRB_ToAddModList_t *SRB_configList, + instance_t instance) { + + gtpv1u_gnb_create_tunnel_req_t create_tunnel_req={0}; + gtpv1u_gnb_create_tunnel_resp_t create_tunnel_resp={0}; + + for (int i=0; i < ue_context_p->ue_context.nb_of_pdusessions; i++) { + pdu_session_param_t *pdu = ue_context_p->ue_context.pduSession + i; + create_tunnel_req.pdusession_id[i] = pdu->param.pdusession_id; + create_tunnel_req.incoming_rb_id[i] = i + 1; + create_tunnel_req.outgoing_qfi[i] = req->pduSession[i].DRBnGRanList[0].qosFlows[0].id; + memcpy(&create_tunnel_req.dst_addr[i].buffer, + &pdu->param.upf_addr.buffer, + sizeof(uint8_t)*20); + create_tunnel_req.dst_addr[i].length = pdu->param.upf_addr.length; + create_tunnel_req.outgoing_teid[i] = pdu->param.gtp_teid; + } + create_tunnel_req.num_tunnels = ue_context_p->ue_context.nb_of_pdusessions; + create_tunnel_req.ue_id = ue_context_p->ue_context.rnti; + + int ret = gtpv1u_create_ngu_tunnel(getCxtE1(instance)->gtpInstN3, &create_tunnel_req, &create_tunnel_resp); + + if (ret != 0) { + LOG_E(NR_RRC,"rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ : gtpv1u_create_ngu_tunnel failed,start to release UE rnti %ld\n", + create_tunnel_req.ue_id); + return ret; + } + + nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP(ctxt_p, + &create_tunnel_resp); + + uint8_t *kRRCenc = NULL; + uint8_t *kRRCint = NULL; + uint8_t *kUPenc = NULL; + uint8_t *kUPint = NULL; + /* Derive the keys from kgnb */ + if (DRB_configList != NULL) { + nr_derive_key_up_enc(ue_context_p->ue_context.ciphering_algorithm, + ue_context_p->ue_context.kgnb, + &kUPenc); + nr_derive_key_up_int(ue_context_p->ue_context.integrity_algorithm, + ue_context_p->ue_context.kgnb, + &kUPint); + } + + nr_derive_key_rrc_enc(ue_context_p->ue_context.ciphering_algorithm, + ue_context_p->ue_context.kgnb, + &kRRCenc); + nr_derive_key_rrc_int(ue_context_p->ue_context.integrity_algorithm, + ue_context_p->ue_context.kgnb, + &kRRCint); + /* Refresh SRBs/DRBs */ + + LOG_D(NR_RRC,"Configuring PDCP DRBs/SRBs for UE %x\n",ue_context_p->ue_context.rnti); + + nr_pdcp_add_srbs(ctxt_p->enb_flag, ctxt_p->rntiMaybeUEid, + SRB_configList, + (ue_context_p->ue_context.integrity_algorithm << 4) + | ue_context_p->ue_context.ciphering_algorithm, + kRRCenc, + kRRCint); + + nr_pdcp_add_drbs(ctxt_p->enb_flag, ctxt_p->rntiMaybeUEid, + DRB_configList, + (ue_context_p->ue_context.integrity_algorithm << 4) + | ue_context_p->ue_context.ciphering_algorithm, + kUPenc, + kUPint, + get_softmodem_params()->sa ? ue_context_p->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); + + return ret; +} + +static NR_SRB_ToAddModList_t **generateSRB2_confList(gNB_RRC_UE_t *ue, NR_SRB_ToAddModList_t *SRB_configList, uint8_t xid) +{ + NR_SRB_ToAddModList_t **SRB_configList2 = NULL; + + SRB_configList2 = &ue->SRB_configList2[xid]; + if (*SRB_configList2 == NULL) { + *SRB_configList2 = CALLOC(1, sizeof(**SRB_configList2)); + NR_SRB_ToAddMod_t *SRB2_config = CALLOC(1, sizeof(*SRB2_config)); + SRB2_config->srb_Identity = 2; + asn1cSeqAdd(&(*SRB_configList2)->list, SRB2_config); + asn1cSeqAdd(&SRB_configList->list, SRB2_config); + } + + return SRB_configList2; +} +static void cucp_cuup_bearer_context_setup_direct(e1ap_bearer_setup_req_t *const req, instance_t instance) { + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)], req->rnti); + protocol_ctxt_t ctxt = {0}; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); + + fill_DRB_configList(&ctxt, ue_context_p); + + gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id]; + // Fixme: xid not random, but almost! + NR_SRB_ToAddModList_t **SRB_configList2 = generateSRB2_confList(&ue_context_p->ue_context, ue_context_p->ue_context.SRB_configList, ue_context_p->ue_context.pduSession[0].xid); + // GTP tunnel for UL + int ret = drb_config_gtpu_create(&ctxt, ue_context_p, req, ue_context_p->ue_context.DRB_configList, *SRB_configList2, rrc->e1_inst); + if (ret < 0) AssertFatal(false, "Unable to configure DRB or to create GTP Tunnel\n"); + + if(!NODE_IS_CU(RC.nrrrc[ctxt.module_id]->node_type)) { + rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p, NULL); + } else { + e1ap_bearer_setup_resp_t resp; // Used to store teids + int remote_port = RC.nrrrc[ctxt.module_id]->eth_params_s.remote_portd; + in_addr_t my_addr = inet_addr(RC.nrrrc[ctxt.module_id]->eth_params_s.my_addr); + instance_t gtpInst = getCxt(CUtype, instance)->gtpInst; + // GTP tunnel for DL + fill_e1ap_bearer_setup_resp(&resp, req, gtpInst, ue_context_p->ue_context.rnti, remote_port, my_addr); + + prepare_and_send_ue_context_modification_f1(ue_context_p, &resp); + } +} + +static void cucp_cuup_bearer_context_mod_direct(e1ap_bearer_setup_req_t *const req, instance_t instance) { + instance_t gtpInst = getCxt(CUtype, instance)->gtpInst; + CU_update_UP_DL_tunnel(req, gtpInst, req->rnti); +} + +void cucp_cuup_message_transfer_direct_init(gNB_RRC_INST *rrc) { + rrc->cucp_cuup.bearer_context_setup = cucp_cuup_bearer_context_setup_direct; + rrc->cucp_cuup.bearer_context_mod = cucp_cuup_bearer_context_mod_direct; +} diff --git a/openair2/RRC/NR/cucp_cuup_e1ap.c b/openair2/RRC/NR/cucp_cuup_e1ap.c new file mode 100644 index 0000000000..069629db0b --- /dev/null +++ b/openair2/RRC/NR/cucp_cuup_e1ap.c @@ -0,0 +1,61 @@ +/* + * 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: + * conmnc_digit_lengtht@openairinterface.org + */ + +#include "cucp_cuup_if.h" +#include + +#include "platform_types.h" +#include "nr_rrc_defs.h" + +#include "nr_rrc_proto.h" +#include "nr_rrc_extern.h" +#include "cucp_cuup_if.h" +#include "openair2/COMMON/e1ap_messages_types.h" +#include "UTIL/OSA/osa_defs.h" +#include +#include "common/ran_context.h" + +extern RAN_CONTEXT_t RC; + +static void cucp_cuup_bearer_context_setup_e1ap(e1ap_bearer_setup_req_t *const req, instance_t instance) { + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[GNB_INSTANCE_TO_MODULE_ID(instance)], req->rnti); + protocol_ctxt_t ctxt = {0}; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); + + fill_DRB_configList(&ctxt, ue_context_p); + MessageDef *msg_p = itti_alloc_new_message(TASK_CUCP_E1, instance, E1AP_BEARER_CONTEXT_SETUP_REQ); + e1ap_bearer_setup_req_t *bearer_req = &E1AP_BEARER_CONTEXT_SETUP_REQ(msg_p); + memcpy(bearer_req, req, sizeof(e1ap_bearer_setup_req_t)); + + itti_send_msg_to_task (TASK_CUCP_E1, instance, msg_p); +} + +static void cucp_cuup_bearer_context_mod_e1ap(e1ap_bearer_setup_req_t *const req, instance_t instance) { + MessageDef *msg = itti_alloc_new_message(TASK_CUCP_E1, instance, E1AP_BEARER_CONTEXT_MODIFICATION_REQ); + e1ap_bearer_setup_req_t *req_msg = &E1AP_BEARER_CONTEXT_SETUP_REQ(msg); + memcpy(req_msg, req, sizeof(*req)); + itti_send_msg_to_task(TASK_CUCP_E1, instance, msg); +} + +void cucp_cuup_message_transfer_e1ap_init(gNB_RRC_INST *rrc) { + rrc->cucp_cuup.bearer_context_setup = cucp_cuup_bearer_context_setup_e1ap; + rrc->cucp_cuup.bearer_context_mod = cucp_cuup_bearer_context_mod_e1ap; +} diff --git a/openair2/RRC/NR/cucp_cuup_if.h b/openair2/RRC/NR/cucp_cuup_if.h new file mode 100644 index 0000000000..079a32e850 --- /dev/null +++ b/openair2/RRC/NR/cucp_cuup_if.h @@ -0,0 +1,42 @@ +/* + * 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: + * conmnc_digit_lengtht@openairinterface.org + */ + +#ifndef CUCP_CUUP_IF_H +#define CUCP_CUUP_IF_H + +#include +#include "platform_types.h" + +struct e1ap_bearer_setup_req_s; +struct e1ap_bearer_setup_resp_s; +typedef void (*cucp_cuup_bearer_context_setup_func_t)(struct e1ap_bearer_setup_req_s *const req, instance_t instance); + +struct gNB_RRC_INST_s; +void cucp_cuup_message_transfer_direct_init(struct gNB_RRC_INST_s *rrc); +void cucp_cuup_message_transfer_e1ap_init(struct gNB_RRC_INST_s *rrc); +void fill_e1ap_bearer_setup_resp(struct e1ap_bearer_setup_resp_s *resp, + struct e1ap_bearer_setup_req_s *const req, + instance_t gtpInst, + ue_id_t ue_id, + int remote_port, + in_addr_t my_addr); +void CU_update_UP_DL_tunnel(struct e1ap_bearer_setup_req_s *const req, instance_t instance, ue_id_t ue_id); +#endif diff --git a/openair2/RRC/NR/nr_rrc_common.c b/openair2/RRC/NR/nr_rrc_common.c index 828e392460..85e9c71eaa 100644 --- a/openair2/RRC/NR/nr_rrc_common.c +++ b/openair2/RRC/NR/nr_rrc_common.c @@ -49,21 +49,14 @@ extern UE_MAC_INST *UE_mac_inst; extern mui_t rrc_gNB_mui; //----------------------------------------------------------------------------- -int rrc_init_nr_global_param(void){ - +void rrc_init_nr_srb_param(NR_LCHAN_DESC *chan) +{ rrc_rlc_register_rrc (rrc_data_ind, NULL); //register with rlc - DCCH_LCHAN_DESC.transport_block_size = 4; - DCCH_LCHAN_DESC.max_transport_blocks = 16; - DCCH_LCHAN_DESC.Delay_class = 1; - DTCH_DL_LCHAN_DESC.transport_block_size = 52; - DTCH_DL_LCHAN_DESC.max_transport_blocks = 20; - DTCH_DL_LCHAN_DESC.Delay_class = 1; - DTCH_UL_LCHAN_DESC.transport_block_size = 52; - DTCH_UL_LCHAN_DESC.max_transport_blocks = 20; - DTCH_UL_LCHAN_DESC.Delay_class = 1; - - return 0; + chan->transport_block_size = 4; + chan->max_transport_blocks = 16; + chan->Delay_class = 1; + return; } //----------------------------------------------------------------------------- diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 6b60d64842..b3026f6a0e 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -43,6 +43,7 @@ #include "common/platform_constants.h" #include "COMMON/platform_types.h" #include "mac_rrc_dl.h" +#include "cucp_cuup_if.h" //#include "COMMON/mac_rrc_primitives.h" @@ -416,7 +417,7 @@ typedef struct rrc_gNB_ue_context_s { // another key for protocol layers but should not be used as a key for RB tree uid_t local_uid; - /* UE id for initial connection to S1AP */ + /* UE id for initial connection to NGAP */ struct gNB_RRC_UE_s ue_context; } rrc_gNB_ue_context_t; @@ -476,6 +477,11 @@ typedef struct nr_mac_rrc_dl_if_s { dl_rrc_message_transfer_func_t dl_rrc_message_transfer; } nr_mac_rrc_dl_if_t; +typedef struct cucp_cuup_if_s { + cucp_cuup_bearer_context_setup_func_t bearer_context_setup; + cucp_cuup_bearer_context_setup_func_t bearer_context_mod; +} cucp_cuup_if_t; + //---NR---(completely change)--------------------- typedef struct gNB_RRC_INST_s { @@ -499,6 +505,9 @@ typedef struct gNB_RRC_INST_s { // RRC configuration gNB_RrcConfigurationReq configuration; + // gNB N3 GTPU instance + instance_t e1_inst; + // other PLMN parameters /// Mobile country code int mcc; @@ -527,6 +536,8 @@ typedef struct gNB_RRC_INST_s { nr_security_configuration_t security; nr_mac_rrc_dl_if_t mac_rrc; + cucp_cuup_if_t cucp_cuup; + } gNB_RRC_INST; #include "nr_rrc_proto.h" //should be put here otherwise compilation error diff --git a/openair2/RRC/NR/nr_rrc_extern.h b/openair2/RRC/NR/nr_rrc_extern.h index 7283b25be0..70493f1010 100644 --- a/openair2/RRC/NR/nr_rrc_extern.h +++ b/openair2/RRC/NR/nr_rrc_extern.h @@ -53,8 +53,7 @@ extern void* bigphys_malloc(int); //uint8_t RACH_TIME_ALLOC; extern uint16_t RACH_FREQ_ALLOC; -//uint8_t NB_RACH; -extern LCHAN_DESC BCCH_LCHAN_DESC,CCCH_LCHAN_DESC,DCCH_LCHAN_DESC,DTCH_DL_LCHAN_DESC,DTCH_UL_LCHAN_DESC; +// uint8_t NB_RACH; extern MAC_MEAS_T BCCH_MEAS_TRIGGER,CCCH_MEAS_TRIGGER,DCCH_MEAS_TRIGGER,DTCH_MEAS_TRIGGER; extern MAC_AVG_T BCCH_MEAS_AVG,CCCH_MEAS_AVG,DCCH_MEAS_AVG, DTCH_MEAS_AVG; @@ -63,7 +62,6 @@ extern UE_PF_PO_t UE_PF_PO[NFAPI_CC_MAX][MAX_MOBILES_PER_ENB]; extern pthread_mutex_t ue_pf_po_mutex; extern uint16_t reestablish_rnti_map[MAX_MOBILES_PER_ENB][2]; -char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigurationReq *configuration); #endif diff --git a/openair2/RRC/NR/nr_rrc_proto.h b/openair2/RRC/NR/nr_rrc_proto.h index 0d7e0e7f97..dc32e71aea 100644 --- a/openair2/RRC/NR/nr_rrc_proto.h +++ b/openair2/RRC/NR/nr_rrc_proto.h @@ -44,12 +44,7 @@ #include "NR_SecurityConfig.h" #define NR_MAX_SUPPORTED_DL_LAYERS 2 - -int rrc_init_nr_global_param(void); - -void rrc_config_nr_buffer(NR_SRB_INFO* Srb_info, - uint8_t Lchan_type, - uint8_t Role); +void rrc_init_nr_srb_param(NR_LCHAN_DESC *chan); uint16_t mac_rrc_nr_data_req(const module_id_t Mod_idP, const int CC_id, @@ -104,18 +99,6 @@ int generate_CG_Config(gNB_RRC_INST *rrc, NR_RRCReconfiguration_t *reconfig, NR_RadioBearerConfig_t *rbconfig); -void apply_macrlc_config(gNB_RRC_INST *rrc, - rrc_gNB_ue_context_t *const ue_context_pP, - const protocol_ctxt_t *const ctxt_pP ); - -void -rrc_gNB_generate_RRCSetup( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP, - const uint8_t *masterCellGroup, - int masterCellGroup_len, - NR_ServingCellConfigCommon_t *scc); - int parse_CG_ConfigInfo(gNB_RRC_INST *rrc, NR_CG_ConfigInfo_t *CG_ConfigInfo, x2ap_ENDC_sgnb_addition_req_t *m); void @@ -124,10 +107,7 @@ rrc_gNB_generate_SecurityModeCommand( rrc_gNB_ue_context_t *const ue_context_pP ); - uint8_t -rrc_gNB_get_next_transaction_identifier( - module_id_t gnb_mod_idP -); +unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP); void rrc_gNB_generate_UECapabilityEnquiry( @@ -209,6 +189,23 @@ rlc_op_status_t nr_rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt const NR_DRB_ToReleaseList_t * const drb2release_listP, struct NR_CellGroupConfig__rlc_BearerToAddModList *rlc_bearer2add_list); +void bearer_context_setup_direct(e1ap_bearer_setup_req_t *req, + instance_t instance); + +void bearer_context_setup_e1ap(e1ap_bearer_setup_req_t *req, + instance_t instance); + +void ue_cxt_mod_send_e1ap(MessageDef *msg, + instance_t instance); + +void ue_cxt_mod_direct(MessageDef *msg, + instance_t instance); + +void fill_DRB_configList(const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *ue_context_pP); + +void prepare_and_send_ue_context_modification_f1(rrc_gNB_ue_context_t *ue_context_p, + e1ap_bearer_setup_resp_t *e1ap_resp); void nr_pdcp_add_srbs(eNB_flag_t enb_flag, ue_id_t rntiMaybeUEid, NR_SRB_ToAddModList_t *const srb2add_list, const uint8_t security_modeP, uint8_t *const kRRCenc, uint8_t *const kUPint); void nr_pdcp_add_drbs(eNB_flag_t enb_flag, diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b5a63cdf0d..0e2d5dac29 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -99,6 +99,10 @@ #include #include #include "LAYER2/nr_rlc/nr_rlc_oai_api.h" +#include "openair2/LAYER2/nr_pdcp/nr_pdcp_e1_api.h" +#include "openair2/F1AP/f1ap_common.h" +#include "openair2/SDAP/nr_sdap/nr_sdap_entity.h" +#include "cucp_cuup_if.h" #include "BIT_STRING.h" #include "assertions.h" @@ -132,10 +136,12 @@ bool DURecvCb(protocol_ctxt_t *ctxt_pP, return true; } -void openair_nr_rrc_on(const protocol_ctxt_t *const ctxt_pP) { - LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_FMT" gNB:OPENAIR NR RRC IN....\n",PROTOCOL_NR_RRC_CTXT_ARGS(ctxt_pP)); - rrc_config_nr_buffer (&RC.nrrrc[ctxt_pP->module_id]->carrier.SI, BCCH, 1); - RC.nrrrc[ctxt_pP->module_id]->carrier.SI.Active = 1; +static void openair_nr_rrc_on(gNB_RRC_INST *rrc) +{ + NR_SRB_INFO *si = &rrc->carrier.SI; + si->Rx_buffer.payload_size = 0; + si->Tx_buffer.payload_size = 0; + si->Active = 1; } ///---------------------------------------------------------------------------------------------------------------/// @@ -193,27 +199,34 @@ static void init_NR_SI(gNB_RRC_INST *rrc, gNB_RrcConfigurationReq *configuration } } -static void rrc_gNB_mac_rrc_init(gNB_RRC_INST *rrc) +static void rrc_gNB_CU_DU_init(gNB_RRC_INST *rrc) { switch (rrc->node_type) { + case ngran_gNB_CUCP: + mac_rrc_dl_f1ap_init(&rrc->mac_rrc); + cucp_cuup_message_transfer_e1ap_init(rrc); + break; case ngran_gNB_CU: mac_rrc_dl_f1ap_init(&rrc->mac_rrc); + cucp_cuup_message_transfer_direct_init(rrc); break; + case ngran_gNB: + mac_rrc_dl_direct_init(&rrc->mac_rrc); + cucp_cuup_message_transfer_direct_init(rrc); + break; 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; - case ngran_gNB: - mac_rrc_dl_direct_init(&rrc->mac_rrc); - break; default: AssertFatal(0 == 1, "Unknown node type %d\n", rrc->node_type); break; } } -char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigurationReq *configuration) { +static void openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigurationReq *configuration) +{ protocol_ctxt_t ctxt = { 0 }; gNB_RRC_INST *rrc=RC.nrrrc[gnb_mod_idP]; PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, gnb_mod_idP, GNB_FLAG_YES, NOT_A_RNTI, 0, 0,gnb_mod_idP); @@ -225,7 +238,7 @@ char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigu AssertFatal(configuration!=NULL,"configuration input is null\n"); rrc->module_id = gnb_mod_idP; rrc->Nb_ue = 0; - rrc_gNB_mac_rrc_init(rrc); + rrc_gNB_CU_DU_init(rrc); uid_linear_allocator_init(&rrc->uid_allocator); RB_INIT(&rrc->rrc_ue_head); rrc->initial_id2_s1ap_ids = hashtable_create (NUMBER_OF_UE_MAX * 2, NULL, NULL); @@ -241,13 +254,12 @@ char openair_rrc_gNB_configuration(const module_id_t gnb_mod_idP, gNB_RrcConfigu rrc->cell_info_configured = 0; LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_FMT" Checking release \n",PROTOCOL_NR_RRC_CTXT_ARGS(&ctxt)); init_NR_SI(rrc, configuration); - rrc_init_nr_global_param(); - openair_nr_rrc_on(&ctxt); - return 0; -}//END openair_rrc_gNB_configuration + openair_nr_rrc_on(rrc); + return; +} // END openair_rrc_gNB_configuration - -void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_addition_req_t *m) { +static void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x2ap_ENDC_sgnb_addition_req_t *m) +{ struct NR_CG_ConfigInfo *cg_configinfo = NULL; asn_dec_rval_t dec_rval = uper_decode_complete(NULL, &asn_DEF_NR_CG_ConfigInfo, @@ -272,24 +284,20 @@ void rrc_gNB_process_AdditionRequestInformation(const module_id_t gnb_mod_idP, x dec_rval.consumed, (dec_rval.consumed +7/8)); } - //----------------------------------------------------------------------------- -uint8_t -rrc_gNB_get_next_transaction_identifier( - module_id_t gnb_mod_idP -) +unsigned int rrc_gNB_get_next_transaction_identifier(module_id_t gnb_mod_idP) //----------------------------------------------------------------------------- { - static uint8_t nr_rrc_transaction_identifier[NUMBER_OF_gNB_MAX]; - nr_rrc_transaction_identifier[gnb_mod_idP] = (nr_rrc_transaction_identifier[gnb_mod_idP] + 1) % NR_RRC_TRANSACTION_IDENTIFIER_NUMBER; - LOG_T(NR_RRC, "generated xid is %d\n", nr_rrc_transaction_identifier[gnb_mod_idP]); - return nr_rrc_transaction_identifier[gnb_mod_idP]; + 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; } -void apply_macrlc_config(gNB_RRC_INST *rrc, - rrc_gNB_ue_context_t *const ue_context_pP, - const protocol_ctxt_t *const ctxt_pP ) { - +static void apply_macrlc_config(gNB_RRC_INST *rrc, rrc_gNB_ue_context_t *const ue_context_pP, const protocol_ctxt_t *const ctxt_pP) +{ NR_CellGroupConfig_t *cgc = get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup : NULL; rrc_mac_config_req_gNB(rrc->module_id, rrc->configuration.pdsch_AntennaPorts, @@ -308,27 +316,18 @@ void apply_macrlc_config(gNB_RRC_INST *rrc, ue_context_pP->ue_context.DRB_configList, NULL, get_softmodem_params()->sa ? cgc->rlc_BearerToAddModList : NULL); - } //----------------------------------------------------------------------------- -void -rrc_gNB_generate_RRCSetup( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP, - const uint8_t *masterCellGroup, - int masterCellGroup_len, - NR_ServingCellConfigCommon_t *scc) +static void rrc_gNB_generate_RRCSetup(const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *const ue_context_pP, + const uint8_t *masterCellGroup, + int masterCellGroup_len, + NR_ServingCellConfigCommon_t *scc) //----------------------------------------------------------------------------- { LOG_I(NR_RRC, "rrc_gNB_generate_RRCSetup for RNTI %04lx\n", ctxt_pP->rntiMaybeUEid); - // T(T_GNB_RRC_SETUP, - // T_INT(ctxt_pP->module_id), - // T_INT(ctxt_pP->frame), - // T_INT(ctxt_pP->subframe), - // T_INT(ctxt_pP->rnti)); - gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; unsigned char buf[1024]; @@ -368,11 +367,7 @@ rrc_gNB_generate_RRCSetup( } //----------------------------------------------------------------------------- -void -rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( - const protocol_ctxt_t *const ctxt_pP, - const int CC_id -) +static void rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest(const protocol_ctxt_t *const ctxt_pP, const int CC_id) //----------------------------------------------------------------------------- { LOG_I(NR_RRC, "generate RRCSetup for RRCReestablishmentRequest \n"); @@ -440,10 +435,7 @@ rrc_gNB_generate_RRCSetup_for_RRCReestablishmentRequest( rrc_instance_p->mac_rrc.dl_rrc_message_transfer(ctxt_pP->module_id, &dl_rrc); } -void -rrc_gNB_generate_RRCReject( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *const ue_context_pP) +static void rrc_gNB_generate_RRCReject(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *const ue_context_pP) //----------------------------------------------------------------------------- { LOG_I(NR_RRC, "rrc_gNB_generate_RRCReject \n"); @@ -482,12 +474,7 @@ rrc_gNB_generate_RRCReject( /* * Process the rrc setup complete message from UE (SRB1 Active) */ -void -rrc_gNB_process_RRCSetupComplete( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *ue_context_pP, - NR_RRCSetupComplete_IEs_t *rrcSetupComplete -) +static void rrc_gNB_process_RRCSetupComplete(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP, NR_RRCSetupComplete_IEs_t *rrcSetupComplete) //----------------------------------------------------------------------------- { LOG_A(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" [RAPROC] Logical Channel UL-DCCH, " "processing NR_RRCSetupComplete from UE (SRB1 Active)\n", @@ -504,22 +491,9 @@ rrc_gNB_process_RRCSetupComplete( } //----------------------------------------------------------------------------- -void -rrc_gNB_generate_defaultRRCReconfiguration( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *ue_context_pP -) +static void rrc_gNB_generate_defaultRRCReconfiguration(const protocol_ctxt_t *const ctxt_pP, rrc_gNB_ue_context_t *ue_context_pP) //----------------------------------------------------------------------------- { - uint8_t buffer[RRC_BUF_SIZE]; - int16_t size; - /*NR_SRB_ToAddModList_t **SRB_configList2 = NULL; - NR_SRB_ToAddModList_t *SRB_configList = ue_context_pP->ue_context.SRB_configList; - NR_DRB_ToAddModList_t **DRB_configList = NULL; - NR_DRB_ToAddModList_t **DRB_configList2 = NULL; - NR_SRB_ToAddMod_t *SRB2_config = NULL; - NR_DRB_ToAddMod_t *DRB_config = NULL; - NR_SDAP_Config_t *sdap_config = NULL;*/ struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL; NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL; @@ -527,66 +501,6 @@ rrc_gNB_generate_defaultRRCReconfiguration( uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); /******************** Radio Bearer Config ********************/ - /* Configure SRB2 */ - /*SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; - if (*SRB_configList2) { - free(*SRB_configList2); - } - *SRB_configList2 = CALLOC(1, sizeof(**SRB_configList2)); - memset(*SRB_configList2, 0, sizeof(**SRB_configList2)); - SRB2_config = CALLOC(1, sizeof(*SRB2_config)); - SRB2_config->srb_Identity = 2; - asn1cSeqAdd(&(*SRB_configList2)->list, SRB2_config); - asn1cSeqAdd(&SRB_configList->list, SRB2_config);*/ - - /* Configure DRB */ - /*DRB_configList = &ue_context_pP->ue_context.DRB_configList; - if (*DRB_configList) { - free(*DRB_configList); - } - *DRB_configList = CALLOC(1, sizeof(**DRB_configList)); - memset(*DRB_configList, 0, sizeof(**DRB_configList)); - - DRB_configList2 = &ue_context_pP->ue_context.DRB_configList2[xid]; - if (*DRB_configList2) { - free(*DRB_configList2); - } - *DRB_configList2 = CALLOC(1, sizeof(**DRB_configList2)); - memset(*DRB_configList2, 0, sizeof(**DRB_configList2)); - - DRB_config = CALLOC(1, sizeof(*DRB_config)); - DRB_config->drb_Identity = 1; - DRB_config->cnAssociation = CALLOC(1, sizeof(*DRB_config->cnAssociation)); - DRB_config->cnAssociation->present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config; - // TODO sdap_Config - sdap_config = CALLOC(1, sizeof(NR_SDAP_Config_t)); - memset(sdap_config, 0, sizeof(NR_SDAP_Config_t)); - DRB_config->cnAssociation->choice.sdap_Config = sdap_config; - // TODO pdcp_Config - DRB_config->reestablishPDCP = NULL; - DRB_config->recoverPDCP = NULL; - DRB_config->pdcp_Config = calloc(1, sizeof(*DRB_config->pdcp_Config)); - DRB_config->pdcp_Config->drb = calloc(1,sizeof(*DRB_config->pdcp_Config->drb)); - DRB_config->pdcp_Config->drb->discardTimer = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->discardTimer)); - *DRB_config->pdcp_Config->drb->discardTimer = NR_PDCP_Config__drb__discardTimer_infinity; - DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL)); - *DRB_config->pdcp_Config->drb->pdcp_SN_SizeUL = NR_PDCP_Config__drb__pdcp_SN_SizeUL_len18bits; - DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL = calloc(1, sizeof(*DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL)); - *DRB_config->pdcp_Config->drb->pdcp_SN_SizeDL = NR_PDCP_Config__drb__pdcp_SN_SizeDL_len18bits; - DRB_config->pdcp_Config->drb->headerCompression.present = NR_PDCP_Config__drb__headerCompression_PR_notUsed; - DRB_config->pdcp_Config->drb->headerCompression.choice.notUsed = 0; - - DRB_config->pdcp_Config->drb->integrityProtection = NULL; - DRB_config->pdcp_Config->drb->statusReportRequired = NULL; - DRB_config->pdcp_Config->drb->outOfOrderDelivery = NULL; - DRB_config->pdcp_Config->moreThanOneRLC = NULL; - - DRB_config->pdcp_Config->t_Reordering = calloc(1, sizeof(*DRB_config->pdcp_Config->t_Reordering)); - *DRB_config->pdcp_Config->t_Reordering = NR_PDCP_Config__t_Reordering_ms100; - DRB_config->pdcp_Config->ext1 = NULL; - - asn1cSeqAdd(&(*DRB_configList)->list, DRB_config); - asn1cSeqAdd(&(*DRB_configList2)->list, DRB_config);*/ dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList)); @@ -594,7 +508,6 @@ rrc_gNB_generate_defaultRRCReconfiguration( for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { dedicatedNAS_Message = CALLOC(1, sizeof(NR_DedicatedNAS_Message_t)); - memset(dedicatedNAS_Message, 0, sizeof(OCTET_STRING_t)); OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer, ue_context_pP->ue_context.pduSession[i].param.nas_pdu.length); @@ -608,7 +521,6 @@ rrc_gNB_generate_defaultRRCReconfiguration( if (ue_context_pP->ue_context.nas_pdu_flag == 1) { dedicatedNAS_Message = CALLOC(1, sizeof(NR_DedicatedNAS_Message_t)); - memset(dedicatedNAS_Message, 0, sizeof(OCTET_STRING_t)); OCTET_STRING_fromBuf(dedicatedNAS_Message, (char *)ue_context_pP->ue_context.nas_pdu.buffer, ue_context_pP->ue_context.nas_pdu.length); @@ -625,21 +537,23 @@ rrc_gNB_generate_defaultRRCReconfiguration( NR_MeasConfig_t *measconfig = get_defaultMeasConfig(&rrc->configuration); gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; - memset(buffer, 0, sizeof(buffer)); - size = do_RRCReconfiguration(ctxt_pP, buffer, sizeof(buffer), - xid, - NULL, //*SRB_configList2, - NULL, //*DRB_configList, - NULL, - NULL, - NULL, - measconfig, - dedicatedNAS_MessageList, - ue_context_pP, - &rrc->carrier, - &rrc->configuration, - NULL, - ue_p->masterCellGroup); + uint8_t buffer[RRC_BUF_SIZE] = {0}; + int size = do_RRCReconfiguration(ctxt_pP, + buffer, + sizeof(buffer), + xid, + NULL, //*SRB_configList2, + NULL, //*DRB_configList, + NULL, + NULL, + NULL, + measconfig, + dedicatedNAS_MessageList, + ue_context_pP, + &rrc->carrier, + &rrc->configuration, + NULL, + ue_p->masterCellGroup); AssertFatal(size > 0, "cannot encode RRCReconfiguration in %s()\n", __func__); LOG_W(RRC, "do_RRCReconfiguration(): size %d\n", size); @@ -647,6 +561,7 @@ rrc_gNB_generate_defaultRRCReconfiguration( xer_fprint(stdout, &asn_DEF_NR_CellGroupConfig, ue_p->masterCellGroup); } + // suspicious if it is always malloced before ? free(ue_context_pP->ue_context.nas_pdu.buffer); LOG_DUMPMSG(NR_RRC, DEBUG_RRC,(char *)buffer, size, "[MSG] RRC Reconfiguration\n"); @@ -666,6 +581,8 @@ rrc_gNB_generate_defaultRRCReconfiguration( ue_context_pP->ue_context.rnti); switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { case ngran_gNB_CU: + case ngran_gNB_CUCP: + case ngran_gNB: nr_rrc_data_req(ctxt_pP, DCCH, rrc_gNB_mui++, @@ -682,28 +599,6 @@ rrc_gNB_generate_defaultRRCReconfiguration( AssertFatal(1==0,"nothing to do for DU\n"); break; - case ngran_gNB: - { - LOG_D(NR_RRC, "[FRAME %05d][RRC_gNB][MOD %u][][--- PDCP_DATA_REQ/%d Bytes (rrcReconfiguration to UE %x MUI %d) --->][PDCP][MOD %u][RB %u]\n", - ctxt_pP->frame, - ctxt_pP->module_id, - size, - ue_context_pP->ue_context.rnti, - rrc_gNB_mui, - ctxt_pP->module_id, - DCCH); - nr_rrc_data_req(ctxt_pP, - DCCH, - rrc_gNB_mui++, - SDU_CONFIRM_NO, - size, - buffer, - PDCP_TRANSMISSION_MODE_CONTROL); - // rrc_pdcp_config_asn1_req - // rrc_rlc_config_asn1_req - } - break; - default : LOG_W(NR_RRC, "Unknown node type %d\n", RC.nrrrc[ctxt_pP->module_id]->node_type); } @@ -735,37 +630,22 @@ rrc_gNB_generate_defaultRRCReconfiguration( } } -//----------------------------------------------------------------------------- -void -rrc_gNB_generate_dedicatedRRCReconfiguration( - const protocol_ctxt_t *const ctxt_pP, - rrc_gNB_ue_context_t *ue_context_pP, - NR_CellGroupConfig_t *cell_groupConfig_from_DU -) -//----------------------------------------------------------------------------- -{ +void fill_DRB_configList(const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *ue_context_pP) { + gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; NR_DRB_ToAddModList_t **DRB_configList = NULL; NR_DRB_ToAddModList_t **DRB_configList2 = NULL; - NR_SRB_ToAddModList_t *SRB_configList = ue_context_pP->ue_context.SRB_configList; - struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList - *dedicatedNAS_MessageList = NULL; - NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL; - uint8_t buffer[RRC_BUF_SIZE]; - uint16_t size; int qos_flow_index = 0; int pdu_sessions_done = 0; int i; uint8_t drb_id_to_setup_start = 0; uint8_t nb_drb_to_setup = rrc->configuration.drbs; long drb_priority[NGAP_MAX_DRBS_PER_UE]; - NR_CellGroupConfig_t *cellGroupConfig = NULL; uint8_t xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); - NR_SRB_ToAddModList_t **SRB_configList2 = generateSRB2_confList(ue_p, SRB_configList, xid); - DRB_configList = &ue_context_pP->ue_context.DRB_configList; if (*DRB_configList) { free(*DRB_configList); @@ -780,8 +660,6 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( *DRB_configList2 = CALLOC(1, sizeof(**DRB_configList2)); memset(*DRB_configList2, 0, sizeof(**DRB_configList2)); - dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList)); - for (i = 0; i < ue_context_pP->ue_context.setup_pdu_sessions; i++) { if (pdu_sessions_done >= ue_context_pP->ue_context.nb_of_pdusessions) { break; @@ -880,11 +758,42 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( asn1cSeqAdd(&(*DRB_configList)->list, DRB_config); asn1cSeqAdd(&(*DRB_configList2)->list, DRB_config); } + LOG_D(RRC, "DRB Priority %ld\n", drb_priority[drb_id]); // To supress warning for now } } ue_context_pP->ue_context.pduSession[i].status = PDU_SESSION_STATUS_DONE; ue_context_pP->ue_context.pduSession[i].xid = xid; + } +} + +//----------------------------------------------------------------------------- +void +rrc_gNB_generate_dedicatedRRCReconfiguration( + const protocol_ctxt_t *const ctxt_pP, + rrc_gNB_ue_context_t *ue_context_pP, + NR_CellGroupConfig_t *cell_groupConfig_from_DU +) +//----------------------------------------------------------------------------- +{ + gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; + long drb_priority[1] = {13}; // For now, we assume only one drb per pdu sessions with a default preiority (will be dynamique in future) + NR_CellGroupConfig_t *cellGroupConfig = NULL; + uint8_t buffer[RRC_BUF_SIZE]; + uint16_t size; + int xid = -1; + + int drb_id_to_setup_start = 1; + NR_DRB_ToAddModList_t *DRB_configList = ue_context_pP->ue_context.DRB_configList; + int nb_drb_to_setup = DRB_configList->list.count; + + struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList *dedicatedNAS_MessageList = NULL; + NR_DedicatedNAS_Message_t *dedicatedNAS_Message = NULL; + dedicatedNAS_MessageList = CALLOC(1, sizeof(struct NR_RRCReconfiguration_v1530_IEs__dedicatedNAS_MessageList)); + + for (int i=0; i < nb_drb_to_setup; i++) { + NR_DRB_ToAddMod_t *DRB_config = DRB_configList->list.array[i]; + if (drb_id_to_setup_start == 1) drb_id_to_setup_start = DRB_config->drb_Identity; if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { dedicatedNAS_Message = CALLOC(1, sizeof(NR_DedicatedNAS_Message_t)); @@ -899,6 +808,8 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( // TODO LOG_E(NR_RRC,"no NAS info (pdusession id %d)\n", i); } + + xid = ue_context_pP->ue_context.pduSession[i].xid; } /* If list is empty free the list and reset the address */ @@ -919,10 +830,14 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( cellGroupConfig = cell_groupConfig_from_DU; } + AssertFatal(xid > -1, "Invalid xid %d. No PDU sessions setup to configure.\n", xid); + NR_SRB_ToAddModList_t *SRB_configList2 = NULL; + SRB_configList2 = ue_context_pP->ue_context.SRB_configList2[xid]; + size = do_RRCReconfiguration(ctxt_pP, buffer, sizeof(buffer), xid, - *SRB_configList2, - *DRB_configList, + SRB_configList2, + DRB_configList, NULL, NULL, NULL, @@ -936,7 +851,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration( LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size,"[MSG] RRC Reconfiguration\n"); /* Free all NAS PDUs */ - for (i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { + for (int i = 0; i < ue_context_pP->ue_context.nb_of_pdusessions; i++) { if (ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer != NULL) { /* Free the NAS PDU buffer and invalidate it */ free(ue_context_pP->ue_context.pduSession[i].param.nas_pdu.buffer); @@ -1208,19 +1123,7 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( } memset(buffer, 0, sizeof(buffer)); - size = do_RRCReconfiguration(ctxt_pP, buffer, sizeof(buffer), xid, - NULL, - NULL, - *DRB_Release_configList2, - NULL, - NULL, - NULL, - dedicatedNAS_MessageList, - NULL, - NULL, - NULL, - NULL, - NULL); + size = do_RRCReconfiguration(ctxt_pP, buffer, sizeof(buffer), xid, NULL, NULL, *DRB_Release_configList2, NULL, NULL, NULL, dedicatedNAS_MessageList, NULL, NULL, NULL, NULL, NULL); ue_context_pP->ue_context.pdu_session_release_command_flag = 1; @@ -1273,10 +1176,6 @@ rrc_gNB_process_RRCReconfigurationComplete( ) { int drb_id; - uint8_t *kRRCenc = NULL; - uint8_t *kRRCint = NULL; - uint8_t *kUPenc = NULL; - uint8_t *kUPint = NULL; NR_DRB_ToAddModList_t *DRB_configList = ue_context_pP->ue_context.DRB_configList2[xid]; NR_SRB_ToAddModList_t *SRB_configList = ue_context_pP->ue_context.SRB_configList2[xid]; NR_DRB_ToReleaseList_t *DRB_Release_configList2 = ue_context_pP->ue_context.DRB_Release_configList2[xid]; @@ -1285,36 +1184,6 @@ rrc_gNB_process_RRCReconfigurationComplete( ue_context_pP->ue_context.ue_reestablishment_timer = 0; - /* Derive the keys from kgnb */ - if (DRB_configList != NULL) { - nr_derive_key_up_enc(ue_context_pP->ue_context.ciphering_algorithm, - ue_context_pP->ue_context.kgnb, - &kUPenc); - nr_derive_key_up_int(ue_context_pP->ue_context.integrity_algorithm, - ue_context_pP->ue_context.kgnb, - &kUPint); - } - - nr_derive_key_rrc_enc(ue_context_pP->ue_context.ciphering_algorithm, - ue_context_pP->ue_context.kgnb, - &kRRCenc); - nr_derive_key_rrc_int(ue_context_pP->ue_context.integrity_algorithm, - ue_context_pP->ue_context.kgnb, - &kRRCint); - /* Refresh SRBs/DRBs */ - - LOG_D(NR_RRC,"Configuring PDCP DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); - - nr_pdcp_add_srbs(ctxt_pP->enb_flag, ctxt_pP->rntiMaybeUEid, SRB_configList, (ue_context_pP->ue_context.integrity_algorithm << 4) | ue_context_pP->ue_context.ciphering_algorithm, kRRCenc, kRRCint); - - nr_pdcp_add_drbs(ctxt_pP->enb_flag, - ctxt_pP->rntiMaybeUEid, - DRB_configList, - (ue_context_pP->ue_context.integrity_algorithm << 4) | ue_context_pP->ue_context.ciphering_algorithm, - kUPenc, - kUPint, - get_softmodem_params()->sa ? ue_context_pP->ue_context.masterCellGroup->rlc_BearerToAddModList : NULL); - /* Refresh SRBs/DRBs */ if (!NODE_IS_CU(RC.nrrrc[ctxt_pP->module_id]->node_type)) { LOG_D(NR_RRC,"Configuring RLC DRBs/SRBs for UE %x\n",ue_context_pP->ue_context.rnti); @@ -1500,13 +1369,7 @@ rrc_gNB_generate_RRCReestablishment( } //----------------------------------------------------------------------------- -void -rrc_gNB_process_RRCConnectionReestablishmentComplete( - const protocol_ctxt_t *const ctxt_pP, - const rnti_t reestablish_rnti, - rrc_gNB_ue_context_t *ue_context_pP, - const uint8_t xid -) +void rrc_gNB_process_RRCConnectionReestablishmentComplete(const protocol_ctxt_t *const ctxt_pP, const rnti_t reestablish_rnti, rrc_gNB_ue_context_t *ue_context_pP, const uint8_t old_xid) //----------------------------------------------------------------------------- { LOG_I(NR_RRC, @@ -1524,12 +1387,12 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( uint8_t buffer[RRC_BUF_SIZE]; uint16_t size; - uint8_t next_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); + uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); int ret = 0; ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED; ue_context_pP->ue_context.ue_rrc_inactivity_timer = 1; // set rrc inactivity when UE goes into RRC_CONNECTED - ue_context_pP->ue_context.reestablishment_xid = next_xid; - SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[xid]; + ue_context_pP->ue_context.reestablishment_xid = new_xid; + SRB_configList2 = &ue_context_pP->ue_context.SRB_configList2[old_xid]; // get old configuration of SRB2 if (*SRB_configList2 != NULL) { @@ -1540,7 +1403,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( for (i = 0; (i < (*SRB_configList2)->list.count) && (i < 3); i++) { if ((*SRB_configList2)->list.array[i]->srb_Identity == 2 ) { - LOG_D(NR_RRC, "get SRB2_config from (ue_context_pP->ue_context.SRB_configList2[%d])\n", xid); + LOG_D(NR_RRC, "get SRB2_config from (ue_context_pP->ue_context.SRB_configList2[%d])\n", old_xid); SRB2_config = (*SRB_configList2)->list.array[i]; break; } @@ -1550,12 +1413,12 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( // SRB2_config = CALLOC(1, sizeof(*SRB2_config)); // SRB2_config->srb_Identity = 2; - SRB_configList2 = &(ue_context_pP->ue_context.SRB_configList2[next_xid]); - DRB_configList2 = &(ue_context_pP->ue_context.DRB_configList2[next_xid]); + SRB_configList2 = &(ue_context_pP->ue_context.SRB_configList2[new_xid]); + DRB_configList2 = &(ue_context_pP->ue_context.DRB_configList2[new_xid]); if (*SRB_configList2) { free(*SRB_configList2); - LOG_D(NR_RRC, "free(ue_context_pP->ue_context.SRB_configList2[%d])\n", next_xid); + LOG_D(NR_RRC, "free(ue_context_pP->ue_context.SRB_configList2[%d])\n", new_xid); } *SRB_configList2 = CALLOC(1, sizeof(**SRB_configList2)); @@ -1566,8 +1429,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( asn1cSeqAdd(&(*SRB_configList2)->list, SRB2_config); LOG_D(NR_RRC, "Add SRB2_config (srb_Identity:%ld) to ue_context_pP->ue_context.SRB_configList\n", SRB2_config->srb_Identity); - LOG_D(NR_RRC, "Add SRB2_config (srb_Identity:%ld) to ue_context_pP->ue_context.SRB_configList2[%d]\n", - SRB2_config->srb_Identity, next_xid); + LOG_D(NR_RRC, "Add SRB2_config (srb_Identity:%ld) to ue_context_pP->ue_context.SRB_configList2[%d]\n", SRB2_config->srb_Identity, new_xid); } else { // SRB configuration list only contains SRB1. LOG_W(NR_RRC,"SRB2 configuration does not exist in SRB configuration list\n"); @@ -1575,7 +1437,7 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( if (*DRB_configList2) { free(*DRB_configList2); - LOG_D(NR_RRC, "free(ue_context_pP->ue_context.DRB_configList2[%d])\n", next_xid); + LOG_D(NR_RRC, "free(ue_context_pP->ue_context.DRB_configList2[%d])\n", new_xid); } *DRB_configList2 = CALLOC(1, sizeof(**DRB_configList2)); @@ -1691,27 +1553,29 @@ rrc_gNB_process_RRCConnectionReestablishmentComplete( /* TODO parameters yet to process ... */ /* TODO should test if pdu session are Ok before! */ ue_context_pP->ue_context.pduSession[i].status = PDU_SESSION_STATUS_DONE; - ue_context_pP->ue_context.pduSession[i].xid = xid; + ue_context_pP->ue_context.pduSession[i].xid = old_xid; LOG_D(NR_RRC, "setting the status for the default DRB (index %d) to (%d,%s)\n", i, ue_context_pP->ue_context.pduSession[i].status, "PDU_SESSION_STATUS_DONE"); } memset(buffer, 0, sizeof(buffer)); - size = do_RRCReconfiguration(ctxt_pP, buffer, sizeof(buffer), - xid, + size = do_RRCReconfiguration(ctxt_pP, + buffer, + sizeof(buffer), + old_xid, *SRB_configList2, - DRB_configList, - NULL, - NULL, - NULL, - NULL, // MeasObj_list, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL); + DRB_configList, + NULL, + NULL, + NULL, + NULL, // MeasObj_list, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)buffer,size, "[MSG] RRC Reconfiguration\n"); @@ -2135,21 +1999,13 @@ int nr_rrc_gNB_decode_ccch(protocol_ctxt_t *const ctxt_pP, // SRB1 ue_context_p->ue_context.Srb1.Active = 1; ue_context_p->ue_context.Srb1.Srb_info.Srb_id = Idx; - memcpy(&ue_context_p->ue_context.Srb1.Srb_info.Lchan_desc[0], - &DCCH_LCHAN_DESC, - LCHAN_DESC_SIZE); - memcpy(&ue_context_p->ue_context.Srb1.Srb_info.Lchan_desc[1], - &DCCH_LCHAN_DESC, - LCHAN_DESC_SIZE); + rrc_init_nr_srb_param(&ue_context_p->ue_context.Srb1.Srb_info.Lchan_desc[0]); + rrc_init_nr_srb_param(&ue_context_p->ue_context.Srb1.Srb_info.Lchan_desc[1]); // SRB2: set it to go through SRB1 with id 1 (DCCH) ue_context_p->ue_context.Srb2.Active = 1; ue_context_p->ue_context.Srb2.Srb_info.Srb_id = Idx; - memcpy(&ue_context_p->ue_context.Srb2.Srb_info.Lchan_desc[0], - &DCCH_LCHAN_DESC, - LCHAN_DESC_SIZE); - memcpy(&ue_context_p->ue_context.Srb2.Srb_info.Lchan_desc[1], - &DCCH_LCHAN_DESC, - LCHAN_DESC_SIZE); + rrc_init_nr_srb_param(&ue_context_p->ue_context.Srb2.Srb_info.Lchan_desc[0]); + rrc_init_nr_srb_param(&ue_context_p->ue_context.Srb2.Srb_info.Lchan_desc[1]); rrc_gNB_generate_RRCReestablishment(ctxt_pP, ue_context_p, 0); @@ -2483,7 +2339,6 @@ rrc_gNB_decode_dcch( nr_rrc_pdcp_config_security(ctxt_pP, ue_context_p, 1); rrc_gNB_generate_UECapabilityEnquiry(ctxt_pP, ue_context_p); - //rrc_gNB_generate_defaultRRCReconfiguration(ctxt_pP, ue_context_p); break; case NR_UL_DCCH_MessageType__c1_PR_securityModeFailure: LOG_DUMPMSG(NR_RRC,DEBUG_RRC,(char *)Rx_sdu,sdu_sizeP, @@ -3499,6 +3354,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, const ch } if (ue_context_p->ue_context.established_pdu_sessions_flag == 1) { + fill_DRB_configList(&ctxt, ue_context_p); rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p, cellGroupConfig); } else { rrc_gNB_generate_defaultRRCReconfiguration(&ctxt, ue_context_p); @@ -3511,10 +3367,31 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, const ch static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, const char *msg_name, instance_t instance){ - f1ap_ue_context_setup_t * resp=&F1AP_UE_CONTEXT_SETUP_RESP(msg_p); + f1ap_ue_context_setup_t *resp=&F1AP_UE_CONTEXT_SETUP_RESP(msg_p); protocol_ctxt_t ctxt = {.rntiMaybeUEid = resp->rnti, .module_id = instance, .instance = instance, .enb_flag = 1, .eNB_index = instance}; gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id]; struct rrc_gNB_ue_context_s *ue_context_p = rrc_gNB_get_ue_context(rrc, ctxt.rntiMaybeUEid); + + e1ap_bearer_setup_req_t req = {0}; + req.numPDUSessionsMod = ue_context_p->ue_context.nb_of_pdusessions; + req.gNB_cu_cp_ue_id = ue_context_p->ue_context.gNB_ue_ngap_id; + req.rnti = ue_context_p->ue_context.rnti; + for (int i=0; i < req.numPDUSessionsMod; i++) { + req.pduSessionMod[i].numDRB2Modify = resp->drbs_to_be_setup_length; + for (int j=0; j < resp->drbs_to_be_setup_length; j++) { + f1ap_drb_to_be_setup_t *drb_f1 = resp->drbs_to_be_setup + j; + DRB_nGRAN_to_setup_t *drb_e1 = req.pduSessionMod[i].DRBnGRanModList + j; + + 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; + } + } + + // send the F1 response message up to update F1-U tunnel info + rrc->cucp_cuup.bearer_context_mod(&req, instance); + NR_CellGroupConfig_t *cellGroupConfig = NULL; if(resp->du_to_cu_rrc_information->cellGroupConfig!=NULL){ @@ -3803,6 +3680,98 @@ void nr_rrc_subframe_process(protocol_ctxt_t *const ctxt_pP, const int CC_id) { } } +int rrc_gNB_process_e1_setup_req(e1ap_setup_req_t *req, instance_t instance) { + + AssertFatal(req->supported_plmns <= PLMN_LIST_MAX_SIZE, "Supported PLMNs is more than PLMN_LIST_MAX_SIZE\n"); + gNB_RRC_INST *rrc = RC.nrrrc[0]; //TODO: remove hardcoding of RC index here + MessageDef *msg_p = itti_alloc_new_message(TASK_RRC_GNB, instance, E1AP_SETUP_RESP); + + e1ap_setup_resp_t *resp = &E1AP_SETUP_RESP(msg_p); + resp->transac_id = req->transac_id; + + for (int i=0; i < req->supported_plmns; i++) { + if (rrc->configuration.mcc[i] == req->plmns[i].mcc && + rrc->configuration.mnc[i] == req->plmns[i].mnc) { + LOG_E(NR_RRC, "PLMNs received from CUUP (mcc:%d, mnc:%d) did not match with PLMNs in RRC (mcc:%d, mnc:%d)\n", + req->plmns[i].mcc, req->plmns[i].mnc, rrc->configuration.mcc[i], rrc->configuration.mnc[i]); + return -1; + } + } + + itti_send_msg_to_task(TASK_CUCP_E1, instance, msg_p); + + return 0; +} + +void prepare_and_send_ue_context_modification_f1(rrc_gNB_ue_context_t *ue_context_p, + e1ap_bearer_setup_resp_t *e1ap_resp) { + + /*Generate a UE context modification request message towards the DU to instruct the DU + *for SRB2 and DRB configuration and get the updates on master cell group config from the DU*/ + + protocol_ctxt_t ctxt = {0}; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); + // TODO: So many hard codings + MessageDef *message_p; + message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_UE_CONTEXT_MODIFICATION_REQ); + f1ap_ue_context_setup_t *req=&F1AP_UE_CONTEXT_MODIFICATION_REQ(message_p); + req->rnti = ue_context_p->ue_context.rnti; + req->mcc = RC.nrrrc[ctxt.module_id]->configuration.mcc[0]; + req->mnc = RC.nrrrc[ctxt.module_id]->configuration.mnc[0]; + req->mnc_digit_length = RC.nrrrc[ctxt.module_id]->configuration.mnc_digit_length[0]; + req->nr_cellid = RC.nrrrc[ctxt.module_id]->nr_cellid; + + /*Instruction towards the DU for SRB2 configuration*/ + req->srbs_to_be_setup = malloc(1*sizeof(f1ap_srb_to_be_setup_t)); + req->srbs_to_be_setup_length = 1; + f1ap_srb_to_be_setup_t *SRBs=req->srbs_to_be_setup; + SRBs[0].srb_id = 2; + SRBs[0].lcid = 2; + + /*Instruction towards the DU for DRB configuration and tunnel creation*/ + req->drbs_to_be_setup_length = e1ap_resp->pduSession[0].numDRBSetup; + req->drbs_to_be_setup = malloc(1*sizeof(f1ap_drb_to_be_setup_t)*req->drbs_to_be_setup_length); + for (int i=0; i < e1ap_resp->pduSession[0].numDRBSetup; i++) { + f1ap_drb_to_be_setup_t *DRBs = req->drbs_to_be_setup + i; + DRBs[i].drb_id = e1ap_resp->pduSession[0].DRBnGRanList[i].id; + DRBs[i].rlc_mode = RLC_MODE_AM; + DRBs[i].up_ul_tnl[0].tl_address = e1ap_resp->pduSession[0].DRBnGRanList[i].UpParamList[0].tlAddress; + DRBs[i].up_ul_tnl[0].port = RC.nrrrc[ctxt.module_id]->eth_params_s.my_portd; + DRBs[i].up_ul_tnl[0].teid = e1ap_resp->pduSession[0].DRBnGRanList[i].UpParamList[0].teId; + DRBs[i].up_ul_tnl_length = 1; + } + + itti_send_msg_to_task (TASK_CU_F1, ctxt.module_id, message_p); +} + +void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp, instance_t instance) { + // Find the UE context from UE ID and send ITTI message to F1AP to send UE context modification message to DU + + uint16_t ue_initial_id = 0; // Making an invalid UE initial ID + rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_from_ngap_ids(instance, ue_initial_id, resp->gNB_cu_cp_ue_id); + protocol_ctxt_t ctxt = {0}; + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); + + gtpv1u_gnb_create_tunnel_resp_t create_tunnel_resp={0}; + create_tunnel_resp.num_tunnels = resp->numPDUSessions; + for (int i=0; i < resp->numPDUSessions; i++) { + create_tunnel_resp.pdusession_id[i] = resp->pduSession[i].id; + create_tunnel_resp.gnb_NGu_teid[i] = resp->pduSession[i].teId; + memcpy(create_tunnel_resp.gnb_addr.buffer, + &resp->pduSession[i].tlAddress, + sizeof(in_addr_t)); + create_tunnel_resp.gnb_addr.length = sizeof(in_addr_t); // IPv4 byte length + } + + nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP(&ctxt, + &create_tunnel_resp); + + ue_context_p->ue_context.setup_pdu_sessions += resp->numPDUSessions; + + // TODO: SV: combine e1ap_bearer_setup_req_t and e1ap_bearer_setup_resp_t and minimize assignments + prepare_and_send_ue_context_modification_f1(ue_context_p, resp); +} + static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults) { DevAssert(measresults->measResultServingMOList.list.count >= 1); @@ -4021,6 +3990,15 @@ void *rrc_gnb_task(void *args_p) { rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(msg_p, msg_name_p, instance); break; + case E1AP_SETUP_REQ: + LOG_I(NR_RRC, "Received E1AP_SETUP_REQ for instance %d\n", (int)instance); + rrc_gNB_process_e1_setup_req(&E1AP_SETUP_REQ(msg_p), instance); + break; + + case E1AP_BEARER_CONTEXT_SETUP_RESP: + LOG_I(NR_RRC, "Received E1AP_BEARER_CONTEXT_SETUP_RESP for instance %d\n", (int)instance); + rrc_gNB_process_e1_bearer_context_setup_resp(&E1AP_BEARER_CONTEXT_SETUP_RESP(msg_p), instance); + case NGAP_PAGING_IND: rrc_gNB_process_PAGING_IND(msg_p, msg_name_p, instance); break; @@ -4063,6 +4041,7 @@ rrc_gNB_generate_SecurityModeCommand( switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { case ngran_gNB_CU: + case ngran_gNB_CUCP: // create an ITTI message memcpy(ue_context_pP->ue_context.Srb1.Srb_info.Tx_buffer.Payload, buffer, size); ue_context_pP->ue_context.Srb1.Srb_info.Tx_buffer.payload_size = size; @@ -4125,6 +4104,7 @@ rrc_gNB_generate_UECapabilityEnquiry( size); switch (RC.nrrrc[ctxt_pP->module_id]->node_type) { case ngran_gNB_CU: + case ngran_gNB_CUCP: nr_rrc_data_req( ctxt_pP, DCCH, @@ -4352,3 +4332,4 @@ void nr_rrc_trigger(protocol_ctxt_t *ctxt, int CC_id, int frame, int subframe) LOG_D(NR_RRC, "Time in RRC: %u/ %u \n", frame, subframe); itti_send_msg_to_task(TASK_RRC_GNB, ctxt->module_id, message_p); } + diff --git a/openair2/RRC/NR/rrc_gNB_GTPV1U.c b/openair2/RRC/NR/rrc_gNB_GTPV1U.c index ec220172d0..3fef7bc0ef 100644 --- a/openair2/RRC/NR/rrc_gNB_GTPV1U.c +++ b/openair2/RRC/NR/rrc_gNB_GTPV1U.c @@ -49,7 +49,6 @@ rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const gtpv1u_enb_create_tunnel_resp_t *const create_tunnel_resp_pP, uint8_t *inde_list ) { - rnti_t rnti; int i; struct rrc_gNB_ue_context_s *ue_context_p = NULL; @@ -57,7 +56,6 @@ rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( LOG_D(RRC, PROTOCOL_RRC_CTXT_UE_FMT" RX CREATE_TUNNEL_RESP num tunnels %u \n", PROTOCOL_RRC_CTXT_UE_ARGS(ctxt_pP), create_tunnel_resp_pP->num_tunnels); - rnti = create_tunnel_resp_pP->rnti; ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[ctxt_pP->module_id], ctxt_pP->rntiMaybeUEid); for (i = 0; i < create_tunnel_resp_pP->num_tunnels; i++) { @@ -74,7 +72,6 @@ rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( create_tunnel_resp_pP->enb_addr.length); } - (void)rnti; /* avoid gcc warning "set but not used" */ return 0; } else { return -1; @@ -84,10 +81,8 @@ rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( int nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list + const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP ) { - rnti_t rnti; int i; struct rrc_gNB_ue_context_s *ue_context_p = NULL; @@ -95,24 +90,21 @@ nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( LOG_D(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" RX CREATE_TUNNEL_RESP num tunnels %u \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), create_tunnel_resp_pP->num_tunnels); - rnti = create_tunnel_resp_pP->ue_id; ue_context_p = rrc_gNB_get_ue_context(RC.nrrrc[ctxt_pP->module_id], ctxt_pP->rntiMaybeUEid); for (i = 0; i < create_tunnel_resp_pP->num_tunnels; i++) { - ue_context_p->ue_context.gnb_gtp_teid[inde_list[i]] = create_tunnel_resp_pP->gnb_NGu_teid[i]; - ue_context_p->ue_context.gnb_gtp_addrs[inde_list[i]] = create_tunnel_resp_pP->gnb_addr; - ue_context_p->ue_context.gnb_gtp_psi[inde_list[i]] = create_tunnel_resp_pP->pdusession_id[i]; - LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP tunnel (%u, %u) bearer UE context index %u, msg index %u, id %u, gtp addr len %d \n", + ue_context_p->ue_context.gnb_gtp_teid[i] = create_tunnel_resp_pP->gnb_NGu_teid[i]; + ue_context_p->ue_context.gnb_gtp_addrs[i] = create_tunnel_resp_pP->gnb_addr; + ue_context_p->ue_context.gnb_gtp_psi[i] = create_tunnel_resp_pP->pdusession_id[i]; + LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP tunnel (%u, %u) bearer UE context index %u, id %u, gtp addr len %d \n", PROTOCOL_NR_RRC_CTXT_UE_ARGS(ctxt_pP), create_tunnel_resp_pP->gnb_NGu_teid[i], - ue_context_p->ue_context.gnb_gtp_teid[inde_list[i]], - inde_list[i], + ue_context_p->ue_context.gnb_gtp_teid[i], i, create_tunnel_resp_pP->pdusession_id[i], create_tunnel_resp_pP->gnb_addr.length); } - (void)rnti; /* avoid gcc warning "set but not used" */ return 0; } else { return -1; diff --git a/openair2/RRC/NR/rrc_gNB_GTPV1U.h b/openair2/RRC/NR/rrc_gNB_GTPV1U.h index 748bd98789..03ef050bcf 100644 --- a/openair2/RRC/NR/rrc_gNB_GTPV1U.h +++ b/openair2/RRC/NR/rrc_gNB_GTPV1U.h @@ -41,8 +41,7 @@ rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( int nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( const protocol_ctxt_t *const ctxt_pP, - const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP, - uint8_t *inde_list + const gtpv1u_gnb_create_tunnel_resp_t *const create_tunnel_resp_pP ); #endif diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 3eccf0ef37..6f3ebcd06d 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -57,6 +57,7 @@ #include "NGAP_Cause.h" #include "NGAP_CauseRadioNetwork.h" #include "f1ap_messages_types.h" +#include "openair2/E1AP/e1ap_asnc.h" extern RAN_CONTEXT_t RC; @@ -242,7 +243,7 @@ rrc_gNB_NGAP_get_ue_ids( } //------------------------------------------------------------------------------ -static struct rrc_gNB_ue_context_s * +struct rrc_gNB_ue_context_s * rrc_gNB_get_ue_context_from_ngap_ids( const instance_t instanceP, const uint16_t ue_initial_idP, @@ -474,7 +475,6 @@ rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ( protocol_ctxt_t ctxt={0}; uint8_t pdu_sessions_done = 0; gtpv1u_gnb_create_tunnel_resp_t create_tunnel_resp; - uint8_t inde_list[NR_NB_RB_MAX - 3]= {0}; int ret = 0; ue_initial_id = NGAP_INITIAL_CONTEXT_SETUP_REQ(msg_p).ue_initial_id; @@ -495,12 +495,14 @@ rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ( return (-1); } else { PROTOCOL_CTXT_SET_BY_INSTANCE(&ctxt, instance, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0); + gNB_RRC_INST *rrc = RC.nrrrc[ctxt.module_id]; ue_context_p->ue_context.gNB_ue_ngap_id = NGAP_INITIAL_CONTEXT_SETUP_REQ (msg_p).gNB_ue_ngap_id; ue_context_p->ue_context.amf_ue_ngap_id = NGAP_INITIAL_CONTEXT_SETUP_REQ (msg_p).amf_ue_ngap_id; ue_context_p->ue_context.nas_pdu_flag = NGAP_INITIAL_CONTEXT_SETUP_REQ (msg_p).nas_pdu_flag; uint8_t nb_pdusessions_tosetup = NGAP_INITIAL_CONTEXT_SETUP_REQ (msg_p).nb_of_pdusessions; if (nb_pdusessions_tosetup != 0) { + AssertFatal(rrc->node_type != ngran_gNB_CUCP, "PDU sessions in Initial context setup request not handled by E1 yet\n"); gtpv1u_gnb_create_tunnel_req_t create_tunnel_req={0}; for (int i = 0; i < NR_NB_RB_MAX - 3; i++) { if(ue_context_p->ue_context.pduSession[i].status >= PDU_SESSION_STATUS_DONE) @@ -520,7 +522,6 @@ rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ( i, create_tunnel_req.outgoing_teid[pdu_sessions_done], create_tunnel_req.pdusession_id[pdu_sessions_done]); - inde_list[pdu_sessions_done] = i; pdu_sessions_done++; if(pdu_sessions_done >= nb_pdusessions_tosetup) { @@ -549,9 +550,8 @@ rrc_gNB_process_NGAP_INITIAL_CONTEXT_SETUP_REQ( nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( &ctxt, - &create_tunnel_resp, - &inde_list[0]); - ue_context_p->ue_context.setup_pdu_sessions += nb_pdusessions_tosetup; + &create_tunnel_resp); + ue_context_p->ue_context.setup_pdu_sessions += NGAP_INITIAL_CONTEXT_SETUP_REQ (msg_p).nb_of_pdusessions; ue_context_p->ue_context.established_pdu_sessions_flag = 1; } @@ -804,6 +804,7 @@ rrc_gNB_process_NGAP_DOWNLINK_NAS( */ switch (RC.nrrrc[ctxt.module_id]->node_type) { case ngran_gNB_CU: + case ngran_gNB_CUCP: /* Transfer data to PDCP */ nr_rrc_data_req ( &ctxt, @@ -945,7 +946,7 @@ rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP( } //------------------------------------------------------------------------------ -int +void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ( MessageDef *msg_p, const char *msg_name, @@ -953,21 +954,20 @@ rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ( ) //------------------------------------------------------------------------------ { + gNB_RRC_INST *rrc; uint16_t ue_initial_id; uint32_t gNB_ue_ngap_id; rrc_gNB_ue_context_t *ue_context_p = NULL; protocol_ctxt_t ctxt={0}; - gtpv1u_gnb_create_tunnel_req_t create_tunnel_req={0}; - gtpv1u_gnb_create_tunnel_resp_t create_tunnel_resp={0}; - uint8_t pdu_sessions_done; - uint8_t inde_list[NR_NB_RB_MAX - 3]= {0}; - int ret = 0; - ue_initial_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).ue_initial_id; - gNB_ue_ngap_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id; + ngap_pdusession_setup_req_t* msg=&NGAP_PDUSESSION_SETUP_REQ(msg_p); + ue_initial_id = msg->ue_initial_id; + gNB_ue_ngap_id = msg->gNB_ue_ngap_id; ue_context_p = rrc_gNB_get_ue_context_from_ngap_ids(instance, ue_initial_id, gNB_ue_ngap_id); + PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); + rrc = RC.nrrrc[ctxt.module_id]; LOG_I(NR_RRC, "[gNB %ld] Received %s: ue_initial_id %d, gNB_ue_ngap_id %u \n", - instance, msg_name, ue_initial_id, gNB_ue_ngap_id); + instance, msg_name, ue_initial_id, gNB_ue_ngap_id); if (ue_context_p == NULL) { MessageDef *msg_fail_p = NULL; @@ -976,110 +976,102 @@ rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ( NGAP_PDUSESSION_SETUP_REQ(msg_fail_p).gNB_ue_ngap_id = gNB_ue_ngap_id; // TODO add failure cause when defined! itti_send_msg_to_task (TASK_NGAP, instance, msg_fail_p); - return (-1); - } else { - memset(&create_tunnel_req, 0, sizeof(gtpv1u_gnb_create_tunnel_req_t)); - uint8_t nb_pdusessions_tosetup = NGAP_PDUSESSION_SETUP_REQ(msg_p).nb_pdusessions_tosetup; - pdu_sessions_done = 0; + return ; + } + + ue_context_p->ue_context.nb_of_pdusessions = msg->nb_pdusessions_tosetup; + ue_context_p->ue_context.gNB_ue_ngap_id = msg->gNB_ue_ngap_id; + ue_context_p->ue_context.amf_ue_ngap_id = msg->amf_ue_ngap_id; - PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, 0, GNB_FLAG_YES, ue_context_p->ue_context.rnti, 0, 0, 0); - for (int i = 0; i < NR_NB_RB_MAX - 3; i++) { - if(ue_context_p->ue_context.pduSession[i].status >= PDU_SESSION_STATUS_DONE) - continue; - ue_context_p->ue_context.pduSession[i].status = PDU_SESSION_STATUS_NEW; - ue_context_p->ue_context.pduSession[i].param = NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done]; - create_tunnel_req.pdusession_id[pdu_sessions_done] = NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done].pdusession_id; - create_tunnel_req.incoming_rb_id[pdu_sessions_done]= i+1; - create_tunnel_req.outgoing_teid[pdu_sessions_done] = NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done].gtp_teid; - create_tunnel_req.outgoing_qfi[pdu_sessions_done] = NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done].qos[0].qfi; - memcpy(create_tunnel_req.dst_addr[pdu_sessions_done].buffer, - NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done].upf_addr.buffer, - sizeof(uint8_t)*20); - create_tunnel_req.dst_addr[pdu_sessions_done].length = NGAP_PDUSESSION_SETUP_REQ(msg_p).pdusession_setup_params[pdu_sessions_done].upf_addr.length; - LOG_I(NR_RRC,"NGAP PDUSESSION SETUP REQ: local index %d teid %u, pdusession id %d \n", - i, - create_tunnel_req.outgoing_teid[pdu_sessions_done], - create_tunnel_req.pdusession_id[pdu_sessions_done]); - inde_list[pdu_sessions_done] = i; - pdu_sessions_done++; + /* Configurations are referred from rrc_gNB_generate_dedicatedRRCReconfiguration() and + rrc_gNB_process_RRCReconfigurationComplete() - if(pdu_sessions_done >= nb_pdusessions_tosetup) { - break; + At CU-CP we configure the E1 bearer context setup parameters (PDU sessions, DRBs and + QoS flows) same as in these functions. At CU-UP we create PDU Sessions and allocate DRBs. + */ + e1ap_bearer_setup_req_t bearer_req; + + bearer_req.gNB_cu_cp_ue_id = gNB_ue_ngap_id; + bearer_req.rnti = ue_context_p->ue_context.rnti; + bearer_req.cipheringAlgorithm = ue_context_p->ue_context.ciphering_algorithm; + memcpy(bearer_req.encryptionKey, ue_context_p->ue_context.kgnb, sizeof(ue_context_p->ue_context.kgnb)); + bearer_req.integrityProtectionAlgorithm = ue_context_p->ue_context.integrity_algorithm; + memcpy(bearer_req.integrityProtectionKey, ue_context_p->ue_context.kgnb, sizeof(ue_context_p->ue_context.kgnb)); + bearer_req.ueDlAggMaxBitRate = msg->ueAggMaxBitRateDownlink; + + bearer_req.numPDUSessions = msg->nb_pdusessions_tosetup; + + for (int i=0; i < bearer_req.numPDUSessions; i++) { + ue_context_p->ue_context.pduSession[i].param = msg->pdusession_setup_params[i]; + + pdu_session_to_setup_t *pdu = bearer_req.pduSession + i; + pdu->sessionId = msg->pdusession_setup_params[i].pdusession_id; + pdu->sessionType = msg->pdusession_setup_params[i].upf_addr.pdu_session_type; + pdu->sst = msg->allowed_nssai[i].sST; + if (rrc->security.do_drb_integrity) { + pdu->integrityProtectionIndication = E1AP_IntegrityProtectionIndication_required; + } else { + pdu->integrityProtectionIndication = E1AP_IntegrityProtectionIndication_not_needed; + } + + if (rrc->security.do_drb_ciphering) { + pdu->confidentialityProtectionIndication = E1AP_ConfidentialityProtectionIndication_required; + } else { + pdu->confidentialityProtectionIndication = E1AP_ConfidentialityProtectionIndication_not_needed; + } + pdu->teId = msg->pdusession_setup_params[i].gtp_teid; + memcpy(&pdu->tlAddress, + msg->pdusession_setup_params[i].upf_addr.buffer, + sizeof(uint8_t)*4); + + ue_context_p->ue_context.pduSession[i].param = msg->pdusession_setup_params[i]; + ue_context_p->ue_context.nb_of_pdusessions = msg->nb_pdusessions_tosetup; + ue_context_p->ue_context.gNB_ue_ngap_id = msg->gNB_ue_ngap_id; + ue_context_p->ue_context.amf_ue_ngap_id = msg->amf_ue_ngap_id; + pdu->numDRB2Setup = 1; // One DRB per PDU Session. TODO: Remove hardcoding + ue_context_p->ue_context.setup_pdu_sessions += pdu->numDRB2Setup; + for (int j=0; j < pdu->numDRB2Setup; j++) { + DRB_nGRAN_to_setup_t *drb = pdu->DRBnGRanList + j; + + drb->id = i + j + 1; + + drb->defaultDRB = E1AP_DefaultDRB_true; + + drb->sDAP_Header_UL = !(rrc->configuration.enable_sdap); + drb->sDAP_Header_DL = !(rrc->configuration.enable_sdap); + + drb->pDCP_SN_Size_UL = E1AP_PDCP_SN_Size_s_18; + drb->pDCP_SN_Size_DL = E1AP_PDCP_SN_Size_s_18; + + drb->discardTimer = E1AP_DiscardTimer_infinity; + drb->reorderingTimer = E1AP_T_Reordering_ms0; + + drb->rLC_Mode = E1AP_RLC_Mode_rlc_am; + + drb->numCellGroups = 1; // assume one cell group associated with a DRB + + for (int k=0; k < drb->numCellGroups; k++) { + cell_group_t *cellGroup = drb->cellGroupList + k; + cellGroup->id = 0; // MCG + } + + drb->numQosFlow2Setup = msg->pdusession_setup_params[i].nb_qos; + for (int k=0; k < drb->numQosFlow2Setup; k++) { + qos_flow_to_setup_t *qos = drb->qosFlows + k; + + qos->id = msg->pdusession_setup_params[i].qos[k].qfi; + qos->fiveQI = msg->pdusession_setup_params[i].qos[k].fiveQI; + qos->fiveQI_type = msg->pdusession_setup_params[i].qos[k].fiveQI_type; + + qos->qoSPriorityLevel = msg->pdusession_setup_params[i].qos[k].allocation_retention_priority.priority_level; + qos->pre_emptionCapability = msg->pdusession_setup_params[i].qos[k].allocation_retention_priority.pre_emp_capability; + qos->pre_emptionVulnerability = msg->pdusession_setup_params[i].qos[k].allocation_retention_priority.pre_emp_vulnerability; } } - - ue_context_p->ue_context.nb_of_pdusessions = NGAP_PDUSESSION_SETUP_REQ(msg_p).nb_pdusessions_tosetup; - ue_context_p->ue_context.gNB_ue_ngap_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).gNB_ue_ngap_id; - ue_context_p->ue_context.amf_ue_ngap_id = NGAP_PDUSESSION_SETUP_REQ(msg_p).amf_ue_ngap_id; - create_tunnel_req.ue_id = ue_context_p->ue_context.rnti; - create_tunnel_req.num_tunnels = pdu_sessions_done; - - ret = gtpv1u_create_ngu_tunnel( - instance, - &create_tunnel_req, - &create_tunnel_resp); - if (ret != 0) { - LOG_E(NR_RRC,"rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ : gtpv1u_create_ngu_tunnel failed,start to release UE %x\n",ue_context_p->ue_context.rnti); - ue_context_p->ue_context.ue_release_timer_ng = 1; - ue_context_p->ue_context.ue_release_timer_thres_ng = 100; - ue_context_p->ue_context.ue_release_timer = 0; - ue_context_p->ue_context.ue_reestablishment_timer = 0; - ue_context_p->ue_context.ul_failure_timer = 20000; // set ul_failure to 20000 for triggering rrc_eNB_send_S1AP_UE_CONTEXT_RELEASE_REQ - // rrc_gNB_free_UE(ctxt.module_id,ue_context_p); - ue_context_p->ue_context.ul_failure_timer = 0; - return (0); - } - nr_rrc_gNB_process_GTPV1U_CREATE_TUNNEL_RESP( - &ctxt, - &create_tunnel_resp, - &inde_list[0]); - ue_context_p->ue_context.setup_pdu_sessions += nb_pdusessions_tosetup; - - // TEST - // ue_context_p->ue_context.pdusession[0].status = PDU_SESSION_STATUS_DONE; - // rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP(&ctxt, ue_context_p, 0); - if(!NODE_IS_CU(RC.nrrrc[ctxt.module_id]->node_type)){ - rrc_gNB_generate_dedicatedRRCReconfiguration(&ctxt, ue_context_p, NULL); - } - else{ - /*Generate a UE context modification request message towards the DU to instruct the DU - *for SRB2 and DRB configuration and get the updates on master cell group config from the DU*/ - MessageDef *message_p; - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, F1AP_UE_CONTEXT_MODIFICATION_REQ); - f1ap_ue_context_setup_t *req=&F1AP_UE_CONTEXT_MODIFICATION_REQ (message_p); - req->rnti = ue_context_p->ue_context.rnti; - req->mcc = RC.nrrrc[ctxt.module_id]->configuration.mcc[0]; - req->mnc = RC.nrrrc[ctxt.module_id]->configuration.mnc[0]; - req->mnc_digit_length = RC.nrrrc[ctxt.module_id]->configuration.mnc_digit_length[0]; - req->nr_cellid = RC.nrrrc[ctxt.module_id]->nr_cellid; - - /*Instruction towards the DU for SRB2 configuration*/ - req->srbs_to_be_setup = malloc(1*sizeof(f1ap_srb_to_be_setup_t)); - req->srbs_to_be_setup_length = 1; - f1ap_srb_to_be_setup_t *SRBs=req->srbs_to_be_setup; - SRBs[0].srb_id = 2; - SRBs[0].lcid = 2; - - /*Instruction towards the DU for DRB configuration and tunnel creation*/ - gtpv1u_gnb_create_tunnel_req_t create_tunnel_req; - memset(&create_tunnel_req, 0, sizeof(gtpv1u_gnb_create_tunnel_req_t)); - req->drbs_to_be_setup = malloc(1*sizeof(f1ap_drb_to_be_setup_t)); - req->drbs_to_be_setup_length = 1; - f1ap_drb_to_be_setup_t *DRBs=req->drbs_to_be_setup; - LOG_D(RRC, "Length of DRB list:%d \n", req->drbs_to_be_setup_length); - DRBs[0].drb_id = 1; - DRBs[0].rlc_mode = RLC_MODE_AM; - DRBs[0].up_ul_tnl[0].tl_address = inet_addr(RC.nrrrc[ctxt.module_id]->eth_params_s.my_addr); - DRBs[0].up_ul_tnl[0].port=RC.nrrrc[ctxt.module_id]->eth_params_s.my_portd; - DRBs[0].up_ul_tnl_length = 1; - DRBs[0].up_dl_tnl[0].tl_address = inet_addr(RC.nrrrc[ctxt.module_id]->eth_params_s.remote_addr); - DRBs[0].up_dl_tnl[0].port=RC.nrrrc[ctxt.module_id]->eth_params_s.remote_portd; - DRBs[0].up_dl_tnl_length = 1; - - itti_send_msg_to_task (TASK_CU_F1, ctxt.module_id, message_p); - } - return(0); } + + rrc->cucp_cuup.bearer_context_setup(&bearer_req, instance); + return; } //------------------------------------------------------------------------------ diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.h b/openair2/RRC/NR/rrc_gNB_NGAP.h index a03e4df41f..6c8a861f03 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.h +++ b/openair2/RRC/NR/rrc_gNB_NGAP.h @@ -101,7 +101,7 @@ rrc_gNB_send_NGAP_PDUSESSION_SETUP_RESP( uint8_t xid ); -int +void rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ( MessageDef *msg_p, const char *msg_name, @@ -189,6 +189,13 @@ nr_rrc_pdcp_config_security( const uint8_t send_security_mode_command ); +struct rrc_gNB_ue_context_s * +rrc_gNB_get_ue_context_from_ngap_ids( + const instance_t instanceP, + const uint16_t ue_initial_idP, + const uint32_t gNB_ue_ngap_idP +); + int rrc_gNB_process_PAGING_IND( MessageDef *msg_p, diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 9c3b6aada0..ff8149b83f 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -22,32 +22,6 @@ #include "rrc_gNB_radio_bearers.h" #include "oai_asn1.h" -NR_SRB_ToAddMod_t *generateSRB2() { - NR_SRB_ToAddMod_t *SRB2_config = NULL; - - SRB2_config = CALLOC(1, sizeof(*SRB2_config)); - SRB2_config->srb_Identity = 2; - - return SRB2_config; -} - -NR_SRB_ToAddModList_t **generateSRB2_confList(gNB_RRC_UE_t *ue, - NR_SRB_ToAddModList_t *SRB_configList, - uint8_t xid) { - NR_SRB_ToAddModList_t **SRB_configList2 = NULL; - - SRB_configList2 = &ue->SRB_configList2[xid]; - if (*SRB_configList2 == NULL) { - *SRB_configList2 = CALLOC(1, sizeof(**SRB_configList2)); - memset(*SRB_configList2, 0, sizeof(**SRB_configList2)); - NR_SRB_ToAddMod_t *SRB2_config = generateSRB2(); - asn1cSeqAdd(&(*SRB_configList2)->list, SRB2_config); - asn1cSeqAdd(&SRB_configList->list, SRB2_config); - } - - return SRB_configList2; -} - NR_DRB_ToAddMod_t *generateDRB(gNB_RRC_UE_t *ue, uint8_t drb_id, const pdu_session_param_t *pduSession, diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.h b/openair2/RRC/NR/rrc_gNB_radio_bearers.h index a686fed767..86ab093ced 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.h +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.h @@ -35,10 +35,6 @@ #define GBR_FLOW (1) #define NONGBR_FLOW (0) -NR_SRB_ToAddMod_t *generateSRB2(void); -NR_SRB_ToAddModList_t **generateSRB2_confList(gNB_RRC_UE_t *ue, - NR_SRB_ToAddModList_t *SRB_configList, - uint8_t xid); NR_DRB_ToAddMod_t *generateDRB(gNB_RRC_UE_t *rrc_ue, uint8_t drb_id, const pdu_session_param_t *pduSession, diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.c b/openair2/SDAP/nr_sdap/nr_sdap_entity.c index 09e80feccf..e9a22eb578 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.c +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.c @@ -35,6 +35,8 @@ typedef struct { static nr_sdap_entity_info sdap_info; +instance_t *N3GTPUInst = NULL; + nr_pdcp_ue_manager_t *nr_pdcp_sdap_get_ue_manager(void); void nr_pdcp_submit_sdap_ctrl_pdu(ue_id_t ue_id, rb_id_t sdap_ctrl_pdu_drb, nr_sdap_ul_hdr_t ctrl_pdu) @@ -216,7 +218,8 @@ static void nr_sdap_rx_entity(nr_sdap_entity_t *entity, req->ue_id = ue_id; req->bearer_id = pdusession_id; LOG_D(SDAP, "%s() sending message to gtp size %d\n", __func__, size-offset); - itti_send_msg_to_task(TASK_GTPV1_U, INSTANCE_DEFAULT, message_p); + // very very dirty hack gloabl var N3GTPUInst + itti_send_msg_to_task(TASK_GTPV1_U, *N3GTPUInst, message_p); } else { //nrUE /* * TS 37.324 5.2 Data transfer diff --git a/openair2/SDAP/nr_sdap/nr_sdap_entity.h b/openair2/SDAP/nr_sdap/nr_sdap_entity.h index e8ed236fd1..4b5303a5b6 100644 --- a/openair2/SDAP/nr_sdap/nr_sdap_entity.h +++ b/openair2/SDAP/nr_sdap/nr_sdap_entity.h @@ -51,6 +51,9 @@ * the GTP-U Extension Header. Inside the DL PDU SESSION INFORMATION frame. * TS 38.415 Fig. 5.5.2.1-1 */ + +extern instance_t *N3GTPUInst; + typedef struct nr_sdap_dl_hdr_s { uint8_t QFI:6; uint8_t RQI:1; diff --git a/openair3/MME_APP/mme_app.c b/openair3/MME_APP/mme_app.c index 2c1d524c15..43a5df2ac8 100644 --- a/openair3/MME_APP/mme_app.c +++ b/openair3/MME_APP/mme_app.c @@ -40,7 +40,8 @@ # include "intertask_interface.h" # include "s1ap_eNB.h" -#include "sctp_eNB_task.h" +# include "sctp_eNB_task.h" +# include "openair3/ocp-gtpu/gtp_itf.h" # include "x2ap_eNB.h" # include "x2ap_messages_types.h" diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index ab2503f1d5..47f4b8445d 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -48,10 +48,7 @@ #include "conversions.h" #include "NGAP_NonDynamic5QIDescriptor.h" -static -int ngap_gNB_handle_ng_setup_response(uint32_t assoc_id, - uint32_t stream, - NGAP_NGAP_PDU_t *pdu); +static int ngap_gNB_handle_ng_setup_response(uint32_t ssoc_id, uint32_t stream, NGAP_NGAP_PDU_t *pdu); static int ngap_gNB_handle_ng_setup_failure(uint32_t assoc_id, uint32_t stream, @@ -188,8 +185,7 @@ void ngap_handle_ng_setup_message(ngap_gNB_amf_data_t *amf_desc_p, int sctp_shut /* If there are no more associated AMF, inform gNB app */ if (amf_desc_p->ngap_gNB_instance->ngap_amf_associated_nb == 0) { - MessageDef *message_p; - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_DEREGISTERED_GNB_IND); + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_DEREGISTERED_GNB_IND); NGAP_DEREGISTERED_GNB_IND(message_p).nb_amf = 0; itti_send_msg_to_task(TASK_GNB_APP, amf_desc_p->ngap_gNB_instance->instance, message_p); } @@ -206,8 +202,7 @@ void ngap_handle_ng_setup_message(ngap_gNB_amf_data_t *amf_desc_p, int sctp_shut /* If there are no more pending messages, inform gNB app */ if (amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb == 0) { - MessageDef *message_p; - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_REGISTER_GNB_CNF); + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_REGISTER_GNB_CNF); NGAP_REGISTER_GNB_CNF(message_p).nb_amf = amf_desc_p->ngap_gNB_instance->ngap_amf_associated_nb; itti_send_msg_to_task(TASK_GNB_APP, amf_desc_p->ngap_gNB_instance->instance, message_p); } @@ -838,7 +833,6 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, int i; ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL; - MessageDef *message_p = NULL; NGAP_InitialContextSetupRequest_t *container; NGAP_InitialContextSetupRequestIEs_t *ie; NGAP_RAN_UE_NGAP_ID_t ran_ue_ngap_id; @@ -890,20 +884,21 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, ue_desc_p->rx_stream = stream; ue_desc_p->amf_ue_ngap_id = amf_ue_ngap_id; - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_INITIAL_CONTEXT_SETUP_REQ); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).ue_initial_id = ue_desc_p->ue_initial_id; + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_INITIAL_CONTEXT_SETUP_REQ); + ngap_initial_context_setup_req_t * msg=&NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p); + msg->ue_initial_id = ue_desc_p->ue_initial_id; ue_desc_p->ue_initial_id = 0; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).gNB_ue_ngap_id = ue_desc_p->gNB_ue_ngap_id; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).amf_ue_ngap_id = ue_desc_p->amf_ue_ngap_id; + msg->gNB_ue_ngap_id = ue_desc_p->gNB_ue_ngap_id; + msg->amf_ue_ngap_id = ue_desc_p->amf_ue_ngap_id; /* id-UEAggregateMaximumBitRate */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_InitialContextSetupRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate, false); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ asn_INTEGER2ulong(&(ie->value.choice.UEAggregateMaximumBitRate.uEAggregateMaximumBitRateUL), - &(NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).ue_ambr.br_ul)); + &(msg->ue_ambr.br_ul)); asn_INTEGER2ulong(&(ie->value.choice.UEAggregateMaximumBitRate.uEAggregateMaximumBitRateDL), - &(NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).ue_ambr.br_dl)); + &(msg->ue_ambr.br_dl)); } else { NGAP_ERROR("could not found NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate\n"); } @@ -914,12 +909,12 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_ProtocolIE_ID_id_GUAMI, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - TBCD_TO_MCC_MNC(&ie->value.choice.GUAMI.pLMNIdentity, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.mcc, - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.mnc, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.mnc_len); + TBCD_TO_MCC_MNC(&ie->value.choice.GUAMI.pLMNIdentity, msg->guami.mcc, + msg->guami.mnc, msg->guami.mnc_len); - OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFRegionID, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.amf_region_id); - OCTET_STRING_TO_INT16(&ie->value.choice.GUAMI.aMFSetID, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.amf_set_id); - OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFPointer, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).guami.amf_pointer); + OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFRegionID, msg->guami.amf_region_id); + OCTET_STRING_TO_INT16(&ie->value.choice.GUAMI.aMFSetID, msg->guami.amf_set_id); + OCTET_STRING_TO_INT8(&ie->value.choice.GUAMI.aMFPointer, msg->guami.amf_pointer); } else {/* ie != NULL */ return -1; } @@ -928,7 +923,7 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtReq, false); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nb_of_pdusessions = + msg->nb_of_pdusessions = ie->value.choice.PDUSessionResourceSetupListCxtReq.list.count; for (i = 0; i < ie->value.choice.PDUSessionResourceSetupListCxtReq.list.count; i++) { @@ -938,19 +933,19 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = NULL; item_p = (NGAP_PDUSessionResourceSetupItemCxtReq_t *)ie->value.choice.PDUSessionResourceSetupListCxtReq.list.array[i]; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].pdusession_id = item_p->pDUSessionID; + msg->pdusession_param[i].pdusession_id = item_p->pDUSessionID; if (item_p->nAS_PDU != NULL) { /* Only copy NAS pdu if present */ - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nas_pdu.length = item_p->nAS_PDU->size; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nas_pdu.buffer = + msg->pdusession_param[i].nas_pdu.length = item_p->nAS_PDU->size; + msg->pdusession_param[i].nas_pdu.buffer = malloc(sizeof(uint8_t) * item_p->nAS_PDU->size); - memcpy(NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nas_pdu.buffer, + memcpy(msg->pdusession_param[i].nas_pdu.buffer, item_p->nAS_PDU->buf, item_p->nAS_PDU->size); NGAP_DEBUG("Received NAS message with the PDUSESSION setup procedure\n"); } else { - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nas_pdu.length = 0; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nas_pdu.buffer = NULL; + msg->pdusession_param[i].nas_pdu.length = 0; + msg->pdusession_param[i].nas_pdu.buffer = NULL; } @@ -979,14 +974,14 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, gTPTunnel_p = pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel; /* Set the transport layer address */ - memcpy(NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].upf_addr.buffer, + memcpy(msg->pdusession_param[i].upf_addr.buffer, gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].upf_addr.length = + msg->pdusession_param[i].upf_addr.length = gTPTunnel_p->transportLayerAddress.size * 8 - gTPTunnel_p->transportLayerAddress.bits_unused; /* GTP tunnel endpoint ID */ - OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].gtp_teid); + OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, msg->pdusession_param[i].gtp_teid); } break; @@ -1001,7 +996,7 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, /* mandatory PDUSessionType */ case NGAP_ProtocolIE_ID_id_PDUSessionType: - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].upf_addr.pdu_session_type = (uint8_t)pdusessionTransfer_ies->value.choice.PDUSessionType; + msg->pdusession_param[i].upf_addr.pdu_session_type = (uint8_t)pdusessionTransfer_ies->value.choice.PDUSessionType; break; /* optional SecurityIndication */ @@ -1021,25 +1016,25 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, DevAssert(pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count > 0); DevAssert(pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count <= NGAP_maxnoofQosFlows); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; + msg->pdusession_param[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; for(int qosIdx = 0; qosIdx < pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; qosIdx++) { qosFlowItem_p = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[qosIdx]; /* Set the QOS informations */ - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; + msg->pdusession_param[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; if(qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.present == NGAP_QosCharacteristics_PR_nonDynamic5QI){ if(qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.choice.nonDynamic5QI != NULL){ - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].qos[qosIdx].fiveQI = + msg->pdusession_param[i].qos[qosIdx].fiveQI = (uint64_t)qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.choice.nonDynamic5QI->fiveQI; } } - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].qos[qosIdx].allocation_retention_priority.priority_level = + msg->pdusession_param[i].qos[qosIdx].allocation_retention_priority.priority_level = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.priorityLevelARP; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = + msg->pdusession_param[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.pre_emptionCapability; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).pdusession_param[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = + msg->pdusession_param[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.pre_emptionVulnerability; } } @@ -1051,14 +1046,14 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, break; default: - NGAP_ERROR("could not found protocolIEs id %ld\n", pdusessionTransfer_ies->id); + NGAP_ERROR("Not found protocolIEs id %ld\n", pdusessionTransfer_ies->id); return -1; } } } /* for i... */ } else {/* ie != NULL */ - NGAP_ERROR("could not found NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtReq\n"); + NGAP_ERROR("Not found NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListCxtReq\n"); } /* id-AllowedNSSAI */ @@ -1075,35 +1070,35 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, if (ie == NULL) { NGAP_WARN("AllowedNSSAI not present, forging 2 NSSAI\n"); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nb_allowed_nssais = 2; + msg->nb_allowed_nssais = 2; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sST = 01; + msg->allowed_nssai[0].sST = 01; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD_flag = 1; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[0] = 01; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[1] = 02; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[0].sD[2] = 03; + msg->allowed_nssai[0].sD_flag = 1; + msg->allowed_nssai[0].sD[0] = 01; + msg->allowed_nssai[0].sD[1] = 02; + msg->allowed_nssai[0].sD[2] = 03; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sST = 01; + msg->allowed_nssai[1].sST = 01; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD_flag = 1; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[0] = 00;//11; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[1] = 00;//22; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[1].sD[2] = 01;//33; + msg->allowed_nssai[1].sD_flag = 1; + msg->allowed_nssai[1].sD[0] = 00;//11; + msg->allowed_nssai[1].sD[1] = 00;//22; + msg->allowed_nssai[1].sD[2] = 01;//33; } else { NGAP_INFO("AllowedNSSAI.list.count %d\n", ie->value.choice.AllowedNSSAI.list.count); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count; + msg->nb_allowed_nssais = ie->value.choice.AllowedNSSAI.list.count; for(i = 0; i < ie->value.choice.AllowedNSSAI.list.count; i++) { allow_nssai_item_p = ie->value.choice.AllowedNSSAI.list.array[i]; - OCTET_STRING_TO_INT8(&allow_nssai_item_p->s_NSSAI.sST, NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sST); + OCTET_STRING_TO_INT8(&allow_nssai_item_p->s_NSSAI.sST, msg->allowed_nssai[i].sST); if(allow_nssai_item_p->s_NSSAI.sD != NULL) { - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sD_flag = 1; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sD[0] = allow_nssai_item_p->s_NSSAI.sD->buf[0]; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sD[1] = allow_nssai_item_p->s_NSSAI.sD->buf[1]; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).allowed_nssai[i].sD[2] = allow_nssai_item_p->s_NSSAI.sD->buf[2]; + msg->allowed_nssai[i].sD_flag = 1; + msg->allowed_nssai[i].sD[0] = allow_nssai_item_p->s_NSSAI.sD->buf[0]; + msg->allowed_nssai[i].sD[1] = allow_nssai_item_p->s_NSSAI.sD->buf[1]; + msg->allowed_nssai[i].sD[2] = allow_nssai_item_p->s_NSSAI.sD->buf[2]; } } } @@ -1116,13 +1111,13 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_ProtocolIE_ID_id_UESecurityCapabilities, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).security_capabilities.nRencryption_algorithms = + msg->security_capabilities.nRencryption_algorithms = BIT_STRING_to_uint16(&ie->value.choice.UESecurityCapabilities.nRencryptionAlgorithms); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).security_capabilities.nRintegrity_algorithms = + msg->security_capabilities.nRintegrity_algorithms = BIT_STRING_to_uint16(&ie->value.choice.UESecurityCapabilities.nRintegrityProtectionAlgorithms); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).security_capabilities.eUTRAencryption_algorithms = + msg->security_capabilities.eUTRAencryption_algorithms = BIT_STRING_to_uint16(&ie->value.choice.UESecurityCapabilities.eUTRAencryptionAlgorithms); - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).security_capabilities.eUTRAintegrity_algorithms = + msg->security_capabilities.eUTRAintegrity_algorithms = BIT_STRING_to_uint16(&ie->value.choice.UESecurityCapabilities.eUTRAintegrityProtectionAlgorithms); } else {/* ie != NULL */ return -1; @@ -1133,7 +1128,7 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_ProtocolIE_ID_id_SecurityKey, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - memcpy(&NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).security_key, + memcpy(&msg->security_key, ie->value.choice.SecurityKey.buf, ie->value.choice.SecurityKey.size); } else {/* ie != NULL */ @@ -1148,11 +1143,11 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, NGAP_MobilityRestrictionList_t *mobility_rest_list_p = NULL; mobility_rest_list_p = &ie->value.choice.MobilityRestrictionList; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).mobility_restriction_flag = 1; + msg->mobility_restriction_flag = 1; TBCD_TO_MCC_MNC(&mobility_rest_list_p->servingPLMN, - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).mobility_restriction.serving_plmn.mcc, - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).mobility_restriction.serving_plmn.mnc, - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).mobility_restriction.serving_plmn.mnc_digit_length); + msg->mobility_restriction.serving_plmn.mcc, + msg->mobility_restriction.serving_plmn.mnc, + msg->mobility_restriction.serving_plmn.mnc_digit_length); } @@ -1162,10 +1157,10 @@ int ngap_gNB_handle_initial_context_request(uint32_t assoc_id, if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ if(ie->value.choice.NAS_PDU.size > 0) { - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nas_pdu_flag = 1; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nas_pdu.length = ie->value.choice.NAS_PDU.size; - NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nas_pdu.buffer = malloc(sizeof(uint8_t) * ie->value.choice.NAS_PDU.size); - memcpy(NGAP_INITIAL_CONTEXT_SETUP_REQ(message_p).nas_pdu.buffer, + msg->nas_pdu_flag = 1; + msg->nas_pdu.length = ie->value.choice.NAS_PDU.size; + msg->nas_pdu.buffer = malloc(sizeof(uint8_t) * ie->value.choice.NAS_PDU.size); + memcpy(msg->nas_pdu.buffer, ie->value.choice.NAS_PDU.buf, ie->value.choice.NAS_PDU.size); } } @@ -1259,7 +1254,6 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, NGAP_RAN_UE_NGAP_ID_t ran_ue_ngap_id; ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL; - MessageDef *message_p = NULL; NGAP_PDUSessionResourceSetupRequest_t *container; NGAP_PDUSessionResourceSetupRequestIEs_t *ie; DevAssert(pdu != NULL); @@ -1313,17 +1307,26 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, (uint64_t)ue_desc_p->amf_ue_ngap_id, amf_ue_ngap_id); } - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_SETUP_REQ); - NGAP_PDUSESSION_SETUP_REQ(message_p).ue_initial_id = ue_desc_p->ue_initial_id; + MessageDef * message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_SETUP_REQ); + ngap_pdusession_setup_req_t * msg=&NGAP_PDUSESSION_SETUP_REQ(message_p); + msg->ue_initial_id = ue_desc_p->ue_initial_id; ue_desc_p->ue_initial_id = 0; - NGAP_PDUSESSION_SETUP_REQ(message_p).gNB_ue_ngap_id = ue_desc_p->gNB_ue_ngap_id; - NGAP_PDUSESSION_SETUP_REQ(message_p).amf_ue_ngap_id = ue_desc_p->amf_ue_ngap_id; + msg->gNB_ue_ngap_id = ue_desc_p->gNB_ue_ngap_id; + msg->amf_ue_ngap_id = ue_desc_p->amf_ue_ngap_id; + + /* UE Aggregated Maximum Bitrate */ + NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PDUSessionResourceSetupRequestIEs_t, ie, container, + NGAP_ProtocolIE_ID_id_UEAggregateMaximumBitRate, true); + asn_INTEGER2ulong(&(ie->value.choice.UEAggregateMaximumBitRate.uEAggregateMaximumBitRateUL), + &msg->ueAggMaxBitRateUplink); + asn_INTEGER2ulong(&(ie->value.choice.UEAggregateMaximumBitRate.uEAggregateMaximumBitRateDL), + &msg->ueAggMaxBitRateDownlink); NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PDUSessionResourceSetupRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceSetupListSUReq, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - NGAP_PDUSESSION_SETUP_REQ(message_p).nb_pdusessions_tosetup = + msg->nb_pdusessions_tosetup = ie->value.choice.PDUSessionResourceSetupListSUReq.list.count; for (int i = 0; i < ie->value.choice.PDUSessionResourceSetupListSUReq.list.count; i++) { @@ -1332,20 +1335,29 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, NGAP_PDUSessionResourceSetupRequestTransfer_t *pdusessionTransfer_p = NULL; NGAP_PDUSessionResourceSetupRequestTransferIEs_t *pdusessionTransfer_ies = NULL; + // PDU session ID item_p = (NGAP_PDUSessionResourceSetupItemSUReq_t *)ie->value.choice.PDUSessionResourceSetupListSUReq.list.array[i]; - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].pdusession_id = item_p->pDUSessionID; + msg->pdusession_setup_params[i].pdusession_id = item_p->pDUSessionID; + // S-NSSAI + OCTET_STRING_TO_INT8(&item_p->s_NSSAI.sST, msg->allowed_nssai[i].sST); + if(item_p->s_NSSAI.sD != NULL) { + msg->allowed_nssai[i].sD_flag = 1; + msg->allowed_nssai[i].sD[0] = item_p->s_NSSAI.sD->buf[0]; + msg->allowed_nssai[i].sD[1] = item_p->s_NSSAI.sD->buf[1]; + msg->allowed_nssai[i].sD[2] = item_p->s_NSSAI.sD->buf[2]; + } // check for the NAS PDU if (item_p->pDUSessionNAS_PDU->size > 0 ) { - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.length = item_p->pDUSessionNAS_PDU->size; - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.buffer = malloc(sizeof(uint8_t) * item_p->pDUSessionNAS_PDU->size); - memcpy(NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.buffer, + msg->pdusession_setup_params[i].nas_pdu.length = item_p->pDUSessionNAS_PDU->size; + msg->pdusession_setup_params[i].nas_pdu.buffer = malloc(sizeof(uint8_t) * item_p->pDUSessionNAS_PDU->size); + memcpy(msg->pdusession_setup_params[i].nas_pdu.buffer, item_p->pDUSessionNAS_PDU->buf, item_p->pDUSessionNAS_PDU->size); - // NGAP_INFO("received a NAS PDU with size %d (%02x.%02x)\n",NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.length, item_p->nAS_PDU.buf[0], item_p->nAS_PDU.buf[1]); + // NGAP_INFO("received a NAS PDU with size %d (%02x.%02x)\n",msg->pdusession_setup_params[i].nas_pdu.length, item_p->nAS_PDU.buf[0], item_p->nAS_PDU.buf[1]); } else { - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.length = 0; - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nas_pdu.buffer = NULL; + msg->pdusession_setup_params[i].nas_pdu.length = 0; + msg->pdusession_setup_params[i].nas_pdu.buffer = NULL; NGAP_WARN("NAS PDU is not provided, generate a PDUSESSION_SETUP Failure (TBD) back to AMF \n"); } @@ -1374,10 +1386,10 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, NGAP_GTPTunnel_t *gTPTunnel_p; gTPTunnel_p = pdusessionTransfer_ies->value.choice.UPTransportLayerInformation.choice.gTPTunnel; /* The transport layer address for the IP packets */ - OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].gtp_teid); - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].upf_addr.length = + OCTET_STRING_TO_INT32(&gTPTunnel_p->gTP_TEID, msg->pdusession_setup_params[i].gtp_teid); + msg->pdusession_setup_params[i].upf_addr.length = gTPTunnel_p->transportLayerAddress.size * 8 - gTPTunnel_p->transportLayerAddress.bits_unused; - memcpy(NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].upf_addr.buffer , + memcpy(msg->pdusession_setup_params[i].upf_addr.buffer , gTPTunnel_p->transportLayerAddress.buf, gTPTunnel_p->transportLayerAddress.size); } break; @@ -1392,7 +1404,7 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, /* mandatory PDUSessionType */ case NGAP_ProtocolIE_ID_id_PDUSessionType: - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].upf_addr.pdu_session_type = (uint8_t)pdusessionTransfer_ies->value.choice.PDUSessionType; + msg->pdusession_setup_params[i].upf_addr.pdu_session_type = (uint8_t)pdusessionTransfer_ies->value.choice.PDUSessionType; break; /* optional SecurityIndication */ @@ -1412,24 +1424,28 @@ int ngap_gNB_handle_pdusession_setup_request(uint32_t assoc_id, DevAssert(pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count > 0); DevAssert(pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count <= NGAP_maxnoofQosFlows); - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; + msg->pdusession_setup_params[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; for(int qosIdx = 0; qosIdx < pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.count; qosIdx++){ qosFlowItem_p = pdusessionTransfer_ies->value.choice.QosFlowSetupRequestList.list.array[qosIdx]; /* Set the QOS informations */ - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; + msg->pdusession_setup_params[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; if(qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.present == NGAP_QosCharacteristics_PR_nonDynamic5QI){ + msg->pdusession_setup_params[i].qos[qosIdx].fiveQI_type = non_dynamic; if(qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.choice.nonDynamic5QI != NULL){ - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].qos[qosIdx].fiveQI = + msg->pdusession_setup_params[i].qos[qosIdx].fiveQI = (uint64_t)qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.choice.nonDynamic5QI->fiveQI; } + } else if (qosFlowItem_p->qosFlowLevelQosParameters.qosCharacteristics.present == NGAP_QosCharacteristics_PR_dynamic5QI) + { + msg->pdusession_setup_params[i].qos[qosIdx].fiveQI_type = dynamic; } - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.priority_level = + msg->pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.priority_level = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.priorityLevelARP; - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = + msg->pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.pre_emptionCapability; - NGAP_PDUSESSION_SETUP_REQ(message_p).pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = + msg->pdusession_setup_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = qosFlowItem_p->qosFlowLevelQosParameters.allocationAndRetentionPriority.pre_emptionVulnerability; } } @@ -1461,7 +1477,6 @@ int ngap_gNB_handle_paging(uint32_t assoc_id, ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_instance_t *ngap_gNB_instance = NULL; - MessageDef *message_p = NULL; NGAP_Paging_t *container; NGAP_PagingIEs_t *ie; DevAssert(pdu != NULL); @@ -1490,42 +1505,44 @@ int ngap_gNB_handle_paging(uint32_t assoc_id, return -1; } - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PAGING_IND); + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PAGING_IND); + ngap_paging_ind_t * msg=&NGAP_PAGING_IND(message_p); + /* convert NGAP_PagingIEs_t to ngap_paging_ind_t */ /* id-UEIdentityIndexValue : convert UE Identity Index value */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_UEPagingIdentity, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - OCTET_STRING_TO_INT16(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->aMFSetID, NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_set_id); - OCTET_STRING_TO_INT8(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->aMFPointer, NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_pointer); - OCTET_STRING_TO_INT32(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->fiveG_TMSI, NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.m_tmsi); + OCTET_STRING_TO_INT16(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->aMFSetID, msg->ue_paging_identity.s_tmsi.amf_set_id); + OCTET_STRING_TO_INT8(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->aMFPointer, msg->ue_paging_identity.s_tmsi.amf_pointer); + OCTET_STRING_TO_INT32(&ie->value.choice.UEPagingIdentity.choice.fiveG_S_TMSI->fiveG_TMSI, msg->ue_paging_identity.s_tmsi.m_tmsi); NGAP_DEBUG("[SCTP %d] Received Paging Identity amf_set_id %d, amf_pointer %d, m_tmsi %d\n", assoc_id, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_set_id, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_pointer, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.m_tmsi); + msg->ue_paging_identity.s_tmsi.amf_set_id, + msg->ue_paging_identity.s_tmsi.amf_pointer, + msg->ue_paging_identity.s_tmsi.m_tmsi); } else { return -1; } - NGAP_PAGING_IND(message_p).paging_drx = NGAP_PAGING_DRX_256; + msg->paging_drx = NGAP_PAGING_DRX_256; /* id-pagingDRX */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PagingDRX, false); /* optional */ if (ie) { - NGAP_PAGING_IND(message_p).paging_drx = ie->value.choice.PagingDRX; + msg->paging_drx = ie->value.choice.PagingDRX; } else { - NGAP_PAGING_IND(message_p).paging_drx = NGAP_PAGING_DRX_256; + msg->paging_drx = NGAP_PAGING_DRX_256; } - memset (&NGAP_PAGING_IND(message_p).plmn_identity[0], 0, sizeof(plmn_identity_t)*256); - memset (&NGAP_PAGING_IND(message_p).tac[0], 0, sizeof(int16_t)*256); - NGAP_PAGING_IND(message_p).tai_size = 0; + memset (&msg->plmn_identity[0], 0, sizeof(plmn_identity_t)*256); + memset (&msg->tac[0], 0, sizeof(int16_t)*256); + msg->tai_size = 0; /* id-TAIList */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PagingIEs_t, ie, container, NGAP_ProtocolIE_ID_id_TAIListForPaging, true); @@ -1536,15 +1553,15 @@ int ngap_gNB_handle_paging(uint32_t assoc_id, for (int i = 0; i < ie->value.choice.TAIListForPaging.list.count; i++) { NGAP_TAIListForPagingItem_t *item_p; item_p = (NGAP_TAIListForPagingItem_t *)ie->value.choice.TAIListForPaging.list.array[i]; - TBCD_TO_MCC_MNC(&(item_p->tAI.pLMNIdentity), NGAP_PAGING_IND(message_p).plmn_identity[i].mcc, - NGAP_PAGING_IND(message_p).plmn_identity[i].mnc, - NGAP_PAGING_IND(message_p).plmn_identity[i].mnc_digit_length); - OCTET_STRING_TO_INT16(&(item_p->tAI.tAC), NGAP_PAGING_IND(message_p).tac[i]); - NGAP_PAGING_IND(message_p).tai_size++; + TBCD_TO_MCC_MNC(&(item_p->tAI.pLMNIdentity), msg->plmn_identity[i].mcc, + msg->plmn_identity[i].mnc, + msg->plmn_identity[i].mnc_digit_length); + OCTET_STRING_TO_INT16(&(item_p->tAI.tAC), msg->tac[i]); + msg->tai_size++; NGAP_DEBUG("[SCTP %d] Received Paging: MCC %d, MNC %d, TAC %d\n", assoc_id, - NGAP_PAGING_IND(message_p).plmn_identity[i].mcc, - NGAP_PAGING_IND(message_p).plmn_identity[i].mnc, - NGAP_PAGING_IND(message_p).tac[i]); + msg->plmn_identity[i].mcc, + msg->plmn_identity[i].mnc, + msg->tac[i]); } } else { return -1; @@ -1552,10 +1569,10 @@ int ngap_gNB_handle_paging(uint32_t assoc_id, //paging parameter values NGAP_DEBUG("[SCTP %d] Received Paging parameters: Paging Identity amf_set_id %d amf_pointer %d m_tmsi %d paging_drx %d paging_priority %d\n",assoc_id, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_set_id, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.amf_pointer, - NGAP_PAGING_IND(message_p).ue_paging_identity.s_tmsi.m_tmsi, - NGAP_PAGING_IND(message_p).paging_drx, NGAP_PAGING_IND(message_p).paging_priority); + msg->ue_paging_identity.s_tmsi.amf_set_id, + msg->ue_paging_identity.s_tmsi.amf_pointer, + msg->ue_paging_identity.s_tmsi.m_tmsi, + msg->paging_drx, msg->paging_priority); /* send message to RRC */ itti_send_msg_to_task(TASK_RRC_GNB, ngap_gNB_instance->instance, message_p); @@ -1570,7 +1587,6 @@ int ngap_gNB_handle_pdusession_modify_request(uint32_t assoc_id, int i, nb_of_pdusessions_failed; ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL; - MessageDef *message_p = NULL; NGAP_PDUSessionResourceModifyRequest_t *container; NGAP_PDUSessionResourceModifyRequestIEs_t *ie; NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id; @@ -1617,8 +1633,9 @@ int ngap_gNB_handle_pdusession_modify_request(uint32_t assoc_id, if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id) { NGAP_WARN("UE context amf_ue_ngap_id is different form that of the message (%ld != %ld)", (uint64_t)ue_desc_p->amf_ue_ngap_id, amf_ue_ngap_id); - message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP); - NGAP_PDUSESSION_MODIFY_RESP (message_p).gNB_ue_ngap_id = gnb_ue_ngap_id; + MessageDef *message_p = itti_alloc_new_message (TASK_RRC_GNB, 0, NGAP_PDUSESSION_MODIFY_RESP); + ngap_pdusession_modify_resp_t* msg=&NGAP_PDUSESSION_MODIFY_RESP(message_p); + msg->gNB_ue_ngap_id = gnb_ue_ngap_id; NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PDUSessionResourceModifyRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceModifyListModReq, true); @@ -1627,32 +1644,32 @@ int ngap_gNB_handle_pdusession_modify_request(uint32_t assoc_id, NGAP_PDUSessionResourceModifyItemModReq_t *item_p; item_p = (NGAP_PDUSessionResourceModifyItemModReq_t *)ie->value.choice.PDUSessionResourceModifyListModReq.list.array[nb_of_pdusessions_failed]; - NGAP_PDUSESSION_MODIFY_RESP(message_p).pdusessions_failed[nb_of_pdusessions_failed].pdusession_id = item_p->pDUSessionID; - NGAP_PDUSESSION_MODIFY_RESP(message_p).pdusessions_failed[nb_of_pdusessions_failed].cause = NGAP_Cause_PR_radioNetwork; - NGAP_PDUSESSION_MODIFY_RESP(message_p).pdusessions_failed[nb_of_pdusessions_failed].cause_value = NGAP_CauseRadioNetwork_unknown_local_UE_NGAP_ID; + msg->pdusessions_failed[nb_of_pdusessions_failed].pdusession_id = item_p->pDUSessionID; + msg->pdusessions_failed[nb_of_pdusessions_failed].cause = NGAP_Cause_PR_radioNetwork; + msg->pdusessions_failed[nb_of_pdusessions_failed].cause_value = NGAP_CauseRadioNetwork_unknown_local_UE_NGAP_ID; } } else { return -1; } - NGAP_PDUSESSION_MODIFY_RESP(message_p).nb_of_pdusessions_failed = nb_of_pdusessions_failed; - ngap_gNB_pdusession_modify_resp(amf_desc_p->ngap_gNB_instance->instance, - &NGAP_PDUSESSION_MODIFY_RESP(message_p)); + msg->nb_of_pdusessions_failed = nb_of_pdusessions_failed; + ngap_gNB_pdusession_modify_resp(amf_desc_p->ngap_gNB_instance->instance,msg); itti_free(TASK_RRC_GNB,message_p); message_p = NULL; return -1; } - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_MODIFY_REQ); - NGAP_PDUSESSION_MODIFY_REQ(message_p).ue_initial_id = ue_desc_p->ue_initial_id; - NGAP_PDUSESSION_MODIFY_REQ(message_p).amf_ue_ngap_id = amf_ue_ngap_id; - NGAP_PDUSESSION_MODIFY_REQ(message_p).gNB_ue_ngap_id = gnb_ue_ngap_id; + MessageDef *message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_MODIFY_REQ); + ngap_pdusession_modify_req_t * msg=&NGAP_PDUSESSION_MODIFY_REQ(message_p); + msg->ue_initial_id = ue_desc_p->ue_initial_id; + msg->amf_ue_ngap_id = amf_ue_ngap_id; + msg->gNB_ue_ngap_id = gnb_ue_ngap_id; /* id-PDUSessionResourceModifyListModReq */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PDUSessionResourceModifyRequestIEs_t, ie, container, NGAP_ProtocolIE_ID_id_PDUSessionResourceModifyListModReq, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - NGAP_PDUSESSION_MODIFY_REQ(message_p).nb_pdusessions_tomodify = + msg->nb_pdusessions_tomodify = ie->value.choice.PDUSessionResourceModifyListModReq.list.count; for (i = 0; i < ie->value.choice.PDUSessionResourceModifyListModReq.list.count; i++) { @@ -1663,17 +1680,17 @@ int ngap_gNB_handle_pdusession_modify_request(uint32_t assoc_id, item_p = (NGAP_PDUSessionResourceModifyItemModReq_t *)ie->value.choice.PDUSessionResourceModifyListModReq.list.array[i]; - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].pdusession_id = item_p->pDUSessionID; + msg->pdusession_modify_params[i].pdusession_id = item_p->pDUSessionID; // check for the NAS PDU if (item_p->nAS_PDU != NULL && item_p->nAS_PDU->size > 0 ) { - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nas_pdu.length = item_p->nAS_PDU->size; - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nas_pdu.buffer = malloc(sizeof(uint8_t) * item_p->nAS_PDU->size); - memcpy(NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nas_pdu.buffer, + msg->pdusession_modify_params[i].nas_pdu.length = item_p->nAS_PDU->size; + msg->pdusession_modify_params[i].nas_pdu.buffer = malloc(sizeof(uint8_t) * item_p->nAS_PDU->size); + memcpy(msg->pdusession_modify_params[i].nas_pdu.buffer, item_p->nAS_PDU->buf, item_p->nAS_PDU->size); } else { - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nas_pdu.length = 0; - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nas_pdu.buffer = NULL; + msg->pdusession_modify_params[i].nas_pdu.length = 0; + msg->pdusession_modify_params[i].nas_pdu.buffer = NULL; continue; } @@ -1718,25 +1735,25 @@ int ngap_gNB_handle_pdusession_modify_request(uint32_t assoc_id, DevAssert(pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count > 0); DevAssert(pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count <= NGAP_maxnoofQosFlows); - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count; + msg->pdusession_modify_params[i].nb_qos = pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count; for(int qosIdx = 0; qosIdx < pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.count; qosIdx++) { qosFlowItem_p = pdusessionTransfer_ies->value.choice.QosFlowAddOrModifyRequestList.list.array[qosIdx]; /* Set the QOS informations */ - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; + msg->pdusession_modify_params[i].qos[qosIdx].qfi = (uint8_t)qosFlowItem_p->qosFlowIdentifier; if(qosFlowItem_p->qosFlowLevelQosParameters) { if (qosFlowItem_p->qosFlowLevelQosParameters->qosCharacteristics.present == NGAP_QosCharacteristics_PR_nonDynamic5QI) { - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].qos[qosIdx].fiveQI = + msg->pdusession_modify_params[i].qos[qosIdx].fiveQI = qosFlowItem_p->qosFlowLevelQosParameters->qosCharacteristics.choice.nonDynamic5QI->fiveQI; } else if (qosFlowItem_p->qosFlowLevelQosParameters->qosCharacteristics.present == NGAP_QosCharacteristics_PR_dynamic5QI) { // TODO } - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.priority_level = + msg->pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.priority_level = qosFlowItem_p->qosFlowLevelQosParameters->allocationAndRetentionPriority.priorityLevelARP; - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = + msg->pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_capability = qosFlowItem_p->qosFlowLevelQosParameters->allocationAndRetentionPriority.pre_emptionCapability; - NGAP_PDUSESSION_MODIFY_REQ(message_p).pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = + msg->pdusession_modify_params[i].qos[qosIdx].allocation_retention_priority.pre_emp_vulnerability = qosFlowItem_p->qosFlowLevelQosParameters->allocationAndRetentionPriority.pre_emptionVulnerability; } } @@ -1784,7 +1801,6 @@ int ngap_gNB_handle_pdusession_release_command(uint32_t assoc_id, int i; ngap_gNB_amf_data_t *amf_desc_p = NULL; ngap_gNB_ue_context_t *ue_desc_p = NULL; - MessageDef *message_p = NULL; NGAP_PDUSessionResourceReleaseCommand_t *container; NGAP_PDUSessionResourceReleaseCommandIEs_t *ie; NGAP_RAN_UE_NGAP_ID_t gnb_ue_ngap_id; @@ -1841,22 +1857,24 @@ int ngap_gNB_handle_pdusession_release_command(uint32_t assoc_id, NGAP_DEBUG("[SCTP %d] Received pdu session release command for gNB_UE_NGAP_ID %lu amf_ue_ngap_id %lu\n", assoc_id, gnb_ue_ngap_id, amf_ue_ngap_id); - message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_RELEASE_COMMAND); - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).gNB_ue_ngap_id = gnb_ue_ngap_id; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).amf_ue_ngap_id = amf_ue_ngap_id; + MessageDef * message_p = itti_alloc_new_message(TASK_NGAP, 0, NGAP_PDUSESSION_RELEASE_COMMAND); + ngap_pdusession_release_command_t * msg=&NGAP_PDUSESSION_RELEASE_COMMAND(message_p); + + msg->gNB_ue_ngap_id = gnb_ue_ngap_id; + msg->amf_ue_ngap_id = amf_ue_ngap_id; /* id-NAS-PDU */ NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_PDUSessionResourceReleaseCommandIEs_t, ie, container, NGAP_ProtocolIE_ID_id_NAS_PDU, false); if(ie && ie->value.choice.NAS_PDU.size > 0) { - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nas_pdu.length = ie->value.choice.NAS_PDU.size; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nas_pdu.buffer = malloc(sizeof(uint8_t) * ie->value.choice.NAS_PDU.size); - memcpy(NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nas_pdu.buffer, + msg->nas_pdu.length = ie->value.choice.NAS_PDU.size; + msg->nas_pdu.buffer = malloc(sizeof(uint8_t) * ie->value.choice.NAS_PDU.size); + memcpy(msg->nas_pdu.buffer, ie->value.choice.NAS_PDU.buf, ie->value.choice.NAS_PDU.size); } else { - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nas_pdu.length = 0; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nas_pdu.buffer = NULL; + msg->nas_pdu.length = 0; + msg->nas_pdu.buffer = NULL; } /* id-PDUSessionResourceToReleaseListRelCmd */ @@ -1864,22 +1882,22 @@ int ngap_gNB_handle_pdusession_release_command(uint32_t assoc_id, NGAP_ProtocolIE_ID_id_PDUSessionResourceToReleaseListRelCmd, true); if (ie != NULL) { /* checked by macro but cppcheck doesn't see it */ - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).nb_pdusessions_torelease = ie->value.choice.PDUSessionResourceToReleaseListRelCmd.list.count; + msg->nb_pdusessions_torelease = ie->value.choice.PDUSessionResourceToReleaseListRelCmd.list.count; for (i = 0; i < ie->value.choice.PDUSessionResourceToReleaseListRelCmd.list.count; i++) { NGAP_PDUSessionResourceToReleaseItemRelCmd_t *item_p; item_p = (NGAP_PDUSessionResourceToReleaseItemRelCmd_t *)ie->value.choice.PDUSessionResourceToReleaseListRelCmd.list.array[i]; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].pdusession_id = item_p->pDUSessionID; + msg->pdusession_release_params[i].pdusession_id = item_p->pDUSessionID; if(item_p->pDUSessionResourceReleaseCommandTransfer.size > 0) { - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].transfer_length = item_p->pDUSessionResourceReleaseCommandTransfer.size; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].transfer_buffer = malloc(sizeof(uint8_t) * item_p->pDUSessionResourceReleaseCommandTransfer.size); - memcpy(NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].transfer_buffer, + msg->pdusession_release_params[i].transfer_length = item_p->pDUSessionResourceReleaseCommandTransfer.size; + msg->pdusession_release_params[i].transfer_buffer = malloc(sizeof(uint8_t) * item_p->pDUSessionResourceReleaseCommandTransfer.size); + memcpy(msg->pdusession_release_params[i].transfer_buffer, item_p->pDUSessionResourceReleaseCommandTransfer.buf, item_p->pDUSessionResourceReleaseCommandTransfer.size); }else { - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].transfer_length = 0; - NGAP_PDUSESSION_RELEASE_COMMAND(message_p).pdusession_release_params[i].transfer_buffer = NULL; + msg->pdusession_release_params[i].transfer_length = 0; + msg->pdusession_release_params[i].transfer_buffer = NULL; NGAP_ERROR("[NGAP] Received pdu session release command for pDUSessionResourceReleaseCommandTransfer is NULL!\n"); } NGAP_DEBUG("[NGAP] Received pdu session release command for pDUSessionID id %ld\n", item_p->pDUSessionID); diff --git a/openair3/SCTP/sctp_eNB_task.c b/openair3/SCTP/sctp_eNB_task.c index 49a0674bdf..14128dd53e 100644 --- a/openair3/SCTP/sctp_eNB_task.c +++ b/openair3/SCTP/sctp_eNB_task.c @@ -617,10 +617,7 @@ sctp_handle_new_association_req( } //------------------------------------------------------------------------------ -static void sctp_send_data( - instance_t instance, - task_id_t task_id, - sctp_data_req_t *sctp_data_req_p) +static void sctp_send_data(sctp_data_req_t *sctp_data_req_p) { struct sctp_cnx_list_elm_s *sctp_cnx = NULL; @@ -660,10 +657,7 @@ static void sctp_send_data( } //------------------------------------------------------------------------------ -static int sctp_close_association( - const instance_t instance, - const task_id_t requestor, - sctp_close_association_t *close_association_p) +static int sctp_close_association(sctp_close_association_t *close_association_p) { struct sctp_cnx_list_elm_s *sctp_cnx = NULL; @@ -1152,10 +1146,8 @@ static void sctp_eNB_process_itti_msg() break; case SCTP_CLOSE_ASSOCIATION: - sctp_close_association(ITTI_MSG_DESTINATION_INSTANCE(received_msg), - ITTI_MSG_ORIGIN_ID(received_msg), - &received_msg->ittiMsg.sctp_close_association); - break; + sctp_close_association(&received_msg->ittiMsg.sctp_close_association); + break; case TERMINATE_MESSAGE: SCTP_WARN("*** Exiting SCTP thread\n"); @@ -1163,9 +1155,7 @@ static void sctp_eNB_process_itti_msg() break; case SCTP_DATA_REQ: { - sctp_send_data(ITTI_MSG_DESTINATION_INSTANCE(received_msg), - ITTI_MSG_ORIGIN_ID(received_msg), - &received_msg->ittiMsg.sctp_data_req); + sctp_send_data(&received_msg->ittiMsg.sctp_data_req); } break; diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 8045e7f635..b598a29c16 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -94,7 +94,6 @@ typedef struct Gtpv1uExtHeader { #define GTP_END_MARKER (254) #define GTP_GPDU (255) - typedef struct gtpv1u_bearer_s { /* TEID used in dl and ul */ teid_t teid_incoming; ///< eNB TEID @@ -127,8 +126,7 @@ class gtpEndPoint { uint8_t foundAddr[20]; int foundAddrLen; int ipVersion; - map ue2te_mapping; - map te2ue_mapping; + map ue2te_mapping; // we use the same port number for source and destination address // this allow using non standard gtp port number (different from 2152) // and so, for example tu run 4G and 5G cores on one system @@ -142,7 +140,7 @@ class gtpEndPoints { pthread_mutex_t gtp_lock=PTHREAD_MUTEX_INITIALIZER; // the instance id will be the Linux socket handler, as this is uniq map instances; - + map te2ue_mapping; gtpEndPoints() { unsigned int seed; fill_random(&seed, sizeof(seed)); @@ -593,22 +591,17 @@ teid_t newGtpuCreateTunnel(instance_t instance, teid_t incoming_teid=gtpv1uNewTeid(); - while ( inst->te2ue_mapping.find(incoming_teid) != inst->te2ue_mapping.end() ) { + while (globGtp.te2ue_mapping.find(incoming_teid) != globGtp.te2ue_mapping.end()) { LOG_W(GTPU, "[%ld] generated a random Teid that exists, re-generating (%x)\n", instance, incoming_teid); incoming_teid=gtpv1uNewTeid(); }; - inst->te2ue_mapping[incoming_teid].ue_id=ue_id; - - inst->te2ue_mapping[incoming_teid].incoming_rb_id= incoming_bearer_id; - - inst->te2ue_mapping[incoming_teid].outgoing_teid= outgoing_teid; - - inst->te2ue_mapping[incoming_teid].callBack=callBack; - - inst->te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP; - - inst->te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id; + globGtp.te2ue_mapping[incoming_teid].ue_id = ue_id; + globGtp.te2ue_mapping[incoming_teid].incoming_rb_id = incoming_bearer_id; + globGtp.te2ue_mapping[incoming_teid].outgoing_teid = outgoing_teid; + globGtp.te2ue_mapping[incoming_teid].callBack = callBack; + globGtp.te2ue_mapping[incoming_teid].callBackSDAP = callBackSDAP; + globGtp.te2ue_mapping[incoming_teid].pdusession_id = (uint8_t)outgoing_bearer_id; gtpv1u_bearer_t *tmp=&inst->ue2te_mapping[ue_id].bearers[outgoing_bearer_id]; @@ -641,13 +634,14 @@ teid_t newGtpuCreateTunnel(instance_t instance, pthread_mutex_unlock(&globGtp.gtp_lock); char ip4[INET_ADDRSTRLEN]; char ip6[INET6_ADDRSTRLEN]; - LOG_I(GTPU, "[%ld] Created tunnel for UE ID %lu, teid for DL: %x, teid for UL %x to remote IPv4: %s, IPv6 %s\n", + LOG_I(GTPU, + "[%ld] Created tunnel for UE ID %lu, teid for incoming: %x, teid for outgoing %x to remote IPv4: %s, IPv6 %s\n", instance, ue_id, tmp->teid_incoming, tmp->teid_outgoing, - inet_ntop(AF_INET,(void *)&tmp->outgoing_ip_addr, ip4,INET_ADDRSTRLEN ), - inet_ntop(AF_INET6,(void *)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN)); + inet_ntop(AF_INET, (void *)&tmp->outgoing_ip_addr, ip4, INET_ADDRSTRLEN), + inet_ntop(AF_INET6, (void *)&tmp->outgoing_ip6_addr.s6_addr, ip6, INET6_ADDRSTRLEN)); return incoming_teid; } @@ -795,7 +789,7 @@ int newGtpuDeleteAllTunnels(instance_t instance, ue_id_t ue_id) { for (auto j=ptrUe->second.bearers.begin(); j!=ptrUe->second.bearers.end(); ++j) { - inst->te2ue_mapping.erase(j->second.teid_incoming); + globGtp.te2ue_mapping.erase(j->second.teid_incoming); nb++; } @@ -826,7 +820,7 @@ int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, pdus if ( ptr2 == ptrUe->second.bearers.end() ) { LOG_E(GTPU,"[%ld] GTP-U instance: delete of not existing tunnel UE ID:RAB: %ld/%x\n", instance, ue_id, pdusession_id[i]); } else { - inst->te2ue_mapping.erase(ptr2->second.teid_incoming); + globGtp.te2ue_mapping.erase(ptr2->second.teid_incoming); nb++; } } @@ -913,9 +907,9 @@ static int Gtpv1uHandleEndMarker(int h, // the socket Linux file handler is the instance id getInstRetInt(h); - auto tunnel=inst->te2ue_mapping.find(ntohl(msgHdr->teid)); + auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); - if ( tunnel == inst->te2ue_mapping.end() ) { + if (tunnel == globGtp.te2ue_mapping.end()) { LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, msgHdr->teid); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; @@ -973,10 +967,9 @@ static int Gtpv1uHandleGpdu(int h, pthread_mutex_lock(&globGtp.gtp_lock); // the socket Linux file handler is the instance id getInstRetInt(h); + auto tunnel = globGtp.te2ue_mapping.find(ntohl(msgHdr->teid)); - auto tunnel=inst->te2ue_mapping.find(ntohl(msgHdr->teid)); - - if ( tunnel == inst->te2ue_mapping.end() ) { + if (tunnel == globGtp.te2ue_mapping.end()) { LOG_E(GTPU,"[%d] Received a incoming packet on unknown teid (%x) Dropping!\n", h, ntohl(msgHdr->teid)); pthread_mutex_unlock(&globGtp.gtp_lock); return GTPNOK; @@ -1143,10 +1136,7 @@ static int Gtpv1uHandleGpdu(int h, * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ extensionHeader->length = 1+sizeof(DlDataDeliveryStatus_flagsT)+3+1+1; gtpv1uCreateAndSendMsg( - h, peerIp, peerPort, GTP_GPDU, - inst->te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, - NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, - extensionHeader->buffer, extensionHeader->length); + h, peerIp, peerPort, GTP_GPDU, globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid, NULL, 0, false, false, 0, 0, NR_RAN_CONTAINER, extensionHeader->buffer, extensionHeader->length); } LOG_D(GTPU,"[%d] Received a %d bytes packet for: teid:%x\n", h, @@ -1224,19 +1214,19 @@ void *gtpv1uTask(void *args) { if (message_p != NULL ) { openAddr_t addr= {0}; - - switch (ITTI_MSG_ID(message_p)) { - // DATA TO BE SENT TO UDP + const instance_t myInstance = ITTI_MSG_DESTINATION_INSTANCE(message_p); + const int msgType = ITTI_MSG_ID(message_p); + LOG_D(GTPU, "GTP-U received %s for instance %ld\n", messages_info[msgType].name, myInstance); + switch (msgType) { + // DATA TO BE SENT TO UDP case GTPV1U_TUNNEL_DATA_REQ: { - gtpv1uSend(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), - >PV1U_TUNNEL_DATA_REQ(message_p), false, false); + gtpv1uSend(compatInst(myInstance), >PV1U_TUNNEL_DATA_REQ(message_p), false, false); } break; case GTPV1U_DU_BUFFER_REPORT_REQ:{ - gtpv1uSendDlDeliveryStatus(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), - >PV1U_DU_BUFFER_REPORT_REQ(message_p)); + gtpv1uSendDlDeliveryStatus(compatInst(myInstance), >PV1U_DU_BUFFER_REPORT_REQ(message_p)); } break; @@ -1248,8 +1238,7 @@ void *gtpv1uTask(void *args) { break; case GTPV1U_ENB_END_MARKER_REQ: - gtpv1uEndTunnel(compatInst(ITTI_MSG_DESTINATION_INSTANCE(message_p)), - >PV1U_TUNNEL_DATA_REQ(message_p)); + gtpv1uEndTunnel(compatInst(myInstance), >PV1U_TUNNEL_DATA_REQ(message_p)); itti_free(TASK_GTPV1_U, GTPV1U_TUNNEL_DATA_REQ(message_p).buffer); break; diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index 86d719c200..8729be9fa5 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -93,8 +93,8 @@ extern "C" { gtpCallbackSDAP callBackSDAP); void GtpuUpdateTunnelOutgoingAddressAndTeid(instance_t instance, - ue_id_t ue_id, - ebi_t bearer_id, + ue_id_t ue_id, + ebi_t bearer_id, in_addr_t newOutgoingAddr, teid_t newOutgoingTeid); @@ -149,7 +149,7 @@ extern "C" { //RB_ENTRY(gtpv1u_ue_data_s) gtpv1u_ue_node; } gtpv1u_ue_data_t; - + #ifdef __cplusplus } #endif From 472e916af62ddcb3ffd70733256f6be0db7fc35f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 1 Feb 2023 18:00:26 +0100 Subject: [PATCH 18/26] Add Dockerfile for nr-cuup --- docker/Dockerfile.nr-cuup.ubuntu18 | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docker/Dockerfile.nr-cuup.ubuntu18 diff --git a/docker/Dockerfile.nr-cuup.ubuntu18 b/docker/Dockerfile.nr-cuup.ubuntu18 new file mode 100644 index 0000000000..94d1ff5ef7 --- /dev/null +++ b/docker/Dockerfile.nr-cuup.ubuntu18 @@ -0,0 +1,63 @@ +#/* +# * 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 +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface BUILD service +# Valid for Ubuntu18.04 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest as gnb-base + +FROM ran-build:latest AS gnb-build + +RUN cp /oai-ran/docker/scripts/gnb_entrypoint.sh /oai-ran/docker/scripts/entrypoint.sh + +FROM ubuntu:bionic as oai-nr-cuup +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ + DEBIAN_FRONTEND=noninteractive apt-get install --yes \ + libsctp1 \ + libconfig9 \ + openssl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR /opt/oai-gnb/bin +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/nr-cuup \ + /oai-ran/docker/scripts/entrypoint.sh \ + ./ + +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/librfsimulator.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ + /usr/local/lib/ + +# Create directory for configuration files +WORKDIR /opt/oai-gnb/etc + +WORKDIR /opt/oai-gnb +ENTRYPOINT ["/opt/oai-gnb/bin/entrypoint.sh"] +CMD ["/opt/oai-gnb/bin/nr-cuup", "-O", "/opt/oai-gnb/etc/gnb.conf"] From e8595334547f47565f86ad62734a33db58473543 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 1 Feb 2023 18:00:02 +0100 Subject: [PATCH 19/26] Build nr-cuup in CI (ubuntu) --- ci-scripts/cls_containerize.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index e02d208945..38305b5b4e 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -360,6 +360,7 @@ class Containerize(): if result is not None: imageNames.append(('oai-enb', 'eNB')) imageNames.append(('oai-gnb', 'gNB')) + imageNames.append(('oai-nr-cuup', 'nr-cuup')) imageNames.append(('oai-lte-ue', 'lteUE')) imageNames.append(('oai-nr-ue', 'nrUE')) if self.host == 'Red Hat': @@ -680,7 +681,7 @@ class Containerize(): orgTag = 'develop' if self.ranAllowMerge: orgTag = 'ci-temp' - imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru'] + imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru', 'oai-nr-cuup'] for image in imageNames: tagToUse = self.ImageTagToUse(image) mySSH.command(f'docker image tag {image}:{orgTag} {tagToUse}', '\$', 5) @@ -814,7 +815,7 @@ class Containerize(): else: logging.debug('Removing test images locally') - imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru'] + imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru', 'oai-nr-cuup'] for image in imageNames: cmd = f'docker rmi {self.ImageTagToUse(image)} || true' if lIpAddr != 'none': @@ -856,7 +857,7 @@ class Containerize(): mySSH.command('cd ' + lSourcePath + '/' + self.yamlPath[self.eNB_instance], '\$', 5) mySSH.command('cp docker-compose.yml ci-docker-compose.yml', '\$', 5) - imagesList = ['oai-enb', 'oai-gnb'] + imagesList = ['oai-enb', 'oai-gnb', 'oai-nr-cuup'] for image in imagesList: imageToUse = self.ImageTagToUse(image) mySSH.command(f'sed -i -e "s#image: {image}:latest#image: {imageToUse}#" ci-docker-compose.yml', '\$', 2) @@ -1066,7 +1067,7 @@ class Containerize(): cmd = 'cd ' + self.yamlPath[0] + ' && cp docker-compose.y*ml docker-compose-ci.yml' subprocess.run(cmd, shell=True) - imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru'] + imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru', 'oai-nr-cuup'] for image in imageNames: tagToUse = self.ImageTagToUse(image) cmd = f'cd {self.yamlPath[0]} && sed -i -e "s@{image}:develop@{tagToUse}@" docker-compose-ci.yml' @@ -1190,7 +1191,7 @@ class Containerize(): cmd = 'cd ' + self.yamlPath[0] + ' && cp docker-compose.y*ml docker-compose-ci.yml' logging.info(cmd) subprocess.run(cmd, shell=True) - imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru'] + imageNames = ['oai-enb', 'oai-gnb', 'oai-lte-ue', 'oai-nr-ue', 'oai-lte-ru', 'oai-nr-cuup'] for image in imageNames: tagToUse = self.ImageTagToUse(image) cmd = f'cd {self.yamlPath[0]} && sed -i -e "s@{image}:develop@{tagToUse}@" docker-compose-ci.yml' From 3ec0f5ee68b512218a33d101df4791ea8a9332b5 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Feb 2023 12:21:19 +0100 Subject: [PATCH 20/26] Correct E1 config for containers --- .../{gnb-cucp.sa.conf => gnb-cucp.sa.f1.conf} | 35 +-- .../{gnb-cuup.sa.conf => gnb-cuup.sa.f1.conf} | 29 +-- .../gnb-du.band78.106prb.rfsim.conf | 245 ++++++++++++++++++ 3 files changed, 264 insertions(+), 45 deletions(-) rename ci-scripts/conf_files/{gnb-cucp.sa.conf => gnb-cucp.sa.f1.conf} (90%) rename ci-scripts/conf_files/{gnb-cuup.sa.conf => gnb-cuup.sa.f1.conf} (75%) create mode 100644 ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf diff --git a/ci-scripts/conf_files/gnb-cucp.sa.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf similarity index 90% rename from ci-scripts/conf_files/gnb-cucp.sa.conf rename to ci-scripts/conf_files/gnb-cucp.sa.f1.conf index 7932098584..0a2f88495e 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf @@ -2,6 +2,7 @@ Active_gNBs = ( "gNB-OAI"); # Asn1_verbosity, choice in: none, info, annoying Asn1_verbosity = "none"; Num_Threads_PUSCH = 8; +sa = 1; gNBs = ( @@ -15,27 +16,17 @@ gNBs = // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; - plmn_list = ({ - mcc = 208; - mnc = 99; - mnc_length = 2; - snssaiList = ( - { - sst = 1; - sd = 0x1; // 0 false, else true - } - ); - - }); + plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); nr_cellid = 12345678L; + force_256qam_off = 1; tr_s_preference = "f1"; local_s_if_name = "lo"; - local_s_address = "127.0.0.4"; - remote_s_address = "127.0.0.3"; + local_s_address = "192.168.72.140"; + remote_s_address = "192.168.72.143"; local_s_portc = 501; local_s_portd = 2152; remote_s_portc = 500; @@ -187,7 +178,7 @@ gNBs = ////////// AMF parameters: - amf_ip_address = ( { ipv4 = "192.168.70.132"; + amf_ip_address = ( { ipv4 = "192.168.71.132"; ipv6 = "192:168:30::17"; active = "yes"; preference = "ipv4"; @@ -198,21 +189,17 @@ gNBs = ( { type = "cp"; - ipv4_cucp = "127.0.0.4"; - port_cucp = 2152; - ipv4_cuup = "127.0.0.5"; - port_cuup = 2152; + ipv4_cucp = "192.168.72.140"; + port_cucp = 38462; + ipv4_cuup = "192.168.72.141"; + port_cuup = 38462; } ) NETWORK_INTERFACES : { - GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; - GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; - GNB_PORT_FOR_S1U = 2152; # Spec 2152 + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.140/24"; }; } ); diff --git a/ci-scripts/conf_files/gnb-cuup.sa.conf b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf similarity index 75% rename from ci-scripts/conf_files/gnb-cuup.sa.conf rename to ci-scripts/conf_files/gnb-cuup.sa.f1.conf index 9e18459f87..2a57a37273 100644 --- a/ci-scripts/conf_files/gnb-cuup.sa.conf +++ b/ci-scripts/conf_files/gnb-cuup.sa.f1.conf @@ -1,6 +1,7 @@ Active_gNBs = ( "gNB-OAI"); # Asn1_verbosity, choice in: none, info, annoying Asn1_verbosity = "none"; +sa = 1; gNBs = ( @@ -14,25 +15,14 @@ gNBs = // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; - plmn_list = ({ - mcc = 208; - mnc = 99; - mnc_length = 2; - snssaiList = ( - { - sst = 1; - sd = 0x1; // 0 false, else true - } - ); - - }); + plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); tr_s_preference = "f1"; local_s_if_name = "lo"; - local_s_address = "127.0.0.4"; - remote_s_address = "127.0.0.3"; + local_s_address = "192.168.72.141"; + remote_s_address = "192.168.72.142"; local_s_portc = 501; local_s_portd = 2152; remote_s_portc = 500; @@ -46,24 +36,21 @@ gNBs = SCTP_OUTSTREAMS = 2; }; - E1_INTERFACE = ( { type = "up"; - ipv4_cucp = "127.0.0.4"; - port_cucp = 2152; - ipv4_cuup = "127.0.0.5"; - port_cuup = 2152; + ipv4_cucp = "192.168.72.140"; + ipv4_cuup = "192.168.72.141"; } ) NETWORK_INTERFACES : { GNB_INTERFACE_NAME_FOR_NG_AMF = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.70.129/24"; + GNB_IPV4_ADDRESS_FOR_NG_AMF = "192.168.71.141/24"; GNB_INTERFACE_NAME_FOR_NGU = "demo-oai"; - GNB_IPV4_ADDRESS_FOR_NGU = "192.168.70.129/24"; + GNB_IPV4_ADDRESS_FOR_NGU = "192.168.71.141/24"; GNB_PORT_FOR_S1U = 2152; # Spec 2152 }; } diff --git a/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf b/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf new file mode 100644 index 0000000000..3c2fbe717e --- /dev/null +++ b/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf @@ -0,0 +1,245 @@ +Active_gNBs = ( "du-rfsim"); +# Asn1_verbosity, choice in: none, info, annoying +Asn1_verbosity = "none"; + +gNBs = +( + { + ////////// Identification parameters: + gNB_ID = 0xe00; + +# cell_type = "CELL_MACRO_GNB"; + + gNB_name = "du-rfsim"; + + // Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code = 1; + plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + + + nr_cellid = 12345678L; + + ////////// Physical parameters: + + min_rxtxtime = 6; + force_256qam_off = 1; + + pdcch_ConfigSIB1 = ( + { + controlResourceSetZero = 12; + searchSpaceZero = 0; + } + ); + + servingCellConfigCommon = ( + { + #spCellConfigCommon + + physCellId = 0; + +# downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB = 641280; + dl_frequencyBand = 78; + # this is 3600 MHz + dl_absoluteFrequencyPointA = 640008; + #scs-SpecificCarrierList + dl_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing = 1; + dl_carrierBandwidth = 106; + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth = 28875; # 6366 12925 12956 28875 12952 +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing = 1; + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero = 12; + initialDLBWPsearchSpaceZero = 0; + + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand = 78; + #scs-SpecificCarrierList + ul_offstToCarrier = 0; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing = 1; + ul_carrierBandwidth = 106; + pMax = 20; + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth = 28875; +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing = 1; + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex = 98; +#prach_msg1_FDM +#0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM = 0; + prach_msg1_FrequencyStart = 0; + zeroCorrelationZoneConfig = 13; + preambleReceivedTargetPower = -96; +#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax = 6; +#powerRampingStep +# 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep = 1; +#ra_ReponseWindow +#1,2,4,8,10,20,40,80 + ra_ResponseWindow = 4; +#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR +#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4; +#oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 14; +#ra_ContentionResolutionTimer +#(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer = 7; + rsrp_ThresholdSSB = 19; +#prach-RootSequenceIndex_PR +#1 = 839, 2 = 139 + prach_RootSequenceIndex_PR = 2; + prach_RootSequenceIndex = 1; + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + # + msg1_SubcarrierSpacing = 1, +# restrictedSetConfig +# 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig = 0, + + msg3_DeltaPreamble = 1; + p0_NominalWithGrant =-90; + +# pucch-ConfigCommon setup : +# pucchGroupHopping +# 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping = 0; + hoppingId = 40; + p0_nominal = -90; +# ssb_PositionsInBurs_BitmapPR +# 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR = 2; + ssb_PositionsInBurst_Bitmap = 1; + +# ssb_periodicityServingCell +# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell = 2; + +# dmrs_TypeA_position +# 0 = pos2, 1 = pos3 + dmrs_TypeA_Position = 0; + +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing = 1; + + + #tdd-UL-DL-ConfigurationCommon +# subcarrierSpacing +# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing = 1; + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity = 6; + nrofDownlinkSlots = 7; + nrofDownlinkSymbols = 6; + nrofUplinkSlots = 2; + nrofUplinkSymbols = 4; + + ssPBCH_BlockPower = -25; + } + + ); + + + # ------- SCTP definitions + SCTP : + { + # Number of streams to use in input/output + SCTP_INSTREAMS = 2; + SCTP_OUTSTREAMS = 2; + }; + } +); + +MACRLCs = ( + { + num_cc = 1; + tr_s_preference = "local_L1"; + tr_n_preference = "f1"; + local_n_if_name = "eth0"; + local_n_address = "192.168.72.142"; + remote_n_address = "192.168.72.140"; + local_n_portc = 500; + local_n_portd = 2152; + remote_n_portc = 501; + remote_n_portd = 2152; + pusch_TargetSNRx10 = 200; + pucch_TargetSNRx10 = 200; + } +); + +L1s = ( +{ + num_cc = 1; + tr_n_preference = "local_mac"; + prach_dtx_threshold = 200; + pucch0_dtx_threshold = 150; + ofdm_offset_divisor = 8; #set this to UINT_MAX for offset 0 +} +); + +RUs = ( + { + local_rf = "yes" + nb_tx = 1 + nb_rx = 1 + att_tx = 0 + att_rx = 0; + bands = [78]; + max_pdschReferenceSignalPower = -27; + max_rxgain = 114; + eNB_instances = [0]; + #beamforming 1x4 matrix: + bf_weights = [0x00007fff, 0x0000, 0x0000, 0x0000]; + clock_src = "internal"; + } +); + +THREAD_STRUCT = ( + { + #three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT" + parallel_config = "PARALLEL_SINGLE_THREAD"; + #two option for worker "WORKER_DISABLE" or "WORKER_ENABLE" + worker_config = "WORKER_ENABLE"; + } +); + +rfsimulator: { + serveraddr = "server"; + serverport = "4043"; + options = (); #("saviq"); or/and "chanmod" + modelname = "AWGN"; + IQfile = "/tmp/rfsimulator.iqs" +} + +log_config : +{ + global_log_level = "info"; + hw_log_level = "info"; + nr_phy_log_level = "info"; + nr_mac_log_level = "info"; + rlc_log_level = "info"; + pdcp_log_level = "info"; + rrc_log_level = "info"; + f1ap_log_level = "debug"; + ngap_log_level = "debug"; +}; From 4d10437474fa46603adc2405d07140d88838f8be Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 1 Feb 2023 18:01:07 +0100 Subject: [PATCH 21/26] Add basic docker-compose for E1 --- .../5g_rfsimulator_e1/docker-compose.yaml | 350 ++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml diff --git a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml new file mode 100644 index 0000000000..3d4689e912 --- /dev/null +++ b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml @@ -0,0 +1,350 @@ +version: '3.8' +services: + oai-nrf: + container_name: "rfsim5g-oai-nrf" + image: oai-nrf:august-dev + environment: + - NRF_INTERFACE_NAME_FOR_SBI=eth0 + - NRF_INTERFACE_PORT_FOR_SBI=80 + - NRF_INTERFACE_HTTP2_PORT_FOR_SBI=9090 + - NRF_API_VERSION=v1 + - INSTANCE=0 + - PID_DIRECTORY=/var/run + networks: + core_net: + ipv4_address: 192.168.71.130 + volumes: + - ../5g_rfsimulator/nrf-healthcheck.sh:/openair-nrf/bin/nrf-healthcheck.sh + healthcheck: + test: /bin/bash -c "/openair-nrf/bin/nrf-healthcheck.sh" + interval: 10s + timeout: 5s + retries: 5 + + mysql: + container_name: "rfsim5g-mysql" + image: mysql:5.7 + volumes: + - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql + - ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh + environment: + - TZ=Europe/Paris + - MYSQL_DATABASE=oai_db + - MYSQL_USER=test + - MYSQL_PASSWORD=test + - MYSQL_ROOT_PASSWORD=linux + healthcheck: + test: /bin/bash -c "/tmp/mysql-healthcheck.sh" + interval: 10s + timeout: 5s + retries: 5 + networks: + core_net: + ipv4_address: 192.168.71.131 + + oai-amf: + container_name: "rfsim5g-oai-amf" + image: oai-amf:august-dev + environment: + - TZ=Europe/paris + - INSTANCE=0 + - PID_DIRECTORY=/var/run + - MCC=208 + - MNC=99 + - REGION_ID=128 + - AMF_SET_ID=1 + - SERVED_GUAMI_MCC_0=208 + - SERVED_GUAMI_MNC_0=99 + - SERVED_GUAMI_REGION_ID_0=128 + - SERVED_GUAMI_AMF_SET_ID_0=1 + - SERVED_GUAMI_MCC_1=460 + - SERVED_GUAMI_MNC_1=11 + - SERVED_GUAMI_REGION_ID_1=10 + - SERVED_GUAMI_AMF_SET_ID_1=1 + - PLMN_SUPPORT_MCC=208 + - PLMN_SUPPORT_MNC=99 + - PLMN_SUPPORT_TAC=0x0001 + - SST_0=1 + - SD_0=0xffffff + - SST_1=1 + - SD_1=1 + - AMF_INTERFACE_NAME_FOR_NGAP=eth0 + - AMF_INTERFACE_NAME_FOR_N11=eth0 + - SMF_INSTANCE_ID_0=1 + - SMF_FQDN_0=oai-smf + - SMF_IPV4_ADDR_0=0.0.0.0 + - SMF_HTTP_VERSION_0=v1 + - SELECTED_0=true + - SMF_INSTANCE_ID_1=2 + - SMF_FQDN_1=oai-smf + - SMF_IPV4_ADDR_1=0.0.0.0 + - SMF_HTTP_VERSION_1=v1 + - SELECTED_1=false + - MYSQL_SERVER=192.168.71.131 + - MYSQL_USER=root + - MYSQL_PASS=linux + - MYSQL_DB=oai_db + - OPERATOR_KEY=c42449363bbad02b66d16bc975d77cc1 + - NRF_IPV4_ADDRESS=192.168.71.130 + - NRF_PORT=80 + - NF_REGISTRATION=yes + - SMF_SELECTION=yes + - USE_FQDN_DNS=yes + - NRF_API_VERSION=v1 + - NRF_FQDN=oai-nrf + - EXTERNAL_AUSF=no + - AUSF_IPV4_ADDRESS=0.0.0.0 + - AUSF_PORT=80 + - AUSF_API_VERSION=v1 + - AUSF_FQDN=localhost + depends_on: + - oai-nrf + - mysql + volumes: + - ../5g_rfsimulator/amf-healthcheck.sh:/openair-amf/bin/amf-healthcheck.sh + healthcheck: + test: /bin/bash -c "/openair-amf/bin/amf-healthcheck.sh" + interval: 10s + timeout: 15s + retries: 5 + networks: + core_net: + ipv4_address: 192.168.71.132 + + oai-smf: + container_name: "rfsim5g-oai-smf" + image: oai-smf:august-dev + environment: + - TZ=Europe/Paris + - INSTANCE=0 + - PID_DIRECTORY=/var/run + - SMF_INTERFACE_NAME_FOR_N4=eth0 + - SMF_INTERFACE_NAME_FOR_SBI=eth0 + - SMF_INTERFACE_PORT_FOR_SBI=80 + - SMF_INTERFACE_HTTP2_PORT_FOR_SBI=9090 + - SMF_API_VERSION=v1 + - DEFAULT_DNS_IPV4_ADDRESS=172.21.3.100 + - DEFAULT_DNS_SEC_IPV4_ADDRESS=4.4.4.4 + - AMF_IPV4_ADDRESS=0.0.0.0 + - AMF_PORT=80 + - AMF_API_VERSION=v1 + - AMF_FQDN=oai-amf + - UDM_IPV4_ADDRESS=127.0.0.1 + - UDM_PORT=80 + - UDM_API_VERSION=v1 + - UDM_FQDN=localhost + - UPF_IPV4_ADDRESS=192.168.71.134 + - UPF_FQDN_0=oai-spgwu + - NRF_IPV4_ADDRESS=192.168.71.130 + - NRF_PORT=80 + - NRF_API_VERSION=v1 + - NRF_FQDN=oai-nrf + - REGISTER_NRF=yes + - DISCOVER_UPF=yes + - USE_FQDN_DNS=yes + - DNN_NI0=oai + - TYPE0=IPv4 + - DNN_RANGE0=12.1.1.2 - 12.1.1.127 + - NSSAI_SST0=1 + - NSSAI_SD0=0xFFFFFF + - SESSION_AMBR_UL0=200Mbps + - SESSION_AMBR_DL0=400Mbps + - DNN_NI2=oai.ipv4 + depends_on: + - oai-nrf + - oai-amf + volumes: + - ../5g_rfsimulator/smf-healthcheck.sh:/openair-smf/bin/smf-healthcheck.sh + healthcheck: + test: /bin/bash -c "/openair-smf/bin/smf-healthcheck.sh" + interval: 10s + timeout: 5s + retries: 5 + networks: + core_net: + ipv4_address: 192.168.71.133 + + oai-spgwu: + container_name: "rfsim5g-oai-spgwu" + image: oai-spgwu-tiny:august-dev + environment: + - TZ=Europe/Paris + - PID_DIRECTORY=/var/run + - SGW_INTERFACE_NAME_FOR_S1U_S12_S4_UP=eth0 + - SGW_INTERFACE_NAME_FOR_SX=eth0 + - PGW_INTERFACE_NAME_FOR_SGI=eth0 + - NETWORK_UE_NAT_OPTION=yes + - NETWORK_UE_IP=12.1.1.0/24 + - SPGWC0_IP_ADDRESS=192.168.71.133 + - BYPASS_UL_PFCP_RULES=no + - MCC=208 + - MNC=99 + - MNC03=099 + - TAC=1 + - GTP_EXTENSION_HEADER_PRESENT=yes + - GW_ID=1 + - REALM=openairinterface.org + - ENABLE_5G_FEATURES=yes + - REGISTER_NRF=yes + - USE_FQDN_NRF=yes + - UPF_FQDN_5G=oai-spgwu + - NRF_IPV4_ADDRESS=192.168.71.130 + - NRF_PORT=80 + - NRF_API_VERSION=v1 + - NRF_FQDN=oai-nrf + - NSSAI_SST_0=1 + - NSSAI_SD_0=0xffffff + - DNN_0=oai + depends_on: + - oai-nrf + - oai-smf + cap_add: + - NET_ADMIN + - SYS_ADMIN + cap_drop: + - ALL + privileged: true + volumes: + - ../5g_rfsimulator/spgwu-healthcheck.sh:/openair-spgwu-tiny/bin/spgwu-healthcheck.sh + healthcheck: + test: /bin/bash -c "/openair-spgwu-tiny/bin/spgwu-healthcheck.sh" + interval: 10s + timeout: 5s + retries: 5 + networks: + core_net: + ipv4_address: 192.168.71.134 + traffic_net: + ipv4_address: 192.168.73.134 + + oai-ext-dn: + image: ubuntu:bionic + privileged: true + container_name: rfsim5g-oai-ext-dn + entrypoint: /bin/bash -c \ + "apt update; apt install -y procps iptables iproute2 iperf iputils-ping;"\ + "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ + "ip route add 12.1.1.0/24 via 192.168.73.134 dev eth0; sleep infinity" + depends_on: + - oai-spgwu + networks: + traffic_net: + ipv4_address: 192.168.73.135 + healthcheck: + test: /bin/bash -c "ping -c 2 192.168.73.134" + interval: 10s + timeout: 5s + retries: 5 + + oai-cucp: + image: oai-gnb:develop + privileged: true + container_name: rfsim5g-oai-cucp + environment: + USE_VOLUMED_CONF: 'yes' + USE_ADDITIONAL_OPTIONS: --sa --rfsim --log_config.global_log_options level,nocolor,time + depends_on: + - oai-ext-dn + networks: + core_net: + ipv4_address: 192.168.71.140 + ran_net: + ipv4_address: 192.168.72.140 + volumes: + - ../../conf_files/gnb-cucp.sa.f1.conf:/opt/oai-gnb/etc/mounted.conf + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + retries: 5 + + oai-cuup: + image: oai-nr-cuup:develop + privileged: true + container_name: rfsim5g-oai-cuup + environment: + USE_VOLUMED_CONF: 'yes' + USE_ADDITIONAL_OPTIONS: --sa --rfsim + #--log_config.global_log_options level,nocolor,time + depends_on: + - oai-cucp + networks: + core_net: + ipv4_address: 192.168.71.141 + ran_net: + ipv4_address: 192.168.72.141 + volumes: + - ../../conf_files/gnb-cuup.sa.f1.conf:/opt/oai-gnb/etc/mounted.conf + healthcheck: + test: /bin/bash -c "pgrep nr-cuup" + interval: 10s + timeout: 5s + retries: 5 + + oai-du: + image: oai-gnb:develop + privileged: true + container_name: rfsim5g-oai-du + environment: + RFSIMULATOR: server + USE_VOLUMED_CONF: 'yes' + USE_ADDITIONAL_OPTIONS: --sa --rfsim --log_config.global_log_options level,nocolor,time + networks: + ran_net: + ipv4_address: 192.168.72.142 + volumes: + - ../../conf_files/gnb-du.band78.106prb.rfsim.conf:/opt/oai-gnb/etc/mounted.conf + healthcheck: + test: /bin/bash -c "pgrep nr-softmodem" + interval: 10s + timeout: 5s + retries: 5 + + oai-nr-ue: + image: oai-nr-ue:develop + privileged: true + container_name: rfsim5g-oai-nr-ue + environment: + RFSIMULATOR: 192.168.72.142 + FULL_IMSI: '208990100001100' + FULL_KEY: 'fec86ba6eb707ed08905757b1bb44b8f' + OPC: 'C42449363BBAD02B66D16BC975D77CC1' + DNN: oai + NSSAI_SST: 1 + USE_ADDITIONAL_OPTIONS: --sa --rfsim -r 106 --numerology 1 -C 3619200000 --nokrnmod --log_config.global_log_options level,nocolor,time + depends_on: + - oai-du + networks: + ran_net: + ipv4_address: 192.168.72.150 + healthcheck: + test: /bin/bash -c "pgrep nr-uesoftmodem" + interval: 10s + timeout: 5s + retries: 5 + +networks: + core_net: + driver: bridge + name: rfsim5g-oai-core-net + ipam: + config: + - subnet: 192.168.71.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-core" + ran_net: + driver: bridge + name: rfsim5g-oai-ran-net + ipam: + config: + - subnet: 192.168.72.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-ran" + traffic_net: + driver: bridge + name: rfsim5g-oai-traffic_net-net + ipam: + config: + - subnet: 192.168.73.128/26 + driver_opts: + com.docker.network.bridge.name: "rfsim5g-traffic" From 616bdf83609e34fe59f34ae0d536dd9b3866f4fa Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 7 Feb 2023 16:47:00 +0100 Subject: [PATCH 22/26] Generalize tshark capture filter, but avoid false positives --- ci-scripts/cls_containerize.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index 38305b5b4e..324e718ba1 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -1161,18 +1161,9 @@ class Containerize(): def CaptureOnDockerNetworks(self): cmd = 'cd ' + self.yamlPath[0] + ' && docker-compose -f docker-compose-ci.yml config | grep com.docker.network.bridge.name | sed -e "s@^.*name: @@"' networkNames = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, universal_newlines=True, timeout=10) - if re.search('4g.*rfsimulator', self.yamlPath[0]) is not None: - # Excluding any traffic from LTE-UE container (192.168.61.30) - # From the trf-gen, keeping only PING traffic - cmd = 'sudo nohup tshark -f "(host 192.168.61.11 and icmp) or (not host 192.168.61.11 and not host 192.168.61.30 and not arp and not port 53 and not port 2152)"' - elif re.search('5g.*rfsimulator', self.yamlPath[0]) is not None: - # Excluding any traffic from NR-UE containers (192.168.71.150 and 192.168.71.151) - # From the ext-dn, keeping only PING traffic - cmd = 'sudo nohup tshark -f "(host 192.168.72.135 and icmp) or (not host 192.168.72.135 and not host 192.168.71.150 and not host 192.168.71.151 and not arp and not port 53 and not port 2152 and not port 2153)"' - elif re.search('5g_l2sim', self.yamlPath[0]) is not None: - cmd = 'sudo nohup tshark -f "(host 192.168.72.135 and icmp) or (not host 192.168.72.135 and not arp and not port 53 and not port 2152 and not port 2153)"' - else: - return + + # Allow only: control plane RAN (SCTP), HTTP of control in CN (port 80), PFCP traffic (port 8805), MySQL (port 3306) + cmd = 'sudo nohup tshark -f "sctp or port 80 or port 8805 or icmp or port 3306"' for name in networkNames.split('\n'): if re.search('rfsim', name) is not None or re.search('l2sim', name) is not None: cmd += ' -i ' + name From 007dcc63a86ebc5aea03a13c59a4f890309659c2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 6 Feb 2023 17:06:32 +0100 Subject: [PATCH 23/26] 5G E1 RFsim test XML test cases --- .../xml_files/container_5g_e1_rfsim.xml | 131 ++++++++++++++++++ .../xml_files/container_5g_e1_rfsim_down.xml | 45 ++++++ 2 files changed, 176 insertions(+) create mode 100644 ci-scripts/xml_files/container_5g_e1_rfsim.xml create mode 100644 ci-scripts/xml_files/container_5g_e1_rfsim_down.xml diff --git a/ci-scripts/xml_files/container_5g_e1_rfsim.xml b/ci-scripts/xml_files/container_5g_e1_rfsim.xml new file mode 100644 index 0000000000..7130976dad --- /dev/null +++ b/ci-scripts/xml_files/container_5g_e1_rfsim.xml @@ -0,0 +1,131 @@ + + + rfsim-5gnr-e1 + CUCP-CUUP-DU E1+F1 split + wrench + + 111111 + 100021 + 000020 + 000021 + 000022 + 000023 + 000024 + 020021 + 020022 + 030021 + 100021 + + + + + Pull_Local_Registry + Pull Images from Local Registry + 0 + oai-gnb oai-nr-cuup oai-nr-ue + + + + DeployGenObject + Deploy MySql Database + yaml_files/5g_rfsimulator_e1 + mysql + 1 + + + + DeployGenObject + Deploy OAI 5G CoreNetwork + yaml_files/5g_rfsimulator_e1 + oai-nrf oai-amf oai-smf oai-spgwu oai-ext-dn + 6 + + + + DeployGenObject + Deploy OAI 5G CU-CP RF sim SA + yaml_files/5g_rfsimulator_e1 + oai-cucp + 7 + + + + DeployGenObject + Deploy OAI 5G CU-UP and DU RF sim SA + yaml_files/5g_rfsimulator_e1 + oai-cuup oai-du + 9 + + + + DeployGenObject + Deploy OAI 5G NR-UE RF sim SA + yaml_files/5g_rfsimulator_e1 + oai-nr-ue + 10 + + + + PingFromContainer + Ping ext-dn from NR-UE + rfsim5g-oai-nr-ue + -I oaitun_ue1 -c 20 192.168.73.135 + 5 + + + + PingFromContainer + Ping NR-UE from ext-dn + rfsim5g-oai-ext-dn + -c 20 12.1.1.2 + 5 + + + + IperfFromContainer + Iperf UDP Downlink + rfsim5g-oai-nr-ue + rfsim5g-oai-ext-dn + -B 12.1.1.2 -u -i 1 -s + -c 12.1.1.2 -u -i 1 -t 30 -b 3M + + + + IperfFromContainer + Iperf UDP Uplink + rfsim5g-oai-ext-dn + rfsim5g-oai-nr-ue + -u -i 1 -s + -B 12.1.1.2 -c 192.168.73.135 -u -i 1 -t 30 -b 1M + + + + UndeployGenObject + Undeploy all OAI 5G stack + yaml_files/5g_rfsimulator_e1 + 1,0,0,0 + 1,0,0,0 + + + diff --git a/ci-scripts/xml_files/container_5g_e1_rfsim_down.xml b/ci-scripts/xml_files/container_5g_e1_rfsim_down.xml new file mode 100644 index 0000000000..a1213c9b9b --- /dev/null +++ b/ci-scripts/xml_files/container_5g_e1_rfsim_down.xml @@ -0,0 +1,45 @@ + + + rfsim-5gnr-down-e1 + CleanUp CUCP-CUUP-DU E1+F1 split + trash + + 100022 + 222222 + + + + + UndeployGenObject + Undeploy all OAI 5G stack + yaml_files/5g_rfsimulator_e1 + + + + Clean_Test_Server_Images + Clean Test Images on Test Server + 0 + + + From 39b7cce49055657b59a91e661aa2321e91600aba Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 12 Feb 2023 14:01:49 +0100 Subject: [PATCH 24/26] Resolve warning --- openair1/SIMULATION/LTE_PHY/dlsim.c | 1 - 1 file changed, 1 deletion(-) diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index 44e79121b2..3823fae4d7 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -563,7 +563,6 @@ int main(int argc, char **argv) { short *uncoded_ber_bit=NULL; int osf=1; frame_t frame_type = FDD; - char title[255]; int numCCE=0; //int dci_length_bytes=0,dci_length=0; //double channel_bandwidth = 5.0, sampling_rate=7.68; From d656022e1e3824496f6c4bae60e692ed3ce507f0 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Feb 2023 18:49:15 +0100 Subject: [PATCH 25/26] Remove pdcch_ConfigSIB1 from new config files --- ci-scripts/conf_files/gnb-cucp.sa.f1.conf | 7 ------- ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf | 7 ------- 2 files changed, 14 deletions(-) diff --git a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf index 0a2f88495e..62063030bd 100644 --- a/ci-scripts/conf_files/gnb-cucp.sa.f1.conf +++ b/ci-scripts/conf_files/gnb-cucp.sa.f1.conf @@ -34,13 +34,6 @@ gNBs = ssb_SubcarrierOffset = 0; min_rxtxtime = 6; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon diff --git a/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf b/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf index 3c2fbe717e..7f6f6c949d 100644 --- a/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf +++ b/ci-scripts/conf_files/gnb-du.band78.106prb.rfsim.conf @@ -24,13 +24,6 @@ gNBs = min_rxtxtime = 6; force_256qam_off = 1; - pdcch_ConfigSIB1 = ( - { - controlResourceSetZero = 12; - searchSpaceZero = 0; - } - ); - servingCellConfigCommon = ( { #spCellConfigCommon From 2de56bf324e582dbb15a6a14ef35956140bc0560 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Feb 2023 10:30:03 +0100 Subject: [PATCH 26/26] Correct NUMA bound devices for caracal/timing test --- ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml | 2 +- ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml index ed80a8b1e1..f9c5115f8a 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run_60.xml @@ -37,7 +37,7 @@ datalog_rt_stats.1x1.60.yaml NR 192.168.20.2 - numactl --cpunodebind=netdev:enp94s0f0 --membind=netdev:enp94s0f0 + numactl --cpunodebind=netdev:enp94s0f0np0 --membind=netdev:enp94s0f0np0 diff --git a/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml b/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml index 831ed5b096..641f5ee0eb 100644 --- a/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml +++ b/ci-scripts/xml_files/gnb_phytest_usrp_run_60_2x2.xml @@ -37,7 +37,7 @@ datalog_rt_stats.60.2x2.yaml NR 192.168.20.2 - numactl --cpunodebind=netdev:enp94s0f0 --membind=netdev:enp94s0f0 + numactl --cpunodebind=netdev:enp94s0f0np0 --membind=netdev:enp94s0f0np0