/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file ngap_gNB_nas_procedures.c
 * \brief NGAP eNb NAS procedure handler
 * \author  S. Roux and Navid Nikaein
 * \date 2010 - 2015
 * \email: navid.nikaein@eurecom.fr
 * \version 1.0
 * @ingroup _ngap
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "assertions.h"
#include "conversions.h"

#include "intertask_interface.h"

#include "ngap_common.h"
#include "ngap_gNB_defs.h"

#include "ngap_gNB_itti_messaging.h"

#include "ngap_gNB_encoder.h"
#include "ngap_gNB_nnsf.h"
#include "ngap_gNB_ue_context.h"
#include "ngap_gNB_nas_procedures.h"
#include "ngap_gNB_management_procedures.h"
#include "msc.h"

//------------------------------------------------------------------------------
int ngap_gNB_handle_nas_first_req(
    instance_t instance, ngap_nas_first_req_t *ngap_nas_first_req_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t          *instance_p = NULL;
    struct ngap_gNB_amf_data_s   *amf_desc_p = NULL;
    struct ngap_gNB_ue_context_s *ue_desc_p  = NULL;
    NGAP_NGAP_PDU_t               pdu;
    NGAP_InitialUEMessage_t      *out;
    NGAP_InitialUEMessage_IEs_t  *ie;
    uint8_t  *buffer = NULL;
    uint32_t  length = 0;
    DevAssert(ngap_nas_first_req_p != NULL);
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    instance_p = ngap_gNB_get_instance(instance);
    DevAssert(instance_p != NULL);
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
    pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_initialUEMessage;
    pdu.choice.initiatingMessage.criticality = NGAP_Criticality_ignore;
    pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_InitialUEMessage;
    out = &pdu.choice.initiatingMessage.value.choice.InitialUEMessage;

    /* Select the AMF corresponding to the provided GUAMI. */
    if (ngap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_guami) {
        amf_desc_p = ngap_gNB_nnsf_select_amf_by_guami(
                         instance_p,
                         ngap_nas_first_req_p->establishment_cause,
                         ngap_nas_first_req_p->ue_identity.guami);

        if (amf_desc_p) {
            NGAP_INFO("[gNB %d] Chose AMF '%s' (assoc_id %d) through GUAMI MCC %d MNC %d AMFGI %d AMFC %d\n",
                      instance,
                      amf_desc_p->amf_name,
                      amf_desc_p->assoc_id,
                      ngap_nas_first_req_p->ue_identity.guami.mcc,
                      ngap_nas_first_req_p->ue_identity.guami.mnc,
                      ngap_nas_first_req_p->ue_identity.guami.amf_group_id,
                      ngap_nas_first_req_p->ue_identity.guami.amf_code);
        }
    }

    if (amf_desc_p == NULL) {
        /* Select the AMF corresponding to the provided s-TMSI. */
        if (ngap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_s_tmsi) {
            amf_desc_p = ngap_gNB_nnsf_select_amf_by_amf_code(
                             instance_p,
                             ngap_nas_first_req_p->establishment_cause,
                             ngap_nas_first_req_p->selected_plmn_identity,
                             ngap_nas_first_req_p->ue_identity.s_tmsi.amf_code);

            if (amf_desc_p) {
                NGAP_INFO("[gNB %d] Chose AMF '%s' (assoc_id %d) through S-TMSI AMFC %d and selected PLMN Identity index %d MCC %d MNC %d\n",
                          instance,
                          amf_desc_p->amf_name,
                          amf_desc_p->assoc_id,
                          ngap_nas_first_req_p->ue_identity.s_tmsi.amf_code,
                          ngap_nas_first_req_p->selected_plmn_identity,
                          instance_p->mcc[ngap_nas_first_req_p->selected_plmn_identity],
                          instance_p->mnc[ngap_nas_first_req_p->selected_plmn_identity]);
            }
        }
    }

    if (amf_desc_p == NULL) {
        /* Select AMF based on the selected PLMN identity, received through RRC
         * Connection Setup Complete */
        amf_desc_p = ngap_gNB_nnsf_select_amf_by_plmn_id(
                         instance_p,
                         ngap_nas_first_req_p->establishment_cause,
                         ngap_nas_first_req_p->selected_plmn_identity);

        if (amf_desc_p) {
            NGAP_INFO("[gNB %d] Chose AMF '%s' (assoc_id %d) through selected PLMN Identity index %d MCC %d MNC %d\n",
                      instance,
                      amf_desc_p->amf_name,
                      amf_desc_p->assoc_id,
                      ngap_nas_first_req_p->selected_plmn_identity,
                      instance_p->mcc[ngap_nas_first_req_p->selected_plmn_identity],
                      instance_p->mnc[ngap_nas_first_req_p->selected_plmn_identity]);
        }
    }

    if (amf_desc_p == NULL) {
        /*
         * If no AMF corresponds to the GUAMI, the s-TMSI, or the selected PLMN
         * identity, selects the AMF with the highest capacity.
         */
        amf_desc_p = ngap_gNB_nnsf_select_amf(
                         instance_p,
                         ngap_nas_first_req_p->establishment_cause);

        if (amf_desc_p) {
            NGAP_INFO("[gNB %d] Chose AMF '%s' (assoc_id %d) through highest relative capacity\n",
                      instance,
                      amf_desc_p->amf_name,
                      amf_desc_p->assoc_id);
        }
    }

    if (amf_desc_p == NULL) {
        /*
         * In case gNB has no AMF associated, the gNB should inform RRC and discard
         * this request.
         */
        NGAP_WARN("No AMF is associated to the gNB\n");
        // TODO: Inform RRC
        return -1;
    }

    /* The gNB should allocate a unique gNB UE NGAP ID for this UE. The value
     * will be used for the duration of the connectivity.
     */
    ue_desc_p = ngap_gNB_allocate_new_UE_context();
    DevAssert(ue_desc_p != NULL);
    /* Keep a reference to the selected AMF */
    ue_desc_p->amf_ref       = amf_desc_p;
    ue_desc_p->ue_initial_id = ngap_nas_first_req_p->ue_initial_id;
    ue_desc_p->gNB_instance  = instance_p;
    ue_desc_p->selected_plmn_identity = ngap_nas_first_req_p->selected_plmn_identity;

    do {
        struct ngap_gNB_ue_context_s *collision_p;
        /* Peek a random value for the gNB_ue_ngap_id */
        ue_desc_p->gNB_ue_ngap_id = (random() + random()) & 0x00ffffff;

        if ((collision_p = RB_INSERT(ngap_ue_map, &instance_p->ngap_ue_head, ue_desc_p))
                == NULL) {
            NGAP_DEBUG("Found usable gNB_ue_ngap_id: 0x%06x %u(10)\n",
                       ue_desc_p->gNB_ue_ngap_id,
                       ue_desc_p->gNB_ue_ngap_id);
            /* Break the loop as the id is not already used by another UE */
            break;
        }
    } while(1);

    /* mandatory */
    ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = ue_desc_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_NAS_PDU;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_NAS_PDU;
#if 1
    ie->value.choice.NAS_PDU.buf = ngap_nas_first_req_p->nas_pdu.buffer;
#else
    ie->value.choice.NAS_PDU.buf = malloc(ngap_nas_first_req_p->nas_pdu.length);
    memcpy(ie->value.choice.NAS_PDU.buf,
           ngap_nas_first_req_p->nas_pdu.buffer,
           ngap_nas_first_req_p->nas_pdu.length);
#endif
    ie->value.choice.NAS_PDU.size = ngap_nas_first_req_p->nas_pdu.length;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_TAI;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_TAI;
    /* Assuming TAI is the TAI from the cell */
    INT16_TO_OCTET_STRING(instance_p->tac, &ie->value.choice.TAI.tAC);
    MCC_MNC_TO_PLMNID(instance_p->mcc[ue_desc_p->selected_plmn_identity],
                      instance_p->mnc[ue_desc_p->selected_plmn_identity],
                      instance_p->mnc_digit_length[ue_desc_p->selected_plmn_identity],
                      &ie->value.choice.TAI.pLMNidentity);
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_EUTRAN_CGI;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_EUTRAN_CGI;
    /* Set the EUTRAN CGI
     * The cell identity is defined on 28 bits but as we use macro enb id,
     * we have to pad.
     */
    //#warning "TODO get cell id from RRC"
    MACRO_GNB_ID_TO_CELL_IDENTITY(instance_p->gNB_id,
                                  0, // Cell ID
                                  &ie->value.choice.EUTRAN_CGI.cell_ID);
    MCC_MNC_TO_TBCD(instance_p->mcc[ue_desc_p->selected_plmn_identity],
                    instance_p->mnc[ue_desc_p->selected_plmn_identity],
                    instance_p->mnc_digit_length[ue_desc_p->selected_plmn_identity],
                    &ie->value.choice.EUTRAN_CGI.pLMNidentity);
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* Set the establishment cause according to those provided by RRC */
    DevCheck(ngap_nas_first_req_p->establishment_cause < RRC_CAUSE_LAST,
             ngap_nas_first_req_p->establishment_cause, RRC_CAUSE_LAST, 0);
    /* mandatory */
    ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_RRC_Establishment_Cause;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_RRC_Establishment_Cause;
    ie->value.choice.RRC_Establishment_Cause = ngap_nas_first_req_p->establishment_cause;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    /* optional */
    if (ngap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_s_tmsi) {
        NGAP_DEBUG("S_TMSI_PRESENT\n");
        ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_S_TMSI;
        ie->criticality = NGAP_Criticality_reject;
        ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_S_TMSI;
        AMF_CODE_TO_OCTET_STRING(ngap_nas_first_req_p->ue_identity.s_tmsi.amf_code,
                                 &ie->value.choice.S_TMSI.mMEC);
        M_TMSI_TO_OCTET_STRING(ngap_nas_first_req_p->ue_identity.s_tmsi.m_tmsi,
                               &ie->value.choice.S_TMSI.m_TMSI);
        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }


    /* optional */
    if (ngap_nas_first_req_p->ue_identity.presenceMask & UE_IDENTITIES_guami) {
        NGAP_DEBUG("GUAMI_ID_PRESENT\n");
        ie = (NGAP_InitialUEMessage_IEs_t *)calloc(1, sizeof(NGAP_InitialUEMessage_IEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_GUAMI_ID;
        ie->criticality = NGAP_Criticality_reject;
        ie->value.present = NGAP_InitialUEMessage_IEs__value_PR_GUAMI;
        MCC_MNC_TO_PLMNID(
            ngap_nas_first_req_p->ue_identity.guami.mcc,
            ngap_nas_first_req_p->ue_identity.guami.mnc,
            ngap_nas_first_req_p->ue_identity.guami.mnc_len,
            &ie->value.choice.GUAMI.pLMN_Identity);
        AMF_GID_TO_OCTET_STRING(ngap_nas_first_req_p->ue_identity.guami.amf_group_id,
                                &ie->value.choice.GUAMI.mME_Group_ID);
        AMF_CODE_TO_OCTET_STRING(ngap_nas_first_req_p->ue_identity.guami.amf_code,
                                 &ie->value.choice.GUAMI.mME_Code);
        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }



    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        /* Failed to encode message */
        DevMessage("Failed to encode initial UE message\n");
    }

    /* Update the current NGAP UE state */
    ue_desc_p->ue_state = NGAP_UE_WAITING_CSR;
    /* Assign a stream for this UE :
     * From 3GPP 36.412 7)Transport layers:
     *  Within the SCTP association established between one AMF and gNB pair:
     *  - a single pair of stream identifiers shall be reserved for the sole use
     *      of NGAP elementary procedures that utilize non UE-associated signalling.
     *  - At least one pair of stream identifiers shall be reserved for the sole use
     *      of NGAP elementary procedures that utilize UE-associated signallings.
     *      However a few pairs (i.e. more than one) should be reserved.
     *  - A single UE-associated signalling shall use one SCTP stream and
     *      the stream should not be changed during the communication of the
     *      UE-associated signalling.
     */
    amf_desc_p->nextstream = (amf_desc_p->nextstream + 1) % amf_desc_p->out_streams;

    if ((amf_desc_p->nextstream == 0) && (amf_desc_p->out_streams > 1)) {
        amf_desc_p->nextstream += 1;
    }

    ue_desc_p->tx_stream = amf_desc_p->nextstream;
    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)NULL,
        0,
        MSC_AS_TIME_FMT" initialUEMessage initiatingMessage gNB_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        ue_desc_p->gNB_ue_ngap_id);
    /* Send encoded message over sctp */
    ngap_gNB_itti_send_sctp_data_req(instance_p->instance, amf_desc_p->assoc_id,
                                     buffer, length, ue_desc_p->tx_stream);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_handle_nas_downlink(uint32_t         assoc_id,
                                 uint32_t         stream,
                                 NGAP_NGAP_PDU_t *pdu)
//------------------------------------------------------------------------------
{
    ngap_gNB_amf_data_t             *amf_desc_p        = NULL;
    ngap_gNB_ue_context_t           *ue_desc_p         = NULL;
    ngap_gNB_instance_t             *ngap_gNB_instance = NULL;
    NGAP_DownlinkNASTransport_t     *container;
    NGAP_DownlinkNASTransport_IEs_t *ie;
    NGAP_GNB_UE_NGAP_ID_t            enb_ue_ngap_id;
    NGAP_AMF_UE_NGAP_ID_t            amf_ue_ngap_id;
    DevAssert(pdu != NULL);

    /* UE-related procedure -> stream != 0 */
    if (stream == 0) {
        NGAP_ERROR("[SCTP %d] Received UE-related procedure on stream == 0\n",
                   assoc_id);
        return -1;
    }

    if ((amf_desc_p = ngap_gNB_get_AMF(NULL, assoc_id, 0)) == NULL) {
        NGAP_ERROR(
            "[SCTP %d] Received NAS downlink message for non existing AMF context\n",
            assoc_id);
        return -1;
    }

    ngap_gNB_instance = amf_desc_p->ngap_gNB_instance;
    /* Prepare the NGAP message to encode */
    container = &pdu->choice.initiatingMessage.value.choice.DownlinkNASTransport;
    NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
                               NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID, true);
    amf_ue_ngap_id = ie->value.choice.AMF_UE_NGAP_ID;

    NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
                               NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID, true);
    enb_ue_ngap_id = ie->value.choice.GNB_UE_NGAP_ID;

    if ((ue_desc_p = ngap_gNB_get_ue_context(ngap_gNB_instance,
                     enb_ue_ngap_id)) == NULL) {
        MSC_LOG_RX_DISCARDED_MESSAGE(
            MSC_NGAP_GNB,
            MSC_NGAP_AMF,
            NULL,
            0,
            MSC_AS_TIME_FMT" downlinkNASTransport  gNB_ue_ngap_id %u amf_ue_ngap_id %u",
            enb_ue_ngap_id,
            amf_ue_ngap_id);
        NGAP_ERROR("[SCTP %d] Received NAS downlink message for non existing UE context gNB_UE_NGAP_ID: 0x%lx\n",
                   assoc_id,
                   enb_ue_ngap_id);
        return -1;
    }

    if (0 == ue_desc_p->rx_stream) {
        ue_desc_p->rx_stream = stream;
    } else if (stream != ue_desc_p->rx_stream) {
        NGAP_ERROR("[SCTP %d] Received UE-related procedure on stream %u, expecting %u\n",
                   assoc_id, stream, ue_desc_p->rx_stream);
        return -1;
    }

    /* Is it the first outcome of the AMF for this UE ? If so store the amf
     * UE ngap id.
     */
    if (ue_desc_p->amf_ue_ngap_id == 0) {
        ue_desc_p->amf_ue_ngap_id = amf_ue_ngap_id;
    } else {
        /* We already have a amf ue ngap id check the received is the same */
        if (ue_desc_p->amf_ue_ngap_id != amf_ue_ngap_id) {
            NGAP_ERROR("[SCTP %d] Mismatch in AMF UE NGAP ID (0x%lx != 0x%"PRIx32"\n",
                       assoc_id,
                       amf_ue_ngap_id,
                       ue_desc_p->amf_ue_ngap_id
                      );
            return -1;
        }
    }

    MSC_LOG_RX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        NULL,
        0,
        MSC_AS_TIME_FMT" downlinkNASTransport  gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        assoc_id,
        amf_ue_ngap_id);

    NGAP_FIND_PROTOCOLIE_BY_ID(NGAP_DownlinkNASTransport_IEs_t, ie, container,
                               NGAP_ProtocolIE_ID_id_NAS_PDU, true);
    /* Forward the NAS PDU to RRC */
    ngap_gNB_itti_send_nas_downlink_ind(ngap_gNB_instance->instance,
                                        ue_desc_p->ue_initial_id,
                                        ue_desc_p->gNB_ue_ngap_id,
                                        ie->value.choice.NAS_PDU.buf,
                                        ie->value.choice.NAS_PDU.size);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_nas_uplink(instance_t instance, ngap_uplink_nas_t *ngap_uplink_nas_p)
