This commit introduces X2AP timers (t_reloc_prep, tx2_reloc_overall). You need to set the values in the configuration file. X2AP can be enabled or disabled in the configuration file too (disabled by default). Some deadcode was removed.
584 lines
23 KiB
C
584 lines
23 KiB
C
/*
|
|
* 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 x2ap_eNB.c
|
|
* \brief x2ap tasks for eNB
|
|
* \author Konstantinos Alexandris <Konstantinos.Alexandris@eurecom.fr>, Cedric Roux <Cedric.Roux@eurecom.fr>, Navid Nikaein <Navid.Nikaein@eurecom.fr>
|
|
* \date 2018
|
|
* \version 1.0
|
|
*/
|
|
|
|
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "intertask_interface.h"
|
|
|
|
#include "x2ap_eNB.h"
|
|
#include "x2ap_eNB_defs.h"
|
|
#include "x2ap_eNB_management_procedures.h"
|
|
#include "x2ap_eNB_handler.h"
|
|
#include "x2ap_eNB_generate_messages.h"
|
|
#include "x2ap_common.h"
|
|
#include "x2ap_ids.h"
|
|
#include "x2ap_timers.h"
|
|
|
|
#include "queue.h"
|
|
#include "assertions.h"
|
|
#include "conversions.h"
|
|
|
|
struct x2ap_enb_map;
|
|
struct x2ap_eNB_data_s;
|
|
|
|
RB_PROTOTYPE(x2ap_enb_map, x2ap_eNB_data_s, entry, x2ap_eNB_compare_assoc_id);
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind);
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind);
|
|
|
|
static
|
|
void x2ap_eNB_handle_register_eNB(instance_t instance,
|
|
x2ap_register_enb_req_t *x2ap_register_eNB);
|
|
static
|
|
void x2ap_eNB_register_eNB(x2ap_eNB_instance_t *instance_p,
|
|
net_ip_address_t *target_eNB_ip_addr,
|
|
net_ip_address_t *local_ip_addr,
|
|
uint16_t in_streams,
|
|
uint16_t out_streams,
|
|
uint32_t enb_port_for_X2C,
|
|
int multi_sd);
|
|
|
|
static
|
|
void x2ap_eNB_handle_handover_req(instance_t instance,
|
|
x2ap_handover_req_t *x2ap_handover_req);
|
|
|
|
static
|
|
void x2ap_eNB_handle_handover_req_ack(instance_t instance,
|
|
x2ap_handover_req_ack_t *x2ap_handover_req_ack);
|
|
|
|
static
|
|
void x2ap_eNB_ue_context_release(instance_t instance,
|
|
x2ap_ue_context_release_t *x2ap_ue_context_release);
|
|
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) {
|
|
int result;
|
|
DevAssert(sctp_data_ind != NULL);
|
|
x2ap_eNB_handle_message(instance, sctp_data_ind->assoc_id, sctp_data_ind->stream,
|
|
sctp_data_ind->buffer, sctp_data_ind->buffer_length);
|
|
result = itti_free(TASK_UNKNOWN, sctp_data_ind->buffer);
|
|
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp) {
|
|
x2ap_eNB_instance_t *instance_p;
|
|
x2ap_eNB_data_t *x2ap_enb_data_p;
|
|
DevAssert(sctp_new_association_resp != NULL);
|
|
printf("x2ap_eNB_handle_sctp_association_resp at 1\n");
|
|
dump_trees();
|
|
instance_p = x2ap_eNB_get_instance(instance);
|
|
DevAssert(instance_p != NULL);
|
|
|
|
/* if the assoc_id is already known, it is certainly because an IND was received
|
|
* before. In this case, just update streams and return
|
|
*/
|
|
if (sctp_new_association_resp->assoc_id != -1) {
|
|
x2ap_enb_data_p = x2ap_get_eNB(instance_p, sctp_new_association_resp->assoc_id,
|
|
sctp_new_association_resp->ulp_cnx_id);
|
|
|
|
if (x2ap_enb_data_p != NULL) {
|
|
/* some sanity check - to be refined at some point */
|
|
if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
|
|
X2AP_ERROR("x2ap_enb_data_p not NULL and sctp state not SCTP_STATE_ESTABLISHED, what to do?\n");
|
|
abort();
|
|
}
|
|
|
|
x2ap_enb_data_p->in_streams = sctp_new_association_resp->in_streams;
|
|
x2ap_enb_data_p->out_streams = sctp_new_association_resp->out_streams;
|
|
return;
|
|
}
|
|
}
|
|
|
|
x2ap_enb_data_p = x2ap_get_eNB(instance_p, -1,
|
|
sctp_new_association_resp->ulp_cnx_id);
|
|
DevAssert(x2ap_enb_data_p != NULL);
|
|
printf("x2ap_eNB_handle_sctp_association_resp at 2\n");
|
|
dump_trees();
|
|
|
|
if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
|
|
X2AP_WARN("Received unsuccessful result for SCTP association (%u), instance %d, cnx_id %u\n",
|
|
sctp_new_association_resp->sctp_state,
|
|
instance,
|
|
sctp_new_association_resp->ulp_cnx_id);
|
|
x2ap_handle_x2_setup_message(instance_p, x2ap_enb_data_p,
|
|
sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
|
|
return;
|
|
}
|
|
|
|
printf("x2ap_eNB_handle_sctp_association_resp at 3\n");
|
|
dump_trees();
|
|
/* Update parameters */
|
|
x2ap_enb_data_p->assoc_id = sctp_new_association_resp->assoc_id;
|
|
x2ap_enb_data_p->in_streams = sctp_new_association_resp->in_streams;
|
|
x2ap_enb_data_p->out_streams = sctp_new_association_resp->out_streams;
|
|
printf("x2ap_eNB_handle_sctp_association_resp at 4\n");
|
|
dump_trees();
|
|
/* Prepare new x2 Setup Request */
|
|
x2ap_eNB_generate_x2_setup_request(instance_p, x2ap_enb_data_p);
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind) {
|
|
x2ap_eNB_instance_t *instance_p;
|
|
x2ap_eNB_data_t *x2ap_enb_data_p;
|
|
printf("x2ap_eNB_handle_sctp_association_ind at 1 (called for instance %d)\n", instance);
|
|
dump_trees();
|
|
DevAssert(sctp_new_association_ind != NULL);
|
|
instance_p = x2ap_eNB_get_instance(instance);
|
|
DevAssert(instance_p != NULL);
|
|
x2ap_enb_data_p = x2ap_get_eNB(instance_p, sctp_new_association_ind->assoc_id, -1);
|
|
|
|
if (x2ap_enb_data_p != NULL) abort();
|
|
|
|
// DevAssert(x2ap_enb_data_p != NULL);
|
|
if (x2ap_enb_data_p == NULL) {
|
|
/* Create new eNB descriptor */
|
|
x2ap_enb_data_p = calloc(1, sizeof(*x2ap_enb_data_p));
|
|
DevAssert(x2ap_enb_data_p != NULL);
|
|
x2ap_enb_data_p->cnx_id = x2ap_eNB_fetch_add_global_cnx_id();
|
|
x2ap_enb_data_p->x2ap_eNB_instance = instance_p;
|
|
/* Insert the new descriptor in list of known eNB
|
|
* but not yet associated.
|
|
*/
|
|
RB_INSERT(x2ap_enb_map, &instance_p->x2ap_enb_head, x2ap_enb_data_p);
|
|
x2ap_enb_data_p->state = X2AP_ENB_STATE_CONNECTED;
|
|
instance_p->x2_target_enb_nb++;
|
|
|
|
if (instance_p->x2_target_enb_pending_nb > 0) {
|
|
instance_p->x2_target_enb_pending_nb--;
|
|
}
|
|
} else {
|
|
X2AP_WARN("x2ap_enb_data_p already exists\n");
|
|
}
|
|
|
|
printf("x2ap_eNB_handle_sctp_association_ind at 2\n");
|
|
dump_trees();
|
|
/* Update parameters */
|
|
x2ap_enb_data_p->assoc_id = sctp_new_association_ind->assoc_id;
|
|
x2ap_enb_data_p->in_streams = sctp_new_association_ind->in_streams;
|
|
x2ap_enb_data_p->out_streams = sctp_new_association_ind->out_streams;
|
|
printf("x2ap_eNB_handle_sctp_association_ind at 3\n");
|
|
dump_trees();
|
|
}
|
|
|
|
int x2ap_eNB_init_sctp (x2ap_eNB_instance_t *instance_p,
|
|
net_ip_address_t *local_ip_addr,
|
|
uint32_t enb_port_for_X2C) {
|
|
// Create and alloc new message
|
|
MessageDef *message;
|
|
sctp_init_t *sctp_init = NULL;
|
|
DevAssert(instance_p != NULL);
|
|
DevAssert(local_ip_addr != NULL);
|
|
message = itti_alloc_new_message (TASK_X2AP, SCTP_INIT_MSG_MULTI_REQ);
|
|
sctp_init = &message->ittiMsg.sctp_init_multi;
|
|
sctp_init->port = enb_port_for_X2C;
|
|
sctp_init->ppid = X2AP_SCTP_PPID;
|
|
sctp_init->ipv4 = 1;
|
|
sctp_init->ipv6 = 0;
|
|
sctp_init->nb_ipv4_addr = 1;
|
|
#if 0
|
|
memcpy(&sctp_init->ipv4_address,
|
|
local_ip_addr,
|
|
sizeof(*local_ip_addr));
|
|
#endif
|
|
sctp_init->ipv4_address[0] = inet_addr(local_ip_addr->ipv4_address);
|
|
/*
|
|
* SR WARNING: ipv6 multi-homing fails sometimes for localhost.
|
|
* * * * Disable it for now.
|
|
*/
|
|
sctp_init->nb_ipv6_addr = 0;
|
|
sctp_init->ipv6_address[0] = "0:0:0:0:0:0:0:1";
|
|
return itti_send_msg_to_task (TASK_SCTP, instance_p->instance, message);
|
|
}
|
|
|
|
static void x2ap_eNB_register_eNB(x2ap_eNB_instance_t *instance_p,
|
|
net_ip_address_t *target_eNB_ip_address,
|
|
net_ip_address_t *local_ip_addr,
|
|
uint16_t in_streams,
|
|
uint16_t out_streams,
|
|
uint32_t enb_port_for_X2C,
|
|
int multi_sd) {
|
|
MessageDef *message = NULL;
|
|
sctp_new_association_req_multi_t *sctp_new_association_req = NULL;
|
|
x2ap_eNB_data_t *x2ap_enb_data = NULL;
|
|
DevAssert(instance_p != NULL);
|
|
DevAssert(target_eNB_ip_address != NULL);
|
|
message = itti_alloc_new_message(TASK_X2AP, SCTP_NEW_ASSOCIATION_REQ_MULTI);
|
|
sctp_new_association_req = &message->ittiMsg.sctp_new_association_req_multi;
|
|
sctp_new_association_req->port = enb_port_for_X2C;
|
|
sctp_new_association_req->ppid = X2AP_SCTP_PPID;
|
|
sctp_new_association_req->in_streams = in_streams;
|
|
sctp_new_association_req->out_streams = out_streams;
|
|
sctp_new_association_req->multi_sd = multi_sd;
|
|
memcpy(&sctp_new_association_req->remote_address,
|
|
target_eNB_ip_address,
|
|
sizeof(*target_eNB_ip_address));
|
|
memcpy(&sctp_new_association_req->local_address,
|
|
local_ip_addr,
|
|
sizeof(*local_ip_addr));
|
|
/* Create new eNB descriptor */
|
|
x2ap_enb_data = calloc(1, sizeof(*x2ap_enb_data));
|
|
DevAssert(x2ap_enb_data != NULL);
|
|
x2ap_enb_data->cnx_id = x2ap_eNB_fetch_add_global_cnx_id();
|
|
sctp_new_association_req->ulp_cnx_id = x2ap_enb_data->cnx_id;
|
|
x2ap_enb_data->assoc_id = -1;
|
|
x2ap_enb_data->x2ap_eNB_instance = instance_p;
|
|
/* Insert the new descriptor in list of known eNB
|
|
* but not yet associated.
|
|
*/
|
|
RB_INSERT(x2ap_enb_map, &instance_p->x2ap_enb_head, x2ap_enb_data);
|
|
x2ap_enb_data->state = X2AP_ENB_STATE_WAITING;
|
|
instance_p->x2_target_enb_nb ++;
|
|
instance_p->x2_target_enb_pending_nb ++;
|
|
itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message);
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_register_eNB(instance_t instance,
|
|
x2ap_register_enb_req_t *x2ap_register_eNB) {
|
|
x2ap_eNB_instance_t *new_instance;
|
|
DevAssert(x2ap_register_eNB != NULL);
|
|
/* Look if the provided instance already exists */
|
|
new_instance = x2ap_eNB_get_instance(instance);
|
|
|
|
if (new_instance != NULL) {
|
|
/* Checks if it is a retry on the same eNB */
|
|
DevCheck(new_instance->eNB_id == x2ap_register_eNB->eNB_id, new_instance->eNB_id, x2ap_register_eNB->eNB_id, 0);
|
|
DevCheck(new_instance->cell_type == x2ap_register_eNB->cell_type, new_instance->cell_type, x2ap_register_eNB->cell_type, 0);
|
|
DevCheck(new_instance->tac == x2ap_register_eNB->tac, new_instance->tac, x2ap_register_eNB->tac, 0);
|
|
DevCheck(new_instance->mcc == x2ap_register_eNB->mcc, new_instance->mcc, x2ap_register_eNB->mcc, 0);
|
|
DevCheck(new_instance->mnc == x2ap_register_eNB->mnc, new_instance->mnc, x2ap_register_eNB->mnc, 0);
|
|
X2AP_WARN("eNB[%d] already registered\n", instance);
|
|
} else {
|
|
new_instance = calloc(1, sizeof(x2ap_eNB_instance_t));
|
|
DevAssert(new_instance != NULL);
|
|
RB_INIT(&new_instance->x2ap_enb_head);
|
|
/* Copy usefull parameters */
|
|
new_instance->instance = instance;
|
|
new_instance->eNB_name = x2ap_register_eNB->eNB_name;
|
|
new_instance->eNB_id = x2ap_register_eNB->eNB_id;
|
|
new_instance->cell_type = x2ap_register_eNB->cell_type;
|
|
new_instance->tac = x2ap_register_eNB->tac;
|
|
new_instance->mcc = x2ap_register_eNB->mcc;
|
|
new_instance->mnc = x2ap_register_eNB->mnc;
|
|
new_instance->mnc_digit_length = x2ap_register_eNB->mnc_digit_length;
|
|
new_instance->num_cc = x2ap_register_eNB->num_cc;
|
|
|
|
x2ap_id_manager_init(&new_instance->id_manager);
|
|
x2ap_timers_init(&new_instance->timers,
|
|
x2ap_register_eNB->t_reloc_prep,
|
|
x2ap_register_eNB->tx2_reloc_overall);
|
|
|
|
for (int i = 0; i< x2ap_register_eNB->num_cc; i++) {
|
|
new_instance->eutra_band[i] = x2ap_register_eNB->eutra_band[i];
|
|
new_instance->downlink_frequency[i] = x2ap_register_eNB->downlink_frequency[i];
|
|
new_instance->uplink_frequency_offset[i] = x2ap_register_eNB->uplink_frequency_offset[i];
|
|
new_instance->Nid_cell[i] = x2ap_register_eNB->Nid_cell[i];
|
|
new_instance->N_RB_DL[i] = x2ap_register_eNB->N_RB_DL[i];
|
|
new_instance->frame_type[i] = x2ap_register_eNB->frame_type[i];
|
|
new_instance->fdd_earfcn_DL[i] = x2ap_register_eNB->fdd_earfcn_DL[i];
|
|
new_instance->fdd_earfcn_UL[i] = x2ap_register_eNB->fdd_earfcn_UL[i];
|
|
}
|
|
|
|
DevCheck(x2ap_register_eNB->nb_x2 <= X2AP_MAX_NB_ENB_IP_ADDRESS,
|
|
X2AP_MAX_NB_ENB_IP_ADDRESS, x2ap_register_eNB->nb_x2, 0);
|
|
memcpy(new_instance->target_enb_x2_ip_address,
|
|
x2ap_register_eNB->target_enb_x2_ip_address,
|
|
x2ap_register_eNB->nb_x2 * sizeof(net_ip_address_t));
|
|
new_instance->nb_x2 = x2ap_register_eNB->nb_x2;
|
|
new_instance->enb_x2_ip_address = x2ap_register_eNB->enb_x2_ip_address;
|
|
new_instance->sctp_in_streams = x2ap_register_eNB->sctp_in_streams;
|
|
new_instance->sctp_out_streams = x2ap_register_eNB->sctp_out_streams;
|
|
new_instance->enb_port_for_X2C = x2ap_register_eNB->enb_port_for_X2C;
|
|
/* Add the new instance to the list of eNB (meaningfull in virtual mode) */
|
|
x2ap_eNB_insert_new_instance(new_instance);
|
|
X2AP_INFO("Registered new eNB[%d] and %s eNB id %u\n",
|
|
instance,
|
|
x2ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
|
|
x2ap_register_eNB->eNB_id);
|
|
|
|
/* initiate the SCTP listener */
|
|
if (x2ap_eNB_init_sctp(new_instance,&x2ap_register_eNB->enb_x2_ip_address,x2ap_register_eNB->enb_port_for_X2C) < 0 ) {
|
|
X2AP_ERROR ("Error while sending SCTP_INIT_MSG to SCTP \n");
|
|
return;
|
|
}
|
|
|
|
X2AP_INFO("eNB[%d] eNB id %u acting as a listner (server)\n",
|
|
instance, x2ap_register_eNB->eNB_id);
|
|
}
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_sctp_init_msg_multi_cnf(
|
|
instance_t instance_id,
|
|
sctp_init_msg_multi_cnf_t *m) {
|
|
x2ap_eNB_instance_t *instance;
|
|
int index;
|
|
DevAssert(m != NULL);
|
|
instance = x2ap_eNB_get_instance(instance_id);
|
|
DevAssert(instance != NULL);
|
|
instance->multi_sd = m->multi_sd;
|
|
|
|
/* Exit if CNF message reports failure.
|
|
* Failure means multi_sd < 0.
|
|
*/
|
|
if (instance->multi_sd < 0) {
|
|
X2AP_ERROR("Error: be sure to properly configure X2 in your configuration file.\n");
|
|
DevAssert(instance->multi_sd >= 0);
|
|
}
|
|
|
|
/* Trying to connect to the provided list of eNB ip address */
|
|
|
|
for (index = 0; index < instance->nb_x2; index++) {
|
|
X2AP_INFO("eNB[%d] eNB id %u acting as an initiator (client)\n",
|
|
instance_id, instance->eNB_id);
|
|
x2ap_eNB_register_eNB(instance,
|
|
&instance->target_enb_x2_ip_address[index],
|
|
&instance->enb_x2_ip_address,
|
|
instance->sctp_in_streams,
|
|
instance->sctp_out_streams,
|
|
instance->enb_port_for_X2C,
|
|
instance->multi_sd);
|
|
}
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_handover_req(instance_t instance,
|
|
x2ap_handover_req_t *x2ap_handover_req)
|
|
{
|
|
x2ap_eNB_instance_t *instance_p;
|
|
x2ap_eNB_data_t *target;
|
|
x2ap_id_manager *id_manager;
|
|
int ue_id;
|
|
|
|
int target_pci = x2ap_handover_req->target_physCellId;
|
|
|
|
instance_p = x2ap_eNB_get_instance(instance);
|
|
DevAssert(instance_p != NULL);
|
|
|
|
target = x2ap_is_eNB_pci_in_list(target_pci);
|
|
DevAssert(target != NULL);
|
|
|
|
/* allocate x2ap ID */
|
|
id_manager = &instance_p->id_manager;
|
|
ue_id = x2ap_allocate_new_id(id_manager);
|
|
if (ue_id == -1) {
|
|
X2AP_ERROR("could not allocate a new X2AP UE ID\n");
|
|
/* TODO: cancel handover: send (to be defined) message to RRC */
|
|
exit(1);
|
|
}
|
|
/* id_source is ue_id, id_target is unknown yet */
|
|
x2ap_set_ids(id_manager, ue_id, x2ap_handover_req->rnti, ue_id, -1);
|
|
x2ap_id_set_state(id_manager, ue_id, X2ID_STATE_SOURCE_PREPARE);
|
|
x2ap_set_reloc_prep_timer(id_manager, ue_id,
|
|
x2ap_timer_get_tti(&instance_p->timers));
|
|
x2ap_id_set_target(id_manager, ue_id, target);
|
|
|
|
x2ap_eNB_generate_x2_handover_request(instance_p, target, x2ap_handover_req, ue_id);
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_handle_handover_req_ack(instance_t instance,
|
|
x2ap_handover_req_ack_t *x2ap_handover_req_ack)
|
|
{
|
|
/* TODO: remove this hack (the goal is to find the correct
|
|
* eNodeB structure for the other end) - we need a proper way for RRC
|
|
* and X2AP to identify eNodeBs
|
|
* RRC knows about mod_id and X2AP knows about eNB_id (eNB_ID in
|
|
* the configuration file)
|
|
* as far as I understand.. CROUX
|
|
*/
|
|
x2ap_eNB_instance_t *instance_p;
|
|
x2ap_eNB_data_t *target;
|
|
int source_assoc_id = x2ap_handover_req_ack->source_assoc_id;
|
|
int ue_id;
|
|
int id_source;
|
|
int id_target;
|
|
|
|
instance_p = x2ap_eNB_get_instance(instance);
|
|
DevAssert(instance_p != NULL);
|
|
|
|
target = x2ap_get_eNB(NULL, source_assoc_id, 0);
|
|
DevAssert(target != NULL);
|
|
|
|
/* rnti is a new information, save it */
|
|
ue_id = x2ap_handover_req_ack->x2_id_target;
|
|
id_source = x2ap_id_get_id_source(&instance_p->id_manager, ue_id);
|
|
id_target = ue_id;
|
|
x2ap_set_ids(&instance_p->id_manager, ue_id, x2ap_handover_req_ack->rnti, id_source, id_target);
|
|
|
|
x2ap_eNB_generate_x2_handover_request_ack(instance_p, target, x2ap_handover_req_ack);
|
|
}
|
|
|
|
static
|
|
void x2ap_eNB_ue_context_release(instance_t instance,
|
|
x2ap_ue_context_release_t *x2ap_ue_context_release)
|
|
{
|
|
x2ap_eNB_instance_t *instance_p;
|
|
x2ap_eNB_data_t *target;
|
|
int source_assoc_id = x2ap_ue_context_release->source_assoc_id;
|
|
int ue_id;
|
|
instance_p = x2ap_eNB_get_instance(instance);
|
|
DevAssert(instance_p != NULL);
|
|
|
|
target = x2ap_get_eNB(NULL, source_assoc_id, 0);
|
|
DevAssert(target != NULL);
|
|
|
|
x2ap_eNB_generate_x2_ue_context_release(instance_p, target, x2ap_ue_context_release);
|
|
|
|
/* free the X2AP UE ID */
|
|
ue_id = x2ap_find_id_from_rnti(&instance_p->id_manager, x2ap_ue_context_release->rnti);
|
|
if (ue_id == -1) {
|
|
X2AP_ERROR("could not find UE %x\n", x2ap_ue_context_release->rnti);
|
|
exit(1);
|
|
}
|
|
x2ap_release_id(&instance_p->id_manager, ue_id);
|
|
}
|
|
|
|
void *x2ap_task(void *arg) {
|
|
MessageDef *received_msg = NULL;
|
|
int result;
|
|
X2AP_DEBUG("Starting X2AP layer\n");
|
|
x2ap_eNB_prepare_internal_data();
|
|
itti_mark_task_ready(TASK_X2AP);
|
|
|
|
while (1) {
|
|
itti_receive_msg(TASK_X2AP, &received_msg);
|
|
|
|
switch (ITTI_MSG_ID(received_msg)) {
|
|
case TERMINATE_MESSAGE:
|
|
X2AP_WARN(" *** Exiting X2AP thread\n");
|
|
itti_exit_task();
|
|
break;
|
|
|
|
case X2AP_SUBFRAME_PROCESS:
|
|
x2ap_check_timers(ITTI_MESSAGE_GET_INSTANCE(received_msg));
|
|
break;
|
|
|
|
case X2AP_REGISTER_ENB_REQ:
|
|
x2ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&X2AP_REGISTER_ENB_REQ(received_msg));
|
|
break;
|
|
|
|
case X2AP_HANDOVER_REQ:
|
|
x2ap_eNB_handle_handover_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&X2AP_HANDOVER_REQ(received_msg));
|
|
break;
|
|
|
|
case X2AP_HANDOVER_REQ_ACK:
|
|
x2ap_eNB_handle_handover_req_ack(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&X2AP_HANDOVER_REQ_ACK(received_msg));
|
|
break;
|
|
|
|
case X2AP_UE_CONTEXT_RELEASE:
|
|
x2ap_eNB_ue_context_release(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&X2AP_UE_CONTEXT_RELEASE(received_msg));
|
|
break;
|
|
|
|
case SCTP_INIT_MSG_MULTI_CNF:
|
|
x2ap_eNB_handle_sctp_init_msg_multi_cnf(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&received_msg->ittiMsg.sctp_init_msg_multi_cnf);
|
|
break;
|
|
|
|
case SCTP_NEW_ASSOCIATION_RESP:
|
|
x2ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&received_msg->ittiMsg.sctp_new_association_resp);
|
|
break;
|
|
|
|
case SCTP_NEW_ASSOCIATION_IND:
|
|
x2ap_eNB_handle_sctp_association_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&received_msg->ittiMsg.sctp_new_association_ind);
|
|
break;
|
|
|
|
case SCTP_DATA_IND:
|
|
x2ap_eNB_handle_sctp_data_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
|
|
&received_msg->ittiMsg.sctp_data_ind);
|
|
break;
|
|
|
|
default:
|
|
X2AP_ERROR("Received unhandled message: %d:%s\n",
|
|
ITTI_MSG_ID(received_msg), ITTI_MSG_NAME(received_msg));
|
|
break;
|
|
}
|
|
|
|
result = itti_free (ITTI_MSG_ORIGIN_ID(received_msg), received_msg);
|
|
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
|
received_msg = NULL;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#include "common/config/config_userapi.h"
|
|
|
|
int is_x2ap_enabled(void)
|
|
{
|
|
static volatile int config_loaded = 0;
|
|
static volatile int enabled = 0;
|
|
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
if (pthread_mutex_lock(&mutex)) goto mutex_error;
|
|
|
|
if (config_loaded) {
|
|
if (pthread_mutex_unlock(&mutex)) goto mutex_error;
|
|
return enabled;
|
|
}
|
|
|
|
char *enable_x2 = NULL;
|
|
paramdef_t p[] = {
|
|
{ "enable_x2", "yes/no", 0, strptr:&enable_x2, defstrval:"", TYPE_STRING, 0 }
|
|
};
|
|
|
|
/* TODO: do it per module - we check only first eNB */
|
|
config_get(p, sizeof(p)/sizeof(paramdef_t), "eNBs.[0]");
|
|
if (enable_x2 != NULL && strcmp(enable_x2, "yes") == 0)
|
|
enabled = 1;
|
|
|
|
config_loaded = 1;
|
|
|
|
if (pthread_mutex_unlock(&mutex)) goto mutex_error;
|
|
|
|
return enabled;
|
|
|
|
mutex_error:
|
|
LOG_E(X2AP, "mutex error\n");
|
|
exit(1);
|
|
}
|