670 lines
26 KiB
C
670 lines
26 KiB
C
/*******************************************************************************
|
|
OpenAirInterface
|
|
Copyright(c) 1999 - 2014 Eurecom
|
|
|
|
OpenAirInterface is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
|
|
OpenAirInterface is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with OpenAirInterface.The full GNU General Public License is
|
|
included in this distribution in the file called "COPYING". If not,
|
|
see <http://www.gnu.org/licenses/>.
|
|
|
|
Contact Information
|
|
OpenAirInterface Admin: openair_admin@eurecom.fr
|
|
OpenAirInterface Tech : openair_tech@eurecom.fr
|
|
OpenAirInterface Dev : openair4g-devel@lists.eurecom.fr
|
|
|
|
Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
|
|
|
|
*******************************************************************************/
|
|
|
|
/*
|
|
play_scenario.c
|
|
-------------------
|
|
AUTHOR : Lionel GAUTHIER
|
|
COMPANY : EURECOM
|
|
EMAIL : Lionel.Gauthier@eurecom.fr
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <libconfig.h>
|
|
#include <inttypes.h>
|
|
#include <getopt.h>
|
|
#include <libgen.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
|
|
#include "intertask_interface_init.h"
|
|
#include "timer.h"
|
|
#include "assertions.h"
|
|
#include "s1ap_common.h"
|
|
#include "intertask_interface.h"
|
|
#include "play_scenario.h"
|
|
#include "sctp_eNB_task.h"
|
|
#include "log.h"
|
|
//------------------------------------------------------------------------------
|
|
#define PLAY_SCENARIO 1
|
|
#define GS_IS_FILE 1
|
|
#define GS_IS_DIR 2
|
|
//------------------------------------------------------------------------------
|
|
char *g_openair_dir = NULL;
|
|
//------------------------------------------------------------------------------
|
|
extern int xmlLoadExtDtdDefaultValue;
|
|
extern int asn_debug;
|
|
extern int asn1_xer_print;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
// test if file exist in current directory
|
|
int is_file_exists( const char const * file_nameP, const char const *file_roleP)
|
|
{
|
|
struct stat s;
|
|
int err = stat(file_nameP, &s);
|
|
if(-1 == err) {
|
|
if(ENOENT == errno) {
|
|
fprintf(stderr, "Please provide a valid %s, %s does not exist\n", file_roleP, file_nameP);
|
|
} else {
|
|
perror("stat");
|
|
exit(1);
|
|
}
|
|
} else {
|
|
if(S_ISREG(s.st_mode)) {
|
|
return GS_IS_FILE;
|
|
} else if(S_ISDIR(s.st_mode)) {
|
|
return GS_IS_DIR;
|
|
} else {
|
|
fprintf(stderr, "Please provide a valid test %s, %s exists but is not found valid\n", file_roleP, file_nameP);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
int et_strip_extension(char *in_filename)
|
|
{
|
|
static const uint8_t name_min_len = 1;
|
|
static const uint8_t max_ext_len = 5; // .pdml !
|
|
fprintf(stdout, "strip_extension %s\n", in_filename);
|
|
|
|
if (NULL != in_filename) {
|
|
/* Check chars starting at end of string to find last '.' */
|
|
for (ssize_t i = strlen(in_filename); i > (name_min_len + max_ext_len); i--) {
|
|
if (in_filename[i] == '.') {
|
|
in_filename[i] = '\0';
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
// return number of splitted items
|
|
int split_path( char * pathP, char *** resP)
|
|
{
|
|
char * saveptr1;
|
|
char * p = strtok_r (pathP, "/", &saveptr1);
|
|
int n_spaces = 0;
|
|
|
|
/// split string and append tokens to 'res'
|
|
while (p) {
|
|
*resP = realloc (*resP, sizeof (char*) * ++n_spaces);
|
|
AssertFatal (*resP, "realloc failed");
|
|
(*resP)[n_spaces-1] = p;
|
|
p = strtok_r (NULL, "/", &saveptr1);
|
|
}
|
|
return n_spaces;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void et_free_packet(et_packet_t* packet)
|
|
{
|
|
if (packet) {
|
|
switch (packet->sctp_hdr.chunk_type) {
|
|
case SCTP_CID_DATA:
|
|
et_free_pointer(packet->sctp_hdr.u.data_hdr.payload.binary_stream);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
et_free_pointer(packet);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void et_free_scenario(et_scenario_t* scenario)
|
|
{
|
|
et_packet_t *packet = NULL;
|
|
et_packet_t *next_packet = NULL;
|
|
if (scenario) {
|
|
packet = scenario->list_packet;
|
|
while (packet) {
|
|
next_packet = packet->next;
|
|
et_free_packet(packet);
|
|
packet = next_packet->next;
|
|
}
|
|
et_free_pointer(scenario);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
char * et_ip2ip_str(const et_ip_t * const ip)
|
|
{
|
|
static char str[INET6_ADDRSTRLEN];
|
|
|
|
sprintf(str, "ERROR");
|
|
switch (ip->address_family) {
|
|
case AF_INET6:
|
|
inet_ntop(AF_INET6, &(ip->address.ipv6), str, INET6_ADDRSTRLEN);
|
|
break;
|
|
case AF_INET:
|
|
inet_ntop(AF_INET, &(ip->address.ipv4), str, INET_ADDRSTRLEN);
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
return str;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
//convert hexstring to len bytes of data
|
|
//returns 0 on success, negative on error
|
|
//data is a buffer of at least len bytes
|
|
//hexstring is upper or lower case hexadecimal, NOT prepended with "0x"
|
|
int et_hex2data(unsigned char * const data, const unsigned char * const hexstring, const unsigned int len)
|
|
{
|
|
unsigned const char *pos = hexstring;
|
|
char *endptr = NULL;
|
|
size_t count = 0;
|
|
|
|
fprintf(stdout, "%s(%s,%d)\n", __FUNCTION__, hexstring, len);
|
|
|
|
if ((len > 1) && (strlen((const char*)hexstring) % 2)) {
|
|
//or hexstring has an odd length
|
|
return -3;
|
|
}
|
|
|
|
if (len == 1) {
|
|
char buf[5] = {'0', 'x', 0, pos[0], '\0'};
|
|
data[0] = strtol(buf, &endptr, 16);
|
|
/* Check for various possible errors */
|
|
AssertFatal ((errno == 0) || (data[0] != 0), "ERROR %s() strtol: %s\n", __FUNCTION__, strerror(errno));
|
|
AssertFatal (endptr != buf, "ERROR %s() No digits were found\n", __FUNCTION__);
|
|
return 0;
|
|
}
|
|
|
|
for(count = 0; count < len/2; count++) {
|
|
char buf[5] = {'0', 'x', pos[0], pos[1], 0};
|
|
data[count] = strtol(buf, &endptr, 16);
|
|
pos += 2 * sizeof(char);
|
|
AssertFatal (endptr[0] == '\0', "ERROR %s() non-hexadecimal character encountered buf %p endptr %p buf %s count %zu pos %p\n", __FUNCTION__, buf, endptr, buf, count, pos);
|
|
AssertFatal (endptr != buf, "ERROR %s() No digits were found\n", __FUNCTION__);
|
|
}
|
|
return 0;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
sctp_cid_t et_chunk_type_str2cid(const xmlChar * const chunk_type_str)
|
|
{
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"DATA"))) { return SCTP_CID_DATA;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"INIT"))) { return SCTP_CID_INIT;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"INIT_ACK"))) { return SCTP_CID_INIT_ACK;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"SACK"))) { return SCTP_CID_SACK;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"HEARTBEAT"))) { return SCTP_CID_HEARTBEAT;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"HEARTBEAT_ACK"))) { return SCTP_CID_HEARTBEAT_ACK;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ABORT"))) { return SCTP_CID_ABORT;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"SHUTDOWN"))) { return SCTP_CID_SHUTDOWN;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"SHUTDOWN_ACK"))) { return SCTP_CID_SHUTDOWN_ACK;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ERROR"))) { return SCTP_CID_ERROR;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"COOKIE_ECHO"))) { return SCTP_CID_COOKIE_ECHO;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"COOKIE_ACK"))) { return SCTP_CID_COOKIE_ACK;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ECN_ECNE"))) { return SCTP_CID_ECN_ECNE;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ECN_CWR"))) { return SCTP_CID_ECN_CWR;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"SHUTDOWN_COMPLETE"))) { return SCTP_CID_SHUTDOWN_COMPLETE;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"AUTH"))) { return SCTP_CID_AUTH;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"FWD_TSN"))) { return SCTP_CID_FWD_TSN;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ASCONF"))) { return SCTP_CID_ASCONF;}
|
|
if ((!xmlStrcmp(chunk_type_str, (const xmlChar *)"ASCONF_ACK"))) { return SCTP_CID_ASCONF_ACK;}
|
|
AssertFatal (0, "ERROR: %s() cannot convert: %s\n", __FUNCTION__, chunk_type_str);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
const char * const et_chunk_type_cid2str(const sctp_cid_t chunk_type)
|
|
{
|
|
switch (chunk_type) {
|
|
case SCTP_CID_DATA: return "DATA"; break;
|
|
case SCTP_CID_INIT: return "INIT"; break;
|
|
case SCTP_CID_INIT_ACK: return "INIT_ACK"; break;
|
|
case SCTP_CID_SACK: return "SACK"; break;
|
|
case SCTP_CID_HEARTBEAT: return "HEARTBEAT"; break;
|
|
case SCTP_CID_HEARTBEAT_ACK: return "HEARTBEAT_ACK"; break;
|
|
case SCTP_CID_ABORT: return "ABORT"; break;
|
|
case SCTP_CID_SHUTDOWN: return "SHUTDOWN"; break;
|
|
case SCTP_CID_SHUTDOWN_ACK: return "SHUTDOWN_ACK"; break;
|
|
case SCTP_CID_ERROR: return "ERROR"; break;
|
|
case SCTP_CID_COOKIE_ECHO: return "COOKIE_ECHO"; break;
|
|
case SCTP_CID_COOKIE_ACK: return "COOKIE_ACK"; break;
|
|
case SCTP_CID_ECN_ECNE: return "ECN_ECNE"; break;
|
|
case SCTP_CID_ECN_CWR: return "ECN_CWR"; break;
|
|
case SCTP_CID_SHUTDOWN_COMPLETE: return "SHUTDOWN_COMPLETE"; break;
|
|
case SCTP_CID_AUTH: return "AUTH"; break;
|
|
case SCTP_CID_FWD_TSN: return "FWD_TSN"; break;
|
|
case SCTP_CID_ASCONF: return "ASCONF"; break;
|
|
case SCTP_CID_ASCONF_ACK: return "ASCONF_ACK"; break;
|
|
default:
|
|
AssertFatal (0, "ERROR %s(): Unknown chunk_type %d!\n", __FUNCTION__, chunk_type);
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
et_packet_action_t et_action_str2et_action_t(const xmlChar * const action)
|
|
{
|
|
if ((!xmlStrcmp(action, (const xmlChar *)"SEND"))) { return ET_PACKET_ACTION_S1C_SEND;}
|
|
if ((!xmlStrcmp(action, (const xmlChar *)"RECEIVE"))) { return ET_PACKET_ACTION_S1C_RECEIVE;}
|
|
AssertFatal (0, "ERROR: %s cannot convert: %s\n", __FUNCTION__, action);
|
|
//if (NULL == action) {return ACTION_S1C_NULL;}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
void et_ip_str2et_ip(const xmlChar * const ip_str, et_ip_t * const ip)
|
|
{
|
|
AssertFatal (NULL != ip_str, "ERROR %s() Cannot convert null string to ip address!\n", __FUNCTION__);
|
|
AssertFatal (NULL != ip, "ERROR %s() out parameter pointer is NULL!\n", __FUNCTION__);
|
|
// store this IP address in sa:
|
|
if (inet_pton(AF_INET, (const char*)ip_str, (void*)&(ip->address.ipv4)) > 0) {
|
|
ip->address_family = AF_INET;
|
|
} else if (inet_pton(AF_INET6, (const char*)ip_str, (void*)&(ip->address.ipv6)) > 0) {
|
|
ip->address_family = AF_INET6;
|
|
} else {
|
|
ip->address_family = AF_UNSPEC;
|
|
AssertFatal (0, "ERROR %s() Could not parse ip address %s!\n", __FUNCTION__, ip_str);
|
|
}
|
|
}
|
|
/*------------------------------------------------------------------------------*/
|
|
uint32_t et_eNB_app_register(const Enb_properties_array_t *enb_properties)
|
|
{
|
|
uint32_t enb_id;
|
|
uint32_t mme_id;
|
|
MessageDef *msg_p;
|
|
uint32_t register_enb_pending = 0;
|
|
char *str = NULL;
|
|
struct in_addr addr;
|
|
|
|
|
|
for (enb_id = 0; (enb_id < enb_properties->number) ; enb_id++) {
|
|
{
|
|
s1ap_register_enb_req_t *s1ap_register_eNB;
|
|
|
|
/* note: there is an implicit relationship between the data structure and the message name */
|
|
msg_p = itti_alloc_new_message (TASK_ENB_APP, S1AP_REGISTER_ENB_REQ);
|
|
|
|
s1ap_register_eNB = &S1AP_REGISTER_ENB_REQ(msg_p);
|
|
|
|
/* Some default/random parameters */
|
|
s1ap_register_eNB->eNB_id = enb_properties->properties[enb_id]->eNB_id;
|
|
s1ap_register_eNB->cell_type = enb_properties->properties[enb_id]->cell_type;
|
|
s1ap_register_eNB->eNB_name = enb_properties->properties[enb_id]->eNB_name;
|
|
s1ap_register_eNB->tac = enb_properties->properties[enb_id]->tac;
|
|
s1ap_register_eNB->mcc = enb_properties->properties[enb_id]->mcc;
|
|
s1ap_register_eNB->mnc = enb_properties->properties[enb_id]->mnc;
|
|
s1ap_register_eNB->mnc_digit_length = enb_properties->properties[enb_id]->mnc_digit_length;
|
|
s1ap_register_eNB->default_drx = enb_properties->properties[enb_id]->pcch_defaultPagingCycle[0];
|
|
|
|
s1ap_register_eNB->nb_mme = enb_properties->properties[enb_id]->nb_mme;
|
|
AssertFatal (s1ap_register_eNB->nb_mme <= S1AP_MAX_NB_MME_IP_ADDRESS, "Too many MME for eNB %d (%d/%d)!", enb_id, s1ap_register_eNB->nb_mme,
|
|
S1AP_MAX_NB_MME_IP_ADDRESS);
|
|
|
|
for (mme_id = 0; mme_id < s1ap_register_eNB->nb_mme; mme_id++) {
|
|
s1ap_register_eNB->mme_ip_address[mme_id].ipv4 = enb_properties->properties[enb_id]->mme_ip_address[mme_id].ipv4;
|
|
s1ap_register_eNB->mme_ip_address[mme_id].ipv6 = enb_properties->properties[enb_id]->mme_ip_address[mme_id].ipv6;
|
|
strncpy (s1ap_register_eNB->mme_ip_address[mme_id].ipv4_address,
|
|
enb_properties->properties[enb_id]->mme_ip_address[mme_id].ipv4_address,
|
|
sizeof(s1ap_register_eNB->mme_ip_address[0].ipv4_address));
|
|
strncpy (s1ap_register_eNB->mme_ip_address[mme_id].ipv6_address,
|
|
enb_properties->properties[enb_id]->mme_ip_address[mme_id].ipv6_address,
|
|
sizeof(s1ap_register_eNB->mme_ip_address[0].ipv6_address));
|
|
}
|
|
|
|
s1ap_register_eNB->sctp_in_streams = enb_properties->properties[enb_id]->sctp_in_streams;
|
|
s1ap_register_eNB->sctp_out_streams = enb_properties->properties[enb_id]->sctp_out_streams;
|
|
|
|
|
|
s1ap_register_eNB->enb_ip_address.ipv6 = 0;
|
|
s1ap_register_eNB->enb_ip_address.ipv4 = 1;
|
|
addr.s_addr = enb_properties->properties[enb_id]->enb_ipv4_address_for_S1_MME;
|
|
str = inet_ntoa(addr);
|
|
strcpy(s1ap_register_eNB->enb_ip_address.ipv4_address, str);
|
|
|
|
itti_send_msg_to_task (TASK_S1AP, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);
|
|
|
|
register_enb_pending++;
|
|
}
|
|
}
|
|
|
|
return register_enb_pending;
|
|
}
|
|
/*------------------------------------------------------------------------------*/
|
|
void *et_eNB_app_task(void *args_p)
|
|
{
|
|
const Enb_properties_array_t *enb_properties_p = NULL;
|
|
uint32_t register_enb_pending;
|
|
uint32_t registered_enb;
|
|
long enb_register_retry_timer_id;
|
|
uint32_t enb_id;
|
|
MessageDef *msg_p = NULL;
|
|
const char *msg_name = NULL;
|
|
instance_t instance;
|
|
int result;
|
|
|
|
itti_mark_task_ready (TASK_ENB_APP);
|
|
|
|
|
|
enb_properties_p = enb_config_get();
|
|
|
|
|
|
/* Try to register each eNB */
|
|
registered_enb = 0;
|
|
register_enb_pending = et_eNB_app_register (enb_properties_p);
|
|
|
|
|
|
do {
|
|
// Wait for a message
|
|
itti_receive_msg (TASK_ENB_APP, &msg_p);
|
|
|
|
msg_name = ITTI_MSG_NAME (msg_p);
|
|
instance = ITTI_MSG_INSTANCE (msg_p);
|
|
|
|
switch (ITTI_MSG_ID(msg_p)) {
|
|
case TERMINATE_MESSAGE:
|
|
itti_exit_task ();
|
|
break;
|
|
|
|
|
|
|
|
case S1AP_REGISTER_ENB_CNF:
|
|
LOG_I(ENB_APP, "[eNB %d] Received %s: associated MME %d\n", instance, msg_name,
|
|
S1AP_REGISTER_ENB_CNF(msg_p).nb_mme);
|
|
|
|
DevAssert(register_enb_pending > 0);
|
|
register_enb_pending--;
|
|
|
|
/* Check if at least eNB is registered with one MME */
|
|
if (S1AP_REGISTER_ENB_CNF(msg_p).nb_mme > 0) {
|
|
registered_enb++;
|
|
}
|
|
|
|
/* Check if all register eNB requests have been processed */
|
|
if (register_enb_pending == 0) {
|
|
if (registered_enb == enb_properties_p->number) {
|
|
/* If all eNB are registered, start scenario */
|
|
|
|
} else {
|
|
uint32_t not_associated = enb_properties_p->number - registered_enb;
|
|
|
|
LOG_W(ENB_APP, " %d eNB %s not associated with a MME, retrying registration in %d seconds ...\n",
|
|
not_associated, not_associated > 1 ? "are" : "is", ET_ENB_REGISTER_RETRY_DELAY);
|
|
|
|
/* Restart the eNB registration process in ENB_REGISTER_RETRY_DELAY seconds */
|
|
if (timer_setup (ET_ENB_REGISTER_RETRY_DELAY, 0, TASK_ENB_APP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
|
|
NULL, &enb_register_retry_timer_id) < 0) {
|
|
LOG_E(ENB_APP, " Can not start eNB register retry timer, use \"sleep\" instead!\n");
|
|
|
|
sleep(ET_ENB_REGISTER_RETRY_DELAY);
|
|
/* Restart the registration process */
|
|
registered_enb = 0;
|
|
register_enb_pending = et_eNB_app_register (enb_properties_p);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case S1AP_DEREGISTERED_ENB_IND:
|
|
LOG_W(ENB_APP, "[eNB %d] Received %s: associated MME %d\n", instance, msg_name,
|
|
S1AP_DEREGISTERED_ENB_IND(msg_p).nb_mme);
|
|
|
|
/* TODO handle recovering of registration */
|
|
break;
|
|
|
|
case TIMER_HAS_EXPIRED:
|
|
LOG_I(ENB_APP, " Received %s: timer_id %d\n", msg_name, TIMER_HAS_EXPIRED(msg_p).timer_id);
|
|
|
|
if (TIMER_HAS_EXPIRED (msg_p).timer_id == enb_register_retry_timer_id) {
|
|
/* Restart the registration process */
|
|
registered_enb = 0;
|
|
register_enb_pending = et_eNB_app_register (enb_properties_p);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
LOG_E(ENB_APP, "Received unexpected message %s\n", msg_name);
|
|
break;
|
|
}
|
|
|
|
result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
|
|
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
|
|
} while (1);
|
|
return NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int et_play_scenario(et_scenario_t* const scenario)
|
|
{
|
|
et_event_t event;
|
|
et_display_scenario(scenario);
|
|
|
|
// create SCTP ITTI task: same as eNB code
|
|
if (itti_create_task (TASK_SCTP, sctp_eNB_task, NULL) < 0) {
|
|
LOG_E(SCTP, "Create task for SCTP failed\n");
|
|
return -1;
|
|
}
|
|
|
|
// create S1AP ITTI task: not as same as eNB code
|
|
if (itti_create_task (TASK_S1AP, et_s1ap_eNB_task, NULL) < 0) {
|
|
LOG_E(S1AP, "Create task for S1AP failed\n");
|
|
return -1;
|
|
}
|
|
|
|
// create ENB_APP ITTI task: not as same as eNB code
|
|
if (itti_create_task (TASK_ENB_APP, et_eNB_app_task, NULL) < 0) {
|
|
LOG_E(ENB_APP, "Create task for ENB_APP failed\n");
|
|
return -1;
|
|
}
|
|
|
|
event.code = ET_EVENT_INIT;
|
|
event.u.init.scenario = scenario;
|
|
et_scenario_fsm_notify_event(event);
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
static void et_usage (
|
|
int argc,
|
|
char *argv[])
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
fprintf (stdout, "Please report any bug to: %s\n",PACKAGE_BUGREPORT);
|
|
fprintf (stdout, "Usage: %s [options]\n\n", argv[0]);
|
|
fprintf (stdout, "\n");
|
|
fprintf (stdout, "Client options:\n");
|
|
fprintf (stdout, "\t-S | --server <server network @> File name (with no path) of a test scenario that has to be replayed (TODO in future?)\n");
|
|
fprintf (stdout, "Server options:\n");
|
|
fprintf (stdout, "\t-d | --test-dir <dir> Directory where a set of files related to a particular test are located\n");
|
|
fprintf (stdout, "\t-c | --enb-conf-file <file> Provide an eNB config file, valid for the testbed\n");
|
|
fprintf (stdout, "\t-s | --scenario <file> File name (with no path) of a test scenario that has to be replayed ()\n");
|
|
fprintf (stdout, "\n");
|
|
fprintf (stdout, "Other options:\n");
|
|
fprintf (stdout, "\t-h | --help Print this help and return\n");
|
|
fprintf (stdout, "\t-v | --version Print informations about the version of this executable\n");
|
|
fprintf (stdout, "\n");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int
|
|
et_config_parse_opt_line (
|
|
int argc,
|
|
char *argv[],
|
|
char **et_dir_name,
|
|
char **scenario_file_name,
|
|
char **enb_config_file_name)
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
int option;
|
|
int rv = 0;
|
|
const Enb_properties_array_t *enb_properties_p = NULL;
|
|
|
|
enum long_option_e {
|
|
LONG_OPTION_START = 0x100, /* Start after regular single char options */
|
|
LONG_OPTION_ENB_CONF_FILE,
|
|
LONG_OPTION_SCENARIO_FILE,
|
|
LONG_OPTION_TEST_DIR,
|
|
LONG_OPTION_HELP,
|
|
LONG_OPTION_VERSION
|
|
};
|
|
|
|
static struct option long_options[] = {
|
|
{"enb-conf-file", required_argument, 0, LONG_OPTION_ENB_CONF_FILE},
|
|
{"scenario ", required_argument, 0, LONG_OPTION_SCENARIO_FILE},
|
|
{"test-dir", required_argument, 0, LONG_OPTION_TEST_DIR},
|
|
{"help", no_argument, 0, LONG_OPTION_HELP},
|
|
{"version", no_argument, 0, LONG_OPTION_VERSION},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
/*
|
|
* Parsing command line
|
|
*/
|
|
while ((option = getopt_long (argc, argv, "vhc:s:d:", long_options, NULL)) != -1) {
|
|
switch (option) {
|
|
case LONG_OPTION_ENB_CONF_FILE:
|
|
case 'c':
|
|
if (optarg) {
|
|
*enb_config_file_name = strdup(optarg);
|
|
printf("eNB config file name is %s\n", *enb_config_file_name);
|
|
rv |= PLAY_SCENARIO;
|
|
}
|
|
break;
|
|
|
|
case LONG_OPTION_SCENARIO_FILE:
|
|
case 's':
|
|
if (optarg) {
|
|
*scenario_file_name = strdup(optarg);
|
|
printf("Scenario file name is %s\n", *scenario_file_name);
|
|
rv |= PLAY_SCENARIO;
|
|
}
|
|
break;
|
|
|
|
case LONG_OPTION_TEST_DIR:
|
|
case 'd':
|
|
if (optarg) {
|
|
*et_dir_name = strdup(optarg);
|
|
if (is_file_exists(*et_dir_name, "test dirname") != GS_IS_DIR) {
|
|
fprintf(stderr, "Please provide a valid test dirname, %s is not a valid directory name\n", *et_dir_name);
|
|
exit(1);
|
|
}
|
|
printf("Test dir name is %s\n", *et_dir_name);
|
|
}
|
|
break;
|
|
|
|
case LONG_OPTION_VERSION:
|
|
case 'v':
|
|
printf("Version %s\n", PACKAGE_VERSION);
|
|
exit (0);
|
|
break;
|
|
|
|
case LONG_OPTION_HELP:
|
|
case 'h':
|
|
default:
|
|
et_usage (argc, argv);
|
|
exit (0);
|
|
}
|
|
}
|
|
if (NULL == *et_dir_name) {
|
|
fprintf(stderr, "Please provide a valid test dirname\n");
|
|
exit(1);
|
|
}
|
|
if (chdir(*et_dir_name) != 0) {
|
|
fprintf(stderr, "ERROR: chdir %s returned %s\n", *et_dir_name, strerror(errno));
|
|
exit(1);
|
|
}
|
|
if (rv & PLAY_SCENARIO) {
|
|
if (NULL == *enb_config_file_name) {
|
|
fprintf(stderr, "ERROR: please provide the original eNB config file name that should be in %s\n", *et_dir_name);
|
|
}
|
|
if (is_file_exists(*enb_config_file_name, "eNB config file") != GS_IS_FILE) {
|
|
fprintf(stderr, "ERROR: original eNB config file name %s is not found in dir %s\n", *enb_config_file_name, *et_dir_name);
|
|
}
|
|
enb_properties_p = enb_config_init(*enb_config_file_name);
|
|
|
|
if (NULL == *scenario_file_name) {
|
|
fprintf(stderr, "ERROR: please provide the scenario file name that should be in %s\n", *et_dir_name);
|
|
}
|
|
if (is_file_exists(*scenario_file_name, "Scenario file") != GS_IS_FILE) {
|
|
fprintf(stderr, "ERROR: Scenario file name %s is not found in dir %s\n", *scenario_file_name, *et_dir_name);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int main( int argc, char **argv )
|
|
//------------------------------------------------------------------------------
|
|
{
|
|
int actions = 0;
|
|
char *et_dir_name = NULL;
|
|
char *scenario_file_name = NULL;
|
|
char *enb_config_file_name = NULL;
|
|
int ret = 0;
|
|
et_scenario_t *scenario = NULL;
|
|
char play_scenario_filename[NAME_MAX];
|
|
|
|
memset(play_scenario_filename, 0, sizeof(play_scenario_filename));
|
|
g_openair_dir = getenv("OPENAIR_DIR");
|
|
if (NULL == g_openair_dir) {
|
|
fprintf(stderr, "ERROR: Could not get OPENAIR_DIR environment variable\n");
|
|
exit(1);
|
|
}
|
|
|
|
// logging
|
|
logInit();
|
|
itti_init(TASK_MAX, THREAD_MAX, MESSAGES_ID_MAX, tasks_info, messages_info, messages_definition_xml, NULL);
|
|
|
|
set_comp_log(S1AP, LOG_TRACE, LOG_MED, 1);
|
|
set_comp_log(SCTP, LOG_TRACE, LOG_MED, 1);
|
|
asn_debug = 0;
|
|
asn1_xer_print = 1;
|
|
|
|
//parameters
|
|
actions = et_config_parse_opt_line (argc, argv, &et_dir_name, &scenario_file_name, &enb_config_file_name); //Command-line options
|
|
if (actions & PLAY_SCENARIO) {
|
|
if (et_generate_xml_scenario(et_dir_name, scenario_file_name,enb_config_file_name, play_scenario_filename) == 0) {
|
|
if (NULL != (scenario = et_generate_scenario(play_scenario_filename))) {
|
|
ret = et_play_scenario(scenario);
|
|
} else {
|
|
fprintf(stderr, "ERROR: Could not generate scenario from tsml file\n");
|
|
ret = -1;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "ERROR: Could not generate tsml scenario from xml file\n");
|
|
ret = -1;
|
|
}
|
|
et_free_pointer(et_dir_name);
|
|
et_free_pointer(scenario_file_name);
|
|
et_free_pointer(enb_config_file_name);
|
|
}
|
|
|
|
return ret;
|
|
}
|