//------------------------------------------------------------------------------
{
    struct ngap_gNB_ue_context_s  *ue_context_p;
    ngap_gNB_instance_t           *ngap_gNB_instance_p;
    NGAP_NGAP_PDU_t                pdu;
    NGAP_UplinkNASTransport_t     *out;
    NGAP_UplinkNASTransport_IEs_t *ie;
    uint8_t  *buffer;
    uint32_t  length;
    DevAssert(ngap_uplink_nas_p != NULL);
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p, ngap_uplink_nas_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: %06x\n",
                  ngap_uplink_nas_p->gNB_ue_ngap_id);
        return -1;
    }

    /* Uplink NAS transport can occur either during an ngap connected state
     * or during initial attach (for example: NAS authentication).
     */
    if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
            ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
        NGAP_WARN("You are attempting to send NAS data over non-connected "
                  "gNB ue ngap id: %u, current state: %d\n",
                  ngap_uplink_nas_p->gNB_ue_ngap_id, ue_context_p->ue_state);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
    pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_uplinkNASTransport;
    pdu.choice.initiatingMessage.criticality = NGAP_Criticality_ignore;
    pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_UplinkNASTransport;
    out = &pdu.choice.initiatingMessage.value.choice.UplinkNASTransport;
    /* mandatory */
    ie = (NGAP_UplinkNASTransport_IEs_t *)calloc(1, sizeof(NGAP_UplinkNASTransport_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UplinkNASTransport_IEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UplinkNASTransport_IEs_t *)calloc(1, sizeof(NGAP_UplinkNASTransport_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UplinkNASTransport_IEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = ue_context_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UplinkNASTransport_IEs_t *)calloc(1, sizeof(NGAP_UplinkNASTransport_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_NAS_PDU;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UplinkNASTransport_IEs__value_PR_NAS_PDU;
    ie->value.choice.NAS_PDU.buf = ngap_uplink_nas_p->nas_pdu.buffer;
    ie->value.choice.NAS_PDU.size = ngap_uplink_nas_p->nas_pdu.length;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UplinkNASTransport_IEs_t *)calloc(1, sizeof(NGAP_UplinkNASTransport_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_EUTRAN_CGI;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_UplinkNASTransport_IEs__value_PR_EUTRAN_CGI;
    MCC_MNC_TO_PLMNID(
        ngap_gNB_instance_p->mcc[ue_context_p->selected_plmn_identity],
        ngap_gNB_instance_p->mnc[ue_context_p->selected_plmn_identity],
        ngap_gNB_instance_p->mnc_digit_length[ue_context_p->selected_plmn_identity],
        &ie->value.choice.EUTRAN_CGI.pLMNidentity);
    //#warning "TODO get cell id from RRC"
    MACRO_GNB_ID_TO_CELL_IDENTITY(ngap_gNB_instance_p->gNB_id,
                                  0,
                                  &ie->value.choice.EUTRAN_CGI.cell_ID);
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UplinkNASTransport_IEs_t *)calloc(1, sizeof(NGAP_UplinkNASTransport_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_TAI;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_UplinkNASTransport_IEs__value_PR_TAI;
    MCC_MNC_TO_PLMNID(
        ngap_gNB_instance_p->mcc[ue_context_p->selected_plmn_identity],
        ngap_gNB_instance_p->mnc[ue_context_p->selected_plmn_identity],
        ngap_gNB_instance_p->mnc_digit_length[ue_context_p->selected_plmn_identity],
        &ie->value.choice.TAI.pLMNidentity);
    TAC_TO_ASN1(ngap_gNB_instance_p->tac, &ie->value.choice.TAI.tAC);
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* optional */


    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode uplink NAS transport\n");
        /* Encode procedure has failed... */
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)NULL,
        0,
        MSC_AS_TIME_FMT" uplinkNASTransport initiatingMessage gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        ue_context_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}


//------------------------------------------------------------------------------
int ngap_gNB_nas_non_delivery_ind(instance_t instance,
                                  ngap_nas_non_delivery_ind_t *ngap_nas_non_delivery_ind)
//------------------------------------------------------------------------------
{
    struct ngap_gNB_ue_context_s        *ue_context_p;
    ngap_gNB_instance_t                 *ngap_gNB_instance_p;
    NGAP_NGAP_PDU_t                      pdu;
    NGAP_NASNonDeliveryIndication_t     *out;
    NGAP_NASNonDeliveryIndication_IEs_t *ie;
    uint8_t  *buffer;
    uint32_t  length;
    DevAssert(ngap_nas_non_delivery_ind != NULL);
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p, ngap_nas_non_delivery_ind->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: %06x\n",
                  ngap_nas_non_delivery_ind->gNB_ue_ngap_id);
        MSC_LOG_EVENT(
            MSC_NGAP_GNB,
            MSC_AS_TIME_FMT" Sent of NAS_NON_DELIVERY_IND to AMF failed, no context for gNB_ue_ngap_id %06x",
            ngap_nas_non_delivery_ind->gNB_ue_ngap_id);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
    pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_NASNonDeliveryIndication;
    pdu.choice.initiatingMessage.criticality = NGAP_Criticality_ignore;
    pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_NASNonDeliveryIndication;
    out = &pdu.choice.initiatingMessage.value.choice.NASNonDeliveryIndication;
    /* mandatory */
    ie = (NGAP_NASNonDeliveryIndication_IEs_t *)calloc(1, sizeof(NGAP_NASNonDeliveryIndication_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_NASNonDeliveryIndication_IEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_NASNonDeliveryIndication_IEs_t *)calloc(1, sizeof(NGAP_NASNonDeliveryIndication_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_NASNonDeliveryIndication_IEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = ue_context_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_NASNonDeliveryIndication_IEs_t *)calloc(1, sizeof(NGAP_NASNonDeliveryIndication_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_NAS_PDU;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_NASNonDeliveryIndication_IEs__value_PR_NAS_PDU;
    ie->value.choice.NAS_PDU.buf = ngap_nas_non_delivery_ind->nas_pdu.buffer;
    ie->value.choice.NAS_PDU.size = ngap_nas_non_delivery_ind->nas_pdu.length;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_NASNonDeliveryIndication_IEs_t *)calloc(1, sizeof(NGAP_NASNonDeliveryIndication_IEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_Cause;
    ie->criticality = NGAP_Criticality_ignore;
    /* Send a dummy cause */
    ie->value.present = NGAP_NASNonDeliveryIndication_IEs__value_PR_Cause;
    ie->value.choice.Cause.present = NGAP_Cause_PR_radioNetwork;
    ie->value.choice.Cause.choice.radioNetwork = NGAP_CauseRadioNetwork_radio_connection_with_ue_lost;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode NAS NON delivery indication\n");
        /* Encode procedure has failed... */
        MSC_LOG_EVENT(
            MSC_NGAP_GNB,
            MSC_AS_TIME_FMT" Sent of NAS_NON_DELIVERY_IND to AMF failed (encoding)");
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" NASNonDeliveryIndication initiatingMessage gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        ue_context_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_initial_ctxt_resp(
    instance_t instance, ngap_initial_context_setup_resp_t *initial_ctxt_resp_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t                   *ngap_gNB_instance_p = NULL;
    struct ngap_gNB_ue_context_s          *ue_context_p        = NULL;
    NGAP_NGAP_PDU_t                        pdu;
    NGAP_InitialContextSetupResponse_t    *out;
    NGAP_InitialContextSetupResponseIEs_t *ie;
    uint8_t  *buffer = NULL;
    uint32_t length;
    int      i;
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(initial_ctxt_resp_p != NULL);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
                        initial_ctxt_resp_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
                  initial_ctxt_resp_p->gNB_ue_ngap_id);
        return -1;
    }

    /* Uplink NAS transport can occur either during an ngap connected state
     * or during initial attach (for example: NAS authentication).
     */
    if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
            ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
        NGAP_WARN("You are attempting to send NAS data over non-connected "
                  "gNB ue ngap id: %06x, current state: %d\n",
                  initial_ctxt_resp_p->gNB_ue_ngap_id, ue_context_p->ue_state);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
    pdu.choice.successfulOutcome.procedureCode = NGAP_ProcedureCode_id_InitialContextSetup;
    pdu.choice.successfulOutcome.criticality = NGAP_Criticality_reject;
    pdu.choice.successfulOutcome.value.present = NGAP_SuccessfulOutcome__value_PR_InitialContextSetupResponse;
    out = &pdu.choice.successfulOutcome.value.choice.InitialContextSetupResponse;
    /* mandatory */
    ie = (NGAP_InitialContextSetupResponseIEs_t *)calloc(1, sizeof(NGAP_InitialContextSetupResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_InitialContextSetupResponseIEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_InitialContextSetupResponseIEs_t *)calloc(1, sizeof(NGAP_InitialContextSetupResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_InitialContextSetupResponseIEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = initial_ctxt_resp_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_InitialContextSetupResponseIEs_t *)calloc(1, sizeof(NGAP_InitialContextSetupResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONSetupListCtxtSURes;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_InitialContextSetupResponseIEs__value_PR_PDUSESSIONSetupListCtxtSURes;

    for (i = 0; i < initial_ctxt_resp_p->nb_of_pdusessions; i++) {
        NGAP_PDUSESSIONSetupItemCtxtSUResIEs_t *item;
        /* mandatory */
        item = (NGAP_PDUSESSIONSetupItemCtxtSUResIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupItemCtxtSUResIEs_t));
        item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONSetupItemCtxtSURes;
        item->criticality = NGAP_Criticality_ignore;
        item->value.present = NGAP_PDUSESSIONSetupItemCtxtSUResIEs__value_PR_PDUSESSIONSetupItemCtxtSURes;
        item->value.choice.PDUSESSIONSetupItemCtxtSURes.e_RAB_ID = initial_ctxt_resp_p->pdusessions[i].pdusession_id;
        GTP_TEID_TO_ASN1(initial_ctxt_resp_p->pdusessions[i].gtp_teid, &item->value.choice.PDUSESSIONSetupItemCtxtSURes.gTP_TEID);
        item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf = malloc(initial_ctxt_resp_p->pdusessions[i].gNB_addr.length);
        memcpy(item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf,
               initial_ctxt_resp_p->pdusessions[i].gNB_addr.buffer,
               initial_ctxt_resp_p->pdusessions[i].gNB_addr.length);
        item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.size = initial_ctxt_resp_p->pdusessions[i].gNB_addr.length;
        item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.bits_unused = 0;
        NGAP_DEBUG("initial_ctxt_resp_p: pdusession ID %ld, enb_addr %d.%d.%d.%d, SIZE %ld \n",
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.e_RAB_ID,
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf[0],
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf[1],
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf[2],
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.buf[3],
                   item->value.choice.PDUSESSIONSetupItemCtxtSURes.transportLayerAddress.size);
        ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONSetupListCtxtSURes.list, item);
    }

    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    /* optional */
    if (initial_ctxt_resp_p->nb_of_pdusessions_failed) {
        ie = (NGAP_InitialContextSetupResponseIEs_t *)calloc(1, sizeof(NGAP_InitialContextSetupResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONFailedToSetupListCtxtSURes;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_InitialContextSetupResponseIEs__value_PR_PDUSESSIONList;

        for (i = 0; i < initial_ctxt_resp_p->nb_of_pdusessions_failed; i++) {
            NGAP_PDUSESSIONItemIEs_t *item;
            /* mandatory */
            item = (NGAP_PDUSESSIONItemIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONItemIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONItem;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONItemIEs__value_PR_PDUSESSIONItem;
            item->value.choice.PDUSESSIONItem.e_RAB_ID = initial_ctxt_resp_p->pdusessions_failed[i].pdusession_id;
            item->value.choice.PDUSESSIONItem.cause.present = initial_ctxt_resp_p->pdusessions_failed[i].cause;

            switch(item->value.choice.PDUSESSIONItem.cause.present) {
            case NGAP_Cause_PR_radioNetwork:
                item->value.choice.PDUSESSIONItem.cause.choice.radioNetwork = initial_ctxt_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_transport:
                item->value.choice.PDUSESSIONItem.cause.choice.transport = initial_ctxt_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_nas:
                item->value.choice.PDUSESSIONItem.cause.choice.nas = initial_ctxt_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_protocol:
                item->value.choice.PDUSESSIONItem.cause.choice.protocol = initial_ctxt_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_misc:
                item->value.choice.PDUSESSIONItem.cause.choice.misc = initial_ctxt_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_NOTHING:
            default:
                break;
            }

            NGAP_DEBUG("initial context setup response: failed pdusession ID %ld\n", item->value.choice.PDUSESSIONItem.e_RAB_ID);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONList.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (0) {
        ie = (NGAP_InitialContextSetupResponseIEs_t *)calloc(1, sizeof(NGAP_InitialContextSetupResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_InitialContextSetupResponseIEs__value_PR_CriticalityDiagnostics;
        // ie->value.choice.CriticalityDiagnostics =;
        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode uplink NAS transport\n");
        /* Encode procedure has failed... */
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" InitialContextSetup successfulOutcome gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        initial_ctxt_resp_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_ue_capabilities(instance_t instance,
                             ngap_ue_cap_info_ind_t *ue_cap_info_ind_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t          *ngap_gNB_instance_p;
    struct ngap_gNB_ue_context_s *ue_context_p;
    NGAP_NGAP_PDU_t                       pdu;
    NGAP_UECapabilityInfoIndication_t    *out;
    NGAP_UECapabilityInfoIndicationIEs_t *ie;
    uint8_t  *buffer;
    uint32_t length;
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(ue_cap_info_ind_p != NULL);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
                        ue_cap_info_ind_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: %u\n",
                  ue_cap_info_ind_p->gNB_ue_ngap_id);
        return -1;
    }

    /* UE capabilities message can occur either during an ngap connected state
     * or during initial attach (for example: NAS authentication).
     */
    if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
            ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
        NGAP_WARN("You are attempting to send NAS data over non-connected "
                  "gNB ue ngap id: %u, current state: %d\n",
                  ue_cap_info_ind_p->gNB_ue_ngap_id, ue_context_p->ue_state);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
    pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_UECapabilityInfoIndication;
    pdu.choice.initiatingMessage.criticality = NGAP_Criticality_ignore;
    pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_UECapabilityInfoIndication;
    out = &pdu.choice.initiatingMessage.value.choice.UECapabilityInfoIndication;
    /* mandatory */
    ie = (NGAP_UECapabilityInfoIndicationIEs_t *)calloc(1, sizeof(NGAP_UECapabilityInfoIndicationIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UECapabilityInfoIndicationIEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UECapabilityInfoIndicationIEs_t *)calloc(1, sizeof(NGAP_UECapabilityInfoIndicationIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UECapabilityInfoIndicationIEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = ue_cap_info_ind_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_UECapabilityInfoIndicationIEs_t *)calloc(1, sizeof(NGAP_UECapabilityInfoIndicationIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_UERadioCapability;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_UECapabilityInfoIndicationIEs__value_PR_UERadioCapability;
    ie->value.choice.UERadioCapability.buf = ue_cap_info_ind_p->ue_radio_cap.buffer;
    ie->value.choice.UERadioCapability.size = ue_cap_info_ind_p->ue_radio_cap.length;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* optional */

    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        /* Encode procedure has failed... */
        NGAP_ERROR("Failed to encode UE capabilities indication\n");
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" UECapabilityInfoIndication initiatingMessage gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        ue_cap_info_ind_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_pdusession_setup_resp(instance_t instance,
                              ngap_pdusession_setup_resp_t *pdusession_setup_resp_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t          *ngap_gNB_instance_p = NULL;
    struct ngap_gNB_ue_context_s *ue_context_p        = NULL;
    NGAP_NGAP_PDU_t               pdu;
    NGAP_PDUSESSIONSetupResponse_t    *out;
    NGAP_PDUSESSIONSetupResponseIEs_t *ie;
    uint8_t  *buffer  = NULL;
    uint32_t length;
    int      i;
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(pdusession_setup_resp_p != NULL);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
                        pdusession_setup_resp_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
                  pdusession_setup_resp_p->gNB_ue_ngap_id);
        return -1;
    }

    /* Uplink NAS transport can occur either during an ngap connected state
     * or during initial attach (for example: NAS authentication).
     */
    if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
            ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
        NGAP_WARN("You are attempting to send NAS data over non-connected "
                  "gNB ue ngap id: %06x, current state: %d\n",
                  pdusession_setup_resp_p->gNB_ue_ngap_id, ue_context_p->ue_state);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
    pdu.choice.successfulOutcome.procedureCode = NGAP_ProcedureCode_id_PDUSESSIONModify;
    pdu.choice.successfulOutcome.criticality = NGAP_Criticality_reject;
    pdu.choice.successfulOutcome.value.present = NGAP_SuccessfulOutcome__value_PR_PDUSESSIONSetupResponse;
    out = &pdu.choice.successfulOutcome.value.choice.PDUSESSIONSetupResponse;
    /* mandatory */
    ie = (NGAP_PDUSESSIONSetupResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONSetupResponseIEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_PDUSESSIONSetupResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONSetupResponseIEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = pdusession_setup_resp_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    /* optional */
    if (pdusession_setup_resp_p->nb_of_pdusessions > 0) {
        ie = (NGAP_PDUSESSIONSetupResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONSetupListBearerSURes;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONSetupResponseIEs__value_PR_PDUSESSIONSetupListBearerSURes;

        for (i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions; i++) {
            NGAP_PDUSESSIONSetupItemBearerSUResIEs_t *item;
            /* mandatory */
            item = (NGAP_PDUSESSIONSetupItemBearerSUResIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupItemBearerSUResIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONSetupItemBearerSURes;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONSetupItemBearerSUResIEs__value_PR_PDUSESSIONSetupItemBearerSURes;
            item->value.choice.PDUSESSIONSetupItemBearerSURes.e_RAB_ID = pdusession_setup_resp_p->pdusessions[i].pdusession_id;
            GTP_TEID_TO_ASN1(pdusession_setup_resp_p->pdusessions[i].gtp_teid, &item->value.choice.PDUSESSIONSetupItemBearerSURes.gTP_TEID);
            item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf = malloc(pdusession_setup_resp_p->pdusessions[i].gNB_addr.length);
            memcpy(item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf,
                   pdusession_setup_resp_p->pdusessions[i].gNB_addr.buffer,
                   pdusession_setup_resp_p->pdusessions[i].gNB_addr.length);
            item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.size = pdusession_setup_resp_p->pdusessions[i].gNB_addr.length;
            item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.bits_unused = 0;
            NGAP_DEBUG("pdusession_setup_resp: pdusession ID %ld, teid %u, enb_addr %d.%d.%d.%d, SIZE %ld\n",
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.e_RAB_ID,
                       pdusession_setup_resp_p->pdusessions[i].gtp_teid,
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf[0],
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf[1],
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf[2],
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.buf[3],
                       item->value.choice.PDUSESSIONSetupItemBearerSURes.transportLayerAddress.size);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONSetupListBearerSURes.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (pdusession_setup_resp_p->nb_of_pdusessions_failed > 0) {
        ie = (NGAP_PDUSESSIONSetupResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONFailedToSetupListBearerSURes;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONSetupResponseIEs__value_PR_PDUSESSIONList;

        for (i = 0; i < pdusession_setup_resp_p->nb_of_pdusessions_failed; i++) {
            NGAP_PDUSESSIONItemIEs_t *item;
            item = (NGAP_PDUSESSIONItemIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONItemIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONItem;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONItemIEs__value_PR_PDUSESSIONItem;
            item->value.choice.PDUSESSIONItem.e_RAB_ID = pdusession_setup_resp_p->pdusessions_failed[i].pdusession_id;
            item->value.choice.PDUSESSIONItem.cause.present = pdusession_setup_resp_p->pdusessions_failed[i].cause;

            switch(item->value.choice.PDUSESSIONItem.cause.present) {
            case NGAP_Cause_PR_radioNetwork:
                item->value.choice.PDUSESSIONItem.cause.choice.radioNetwork = pdusession_setup_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_transport:
                item->value.choice.PDUSESSIONItem.cause.choice.transport = pdusession_setup_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_nas:
                item->value.choice.PDUSESSIONItem.cause.choice.nas = pdusession_setup_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_protocol:
                item->value.choice.PDUSESSIONItem.cause.choice.protocol = pdusession_setup_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_misc:
                item->value.choice.PDUSESSIONItem.cause.choice.misc = pdusession_setup_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_NOTHING:
            default:
                break;
            }

            NGAP_DEBUG("pdusession_modify_resp: failed pdusession ID %ld\n", item->value.choice.PDUSESSIONItem.e_RAB_ID);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONList.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (0) {
        ie = (NGAP_PDUSESSIONSetupResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONSetupResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONSetupResponseIEs__value_PR_CriticalityDiagnostics;
        // ie->value.choice.CriticalityDiagnostics = ;
        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* NGAP_PDUSESSIONSetupListBearerSURes_t  e_RABSetupListBearerSURes;
    memset(&e_RABSetupListBearerSURes, 0, sizeof(NGAP_PDUSESSIONSetupListBearerSURes_t));
    if (ngap_encode_ngap_pdusessionsetuplistbearersures(&e_RABSetupListBearerSURes, &initial_ies_p->e_RABSetupListBearerSURes.ngap_PDUSESSIONSetupItemBearerSURes) < 0 )
      return -1;
    ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_NGAP_PDUSESSIONSetupListBearerSURes, &e_RABSetupListBearerSURes);
    */
    fprintf(stderr, "start encode\n");

    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode uplink transport\n");
        /* Encode procedure has failed... */
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" E_RAN Setup successfulOutcome gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        pdusession_setup_resp_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}

//------------------------------------------------------------------------------
int ngap_gNB_pdusession_modify_resp(instance_t instance,
                               ngap_pdusession_modify_resp_t *pdusession_modify_resp_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t           *ngap_gNB_instance_p = NULL;
    struct ngap_gNB_ue_context_s  *ue_context_p        = NULL;
    NGAP_NGAP_PDU_t                pdu;
    NGAP_PDUSESSIONModifyResponse_t    *out;
    NGAP_PDUSESSIONModifyResponseIEs_t *ie;
    uint8_t  *buffer  = NULL;
    uint32_t length;
    int      i;
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(pdusession_modify_resp_p != NULL);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
                        pdusession_modify_resp_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
                  pdusession_modify_resp_p->gNB_ue_ngap_id);
        return -1;
    }

    /* Uplink NAS transport can occur either during an ngap connected state
     * or during initial attach (for example: NAS authentication).
     */
    if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
            ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
        NGAP_WARN("You are attempting to send NAS data over non-connected "
                  "gNB ue ngap id: %06x, current state: %d\n",
                  pdusession_modify_resp_p->gNB_ue_ngap_id, ue_context_p->ue_state);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
    pdu.choice.successfulOutcome.procedureCode = NGAP_ProcedureCode_id_PDUSESSIONModify;
    pdu.choice.successfulOutcome.criticality = NGAP_Criticality_reject;
    pdu.choice.successfulOutcome.value.present = NGAP_SuccessfulOutcome__value_PR_PDUSESSIONModifyResponse;
    out = &pdu.choice.successfulOutcome.value.choice.PDUSESSIONModifyResponse;
    /* mandatory */
    ie = (NGAP_PDUSESSIONModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONModifyResponseIEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_PDUSESSIONModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONModifyResponseIEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = pdusession_modify_resp_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    /* optional */
    if (pdusession_modify_resp_p->nb_of_pdusessions > 0) {
        ie = (NGAP_PDUSESSIONModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONModifyListBearerModRes;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONModifyResponseIEs__value_PR_PDUSESSIONModifyListBearerModRes;

        for (i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions; i++) {
            NGAP_PDUSESSIONModifyItemBearerModResIEs_t *item;
            item = (NGAP_PDUSESSIONModifyItemBearerModResIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyItemBearerModResIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONModifyItemBearerModRes;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONModifyItemBearerModResIEs__value_PR_PDUSESSIONModifyItemBearerModRes;
            item->value.choice.PDUSESSIONModifyItemBearerModRes.e_RAB_ID = pdusession_modify_resp_p->pdusessions[i].pdusession_id;
            NGAP_DEBUG("pdusession_modify_resp: modified pdusession ID %ld\n", item->value.choice.PDUSESSIONModifyItemBearerModRes.e_RAB_ID);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONModifyListBearerModRes.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (pdusession_modify_resp_p->nb_of_pdusessions_failed > 0) {
        ie = (NGAP_PDUSESSIONModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONFailedToModifyList;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONModifyResponseIEs__value_PR_PDUSESSIONList;

        for (i = 0; i < pdusession_modify_resp_p->nb_of_pdusessions_failed; i++) {
            NGAP_PDUSESSIONItemIEs_t *item;
            item = (NGAP_PDUSESSIONItemIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONItemIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONItem;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONItemIEs__value_PR_PDUSESSIONItem;
            item->value.choice.PDUSESSIONItem.e_RAB_ID = pdusession_modify_resp_p->pdusessions_failed[i].pdusession_id;
            item->value.choice.PDUSESSIONItem.cause.present = pdusession_modify_resp_p->pdusessions_failed[i].cause;

            switch(item->value.choice.PDUSESSIONItem.cause.present) {
            case NGAP_Cause_PR_radioNetwork:
                item->value.choice.PDUSESSIONItem.cause.choice.radioNetwork = pdusession_modify_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_transport:
                item->value.choice.PDUSESSIONItem.cause.choice.transport = pdusession_modify_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_nas:
                item->value.choice.PDUSESSIONItem.cause.choice.nas = pdusession_modify_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_protocol:
                item->value.choice.PDUSESSIONItem.cause.choice.protocol = pdusession_modify_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_misc:
                item->value.choice.PDUSESSIONItem.cause.choice.misc = pdusession_modify_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_NOTHING:
            default:
                break;
            }

            NGAP_DEBUG("pdusession_modify_resp: failed pdusession ID %ld\n", item->value.choice.PDUSESSIONItem.e_RAB_ID);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONList.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (0) {
        ie = (NGAP_PDUSESSIONModifyResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModifyResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_CriticalityDiagnostics;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONModifyResponseIEs__value_PR_CriticalityDiagnostics;
        // ie->value.choice.CriticalityDiagnostics = ;
        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    fprintf(stderr, "start encode\n");

    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode uplink transport\n");
        /* Encode procedure has failed... */
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" E_RAN Modify successful Outcome gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        pdusession_modify_resp_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    return 0;
}
//------------------------------------------------------------------------------
int ngap_gNB_pdusession_release_resp(instance_t instance,
                                ngap_pdusession_release_resp_t *pdusession_release_resp_p)
//------------------------------------------------------------------------------
{
    ngap_gNB_instance_t            *ngap_gNB_instance_p = NULL;
    struct ngap_gNB_ue_context_s   *ue_context_p        = NULL;
    NGAP_NGAP_PDU_t                 pdu;
    NGAP_PDUSESSIONReleaseResponse_t    *out;
    NGAP_PDUSESSIONReleaseResponseIEs_t *ie;
    uint8_t  *buffer  = NULL;
    uint32_t length;
    int      i;
    /* Retrieve the NGAP gNB instance associated with Mod_id */
    ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
    DevAssert(pdusession_release_resp_p != NULL);
    DevAssert(ngap_gNB_instance_p != NULL);

    if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
                        pdusession_release_resp_p->gNB_ue_ngap_id)) == NULL) {
        /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
        NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: %u\n",
                  pdusession_release_resp_p->gNB_ue_ngap_id);
        return -1;
    }

    /* Prepare the NGAP message to encode */
    memset(&pdu, 0, sizeof(pdu));
    pdu.present = NGAP_NGAP_PDU_PR_successfulOutcome;
    pdu.choice.successfulOutcome.procedureCode = NGAP_ProcedureCode_id_PDUSESSIONRelease;
    pdu.choice.successfulOutcome.criticality = NGAP_Criticality_reject;
    pdu.choice.successfulOutcome.value.present = NGAP_SuccessfulOutcome__value_PR_PDUSESSIONReleaseResponse;
    out = &pdu.choice.successfulOutcome.value.choice.PDUSESSIONReleaseResponse;
    /* mandatory */
    ie = (NGAP_PDUSESSIONReleaseResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONReleaseResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONReleaseResponseIEs__value_PR_AMF_UE_NGAP_ID;
    ie->value.choice.AMF_UE_NGAP_ID = ue_context_p->amf_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    /* mandatory */
    ie = (NGAP_PDUSESSIONReleaseResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONReleaseResponseIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
    ie->criticality = NGAP_Criticality_ignore;
    ie->value.present = NGAP_PDUSESSIONReleaseResponseIEs__value_PR_GNB_UE_NGAP_ID;
    ie->value.choice.GNB_UE_NGAP_ID = pdusession_release_resp_p->gNB_ue_ngap_id;
    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

    /* optional */
    if (pdusession_release_resp_p->nb_of_pdusessions_released > 0) {
        ie = (NGAP_PDUSESSIONReleaseResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONReleaseResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONReleaseListBearerRelComp;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONReleaseResponseIEs__value_PR_PDUSESSIONReleaseListBearerRelComp;

        for (i = 0; i < pdusession_release_resp_p->nb_of_pdusessions_released; i++) {
            NGAP_PDUSESSIONReleaseItemBearerRelCompIEs_t *item;
            item = (NGAP_PDUSESSIONReleaseItemBearerRelCompIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONReleaseItemBearerRelCompIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONReleaseItemBearerRelComp;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONReleaseItemBearerRelCompIEs__value_PR_PDUSESSIONReleaseItemBearerRelComp;
            item->value.choice.PDUSESSIONReleaseItemBearerRelComp.e_RAB_ID = pdusession_release_resp_p->pdusession_release[i].pdusession_id;
            NGAP_DEBUG("pdusession_release_resp: pdusession ID %ld\n", item->value.choice.PDUSESSIONReleaseItemBearerRelComp.e_RAB_ID);
            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONReleaseListBearerRelComp.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }

    /* optional */
    if (pdusession_release_resp_p->nb_of_pdusessions_failed > 0) {
        ie = (NGAP_PDUSESSIONReleaseResponseIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONReleaseResponseIEs_t));
        ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONFailedToReleaseList;
        ie->criticality = NGAP_Criticality_ignore;
        ie->value.present = NGAP_PDUSESSIONReleaseResponseIEs__value_PR_PDUSESSIONList;

        for (i = 0; i < pdusession_release_resp_p->nb_of_pdusessions_failed; i++) {
            NGAP_PDUSESSIONItemIEs_t *item;
            item = (NGAP_PDUSESSIONItemIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONItemIEs_t));
            item->id = NGAP_ProtocolIE_ID_id_PDUSESSIONItem;
            item->criticality = NGAP_Criticality_ignore;
            item->value.present = NGAP_PDUSESSIONItemIEs__value_PR_PDUSESSIONItem;
            item->value.choice.PDUSESSIONItem.e_RAB_ID = pdusession_release_resp_p->pdusessions_failed[i].pdusession_id;
            item->value.choice.PDUSESSIONItem.cause.present = pdusession_release_resp_p->pdusessions_failed[i].cause;

            switch(item->value.choice.PDUSESSIONItem.cause.present) {
            case NGAP_Cause_PR_radioNetwork:
                item->value.choice.PDUSESSIONItem.cause.choice.radioNetwork = pdusession_release_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_transport:
                item->value.choice.PDUSESSIONItem.cause.choice.transport = pdusession_release_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_nas:
                item->value.choice.PDUSESSIONItem.cause.choice.nas = pdusession_release_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_protocol:
                item->value.choice.PDUSESSIONItem.cause.choice.protocol = pdusession_release_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_misc:
                item->value.choice.PDUSESSIONItem.cause.choice.misc = pdusession_release_resp_p->pdusessions_failed[i].cause_value;
                break;

            case NGAP_Cause_PR_NOTHING:
            default:
                break;
            }

            ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONList.list, item);
        }

        ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
    }



    if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
        NGAP_ERROR("Failed to encode release response\n");
        /* Encode procedure has failed... */
        return -1;
    }

    MSC_LOG_TX_MESSAGE(
        MSC_NGAP_GNB,
        MSC_NGAP_AMF,
        (const char *)buffer,
        length,
        MSC_AS_TIME_FMT" E_RAN Release successfulOutcome gNB_ue_ngap_id %u amf_ue_ngap_id %u",
        0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
        pdusession_release_resp_p->gNB_ue_ngap_id,
        ue_context_p->amf_ue_ngap_id);
    /* UE associated signalling -> use the allocated stream */
    ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                     ue_context_p->amf_ref->assoc_id, buffer,
                                     length, ue_context_p->tx_stream);
    NGAP_INFO("pdusession_release_response sended gNB_UE_NGAP_ID %d  amf_ue_ngap_id %d nb_of_pdusessions_released %d nb_of_pdusessions_failed %d\n",
              pdusession_release_resp_p->gNB_ue_ngap_id, ue_context_p->amf_ue_ngap_id,pdusession_release_resp_p->nb_of_pdusessions_released,pdusession_release_resp_p->nb_of_pdusessions_failed);
    return 0;
}

int ngap_gNB_path_switch_req(instance_t instance,
                             ngap_path_switch_req_t *path_switch_req_p)
//------------------------------------------------------------------------------
{
  ngap_gNB_instance_t          *ngap_gNB_instance_p = NULL;
  struct ngap_gNB_ue_context_s *ue_context_p        = NULL;
  struct ngap_gNB_amf_data_s   *amf_desc_p = NULL;

  NGAP_NGAP_PDU_t                 pdu;
  NGAP_PathSwitchRequest_t       *out;
  NGAP_PathSwitchRequestIEs_t    *ie;

  NGAP_PDUSESSIONToBeSwitchedDLItemIEs_t *e_RABToBeSwitchedDLItemIEs;
  NGAP_PDUSESSIONToBeSwitchedDLItem_t    *e_RABToBeSwitchedDLItem;

  uint8_t  *buffer = NULL;
  uint32_t length;
  int      ret = 0;//-1;

  /* Retrieve the NGAP gNB instance associated with Mod_id */
  ngap_gNB_instance_p = ngap_gNB_get_instance(instance);

  DevAssert(path_switch_req_p != NULL);
  DevAssert(ngap_gNB_instance_p != NULL);

  //if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
    //                                          path_switch_req_p->gNB_ue_ngap_id)) == NULL) {
    /* The context for this gNB ue ngap id doesn't exist in the map of gNB UEs */
    //NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
      //        path_switch_req_p->gNB_ue_ngap_id);
    //return -1;
  //}

  /* Uplink NAS transport can occur either during an ngap connected state
   * or during initial attach (for example: NAS authentication).
   */
  //if (!(ue_context_p->ue_state == NGAP_UE_CONNECTED ||
       // ue_context_p->ue_state == NGAP_UE_WAITING_CSR)) {
    //NGAP_WARN("You are attempting to send NAS data over non-connected "
        //      "gNB ue ngap id: %06x, current state: %d\n",
          //    path_switch_req_p->gNB_ue_ngap_id, ue_context_p->ue_state);
    //return -1;
  //}

  /* Select the AMF corresponding to the provided GUAMI. */
  amf_desc_p = ngap_gNB_nnsf_select_amf_by_guami_no_cause(ngap_gNB_instance_p, path_switch_req_p->ue_guami);

  if (amf_desc_p == NULL) {
    /*
     * In case gNB has no AMF associated, the gNB should inform RRC and discard
     * this request.
     */

    NGAP_WARN("No AMF is associated to the gNB\n");
    // TODO: Inform RRC
    return -1;
  }

  /* The gNB should allocate a unique gNB UE NGAP ID for this UE. The value
   * will be used for the duration of the connectivity.
   */
  ue_context_p = ngap_gNB_allocate_new_UE_context();
  DevAssert(ue_context_p != NULL);

  /* Keep a reference to the selected AMF */
  ue_context_p->amf_ref       = amf_desc_p;
  ue_context_p->ue_initial_id = path_switch_req_p->ue_initial_id;
  ue_context_p->gNB_instance  = ngap_gNB_instance_p;

  do {
    struct ngap_gNB_ue_context_s *collision_p;

    /* Peek a random value for the gNB_ue_ngap_id */
    ue_context_p->gNB_ue_ngap_id = (random() + random()) & 0x00ffffff;

    if ((collision_p = RB_INSERT(ngap_ue_map, &ngap_gNB_instance_p->ngap_ue_head, ue_context_p))
        == NULL) {
      NGAP_DEBUG("Found usable gNB_ue_ngap_id: 0x%06x %u(10)\n",
                 ue_context_p->gNB_ue_ngap_id,
                 ue_context_p->gNB_ue_ngap_id);
      /* Break the loop as the id is not already used by another UE */
      break;
    }
  } while(1);
  
  ue_context_p->amf_ue_ngap_id = path_switch_req_p->amf_ue_ngap_id;

  /* Prepare the NGAP message to encode */
  memset(&pdu, 0, sizeof(pdu));
  pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
  pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_PathSwitchRequest;
  pdu.choice.initiatingMessage.criticality = NGAP_Criticality_reject;
  pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_PathSwitchRequest;
  out = &pdu.choice.initiatingMessage.value.choice.PathSwitchRequest;

  /* mandatory */
  ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_GNB_UE_NGAP_ID;
  ie->value.choice.GNB_UE_NGAP_ID = ue_context_p->gNB_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  /* mandatory */
  if (path_switch_req_p->nb_of_pdusessions > 0) {
    ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
    ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeSwitchedDLList;
    ie->criticality = NGAP_Criticality_reject;
    ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_PDUSESSIONToBeSwitchedDLList;

    for (int i = 0; i < path_switch_req_p->nb_of_pdusessions; i++) {
      e_RABToBeSwitchedDLItemIEs = (NGAP_PDUSESSIONToBeSwitchedDLItemIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONToBeSwitchedDLItemIEs_t));
      e_RABToBeSwitchedDLItemIEs->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeSwitchedDLItem;
      e_RABToBeSwitchedDLItemIEs->criticality = NGAP_Criticality_reject;
      e_RABToBeSwitchedDLItemIEs->value.present = NGAP_PDUSESSIONToBeSwitchedDLItemIEs__value_PR_PDUSESSIONToBeSwitchedDLItem;

      e_RABToBeSwitchedDLItem = &e_RABToBeSwitchedDLItemIEs->value.choice.PDUSESSIONToBeSwitchedDLItem;
      e_RABToBeSwitchedDLItem->e_RAB_ID = path_switch_req_p->pdusessions_tobeswitched[i].pdusession_id;
      INT32_TO_OCTET_STRING(path_switch_req_p->pdusessions_tobeswitched[i].gtp_teid, &e_RABToBeSwitchedDLItem->gTP_TEID);

      e_RABToBeSwitchedDLItem->transportLayerAddress.size  = path_switch_req_p->pdusessions_tobeswitched[i].gNB_addr.length;
      e_RABToBeSwitchedDLItem->transportLayerAddress.bits_unused = 0;

      e_RABToBeSwitchedDLItem->transportLayerAddress.buf = calloc(1,e_RABToBeSwitchedDLItem->transportLayerAddress.size);

      memcpy (e_RABToBeSwitchedDLItem->transportLayerAddress.buf,
                path_switch_req_p->pdusessions_tobeswitched[i].gNB_addr.buffer,
                path_switch_req_p->pdusessions_tobeswitched[i].gNB_addr.length);

      NGAP_DEBUG("path_switch_req: pdusession ID %ld, teid %u, enb_addr %d.%d.%d.%d, SIZE %zu\n",
               e_RABToBeSwitchedDLItem->e_RAB_ID,
               path_switch_req_p->pdusessions_tobeswitched[i].gtp_teid,
               e_RABToBeSwitchedDLItem->transportLayerAddress.buf[0],
               e_RABToBeSwitchedDLItem->transportLayerAddress.buf[1],
               e_RABToBeSwitchedDLItem->transportLayerAddress.buf[2],
               e_RABToBeSwitchedDLItem->transportLayerAddress.buf[3],
               e_RABToBeSwitchedDLItem->transportLayerAddress.size);

      ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONToBeSwitchedDLList.list, e_RABToBeSwitchedDLItemIEs);
    }

    ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);
  }

  /* mandatory */
  ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_SourceAMF_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_AMF_UE_NGAP_ID;
  ie->value.choice.AMF_UE_NGAP_ID = path_switch_req_p->amf_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  /* mandatory */
  ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_EUTRAN_CGI;
  ie->criticality = NGAP_Criticality_ignore;
  ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_EUTRAN_CGI;
  MACRO_GNB_ID_TO_CELL_IDENTITY(ngap_gNB_instance_p->gNB_id,
                                0,
                                &ie->value.choice.EUTRAN_CGI.cell_ID);
  MCC_MNC_TO_TBCD(ngap_gNB_instance_p->mcc[0],
                  ngap_gNB_instance_p->mnc[0],
                  ngap_gNB_instance_p->mnc_digit_length[0],
                  &ie->value.choice.EUTRAN_CGI.pLMNidentity);
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  /* mandatory */
  ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_TAI;
  ie->criticality = NGAP_Criticality_ignore;
  ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_TAI;
  /* Assuming TAI is the TAI from the cell */
  INT16_TO_OCTET_STRING(ngap_gNB_instance_p->tac, &ie->value.choice.TAI.tAC);
  MCC_MNC_TO_PLMNID(ngap_gNB_instance_p->mcc[0],
                    ngap_gNB_instance_p->mnc[0],
                    ngap_gNB_instance_p->mnc_digit_length[0],
                    &ie->value.choice.TAI.pLMNidentity);
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  /* mandatory */
  ie = (NGAP_PathSwitchRequestIEs_t *)calloc(1, sizeof(NGAP_PathSwitchRequestIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_UESecurityCapabilities;
  ie->criticality = NGAP_Criticality_ignore;
  ie->value.present = NGAP_PathSwitchRequestIEs__value_PR_UESecurityCapabilities;
  ENCRALG_TO_BIT_STRING(path_switch_req_p->security_capabilities.encryption_algorithms,
              &ie->value.choice.UESecurityCapabilities.encryptionAlgorithms);
  INTPROTALG_TO_BIT_STRING(path_switch_req_p->security_capabilities.integrity_algorithms,
              &ie->value.choice.UESecurityCapabilities.integrityProtectionAlgorithms);
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) {
    NGAP_ERROR("Failed to encode Path Switch Req \n");
    /* Encode procedure has failed... */
    return -1;
  }

  /* Update the current NGAP UE state */
  ue_context_p->ue_state = NGAP_UE_WAITING_CSR;

  /* Assign a stream for this UE :
   * From 3GPP 36.412 7)Transport layers:
   *  Within the SCTP association established between one AMF and gNB pair:
   *  - a single pair of stream identifiers shall be reserved for the sole use
   *      of NGAP elementary procedures that utilize non UE-associated signalling.
   *  - At least one pair of stream identifiers shall be reserved for the sole use
   *      of NGAP elementary procedures that utilize UE-associated signallings.
   *      However a few pairs (i.e. more than one) should be reserved.
   *  - A single UE-associated signalling shall use one SCTP stream and
   *      the stream should not be changed during the communication of the
   *      UE-associated signalling.
   */
  amf_desc_p->nextstream = (amf_desc_p->nextstream + 1) % amf_desc_p->out_streams;

  if ((amf_desc_p->nextstream == 0) && (amf_desc_p->out_streams > 1)) {
    amf_desc_p->nextstream += 1;
  }

  ue_context_p->tx_stream = amf_desc_p->nextstream;

  MSC_LOG_TX_MESSAGE(
    MSC_NGAP_GNB,
    MSC_NGAP_AMF,
    (const char *)buffer,
    length,
    MSC_AS_TIME_FMT" E_RAN Setup successfulOutcome gNB_ue_ngap_id %u amf_ue_ngap_id %u",
    0,0,//MSC_AS_TIME_ARGS(ctxt_pP),
    ue_context_p->gNB_ue_ngap_id,
    path_switch_req_p->amf_ue_ngap_id);

  /* UE associated signalling -> use the allocated stream */
  ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                   amf_desc_p->assoc_id, buffer,
                                   length, ue_context_p->tx_stream);

  return ret;
}


//-----------------------------------------------------------------------------
/*
* gNB generate a S1 PDUSESSION Modification Indication towards AMF
*/
/*int ngap_gNB_generate_PDUSESSION_Modification_Indication(
		instance_t instance,
  ngap_pdusession_modification_ind_t *pdusession_modification_ind)
//-----------------------------------------------------------------------------
{
  struct ngap_gNB_ue_context_s        *ue_context_p        = NULL;
  NGAP_NGAP_PDU_t            pdu;
  NGAP_PDUSESSIONModificationIndication_t     *out = NULL;
  NGAP_PDUSESSIONModificationIndicationIEs_t   *ie = NULL;
  NGAP_PDUSESSIONToBeModifiedItemBearerModInd_t 	  *PDUSESSION_ToBeModifiedItem_BearerModInd = NULL;
  NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t *PDUSESSION_ToBeModifiedItem_BearerModInd_IEs = NULL;

  NGAP_PDUSESSIONNotToBeModifiedItemBearerModInd_t 	  *PDUSESSION_NotToBeModifiedItem_BearerModInd = NULL;
  NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t  *PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs = NULL;


  ngap_gNB_instance_t          *ngap_gNB_instance_p = NULL;
  ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
  uint8_t  *buffer = NULL;
  uint32_t  len = 0;
  int       ret = 0;
  DevAssert(ngap_gNB_instance_p != NULL);
  DevAssert(pdusession_modification_ind != NULL);

  int num_pdusessions_tobemodified = pdusession_modification_ind->nb_of_pdusessions_tobemodified;
  int num_pdusessions_nottobemodified = pdusession_modification_ind->nb_of_pdusessions_nottobemodified;

  uint32_t CSG_id = 0;

  if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
		  pdusession_modification_ind->gNB_ue_ngap_id)) == NULL) {
          // The context for this gNB ue ngap id doesn't exist in the map of gNB UEs 
          NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
        		  pdusession_modification_ind->gNB_ue_ngap_id);
          return -1;
  }

  // Prepare the NGAP message to encode 
  memset(&pdu, 0, sizeof(pdu));
  pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
  pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_PDUSESSIONModificationIndication;
  pdu.choice.initiatingMessage.criticality = NGAP_Criticality_reject;
  pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_PDUSESSIONModificationIndication;
  out = &pdu.choice.initiatingMessage.value.choice.PDUSESSIONModificationIndication;
  // mandatory 
  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_AMF_UE_NGAP_ID;
  ie->value.choice.AMF_UE_NGAP_ID = pdusession_modification_ind->amf_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_GNB_UE_NGAP_ID;
  ie->value.choice.GNB_UE_NGAP_ID = pdusession_modification_ind->gNB_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  //E-RABs to be modified list
  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeModifiedListBearerModInd;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONToBeModifiedListBearerModInd;

  //The following two for-loops here will probably need to change. We should do a different type of search
  for(int i=0; i<num_pdusessions_tobemodified; i++){
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs = (NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t *)calloc(1,sizeof(NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t));
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeModifiedItemBearerModInd;
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->criticality = NGAP_Criticality_reject;
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->value.present = NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs__value_PR_PDUSESSIONToBeModifiedItemBearerModInd;
	  PDUSESSION_ToBeModifiedItem_BearerModInd = &PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->value.choice.PDUSESSIONToBeModifiedItemBearerModInd;

	  {
	  PDUSESSION_ToBeModifiedItem_BearerModInd->e_RAB_ID = pdusession_modification_ind->pdusessions_tobemodified[i].pdusession_id;

	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size  = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length/8;
	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.bits_unused = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length%8;
	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.buf = calloc(1, PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size);
	  memcpy (PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.buf, pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.buffer,
			  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size);

	  INT32_TO_OCTET_STRING(pdusession_modification_ind->pdusessions_tobemodified[i].gtp_teid, &PDUSESSION_ToBeModifiedItem_BearerModInd->dL_GTP_TEID);

	  }
	  ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONToBeModifiedListBearerModInd.list, PDUSESSION_ToBeModifiedItem_BearerModInd_IEs);
  }

  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  //E-RABs NOT to be modified list
  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONNotToBeModifiedListBearerModInd;
  ie->criticality = NGAP_Criticality_reject;
  if(num_pdusessions_nottobemodified > 0) {
	  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONNotToBeModifiedListBearerModInd;

	  for(int i=0; i<num_pdusessions_nottobemodified; i++){
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs = (NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t *)calloc(1,sizeof(NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t));
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->id = NGAP_ProtocolIE_ID_id_PDUSESSIONNotToBeModifiedItemBearerModInd;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->criticality = NGAP_Criticality_reject;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->value.present = NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs__value_PR_PDUSESSIONNotToBeModifiedItemBearerModInd;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd = &PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->value.choice.PDUSESSIONNotToBeModifiedItemBearerModInd;

		  {
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->e_RAB_ID = pdusession_modification_ind->pdusessions_nottobemodified[i].pdusession_id;

			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size  = pdusession_modification_ind->pdusessions_nottobemodified[i].gNB_addr.length/8;
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.bits_unused = pdusession_modification_ind->pdusessions_nottobemodified[i].gNB_addr.length%8;
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.buf =
	  	    				calloc(1, PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size);
			  memcpy (PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.buf, pdusession_modification_ind->pdusessions_nottobemodified[i].gNB_addr.buffer,
					  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size);

			  INT32_TO_OCTET_STRING(pdusession_modification_ind->pdusessions_nottobemodified[i].gtp_teid, &PDUSESSION_NotToBeModifiedItem_BearerModInd->dL_GTP_TEID);

		  }
		  ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONNotToBeModifiedListBearerModInd.list, PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs);
	  }
  }
  else{
	  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONNotToBeModifiedListBearerModInd;
	  ie->value.choice.PDUSESSIONNotToBeModifiedListBearerModInd.list.size = 0;
  }  
  
	   

  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_CSGMembershipInfo;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_CSGMembershipInfo;
  ie->value.choice.CSGMembershipInfo.cSGMembershipStatus = NGAP_CSGMembershipStatus_member;
  INT32_TO_BIT_STRING(CSG_id, &ie->value.choice.CSGMembershipInfo.cSG_Id);
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);


  if (ngap_gNB_encode_pdu(&pdu, &buffer, &len) < 0) {
    NGAP_ERROR("Failed to encode S1 E-RAB modification indication \n");
    return -1;
  }

  // Non UE-Associated signalling -> stream = 0 
  NGAP_INFO("Size of encoded message: %d \n", len);
  ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                       ue_context_p->amf_ref->assoc_id, buffer,
                                       len, ue_context_p->tx_stream);  

//ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, len, 0);
  return ret;
}*/

int ngap_gNB_generate_PDUSESSION_Modification_Indication(
		instance_t instance,
  ngap_pdusession_modification_ind_t *pdusession_modification_ind)
//-----------------------------------------------------------------------------
{
  struct ngap_gNB_ue_context_s        *ue_context_p        = NULL;
  NGAP_NGAP_PDU_t            pdu;
  NGAP_PDUSESSIONModificationIndication_t     *out = NULL;
  NGAP_PDUSESSIONModificationIndicationIEs_t   *ie = NULL;
  NGAP_PDUSESSIONToBeModifiedItemBearerModInd_t 	  *PDUSESSION_ToBeModifiedItem_BearerModInd = NULL;
  NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t *PDUSESSION_ToBeModifiedItem_BearerModInd_IEs = NULL;

  //NGAP_PDUSESSIONNotToBeModifiedItemBearerModInd_t 	  *PDUSESSION_NotToBeModifiedItem_BearerModInd = NULL;
  //NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t  *PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs = NULL;


  ngap_gNB_instance_t          *ngap_gNB_instance_p = NULL;
  ngap_gNB_instance_p = ngap_gNB_get_instance(instance);
  uint8_t  *buffer = NULL;
  uint32_t  len = 0;
  int       ret = 0;
  DevAssert(ngap_gNB_instance_p != NULL);
  DevAssert(pdusession_modification_ind != NULL);

  int num_pdusessions_tobemodified = pdusession_modification_ind->nb_of_pdusessions_tobemodified;
  //int num_pdusessions_nottobemodified = pdusession_modification_ind->nb_of_pdusessions_nottobemodified;

  //uint32_t CSG_id = 0;
  //uint32_t pseudo_gtp_teid = 10;

  if ((ue_context_p = ngap_gNB_get_ue_context(ngap_gNB_instance_p,
		  pdusession_modification_ind->gNB_ue_ngap_id)) == NULL) {
          // The context for this gNB ue ngap id doesn't exist in the map of gNB UEs 
          NGAP_WARN("Failed to find ue context associated with gNB ue ngap id: 0x%06x\n",
        		  pdusession_modification_ind->gNB_ue_ngap_id);
          return -1;
  }

  // Prepare the NGAP message to encode 
  memset(&pdu, 0, sizeof(pdu));
  pdu.present = NGAP_NGAP_PDU_PR_initiatingMessage;
  pdu.choice.initiatingMessage.procedureCode = NGAP_ProcedureCode_id_PDUSESSIONModificationIndication;
  pdu.choice.initiatingMessage.criticality = NGAP_Criticality_reject;
  pdu.choice.initiatingMessage.value.present = NGAP_InitiatingMessage__value_PR_PDUSESSIONModificationIndication;
  out = &pdu.choice.initiatingMessage.value.choice.PDUSESSIONModificationIndication;
  /* mandatory */
  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_AMF_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_AMF_UE_NGAP_ID;
  ie->value.choice.AMF_UE_NGAP_ID = pdusession_modification_ind->amf_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_gNB_UE_NGAP_ID;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_GNB_UE_NGAP_ID;
  ie->value.choice.GNB_UE_NGAP_ID = pdusession_modification_ind->gNB_ue_ngap_id;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  //E-RABs to be modified list
  ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeModifiedListBearerModInd;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONToBeModifiedListBearerModInd;

  //The following two for-loops here will probably need to change. We should do a different type of search
  for(int i=0; i<num_pdusessions_tobemodified; i++){
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs = (NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t *)calloc(1,sizeof(NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs_t));
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->id = NGAP_ProtocolIE_ID_id_PDUSESSIONToBeModifiedItemBearerModInd;
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->criticality = NGAP_Criticality_reject;
	  PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->value.present = NGAP_PDUSESSIONToBeModifiedItemBearerModIndIEs__value_PR_PDUSESSIONToBeModifiedItemBearerModInd;
	  PDUSESSION_ToBeModifiedItem_BearerModInd = &PDUSESSION_ToBeModifiedItem_BearerModInd_IEs->value.choice.PDUSESSIONToBeModifiedItemBearerModInd;

	  {
	  PDUSESSION_ToBeModifiedItem_BearerModInd->e_RAB_ID = pdusession_modification_ind->pdusessions_tobemodified[i].pdusession_id;

	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size  = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length/8;
	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.bits_unused = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length%8;
	  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.buf = calloc(1, PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size);
	  memcpy (PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.buf, pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.buffer,
			  PDUSESSION_ToBeModifiedItem_BearerModInd->transportLayerAddress.size);

	  INT32_TO_OCTET_STRING(pdusession_modification_ind->pdusessions_tobemodified[i].gtp_teid, &PDUSESSION_ToBeModifiedItem_BearerModInd->dL_GTP_TEID);

	  }
	  ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONToBeModifiedListBearerModInd.list, PDUSESSION_ToBeModifiedItem_BearerModInd_IEs);
  }

  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);

  //E-RABs NOT to be modified list
  /*ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_PDUSESSIONNotToBeModifiedListBearerModInd;
  ie->criticality = NGAP_Criticality_reject;
  //if(num_pdusessions_nottobemodified > 0) {
	  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONNotToBeModifiedListBearerModInd;

	  for(int i=0; i<num_pdusessions_tobemodified; i++){
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs = (NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t *)calloc(1,sizeof(NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs_t));
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->id = NGAP_ProtocolIE_ID_id_PDUSESSIONNotToBeModifiedItemBearerModInd;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->criticality = NGAP_Criticality_reject;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->value.present = NGAP_PDUSESSIONNotToBeModifiedItemBearerModIndIEs__value_PR_PDUSESSIONNotToBeModifiedItemBearerModInd;
		  PDUSESSION_NotToBeModifiedItem_BearerModInd = &PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs->value.choice.PDUSESSIONNotToBeModifiedItemBearerModInd;

		  {
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->e_RAB_ID = 10; //pdusession_modification_ind->pdusessions_tobemodified[i].pdusession_id;

			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size  = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length/8;
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.bits_unused = pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.length%8;
			  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.buf =
	  	    				calloc(1, PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size);
			  memcpy (PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.buf, pdusession_modification_ind->pdusessions_tobemodified[i].gNB_addr.buffer,
					  PDUSESSION_NotToBeModifiedItem_BearerModInd->transportLayerAddress.size);

			  //INT32_TO_OCTET_STRING(pdusession_modification_ind->pdusessions_tobemodified[i].gtp_teid, &PDUSESSION_NotToBeModifiedItem_BearerModInd->dL_GTP_TEID);
			    INT32_TO_OCTET_STRING(pseudo_gtp_teid, &PDUSESSION_NotToBeModifiedItem_BearerModInd->dL_GTP_TEID);

		  }
		  ASN_SEQUENCE_ADD(&ie->value.choice.PDUSESSIONNotToBeModifiedListBearerModInd.list, PDUSESSION_NotToBeModifiedItem_BearerModInd_IEs);
	  }
 // }
  //else{
//	  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_PDUSESSIONNotToBeModifiedListBearerModInd;
//	  ie->value.choice.PDUSESSIONNotToBeModifiedListBearerModInd.list.size = 0;
//  } / 
  
	   

  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);*/

  /*ie = (NGAP_PDUSESSIONModificationIndicationIEs_t *)calloc(1, sizeof(NGAP_PDUSESSIONModificationIndicationIEs_t));
  ie->id = NGAP_ProtocolIE_ID_id_CSGMembershipInfo;
  ie->criticality = NGAP_Criticality_reject;
  ie->value.present = NGAP_PDUSESSIONModificationIndicationIEs__value_PR_CSGMembershipInfo;
  ie->value.choice.CSGMembershipInfo.cSGMembershipStatus = NGAP_CSGMembershipStatus_member;
  INT32_TO_BIT_STRING(CSG_id, &ie->value.choice.CSGMembershipInfo.cSG_Id);
  ie->value.choice.CSGMembershipInfo.cSG_Id.bits_unused=5; 
  ie->value.choice.CSGMembershipInfo.cellAccessMode = NGAP_CellAccessMode_hybrid;
  ASN_SEQUENCE_ADD(&out->protocolIEs.list, ie);*/
  
  if (ngap_gNB_encode_pdu(&pdu, &buffer, &len) < 0) {
    NGAP_ERROR("Failed to encode S1 E-RAB modification indication \n");
    return -1;
  }

  // Non UE-Associated signalling -> stream = 0 
  NGAP_INFO("Size of encoded message: %d \n", len);
  ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance,
                                       ue_context_p->amf_ref->assoc_id, buffer,
                                       len, ue_context_p->tx_stream);  

//ngap_gNB_itti_send_sctp_data_req(ngap_gNB_instance_p->instance, ue_context_p->amf_ref->assoc_id, buffer, len, 0);
  return ret;
}