Skip to content
Snippets Groups Projects
Commit 365527d4 authored by Dr.-Ing.  Javier Morgade's avatar Dr.-Ing. Javier Morgade
Browse files

M3AP 3GPP TS 36.444 interface implemented:

        1.MME entity developed to handle M3 side (new dedicated task developed: TASK_MME_APP) (these procedures should be moved to OPENAIR-CN ... just implemented to easy the E-UTRAN MBMS stuff development)
        2.MCE side M3 interface procedures (new dedicated task developed: TASK_M3AP_MCE)
        3.MME side M3 interface procedures (new dedicated task developed: TASK_M2AP_MME) (these procedures should be moved to OPENAIR-CN ... just implemented to easy the E-UTRAN MBMS stuff development)
        4.ASN1 bindings for m3ap-14.0.0.asn1 implemented and tested
        5.MME config parameters

	ACKNOWLEDGEMENT:
 	1. This commit was developed at Vicomtech (https://www.vicomtech.org) under UE project CDN-X-ALL: "CDN edge-cloud computing for efficient cache and reliable streaming aCROSS Aggregated unicast-multicast LinkS"
 	2. Project funded by Fed4FIRE+ OC5 (https://www.fed4fire.eu

)

Signed-off-by: Dr.-Ing.  Javier Morgade's avatarJavier Morgade <javier.morgade@ieee.org>
parent ab04e0e4
No related branches found
No related tags found
5 merge requests!755Integration develop-nr 2020 week 03,!754Release v1.2.0 candidate,!742Develop integration branch: 2019 week 51,!739Oai test fed4fire old,!736Ci test fallback
Showing
with 6467 additions and 0 deletions
/*
* 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 m3ap_MCE.c
* \brief m3ap tasks for MCE
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "intertask_interface.h"
#include "m3ap_MCE.h"
#include "m3ap_MCE_defs.h"
#include "m3ap_MCE_management_procedures.h"
#include "m3ap_MCE_handler.h"
//#include "m3ap_MCE_generate_messages.h"
#include "m3ap_common.h"
#include "m3ap_MCE_interface_management.h"
#include "m3ap_ids.h"
#include "m3ap_timers.h"
#include "queue.h"
#include "assertions.h"
#include "conversions.h"
struct m3ap_mce_map;
struct m3ap_MCE_data_s;
m3ap_setup_req_t * m3ap_mce_data_g;
RB_PROTOTYPE(m3ap_mce_map, m3ap_MCE_data_s, entry, m3ap_MCE_compare_assoc_id);
static
void m3ap_MCE_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind);
static
void m3ap_MCE_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
static
void m3ap_MCE_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind);
static
void m3ap_MCE_handle_register_MCE(instance_t instance,
m3ap_register_mce_req_t *m3ap_register_MME);
static
void m3ap_MCE_register_MCE(m3ap_MCE_instance_t *instance_p,
net_ip_address_t *target_MME_ip_addr,
net_ip_address_t *local_ip_addr,
uint16_t in_streams,
uint16_t out_streams,
uint32_t mce_port_for_M3C,
int multi_sd);
//static
//void m3ap_eNB_handle_handover_req(instance_t instance,
// m3ap_handover_req_t *m3ap_handover_req);
//
//static
//void m3ap_eNB_handle_handover_req_ack(instance_t instance,
// m3ap_handover_req_ack_t *m3ap_handover_req_ack);
//
//static
//void m3ap_eNB_ue_context_release(instance_t instance,
// m3ap_ue_context_release_t *m3ap_ue_context_release);
//
static
void m3ap_MCE_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) {
int result;
DevAssert(sctp_data_ind != NULL);
m3ap_MCE_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 m3ap_MCE_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp) {
m3ap_MCE_instance_t *instance_p;
m3ap_MCE_data_t *m3ap_mce_data_p;
DevAssert(sctp_new_association_resp != NULL);
//printf("m3ap_eNB_handle_sctp_association_resp at 1\n");
//dump_trees();
instance_p = m3ap_MCE_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) {
m3ap_mce_data_p = m3ap_get_MCE(instance_p, sctp_new_association_resp->assoc_id,
sctp_new_association_resp->ulp_cnx_id);
if (m3ap_mce_data_p != NULL) {
/* some sanity check - to be refined at some point */
if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
M3AP_ERROR("m3ap_mce_data_p not NULL and sctp state not SCTP_STATE_ESTABLISHED, what to do?\n");
abort();
}
m3ap_mce_data_p->in_streams = sctp_new_association_resp->in_streams;
m3ap_mce_data_p->out_streams = sctp_new_association_resp->out_streams;
return;
}
}
m3ap_mce_data_p = m3ap_get_MCE(instance_p, -1,
sctp_new_association_resp->ulp_cnx_id);
DevAssert(m3ap_mce_data_p != NULL);
//printf("m3ap_MCE_handle_sctp_association_resp at 2\n");
//dump_trees_m3();
if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
M3AP_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);
//m3ap_handle_m3_setup_message(instance_p, m3ap_enb_data_p,
//sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
return;
}
//printf("m3ap_MCE_handle_sctp_association_resp at 3\n");
//dump_trees_m3();
/* Update parameters */
m3ap_mce_data_p->assoc_id = sctp_new_association_resp->assoc_id;
m3ap_mce_data_p->in_streams = sctp_new_association_resp->in_streams;
m3ap_mce_data_p->out_streams = sctp_new_association_resp->out_streams;
//printf("m3ap_MCE_handle_sctp_association_resp at 4\n");
//dump_trees_m3();
m3ap_mce_data_g->assoc_id = sctp_new_association_resp->assoc_id;
m3ap_mce_data_g->sctp_in_streams = sctp_new_association_resp->in_streams;
m3ap_mce_data_g->sctp_out_streams = sctp_new_association_resp->out_streams;
/* Prepare new m3 Setup Request */
//m3ap_eNB_generate_m3_setup_request(instance_p, m3ap_enb_data_p);
MCE_send_M3_SETUP_REQUEST(instance_p, m3ap_mce_data_p);
}
static
void m3ap_MCE_handle_sctp_association_ind(instance_t instance, sctp_new_association_ind_t *sctp_new_association_ind) {
m3ap_MCE_instance_t *instance_p;
m3ap_MCE_data_t *m3ap_mce_data_p;
//printf("m3ap_MCE_handle_sctp_association_ind at 1 (called for instance %d)\n", instance);
//dump_trees_m3();
DevAssert(sctp_new_association_ind != NULL);
instance_p = m3ap_MCE_get_instance(instance);
DevAssert(instance_p != NULL);
m3ap_mce_data_p = m3ap_get_MCE(instance_p, sctp_new_association_ind->assoc_id, -1);
if (m3ap_mce_data_p != NULL) abort();
// DevAssert(m3ap_mce_data_p != NULL);
if (m3ap_mce_data_p == NULL) {
/* Create new MCE descriptor */
m3ap_mce_data_p = calloc(1, sizeof(*m3ap_mce_data_p));
DevAssert(m3ap_mce_data_p != NULL);
m3ap_mce_data_p->cnx_id = m3ap_MCE_fetch_add_global_cnx_id();
m3ap_mce_data_p->m3ap_MCE_instance = instance_p;
/* Insert the new descriptor in list of known eNB
* but not yet associated.
*/
RB_INSERT(m3ap_mce_map, &instance_p->m3ap_mce_head, m3ap_mce_data_p);
m3ap_mce_data_p->state = M3AP_MCE_STATE_CONNECTED;
instance_p->m3_target_mme_nb++;
if (instance_p->m3_target_mme_pending_nb > 0) {
instance_p->m3_target_mme_pending_nb--;
}
} else {
M3AP_WARN("m3ap_mce_data_p already exists\n");
}
//printf("m3ap_MCE_handle_sctp_association_ind at 2\n");
//dump_trees_m3();
/* Update parameters */
m3ap_mce_data_p->assoc_id = sctp_new_association_ind->assoc_id;
m3ap_mce_data_p->in_streams = sctp_new_association_ind->in_streams;
m3ap_mce_data_p->out_streams = sctp_new_association_ind->out_streams;
//printf("m3ap_MCE_handle_sctp_association_ind at 3\n");
//dump_trees_m3();
}
int m3ap_MCE_init_sctp (m3ap_MCE_instance_t *instance_p,
net_ip_address_t *local_ip_addr,
uint32_t mce_port_for_M3C) {
// 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_M3AP_MCE, SCTP_INIT_MSG_MULTI_REQ);
sctp_init = &message->ittiMsg.sctp_init_multi;
sctp_init->port = mce_port_for_M3C;
sctp_init->ppid = M3AP_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 m3ap_MCE_register_MCE(m3ap_MCE_instance_t *instance_p,
net_ip_address_t *target_MCE_ip_address,
net_ip_address_t *local_ip_addr,
uint16_t in_streams,
uint16_t out_streams,
uint32_t mce_port_for_M3C,
int multi_sd) {
MessageDef *message = NULL;
sctp_new_association_req_multi_t *sctp_new_association_req = NULL;
m3ap_MCE_data_t *m3ap_mce_data = NULL;
DevAssert(instance_p != NULL);
DevAssert(target_MCE_ip_address != NULL);
message = itti_alloc_new_message(TASK_M3AP_MCE, SCTP_NEW_ASSOCIATION_REQ_MULTI);
sctp_new_association_req = &message->ittiMsg.sctp_new_association_req_multi;
sctp_new_association_req->port = mce_port_for_M3C;
sctp_new_association_req->ppid = M3AP_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_MCE_ip_address,
sizeof(*target_MCE_ip_address));
memcpy(&sctp_new_association_req->local_address,
local_ip_addr,
sizeof(*local_ip_addr));
/* Create new MCE descriptor */
m3ap_mce_data = calloc(1, sizeof(*m3ap_mce_data));
DevAssert(m3ap_mce_data != NULL);
m3ap_mce_data->cnx_id = m3ap_MCE_fetch_add_global_cnx_id();
sctp_new_association_req->ulp_cnx_id = m3ap_mce_data->cnx_id;
m3ap_mce_data->assoc_id = -1;
m3ap_mce_data->m3ap_MCE_instance = instance_p;
/* Insert the new descriptor in list of known eNB
* but not yet associated.
*/
m3ap_mce_data_g = (m3ap_setup_req_t*)calloc(1,sizeof(m3ap_setup_req_t));
RB_INSERT(m3ap_mce_map, &instance_p->m3ap_mce_head, m3ap_mce_data);
m3ap_mce_data->state = M3AP_MCE_STATE_WAITING;
instance_p->m3_target_mme_nb ++;
instance_p->m3_target_mme_pending_nb ++;
itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message);
}
static
void m3ap_MCE_handle_register_MCE(instance_t instance,
m3ap_register_mce_req_t *m3ap_register_MCE) {
m3ap_MCE_instance_t *new_instance;
DevAssert(m3ap_register_MCE != NULL);
/* Look if the provided instance already exists */
new_instance = m3ap_MCE_get_instance(instance);
if (new_instance != NULL) {
/* Checks if it is a retry on the same MCE */
DevCheck(new_instance->MCE_id == m3ap_register_MCE->MCE_id, new_instance->MCE_id, m3ap_register_MCE->MCE_id, 0);
DevCheck(new_instance->cell_type == m3ap_register_MCE->cell_type, new_instance->cell_type, m3ap_register_MCE->cell_type, 0);
DevCheck(new_instance->tac == m3ap_register_MCE->tac, new_instance->tac, m3ap_register_MCE->tac, 0);
DevCheck(new_instance->mcc == m3ap_register_MCE->mcc, new_instance->mcc, m3ap_register_MCE->mcc, 0);
DevCheck(new_instance->mnc == m3ap_register_MCE->mnc, new_instance->mnc, m3ap_register_MCE->mnc, 0);
M3AP_WARN("MCE[%d] already registered\n", instance);
} else {
new_instance = calloc(1, sizeof(m3ap_MCE_instance_t));
DevAssert(new_instance != NULL);
RB_INIT(&new_instance->m3ap_mce_head);
/* Copy usefull parameters */
new_instance->instance = instance;
new_instance->MCE_name = m3ap_register_MCE->MCE_name;
new_instance->MCE_id = m3ap_register_MCE->MCE_id;
new_instance->cell_type = m3ap_register_MCE->cell_type;
new_instance->tac = m3ap_register_MCE->tac;
new_instance->mcc = m3ap_register_MCE->mcc;
new_instance->mnc = m3ap_register_MCE->mnc;
new_instance->mnc_digit_length = m3ap_register_MCE->mnc_digit_length;
new_instance->num_cc = m3ap_register_MCE->num_cc;
m3ap_id_manager_init(&new_instance->id_manager);
m3ap_timers_init(&new_instance->timers,
m3ap_register_MCE->t_reloc_prep,
m3ap_register_MCE->tm3_reloc_overall);
for (int i = 0; i< m3ap_register_MCE->num_cc; i++) {
new_instance->eutra_band[i] = m3ap_register_MCE->eutra_band[i];
new_instance->downlink_frequency[i] = m3ap_register_MCE->downlink_frequency[i];
new_instance->uplink_frequency_offset[i] = m3ap_register_MCE->uplink_frequency_offset[i];
new_instance->Nid_cell[i] = m3ap_register_MCE->Nid_cell[i];
new_instance->N_RB_DL[i] = m3ap_register_MCE->N_RB_DL[i];
new_instance->frame_type[i] = m3ap_register_MCE->frame_type[i];
new_instance->fdd_earfcn_DL[i] = m3ap_register_MCE->fdd_earfcn_DL[i];
new_instance->fdd_earfcn_UL[i] = m3ap_register_MCE->fdd_earfcn_UL[i];
}
DevCheck(m3ap_register_MCE->nb_m3 <= M3AP_MAX_NB_MCE_IP_ADDRESS,
M3AP_MAX_NB_MCE_IP_ADDRESS, m3ap_register_MCE->nb_m3, 0);
memcpy(new_instance->target_mme_m3_ip_address,
m3ap_register_MCE->target_mme_m3_ip_address,
m3ap_register_MCE->nb_m3 * sizeof(net_ip_address_t));
new_instance->nb_m3 = m3ap_register_MCE->nb_m3;
new_instance->mme_m3_ip_address = m3ap_register_MCE->mme_m3_ip_address;
new_instance->sctp_in_streams = m3ap_register_MCE->sctp_in_streams;
new_instance->sctp_out_streams = m3ap_register_MCE->sctp_out_streams;
new_instance->mce_port_for_M3C = m3ap_register_MCE->mme_port_for_M3C;
/* Add the new instance to the list of MCE (meaningfull in virtual mode) */
m3ap_MCE_insert_new_instance(new_instance);
M3AP_INFO("Registered new MCE[%d] and %s MCE id %u\n",
instance,
m3ap_register_MCE->cell_type == CELL_MACRO_ENB ? "macro" : "home",
m3ap_register_MCE->MCE_id);
printf("ipv4_address %s\n",m3ap_register_MCE->mme_m3_ip_address.ipv4_address);
/* initiate the SCTP listener */
if (m3ap_MCE_init_sctp(new_instance,&m3ap_register_MCE->mme_m3_ip_address,m3ap_register_MCE->mme_port_for_M3C) < 0 ) {
M3AP_ERROR ("Error while sending SCTP_INIT_MSG to SCTP \n");
return;
}
M3AP_INFO("MCE[%d] MCE id %u acting as a listner (server)\n",
instance, m3ap_register_MCE->MCE_id);
}
}
static
void m3ap_MCE_handle_sctp_init_msg_multi_cnf(
instance_t instance_id,
sctp_init_msg_multi_cnf_t *m) {
m3ap_MCE_instance_t *instance;
int index;
DevAssert(m != NULL);
instance = m3ap_MCE_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) {
M3AP_ERROR("Error: be sure to properly configure M3 in your configuration file.\n");
DevAssert(instance->multi_sd >= 0);
}
/* Trying to connect to the provided list of MCE ip address */
for (index = 0; index < instance->nb_m3; index++) {
M3AP_INFO("MCE[%d] MCE id %u acting as an initiator (client)\n",
instance_id, instance->MCE_id);
m3ap_MCE_register_MCE(instance,
&instance->target_mme_m3_ip_address[index],
&instance->mme_m3_ip_address,
instance->sctp_in_streams,
instance->sctp_out_streams,
instance->mce_port_for_M3C,
instance->multi_sd);
}
}
//static
//void m3ap_eNB_handle_handover_req(instance_t instance,
// m3ap_handover_req_t *m3ap_handover_req)
//{
// m3ap_eNB_instance_t *instance_p;
// m3ap_eNB_data_t *target;
// m3ap_id_manager *id_manager;
// int ue_id;
//
// int target_pci = m3ap_handover_req->target_physCellId;
//
// instance_p = m3ap_eNB_get_instance(instance);
// DevAssert(instance_p != NULL);
//
// target = m3ap_is_eNB_pci_in_list(target_pci);
// DevAssert(target != NULL);
//
// /* allocate m3ap ID */
// id_manager = &instance_p->id_manager;
// ue_id = m3ap_allocate_new_id(id_manager);
// if (ue_id == -1) {
// M3AP_ERROR("could not allocate a new M3AP 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 */
// m3ap_set_ids(id_manager, ue_id, m3ap_handover_req->rnti, ue_id, -1);
// m3ap_id_set_state(id_manager, ue_id, M2ID_STATE_SOURCE_PREPARE);
// m3ap_set_reloc_prep_timer(id_manager, ue_id,
// m3ap_timer_get_tti(&instance_p->timers));
// m3ap_id_set_target(id_manager, ue_id, target);
//
// m3ap_eNB_generate_m3_handover_request(instance_p, target, m3ap_handover_req, ue_id);
//}
//static
//void m3ap_eNB_handle_handover_req_ack(instance_t instance,
// m3ap_handover_req_ack_t *m3ap_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 M3AP to identify eNodeBs
// * RRC knows about mod_id and M3AP knows about eNB_id (eNB_ID in
// * the configuration file)
// * as far as I understand.. CROUX
// */
// m3ap_eNB_instance_t *instance_p;
// m3ap_eNB_data_t *target;
// int source_assoc_id = m3ap_handover_req_ack->source_assoc_id;
// int ue_id;
// int id_source;
// int id_target;
//
// instance_p = m3ap_eNB_get_instance(instance);
// DevAssert(instance_p != NULL);
//
// target = m3ap_get_eNB(NULL, source_assoc_id, 0);
// DevAssert(target != NULL);
//
// /* rnti is a new information, save it */
// ue_id = m3ap_handover_req_ack->m3_id_target;
// id_source = m3ap_id_get_id_source(&instance_p->id_manager, ue_id);
// id_target = ue_id;
// m3ap_set_ids(&instance_p->id_manager, ue_id, m3ap_handover_req_ack->rnti, id_source, id_target);
//
// m3ap_eNB_generate_m3_handover_request_ack(instance_p, target, m3ap_handover_req_ack);
//}
//
//static
//void m3ap_eNB_ue_context_release(instance_t instance,
// m3ap_ue_context_release_t *m3ap_ue_context_release)
//{
// m3ap_eNB_instance_t *instance_p;
// m3ap_eNB_data_t *target;
// int source_assoc_id = m3ap_ue_context_release->source_assoc_id;
// int ue_id;
// instance_p = m3ap_eNB_get_instance(instance);
// DevAssert(instance_p != NULL);
//
// target = m3ap_get_eNB(NULL, source_assoc_id, 0);
// DevAssert(target != NULL);
//
// m3ap_eNB_generate_m3_ue_context_release(instance_p, target, m3ap_ue_context_release);
//
// /* free the M3AP UE ID */
// ue_id = m3ap_find_id_from_rnti(&instance_p->id_manager, m3ap_ue_context_release->rnti);
// if (ue_id == -1) {
// M3AP_ERROR("could not find UE %x\n", m3ap_ue_context_release->rnti);
// exit(1);
// }
// m3ap_release_id(&instance_p->id_manager, ue_id);
//}
uint8_t m3ap_m3setup[] = {
0x00, 0x07, 0x00, 0x23, 0x00, 0x00, 0x03, 0x00,
0x12, 0x00, 0x07, 0x40, 0x55, 0xf5, 0x01, 0x00,
0x25, 0x00, 0x00, 0x13, 0x40, 0x0a, 0x03, 0x80,
0x4d, 0x33, 0x2d, 0x65, 0x4e, 0x42, 0x33, 0x37,
0x00, 0x14, 0x00, 0x03, 0x01, 0x00, 0x01
};
uint8_t m3ap_start[] = {
0x00, 0x00, 0x00, 0x4b, 0x00, 0x00, 0x07, 0x00,
0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00,
0x07, 0x00, 0x55, 0xf5, 0x01, 0x00, 0x00, 0x01,
0x00, 0x04, 0x00, 0x0d, 0x60, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x40, 0x01,
0x7a, 0x00, 0x05, 0x00, 0x03, 0x07, 0x08, 0x00,
0x00, 0x06, 0x00, 0x04, 0x03, 0x00, 0x00, 0x00,
0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x07, 0x00,
0x0e, 0x00, 0xe8, 0x0a, 0x0a, 0x0a, 0x00, 0x0a,
0xc8, 0x0a, 0xfe, 0x00, 0x00, 0x00, 0x01
};
void *m3ap_MCE_task(void *arg) {
MessageDef *received_msg = NULL;
int result;
M3AP_DEBUG("Starting M3AP MCE layer\n");
m3ap_MCE_prepare_internal_data();
itti_mark_task_ready(TASK_M3AP_MCE);
while (1) {
itti_receive_msg(TASK_M3AP_MCE, &received_msg);
switch (ITTI_MSG_ID(received_msg)) {
case MESSAGE_TEST:
// LOG_W(M3AP,"MCE Received Test Message ... TODO\n");
// //MessageDef * message_p = itti_alloc_new_message(TASK_M3AP, MESSAGE_TEST);
// //itti_send_msg_to_task(TASK_M2AP_ENB, 1/*ctxt_pP->module_id*/, message_p);
//
// asn_dec_rval_t dec_ret;
//
//
// uint8_t *buffer;
// uint32_t len;
// M3AP_M3AP_PDU_t pdu;
// memset(&pdu, 0, sizeof(pdu));
//
// buffer = &m3ap_m3setup[0];
// //buffer = &m3ap_start[0];
// len=8*4+7;
// //len=8*9+7;
//
// //if (m3ap_decode_pdu(&pdu, buffer, len) < 0) {
// // LOG_E(M3AP, "Failed to decode PDU\n");
// //re//turn -1;
// //}
// M3AP_M3AP_PDU_t *pdu2 = &pdu;
// dec_ret = aper_decode(NULL,
// &asn_DEF_M3AP_M3AP_PDU,
// (void **)&pdu2,
// buffer,
// len,
// 0,
// 0);
//
// LOG_W(M3AP,"Trying to print %d\n",(dec_ret.code == RC_OK));
// xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, &pdu);
// LOG_W(M3AP,"Trying to print out \n");
break;
case TERMINATE_MESSAGE:
M3AP_WARN(" *** Exiting M3AP thread\n");
itti_exit_task();
break;
case M3AP_SUBFRAME_PROCESS:
m3ap_check_timers(ITTI_MESSAGE_GET_INSTANCE(received_msg));
break;
case M3AP_REGISTER_MCE_REQ:
LOG_I(M3AP,"MCE Received M3AP_REGISTER_MCE_REQ Message\n");
m3ap_MCE_handle_register_MCE(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&M3AP_REGISTER_MCE_REQ(received_msg));
break;
case M3AP_MBMS_SESSION_START_RESP:
LOG_I(M3AP,"MCE M3AP_MBMS_SESSION_START_RESP Message\n");
MCE_send_MBMS_SESSION_START_RESPONSE(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&M3AP_MBMS_SESSION_START_RESP(received_msg));
break;
case M3AP_MBMS_SESSION_STOP_RESP:
LOG_I(M3AP,"MCE M3AP_MBMS_SESSION_STOP_RESP Message\n");
MCE_send_MBMS_SESSION_STOP_RESPONSE(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&M3AP_MBMS_SESSION_START_RESP(received_msg));
break;
case M3AP_MBMS_SESSION_UPDATE_RESP:
LOG_I(M3AP,"MCE M3AP_MBMS_SESSION_UPDATE_RESP Message\n");
//MCE_send_MBMS_SESSION_UPDATE_RESPONSE(ITTI_MESSAGE_GET_INSTANCE(received_msg),
//&M3AP_MBMS_SESSION_START_RESP(received_msg));
break;
// case M3AP_HANDOVER_REQ:
// m3ap_eNB_handle_handover_req(ITTI_MESSAGE_GET_INSTANCE(received_msg),
// &M3AP_HANDOVER_REQ(received_msg));
// break;
//
// case M3AP_HANDOVER_REQ_ACK:
// m3ap_eNB_handle_handover_req_ack(ITTI_MESSAGE_GET_INSTANCE(received_msg),
// &M3AP_HANDOVER_REQ_ACK(received_msg));
// break;
//
// case M3AP_UE_CONTEXT_RELEASE:
// m3ap_eNB_ue_context_release(ITTI_MESSAGE_GET_INSTANCE(received_msg),
// &M3AP_UE_CONTEXT_RELEASE(received_msg));
// break;
//
case SCTP_INIT_MSG_MULTI_CNF:
LOG_D(M3AP,"MCE Received SCTP_INIT_MSG_MULTI_CNF Message\n");
m3ap_MCE_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:
LOG_D(M3AP,"MCE Received SCTP_NEW_ASSOCIATION_RESP Message\n");
m3ap_MCE_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&received_msg->ittiMsg.sctp_new_association_resp);
break;
case SCTP_NEW_ASSOCIATION_IND:
LOG_D(M3AP,"MCE Received SCTP_NEW_ASSOCIATION Message\n");
m3ap_MCE_handle_sctp_association_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&received_msg->ittiMsg.sctp_new_association_ind);
break;
case SCTP_DATA_IND:
LOG_D(M3AP,"MCE Received SCTP_DATA_IND Message\n");
m3ap_MCE_handle_sctp_data_ind(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&received_msg->ittiMsg.sctp_data_ind);
break;
default:
M3AP_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_m3ap_MCE_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_m3 = NULL;
paramdef_t p[] = {
{ "enable_mce_m3", "yes/no", 0, strptr:&enable_m3, defstrval:"", TYPE_STRING, 0 }
};
/* TODO: do it per module - we check only first MCE */
config_get(p, sizeof(p)/sizeof(paramdef_t), "MCEs.[0]");
if (enable_m3 != NULL && strcmp(enable_m3, "yes") == 0)
enabled = 1;
config_loaded = 1;
if (pthread_mutex_unlock(&mutex)) goto mutex_error;
return enabled;
mutex_error:
LOG_E(M3AP, "mutex error\n");
exit(1);
}
/*
* 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 m3ap_eNB.h
* \brief m3ap 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 <stdio.h>
#include <stdint.h>
/** @defgroup _m3ap_impl_ M3AP Layer Reference Implementation
* @ingroup _ref_implementation_
* @{
*/
#ifndef M3AP_MCE_H_
#define M3AP_MCE_H_
#include "m3ap_MCE_defs.h"
int m3ap_MCE_init_sctp (m3ap_MCE_instance_t *instance_p,
net_ip_address_t *local_ip_addr,
uint32_t mce_port_for_M3C);
void *m3ap_MCE_task(void *arg);
int is_m3ap_MCE_enabled(void);
#endif /* M3AP_H_ */
/**
* @}
*/
/*
* 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 m3ap_MCE_defs.h
* \brief m3ap struct definitions for MCE
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#include <stdint.h>
#include "queue.h"
#include "tree.h"
#include "sctp_eNB_defs.h"
#include "m3ap_ids.h" //looks X2AP specific for HO
#include "m3ap_timers.h"
#ifndef M3AP_MCE_DEFS_H_
#define M3AP_MCE_DEFS_H_
#define M3AP_MCE_NAME_LENGTH_MAX (150)
typedef enum {
/* Disconnected state: initial state for any association. */
M3AP_MCE_STATE_DISCONNECTED = 0x0,
/* State waiting for m2 Setup response message if the target MCE accepts or
* M3 Setup failure if rejects the MCE.
*/
M3AP_MCE_STATE_WAITING = 0x1,
/* The MCE is successfully connected to another MCE. */
M3AP_MCE_STATE_CONNECTED = 0x2,
/* M3AP is ready, and the MCE is successfully connected to another MCE. */
M3AP_MCE_STATE_READY = 0x3,
M3AP_MCE_STATE_OVERLOAD = 0x4,
M3AP_MCE_STATE_RESETTING = 0x5,
/* Max number of states available */
M3AP_MCE_STATE_MAX,
} m3ap_MCE_state_t;
/* Served PLMN identity element */
/*struct plmn_identity_s {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
STAILQ_ENTRY(plmn_identity_s) next;
};*/
/* Served group id element */
/*struct served_group_id_s {
uint16_t mce_group_id;
STAILQ_ENTRY(served_group_id_s) next;
};*/
/* Served enn code for a particular MCE */
/*struct mce_code_s {
uint8_t mce_code;
STAILQ_ENTRY(mce_code_s) next;
};*/
struct m3ap_MCE_instance_s;
/* This structure describes association of a MCE to another MCE */
typedef struct m3ap_MCE_data_s {
/* MCE descriptors tree, ordered by sctp assoc id */
RB_ENTRY(m3ap_MCE_data_s) entry;
/* This is the optional name provided by the MME */
char *MCE_name;
/* target MCE ID */
uint32_t MCE_id;
/* Current MCE load information (if any). */
//m3ap_load_state_t overload_state;
/* Current MCE->MCE M3AP association state */
m3ap_MCE_state_t state;
/* Next usable stream for UE signalling */
int32_t nextstream;
/* Number of input/ouput streams */
uint16_t in_streams;
uint16_t out_streams;
/* Connexion id used between SCTP/M3AP */
uint16_t cnx_id;
/* SCTP association id */
int32_t assoc_id;
/* Nid cells */
uint32_t Nid_cell[MAX_NUM_CCs];
int num_cc;
/* Only meaningfull in virtual mode */
struct m3ap_MCE_instance_s *m3ap_MCE_instance;
} m3ap_MCE_data_t;
typedef struct m3ap_MCE_instance_s {
/* used in simulation to store multiple MCE instances*/
STAILQ_ENTRY(m3ap_MCE_instance_s) m3ap_MCE_entries;
/* Number of target MCEs requested by MCE (tree size) */
uint32_t m3_target_mme_nb;
/* Number of target MCEs for which association is pending */
uint32_t m3_target_mme_pending_nb;
/* Number of target MCE successfully associated to MCE */
uint32_t m3_target_mme_associated_nb;
/* Tree of M3AP MCE associations ordered by association ID */
RB_HEAD(m3ap_mce_map, m3ap_MCE_data_s) m3ap_mce_head;
/* Tree of UE ordered by MCE_ue_m3ap_id's */
// RB_HEAD(m3ap_ue_map, m3ap_MCE_ue_context_s) m3ap_ue_head;
/* For virtual mode, mod_id as defined in the rest of the L1/L2 stack */
instance_t instance;
/* Displayable name of MCE */
char *MCE_name;
/* Unique MCE_id to identify the MCE within EPC.
* In our case the MCE is a macro MCE so the id will be 20 bits long.
* For Home MCE id, this field should be 28 bits long.
*/
uint32_t MCE_id;
/* The type of the cell */
cell_type_t cell_type;
/* Tracking area code */
uint16_t tac;
/* Mobile Country Code
* Mobile Network Code
*/
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
/* CC params */
int16_t eutra_band[MAX_NUM_CCs];
uint32_t downlink_frequency[MAX_NUM_CCs];
int32_t uplink_frequency_offset[MAX_NUM_CCs];
uint32_t Nid_cell[MAX_NUM_CCs];
int16_t N_RB_DL[MAX_NUM_CCs];
lte_frame_type_t frame_type[MAX_NUM_CCs];
uint32_t fdd_earfcn_DL[MAX_NUM_CCs];
uint32_t fdd_earfcn_UL[MAX_NUM_CCs];
int num_cc;
net_ip_address_t target_mme_m3_ip_address[M3AP_MAX_NB_MCE_IP_ADDRESS];
uint8_t nb_m3;
net_ip_address_t mme_m3_ip_address;
uint16_t sctp_in_streams;
uint16_t sctp_out_streams;
uint32_t mce_port_for_M3C;
int multi_sd;
m3ap_id_manager id_manager;
m3ap_timers_t timers;
} m3ap_MCE_instance_t;
typedef struct {
/* List of served MCEs
* Only used for virtual mode
*/
STAILQ_HEAD(m3ap_MCE_instances_head_s, m3ap_MCE_instance_s) m3ap_MCE_instances_head;
/* Nb of registered MCEs */
uint8_t nb_registered_MCEs;
/* Generate a unique connexion id used between M3AP and SCTP */
uint16_t global_cnx_id;
} m3ap_MCE_internal_data_t;
int m3ap_MCE_compare_assoc_id(struct m3ap_MCE_data_s *p1, struct m3ap_MCE_data_s *p2);
/* Generate the tree management functions */
struct m3ap_MCE_map;
struct m3ap_MCE_data_s;
RB_PROTOTYPE(m3ap_MCE_map, m3ap_MCE_data_s, entry, m3ap_MCE_compare_assoc_id);
#endif /* M3AP_MCE_DEFS_H_ */
/*
* 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 m2ap_eNB_generate_messages.h
* \brief m2ap procedures for eNB
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#ifndef M2AP_ENB_GENERATE_MESSAGES_H_
#define M2AP_ENB_GENERATE_MESSAGES_H_
#include "m2ap_eNB_defs.h"
#include "m2ap_common.h"
int m2ap_eNB_generate_m2_setup_request(m2ap_eNB_instance_t *instance_p,
m2ap_eNB_data_t *m2ap_eNB_data_p);
int m2ap_MCE_generate_m2_setup_response(m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *m2ap_eNB_data_p);
/*int m2ap_MCE_generate_m2_setup_failure(instance_t instance,
uint32_t assoc_id,
M2AP_Cause_PR cause_type,
long cause_value,
long time_to_wait);*/
int m2ap_eNB_set_cause (M2AP_Cause_t * cause_p,
M2AP_Cause_PR cause_type,
long cause_value);
//int m2ap_eNB_generate_m2_handover_request (m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *m2ap_eNB_data_p,
// m2ap_handover_req_t *m2ap_handover_req, int ue_id);
//
//int m2ap_eNB_generate_m2_handover_request_ack (m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *m2ap_eNB_data_p,
// m2ap_handover_req_ack_t *m2ap_handover_req_ack);
//
//int m2ap_eNB_generate_m2_ue_context_release (m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *m2ap_eNB_data_p,
// m2ap_ue_context_release_t *m2ap_ue_context_release);
//
//int m2ap_eNB_generate_m2_handover_cancel (m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *m2ap_eNB_data_p,
// int m2_ue_id,
// m2ap_handover_cancel_cause_t cause);
#endif /* M2AP_ENB_GENERATE_MESSAGES_H_ */
This diff is collapsed.
/*
* 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 m3ap_eNB_handler.c
* \brief m3ap handler procedures for eNB
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#include <stdint.h>
#include "intertask_interface.h"
#include "asn1_conversions.h"
#include "m3ap_common.h"
#include "m3ap_MCE_defs.h"
#include "m3ap_handler.h"
#include "m3ap_decoder.h"
#include "m3ap_ids.h"
//#include "m3ap_MCE_management_procedures.h"
#include "m3ap_MCE_generate_messages.h"
//#include "m3ap_MME_interface_management.h"
#include "m3ap_MCE_interface_management.h"
#include "msc.h"
#include "assertions.h"
#include "conversions.h"
/* Handlers matrix. Only eNB related procedure present here */
m3ap_MCE_message_decoded_callback m3ap_MCE_messages_callback[][3] = {
{ MCE_handle_MBMS_SESSION_START_REQUEST, 0, 0 }, /* MBMSSessionStart */
{ MCE_handle_MBMS_SESSION_STOP_REQUEST, 0, 0 }, /* MBMSSessionStop */
{ 0, 0, 0 }, /* Error Indication */
{ 0, 0, 0 }, /* Reset */
{ 0, 0, 0 }, /* ??? */
{ MCE_handle_MBMS_SESSION_UPDATE_REQUEST, 0, 0 }, /* MBMSSessionUpdate */
{ 0, 0, 0 }, /* MCEConfigurationUpdate */
{ 0, MCE_handle_M3_SETUP_RESPONSE, 0 } /* M3 Setup */
};
static char *m3ap_direction2String(int m3ap_dir) {
static char *m3ap_direction_String[] = {
"", /* Nothing */
"Originating message", /* originating message */
"Successfull outcome", /* successfull outcome */
"UnSuccessfull outcome", /* successfull outcome */
};
return(m3ap_direction_String[m3ap_dir]);
}
int m3ap_MCE_handle_message(instance_t instance, uint32_t assoc_id, int32_t stream,
const uint8_t * const data, const uint32_t data_length)
{
M3AP_M3AP_PDU_t pdu;
int ret;
DevAssert(data != NULL);
memset(&pdu, 0, sizeof(pdu));
if (m3ap_decode_pdu(&pdu, data, data_length) < 0) {
LOG_E(M3AP, "Failed to decode PDU\n");
return -1;
}
/* Checking procedure Code and direction of message */
if (pdu.choice.initiatingMessage.procedureCode > sizeof(m3ap_MCE_messages_callback) / (3 * sizeof(
m3ap_message_decoded_callback))
|| (pdu.present > M3AP_M3AP_PDU_PR_unsuccessfulOutcome)) {
LOG_E(M3AP, "[SCTP %d] Either procedureCode %ld or direction %d exceed expected\n",
assoc_id, pdu.choice.initiatingMessage.procedureCode, pdu.present);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_M3AP_M3AP_PDU, &pdu);
return -1;
}
/* No handler present.
* This can mean not implemented or no procedure for eNB (wrong direction).
*/
if (m3ap_MCE_messages_callback[pdu.choice.initiatingMessage.procedureCode][pdu.present - 1] == NULL) {
LOG_E(M3AP, "[SCTP %d] No handler for procedureCode %ld in %s\n",
assoc_id, pdu.choice.initiatingMessage.procedureCode,
m3ap_direction2String(pdu.present - 1));
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_M3AP_M3AP_PDU, &pdu);
return -1;
}
/* Calling the right handler */
LOG_I(M3AP, "Calling handler with instance %d\n",instance);
ret = (*m3ap_MCE_messages_callback[pdu.choice.initiatingMessage.procedureCode][pdu.present - 1])
(instance, assoc_id, stream, &pdu);
ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_M3AP_M3AP_PDU, &pdu);
return ret;
}
/*
* 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 m2ap_handler.h
* \brief m2ap handler procedures for eNB
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#ifndef M2AP_MCE_HANDLERS_H_
#define M2AP_MCE_HANDLERS_H_
#include "m2ap_MCE_defs.h"
//void m3ap_handle_m2_setup_message(m2ap_eNB_instance_t *instance_p, m2ap_eNB_data_t *eNB_desc_p, int sctp_shutdown);
//int m3ap_eNB_handle_message(instance_t instance, uint32_t assoc_id, int32_t stream,
//const uint8_t * const data, const uint32_t data_length);
int m3ap_MCE_handle_message(instance_t instance, uint32_t assoc_id, int32_t stream,
const uint8_t * const data, const uint32_t data_length);
#endif /* M2AP_MCE_HANDLERS_H_ */
This diff is collapsed.
/*
* 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 m3ap_MCE_interface_management.h
* \brief m3ap interface management for MCE
* \author Javier Morgade
* \date 2019
* \version 0.1
* \company Vicomtech
* \email: javier.morgade@ieee.org
* \note
* \warning
*/
#ifndef M3AP_MCE_INTERFACE_MANAGEMENT_H_
#define M3AP_MCE_INTERFACE_MANAGEMENT_H_
/*
* Session Start
*/
int MCE_handle_MBMS_SESSION_START_REQUEST(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
int MCE_send_MBMS_SESSION_START_RESPONSE(instance_t instance, m3ap_session_start_resp_t * m3ap_session_start_resp);
int MCE_send_MBMS_SESSION_START_FAILURE(instance_t instance, m3ap_session_start_failure_t * m3ap_session_start_failure);
/*
* Session Stop
*/
int MCE_handle_MBMS_SESSION_STOP_REQUEST(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
/*
* Session Update
*/
int MCE_handle_MBMS_SESSION_UPDATE_REQUEST(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
int MCE_send_MBMS_SESSION_UPDATE_RESPONSE(instance_t instance, m3ap_mbms_session_update_resp_t * m3ap_mbms_session_update_resp);
int MCE_send_MBMS_SESSION_UPDATE_FAILURE(instance_t instance, m3ap_mbms_session_update_failure_t * m3ap_mbms_session_update_failure);
int MCE_send_MBMS_SESSION_STOP_RESPONSE(instance_t instance, m3ap_session_start_resp_t * m3ap_session_start_resp);
/*
* Reset
*/
int MCE_handle_RESET(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
int MCE_send_RESET_ACKKNOWLEDGE(instance_t instance, M3AP_ResetAcknowledge_t *ResetAcknowledge);
int MCE_send_RESET(instance_t instance, M3AP_Reset_t *Reset);
int MCE_handle_RESET_ACKNOWLEDGE(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
/*
* M3AP Setup
*/
int MCE_send_M3_SETUP_REQUEST( m3ap_MCE_instance_t *instance_p, m3ap_MCE_data_t *m3ap_MCE_data_p);
int MCE_handle_M3_SETUP_RESPONSE(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
int MCE_handle_M3_SETUP_FAILURE(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
/*
* Error Indication
*/
int MCE_send_ERROR_INDICATION(instance_t instance, M3AP_M3AP_PDU_t *pdu_p);
int MCE_handle_ERROR_INDICATION(instance_t instance,
uint32_t assoc_id,
uint32_t stream,
M3AP_M3AP_PDU_t *pdu);
#endif /* M3AP_MCE_INTERFACE_MANAGEMENT_H_ */
/*
* 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 m3ap_MCE_management_procedures.c
* \brief m3ap tasks for MCE
* \author Javier Morgade <javier.morade@ieee.org>
* \date 2018
* \version 1.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "intertask_interface.h"
#include "assertions.h"
#include "conversions.h"
#include "m3ap_common.h"
#include "m3ap_MCE_defs.h"
#include "m3ap_MCE.h"
#define M3AP_DEBUG_LIST
#ifdef M3AP_DEBUG_LIST
# define M3AP_MCE_LIST_OUT(x, args...) M3AP_DEBUG("[MCE]%*s"x"\n", 4*indent, "", ##args)
#else
# define M3AP_MCE_LIST_OUT(x, args...)
#endif
static int indent = 0;
m3ap_MCE_internal_data_t m3ap_MCE_internal_data;
RB_GENERATE(m3ap_mce_map, m3ap_MCE_data_s, entry, m3ap_MCE_compare_assoc_id);
int m3ap_MCE_compare_assoc_id(
struct m3ap_MCE_data_s *p1, struct m3ap_MCE_data_s *p2)
{
if (p1->assoc_id == -1) {
if (p1->cnx_id < p2->cnx_id) {
return -1;
}
if (p1->cnx_id > p2->cnx_id) {
return 1;
}
} else {
if (p1->assoc_id < p2->assoc_id) {
return -1;
}
if (p1->assoc_id > p2->assoc_id) {
return 1;
}
}
/* Matching reference */
return 0;
}
uint16_t m3ap_MCE_fetch_add_global_cnx_id(void)
{
return ++m3ap_MCE_internal_data.global_cnx_id;
}
void m3ap_MCE_prepare_internal_data(void)
{
memset(&m3ap_MCE_internal_data, 0, sizeof(m3ap_MCE_internal_data));
STAILQ_INIT(&m3ap_MCE_internal_data.m3ap_MCE_instances_head);
}
void m3ap_MCE_insert_new_instance(m3ap_MCE_instance_t *new_instance_p)
{
DevAssert(new_instance_p != NULL);
STAILQ_INSERT_TAIL(&m3ap_MCE_internal_data.m3ap_MCE_instances_head,
new_instance_p, m3ap_MCE_entries);
}
void dump_tree_m3(m3ap_MCE_data_t *t)
{
if (t == NULL) return;
printf("-----------------------\n");
printf("MCE id %d %s\n", t->MCE_id, t->MCE_name);
printf("state %d\n", t->state);
printf("nextstream %d\n", t->nextstream);
printf("in_streams %d out_streams %d\n", t->in_streams, t->out_streams);
printf("cnx_id %d assoc_id %d\n", t->cnx_id, t->assoc_id);
dump_tree_m3(t->entry.rbe_left);
dump_tree_m3(t->entry.rbe_right);
}
void dump_trees_m3(void)
{
m3ap_MCE_instance_t *zz;
STAILQ_FOREACH(zz, &m3ap_MCE_internal_data.m3ap_MCE_instances_head,
m3ap_MCE_entries) {
printf("here comes the tree (instance %d):\n---------------------------------------------\n", zz->instance);
dump_tree_m3(zz->m3ap_mce_head.rbh_root);
printf("---------------------------------------------\n");
}
}
struct m3ap_MCE_data_s *m3ap_get_MCE(m3ap_MCE_instance_t *instance_p,
int32_t assoc_id,
uint16_t cnx_id)
{
struct m3ap_MCE_data_s temp;
struct m3ap_MCE_data_s *found;
printf("m3ap_get_MCE at 1 (looking for assoc_id %d cnx_id %d)\n", assoc_id, cnx_id);
dump_trees_m3();
memset(&temp, 0, sizeof(struct m3ap_MCE_data_s));
temp.assoc_id = assoc_id;
temp.cnx_id = cnx_id;
if (instance_p == NULL) {
STAILQ_FOREACH(instance_p, &m3ap_MCE_internal_data.m3ap_MCE_instances_head,
m3ap_MCE_entries) {
found = RB_FIND(m3ap_mce_map, &instance_p->m3ap_mce_head, &temp);
if (found != NULL) {
return found;
}
}
} else {
return RB_FIND(m3ap_mce_map, &instance_p->m3ap_mce_head, &temp);
}
return NULL;
}
m3ap_MCE_instance_t *m3ap_MCE_get_instance(instance_t instance)
{
m3ap_MCE_instance_t *temp = NULL;
STAILQ_FOREACH(temp, &m3ap_MCE_internal_data.m3ap_MCE_instances_head,
m3ap_MCE_entries) {
if (temp->instance == instance) {
/* Matching occurence */
return temp;
}
}
return NULL;
}
/// utility functions
void m3ap_dump_MCE (m3ap_MCE_data_t * MCE_ref);
void
m3ap_dump_MCE_list (void) {
m3ap_MCE_instance_t *inst = NULL;
struct m3ap_MCE_data_s *found = NULL;
struct m3ap_MCE_data_s temp;
memset(&temp, 0, sizeof(struct m3ap_MCE_data_s));
STAILQ_FOREACH (inst, &m3ap_MCE_internal_data.m3ap_MCE_instances_head, m3ap_MCE_entries) {
found = RB_FIND(m3ap_mce_map, &inst->m3ap_mce_head, &temp);
m3ap_dump_MCE (found);
}
}
void m3ap_dump_MCE (m3ap_MCE_data_t * MCE_ref) {
if (MCE_ref == NULL) {
return;
}
M3AP_MCE_LIST_OUT ("");
M3AP_MCE_LIST_OUT ("MCE name: %s", MCE_ref->MCE_name == NULL ? "not present" : MCE_ref->MCE_name);
M3AP_MCE_LIST_OUT ("MCE STATE: %07x", MCE_ref->state);
M3AP_MCE_LIST_OUT ("MCE ID: %07x", MCE_ref->MCE_id);
indent++;
M3AP_MCE_LIST_OUT ("SCTP cnx id: %d", MCE_ref->cnx_id);
M3AP_MCE_LIST_OUT ("SCTP assoc id: %d", MCE_ref->assoc_id);
M3AP_MCE_LIST_OUT ("SCTP instreams: %d", MCE_ref->in_streams);
M3AP_MCE_LIST_OUT ("SCTP outstreams: %d", MCE_ref->out_streams);
indent--;
}
m3ap_MCE_data_t * m3ap_is_MCE_pci_in_list (const uint32_t pci)
{
m3ap_MCE_instance_t *inst;
struct m3ap_MCE_data_s *elm;
STAILQ_FOREACH(inst, &m3ap_MCE_internal_data.m3ap_MCE_instances_head, m3ap_MCE_entries) {
RB_FOREACH(elm, m3ap_mce_map, &inst->m3ap_mce_head) {
for (int i = 0; i<elm->num_cc; i++) {
if (elm->Nid_cell[i] == pci) {
return elm;
}
}
}
}
return NULL;
}
m3ap_MCE_data_t * m3ap_is_MCE_id_in_list (const uint32_t MCE_id)
{
m3ap_MCE_instance_t *inst;
struct m3ap_MCE_data_s *elm;
STAILQ_FOREACH(inst, &m3ap_MCE_internal_data.m3ap_MCE_instances_head, m3ap_MCE_entries) {
RB_FOREACH(elm, m3ap_mce_map, &inst->m3ap_mce_head) {
if (elm->MCE_id == MCE_id)
return elm;
}
}
return NULL;
}
m3ap_MCE_data_t * m3ap_is_MCE_assoc_id_in_list (const uint32_t sctp_assoc_id)
{
m3ap_MCE_instance_t *inst;
struct m3ap_MCE_data_s *found;
struct m3ap_MCE_data_s temp;
temp.assoc_id = sctp_assoc_id;
temp.cnx_id = -1;
STAILQ_FOREACH(inst, &m3ap_MCE_internal_data.m3ap_MCE_instances_head, m3ap_MCE_entries) {
found = RB_FIND(m3ap_mce_map, &inst->m3ap_mce_head, &temp);
if (found != NULL){
if (found->assoc_id == sctp_assoc_id) {
return found;
}
}
}
return NULL;
}
/*
* 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 m3ap_MCE_management_procedures.h
* \brief m3ap tasks for MCE
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#ifndef M3AP_MCE_MANAGEMENT_PROCEDURES_H_
#define M3AP_MCE_MANAGEMENT_PROCEDURES_H
void m3ap_MCE_prepare_internal_data(void);
void dump_trees_m3(void);
void m3ap_MCE_insert_new_instance(m3ap_MCE_instance_t *new_instance_p);
m3ap_MCE_instance_t *m3ap_MCE_get_instance(uint8_t mod_id);
uint16_t m3ap_MCE_fetch_add_global_cnx_id(void);
m3ap_MCE_data_t* m3ap_is_MCE_id_in_list(uint32_t MCE_id);
m3ap_MCE_data_t* m3ap_is_MCE_assoc_id_in_list(uint32_t sctp_assoc_id);
m3ap_MCE_data_t* m3ap_is_MCE_pci_in_list (const uint32_t pci);
struct m3ap_MCE_data_s *m3ap_get_MCE(m3ap_MCE_instance_t *instance_p,
int32_t assoc_id,
uint16_t cnx_id);
#endif /* M3AP_MCE_MANAGEMENT_PROCEDURES_H_ */
This diff is collapsed.
/*
* 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 m3ap_MME.h
* \brief m3ap tasks for MME
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#include <stdio.h>
#include <stdint.h>
/** @defgroup _m3ap_impl_ M3AP Layer Reference Implementation
* @ingroup _ref_implementation_
* @{
*/
#ifndef M3AP_MME_H_
#define M3AP_MME_H_
#include "m3ap_MME_defs.h"
int m3ap_MME_init_sctp (m3ap_MME_instance_t *instance_p,
net_ip_address_t *local_ip_addr,
uint32_t enb_port_for_M3C);
void *m3ap_MME_task(void *arg);
int is_m3ap_MME_enabled(void);
#endif /* M3AP_MCE_H_ */
/**
* @}
*/
/*
* 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 m3ap_MME_defs.h
* \brief m2ap struct definitions for MME
* \author Javier Morgade <javier.morgade@ieee.org>
* \date 2019
* \version 0.1
*/
#include <stdint.h>
#include "queue.h"
#include "tree.h"
#include "sctp_eNB_defs.h"
#include "m3ap_ids.h" //looks X2AP specific for HO
#include "m3ap_timers.h"
#ifndef M3AP_MME_DEFS_H_
#define M3AP_MME_DEFS_H_
#define M3AP_MME_NAME_LENGTH_MAX (150)
typedef enum {
/* Disconnected state: initial state for any association. */
M3AP_MME_STATE_DISCONNECTED = 0x0,
/* State waiting for m2 Setup response message if the target MME accepts or
* M2 Setup failure if rejects the MME.
*/
M3AP_MME_STATE_WAITING = 0x1,
/* The MME is successfully connected to another MME. */
M3AP_MME_STATE_CONNECTED = 0x2,
/* M3AP is ready, and the MME is successfully connected to another MME. */
M3AP_MME_STATE_READY = 0x3,
M3AP_MME_STATE_OVERLOAD = 0x4,
M3AP_MME_STATE_RESETTING = 0x5,
/* Max number of states available */
M3AP_MME_STATE_MAX,
} m3ap_MME_state_t;
/* Served PLMN identity element */
/*struct plmn_identity_s {
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
STAILQ_ENTRY(plmn_identity_s) next;
};*/
/* Served group id element */
/*struct served_group_id_s {
uint16_t mce_group_id;
STAILQ_ENTRY(served_group_id_s) next;
};*/
/* Served enn code for a particular MME */
/*struct mce_code_s {
uint8_t mce_code;
STAILQ_ENTRY(mce_code_s) next;
};*/
struct m3ap_MME_instance_s;
/* This structure describes association of a MME to another MME */
typedef struct m3ap_MME_data_s {
/* MME descriptors tree, ordered by sctp assoc id */
RB_ENTRY(m3ap_MME_data_s) entry;
/* This is the optional name provided by the MME */
char *MME_name;
/* target MME ID */
uint32_t MME_id;
/* Current MME load information (if any). */
//m3ap_load_state_t overload_state;
/* Current MME->MME M3AP association state */
m3ap_MME_state_t state;
/* Next usable stream for UE signalling */
int32_t nextstream;
/* Number of input/ouput streams */
uint16_t in_streams;
uint16_t out_streams;
/* Connexion id used between SCTP/M3AP */
uint16_t cnx_id;
/* SCTP association id */
int32_t assoc_id;
/* Nid cells */
uint32_t Nid_cell[MAX_NUM_CCs];
int num_cc;
/* Only meaningfull in virtual mode */
struct m3ap_MME_instance_s *m3ap_MME_instance;
} m3ap_MME_data_t;
typedef struct m3ap_MME_instance_s {
/* used in simulation to store multiple MME instances*/
STAILQ_ENTRY(m3ap_MME_instance_s) m3ap_MME_entries;
/* Number of target MMEs requested by MME (tree size) */
uint32_t m2_target_mce_nb;
/* Number of target MMEs for which association is pending */
uint32_t m2_target_mce_pending_nb;
/* Number of target MME successfully associated to MME */
uint32_t m2_target_mce_associated_nb;
/* Tree of M3AP MME associations ordered by association ID */
RB_HEAD(m3ap_mme_map, m3ap_MME_data_s) m3ap_mme_head;
/* Tree of UE ordered by MME_ue_m3ap_id's */
// RB_HEAD(m3ap_ue_map, m3ap_MME_ue_context_s) m3ap_ue_head;
/* For virtual mode, mod_id as defined in the rest of the L1/L2 stack */
instance_t instance;
/* Displayable name of MME */
char *MME_name;
/* Unique MME_id to identify the MME within EPC.
* In our case the MME is a macro MME so the id will be 20 bits long.
* For Home MME id, this field should be 28 bits long.
*/
uint32_t MME_id;
/* The type of the cell */
cell_type_t cell_type;
/* Tracking area code */
uint16_t tac;
/* Mobile Country Code
* Mobile Network Code
*/
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
/* CC params */
int16_t eutra_band[MAX_NUM_CCs];
uint32_t downlink_frequency[MAX_NUM_CCs];
int32_t uplink_frequency_offset[MAX_NUM_CCs];
uint32_t Nid_cell[MAX_NUM_CCs];
int16_t N_RB_DL[MAX_NUM_CCs];
lte_frame_type_t frame_type[MAX_NUM_CCs];
uint32_t fdd_earfcn_DL[MAX_NUM_CCs];
uint32_t fdd_earfcn_UL[MAX_NUM_CCs];
int num_cc;
net_ip_address_t target_mme_m3_ip_address[M3AP_MAX_NB_MME_IP_ADDRESS];
uint8_t nb_m3;
net_ip_address_t mme_m3_ip_address;
uint16_t sctp_in_streams;
uint16_t sctp_out_streams;
uint32_t mme_port_for_M3C;
int multi_sd;
m3ap_id_manager id_manager;
m3ap_timers_t timers;
} m3ap_MME_instance_t;
typedef struct {
/* List of served MMEs
* Only used for virtual mode
*/
STAILQ_HEAD(m3ap_MME_instances_head_s, m3ap_MME_instance_s) m3ap_MME_instances_head;
/* Nb of registered MMEs */
uint8_t nb_registered_MMEs;
/* Generate a unique connexion id used between M3AP and SCTP */
uint16_t global_cnx_id;
} m3ap_MME_internal_data_t;
int m3ap_MME_compare_assoc_id(struct m3ap_MME_data_s *p1, struct m3ap_MME_data_s *p2);
/* Generate the tree management functions */
struct m3ap_MME_map;
struct m3ap_MME_data_s;
RB_PROTOTYPE(m3ap_MME_map, m3ap_MME_data_s, entry, m3ap_MME_compare_assoc_id);
#endif /* M3AP_MME_DEFS_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment