Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*******************************************************************************
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 <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

gauthier
committed

gauthier
committed
#include "timer.h"
#include "assertions.h"
#include "intertask_interface.h"

gauthier
committed
#include "play_scenario.h"
#include "sctp_eNB_task.h"
#include "sctp_default_values.h"
//------------------------------------------------------------------------------
#define PLAY_SCENARIO 1
#define GS_IS_FILE 1
#define GS_IS_DIR 2
//------------------------------------------------------------------------------
Enb_properties_array_t g_enb_properties;
//------------------------------------------------------------------------------
extern et_scenario_t *g_scenario;
extern int xmlLoadExtDtdDefaultValue;
extern int asn_debug;
extern int asn1_xer_print;
extern pthread_mutex_t g_fsm_lock;
//------------------------------------------------------------------------------
// MEMO:
// Scenario with several eNBs: We may have to create ethx.y interfaces
//
//------------------------------------------------------------------------------
// 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;
}
//------------------------------------------------------------------------------

gauthier
committed
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; i--) {
if (in_filename[i] == '.') {
in_filename[i] = '\0';
return i;
}
}
}
return -1;
}
//------------------------------------------------------------------------------
// return number of splitted items
void et_get_shift_arg( char * line_argument, shift_packet_t * const shift)
{
int len = strlen(line_argument);
int i = 0;
int j = 0;
int num_milli = 0;
char my_num[64];
int negative = 0;
while ((line_argument[i] != ':') && (i < len)) {
if (isdigit(line_argument[i])) { // may occur '\"'
my_num[j++] = line_argument[i];
}
AssertFatal(':' == line_argument[i], "Bad format");
i += 1; // ':'
my_num[j++] = '\0';
shift->frame_number = atoi(my_num);
AssertFatal(i<len, "Shift argument %s bad format", line_argument);
if (line_argument[i] == '-') {
negative = 1;
i += 1;
} else if (line_argument[i] == '+') {
i += 1;
}
AssertFatal(i<len, "Shift argument %s bad format", line_argument);
while ((line_argument[i] != '.') && (i < len)) {
my_num[j++] = line_argument[i++];
}
my_num[j] = '\0';
j = 0;
i += 1;
shift->shift_seconds = atoi(my_num);
// may omit .mmm, accept .m or .mm or .mmm or ...
while ((i < len) && (num_milli++ < 3)){
my_num[j++] = line_argument[i++];
}
while (num_milli++ < 6){
my_num[j++] = '0';
}
my_num[j] = '\0';
shift->shift_microseconds = atoi(my_num);
if (negative == 1) {
shift->shift_seconds = - shift->shift_seconds;
shift->shift_microseconds = - shift->shift_microseconds;
}
}
//------------------------------------------------------------------------------
// 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;
}
//------------------------------------------------------------------------------

gauthier
committed
void et_free_packet(et_packet_t* packet)
{
if (packet) {
switch (packet->sctp_hdr.chunk_type) {
case SCTP_CID_DATA:

gauthier
committed
et_free_pointer(packet->sctp_hdr.u.data_hdr.payload.binary_stream);

gauthier
committed
et_free_pointer(packet);
//------------------------------------------------------------------------------

gauthier
committed
void et_free_scenario(et_scenario_t* scenario)

gauthier
committed
et_packet_t *packet = NULL;
et_packet_t *next_packet = NULL;
if (scenario) {
packet = scenario->list_packet;
while (packet) {
next_packet = packet->next;

gauthier
committed
et_free_packet(packet);

gauthier
committed
et_free_pointer(scenario);
pthread_mutex_destroy(&g_fsm_lock);
}
}
//------------------------------------------------------------------------------

gauthier
committed
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"

gauthier
committed
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;
}
//------------------------------------------------------------------------------

gauthier
committed
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);
}
//------------------------------------------------------------------------------

gauthier
committed
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: Unknown chunk_type %d!\n", chunk_type);
}
}
//------------------------------------------------------------------------------
const char * const et_error_match2str(const int err)
{
switch (err) {
// from asn_compare.h
case COMPARE_ERR_CODE_NO_MATCH: return "CODE_NO_MATCH"; break;
case COMPARE_ERR_CODE_TYPE_MISMATCH: return "TYPE_MISMATCH"; break;
case COMPARE_ERR_CODE_TYPE_ARG_NULL: return "TYPE_ARG_NULL"; break;
case COMPARE_ERR_CODE_VALUE_NULL: return "VALUE_NULL"; break;
case COMPARE_ERR_CODE_VALUE_ARG_NULL: return "VALUE_ARG_NULL"; break;
case COMPARE_ERR_CODE_CHOICE_NUM: return "CHOICE_NUM"; break;
case COMPARE_ERR_CODE_CHOICE_PRESENT: return "CHOICE_PRESENT"; break;
case COMPARE_ERR_CODE_CHOICE_MALFORMED: return "CHOICE_MALFORMED"; break;
case COMPARE_ERR_CODE_SET_MALFORMED: return "SET_MALFORMED"; break;
case COMPARE_ERR_CODE_COLLECTION_NUM_ELEMENTS: return "COLLECTION_NUM_ELEMENTS"; break;
// from play_scenario.h
case ET_ERROR_MATCH_PACKET_SCTP_CHUNK_TYPE: return "SCTP_CHUNK_TYPE"; break;
case ET_ERROR_MATCH_PACKET_SCTP_PPID: return "SCTP_PPID"; break;
case ET_ERROR_MATCH_PACKET_SCTP_ASSOC_ID: return "SCTP_ASSOC_ID"; break;
case ET_ERROR_MATCH_PACKET_SCTP_STREAM_ID: return "SCTP_STREAM_ID"; break;
case ET_ERROR_MATCH_PACKET_SCTP_SSN: return "SCTP_SSN"; break;
case ET_ERROR_MATCH_PACKET_S1AP_PRESENT: return "S1AP_PRESENT"; break;
case ET_ERROR_MATCH_PACKET_S1AP_PROCEDURE_CODE: return "S1AP_PROCEDURE_CODE"; break;
case ET_ERROR_MATCH_PACKET_S1AP_CRITICALITY: return "S1AP_CRITICALITY"; break;
default:
AssertFatal (0, "ERROR: Unknown match error %d!(TODO handle an1c error codes)\n", err);
}
}
//------------------------------------------------------------------------------

gauthier
committed
et_packet_action_t et_action_str2et_action_t(const xmlChar * const action)

gauthier
committed
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: cannot convert: %s\n", action);
//if (NULL == action) {return ACTION_S1C_NULL;}
}
//------------------------------------------------------------------------------

gauthier
committed
void et_ip_str2et_ip(const xmlChar * const ip_str, et_ip_t * const ip)
AssertFatal (NULL != ip_str, "ERROR Cannot convert null string to ip address!\n");
AssertFatal (NULL != ip, "ERROR out parameter pointer is NULL!\n");
// 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;
strncpy((char *)ip->str, (const char *)ip_str, INET_ADDRSTRLEN+1);
} else if (inet_pton(AF_INET6, (const char*)ip_str, (void*)&(ip->address.ipv6)) > 0) {
ip->address_family = AF_INET6;
strncpy((char *)ip->str, (const char *)ip_str, INET6_ADDRSTRLEN+1);
} else {
ip->address_family = AF_UNSPEC;
AssertFatal (0, "ERROR %s() Could not parse ip address %s!\n", __FUNCTION__, ip_str);
}
}
//------------------------------------------------------------------------------
int et_compare_et_ip_to_net_ip_address(const et_ip_t * const ip, const net_ip_address_t * const net_ip)
{
AssertFatal (NULL != ip, "ERROR ip parameter\n");
AssertFatal (NULL != net_ip, "ERROR net_ip parameter\n");
switch (ip->address_family) {
case AF_INET:
if (net_ip->ipv4) {
//S1AP_DEBUG("%s(%s,%s)=%d\n",__FUNCTION__,ip->str, net_ip->ipv4_address, strcmp(ip->str, net_ip->ipv4_address));
return strcmp(ip->str, net_ip->ipv4_address);
}
//S1AP_DEBUG("%s(%s,%s)=-1 (IP version (4) not matching)\n",__FUNCTION__,ip->str, net_ip->ipv4_address);
return -1;
break;
case AF_INET6:
if (net_ip->ipv6) {
//S1AP_DEBUG("%s(%s,%s)=%d\n",__FUNCTION__,ip->str, net_ip->ipv4_address, strcmp(ip->str, net_ip->ipv6_address));
return strcmp(ip->str, net_ip->ipv6_address);
}
//S1AP_DEBUG("%s(%s,%s)=-1 (IP version (6) not matching)\n",__FUNCTION__,ip->str, net_ip->ipv6_address);
return -1;
break;
default:
S1AP_DEBUG("%s(%s,...)=-1 (unknown IP version)\n",__FUNCTION__,ip->str);
return -1;
}
}
#ifdef LIBCONFIG_LONG
#define libconfig_int long
#else
#define libconfig_int int
#endif
//------------------------------------------------------------------------------
void et_enb_config_init(const char const * lib_config_file_name_pP)
//------------------------------------------------------------------------------
{
config_t cfg;
config_setting_t *setting = NULL;
config_setting_t *subsetting = NULL;
config_setting_t *setting_mme_addresses = NULL;
config_setting_t *setting_mme_address = NULL;
config_setting_t *setting_enb = NULL;
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
int num_enb_properties = 0;
int enb_properties_index = 0;
int num_enbs = 0;
int num_mme_address = 0;
int i = 0;
int j = 0;
int parse_errors = 0;
libconfig_int enb_id = 0;
const char* cell_type = NULL;
const char* tac = 0;
const char* enb_name = NULL;
const char* mcc = 0;
const char* mnc = 0;
char* ipv4 = NULL;
char* ipv6 = NULL;
char* active = NULL;
char* preference = NULL;
const char* active_enb[MAX_ENB];
char* enb_interface_name_for_S1U = NULL;
char* enb_ipv4_address_for_S1U = NULL;
libconfig_int enb_port_for_S1U = 0;
char* enb_interface_name_for_S1_MME = NULL;
char* enb_ipv4_address_for_S1_MME = NULL;
char *address = NULL;
char *cidr = NULL;
AssertFatal (lib_config_file_name_pP != NULL,
"Bad parameter lib_config_file_name_pP %s , must reference a valid eNB config file\n",
lib_config_file_name_pP);
memset((char*)active_enb, 0 , MAX_ENB * sizeof(char*));
config_init(&cfg);
/* Read the file. If there is an error, report it and exit. */
if (! config_read_file(&cfg, lib_config_file_name_pP)) {
config_destroy(&cfg);
AssertFatal (0, "Failed to parse eNB configuration file %s!\n", lib_config_file_name_pP);
}
// Get list of active eNBs, (only these will be configured)
setting = config_lookup(&cfg, ENB_CONFIG_STRING_ACTIVE_ENBS);
if (setting != NULL) {
num_enbs = config_setting_length(setting);
for (i = 0; i < num_enbs; i++) {
setting_enb = config_setting_get_elem(setting, i);
active_enb[i] = config_setting_get_string (setting_enb);
AssertFatal (active_enb[i] != NULL,
"Failed to parse config file %s, %uth attribute %s \n",
lib_config_file_name_pP, i, ENB_CONFIG_STRING_ACTIVE_ENBS);
active_enb[i] = strdup(active_enb[i]);
num_enb_properties += 1;
}
}
/* Output a list of all eNBs. */
setting = config_lookup(&cfg, ENB_CONFIG_STRING_ENB_LIST);
if (setting != NULL) {
enb_properties_index = g_enb_properties.number;
parse_errors = 0;
num_enbs = config_setting_length(setting);
for (i = 0; i < num_enbs; i++) {
setting_enb = config_setting_get_elem(setting, i);
if (! config_setting_lookup_int(setting_enb, ENB_CONFIG_STRING_ENB_ID, &enb_id)) {
/* Calculate a default eNB ID */
# if defined(ENABLE_USE_MME)
uint32_t hash;
hash = et_s1ap_generate_eNB_id ();
enb_id = i + (hash & 0xFFFF8);
# else
enb_id = i;
# endif
}
if ( !( config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_CELL_TYPE, &cell_type)
&& config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_ENB_NAME, &enb_name)
&& config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_TRACKING_AREA_CODE, &tac)
&& config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_MOBILE_COUNTRY_CODE, &mcc)
&& config_setting_lookup_string(setting_enb, ENB_CONFIG_STRING_MOBILE_NETWORK_CODE, &mnc)
)
) {
AssertError (0, parse_errors ++,
"Failed to parse eNB configuration file %s, %u th enb\n",
lib_config_file_name_pP, i);
continue; // FIXME this prevents segfaults below, not sure what happens after function exit
}
// search if in active list
for (j=0; j < num_enb_properties; j++) {
if (strcmp(active_enb[j], enb_name) == 0) {
g_enb_properties.properties[enb_properties_index] = calloc(1, sizeof(Enb_properties_t));
g_enb_properties.properties[enb_properties_index]->eNB_id = enb_id;
if (strcmp(cell_type, "CELL_MACRO_ENB") == 0) {
g_enb_properties.properties[enb_properties_index]->cell_type = CELL_MACRO_ENB;
} else if (strcmp(cell_type, "CELL_HOME_ENB") == 0) {
g_enb_properties.properties[enb_properties_index]->cell_type = CELL_HOME_ENB;
} else {
AssertError (0, parse_errors ++,
"Failed to parse eNB configuration file %s, enb %d unknown value \"%s\" for cell_type choice: CELL_MACRO_ENB or CELL_HOME_ENB !\n",
lib_config_file_name_pP, i, cell_type);
}
g_enb_properties.properties[enb_properties_index]->eNB_name = strdup(enb_name);
g_enb_properties.properties[enb_properties_index]->tac = (uint16_t)atoi(tac);
g_enb_properties.properties[enb_properties_index]->mcc = (uint16_t)atoi(mcc);
g_enb_properties.properties[enb_properties_index]->mnc = (uint16_t)atoi(mnc);
g_enb_properties.properties[enb_properties_index]->mnc_digit_length = strlen(mnc);
AssertFatal((g_enb_properties.properties[enb_properties_index]->mnc_digit_length == 2) ||
(g_enb_properties.properties[enb_properties_index]->mnc_digit_length == 3),
"BAD MNC DIGIT LENGTH %d",
g_enb_properties.properties[i]->mnc_digit_length);
setting_mme_addresses = config_setting_get_member (setting_enb, ENB_CONFIG_STRING_MME_IP_ADDRESS);
num_mme_address = config_setting_length(setting_mme_addresses);
g_enb_properties.properties[enb_properties_index]->nb_mme = 0;
for (j = 0; j < num_mme_address; j++) {
setting_mme_address = config_setting_get_elem(setting_mme_addresses, j);
if ( !(
config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IPV4_ADDRESS, (const char **)&ipv4)
&& config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IPV6_ADDRESS, (const char **)&ipv6)
&& config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IP_ADDRESS_ACTIVE, (const char **)&active)
&& config_setting_lookup_string(setting_mme_address, ENB_CONFIG_STRING_MME_IP_ADDRESS_PREFERENCE, (const char **)&preference)
)
) {
AssertError (0, parse_errors ++,
"Failed to parse eNB configuration file %s, %u th enb %u th mme address !\n",
lib_config_file_name_pP, i, j);
continue; // FIXME will prevent segfaults below, not sure what happens at function exit...
}
g_enb_properties.properties[enb_properties_index]->nb_mme += 1;
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4_address = strdup(ipv4);
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6_address = strdup(ipv6);
if (strcmp(active, "yes") == 0) {
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].active = 1;
} // else { (calloc)
if (strcmp(preference, "ipv4") == 0) {
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4 = 1;
} else if (strcmp(preference, "ipv6") == 0) {
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6 = 1;
} else if (strcmp(preference, "no") == 0) {
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv4 = 1;
g_enb_properties.properties[enb_properties_index]->mme_ip_address[j].ipv6 = 1;
}
}
// SCTP SETTING
g_enb_properties.properties[enb_properties_index]->sctp_out_streams = SCTP_OUT_STREAMS;
g_enb_properties.properties[enb_properties_index]->sctp_in_streams = SCTP_IN_STREAMS;
subsetting = config_setting_get_member (setting_enb, ENB_CONFIG_STRING_SCTP_CONFIG);
if (subsetting != NULL) {
if ( (config_setting_lookup_int( subsetting, ENB_CONFIG_STRING_SCTP_INSTREAMS, &my_int) )) {
g_enb_properties.properties[enb_properties_index]->sctp_in_streams = (uint16_t)my_int;
}
if ( (config_setting_lookup_int( subsetting, ENB_CONFIG_STRING_SCTP_OUTSTREAMS, &my_int) )) {
g_enb_properties.properties[enb_properties_index]->sctp_out_streams = (uint16_t)my_int;
}
}
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
// NETWORK_INTERFACES
subsetting = config_setting_get_member (setting_enb, ENB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG);
if (subsetting != NULL) {
if ( (
config_setting_lookup_string( subsetting, ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1_MME,
(const char **)&enb_interface_name_for_S1_MME)
&& config_setting_lookup_string( subsetting, ENB_CONFIG_STRING_ENB_IPV4_ADDRESS_FOR_S1_MME,
(const char **)&enb_ipv4_address_for_S1_MME)
&& config_setting_lookup_string( subsetting, ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1U,
(const char **)&enb_interface_name_for_S1U)
&& config_setting_lookup_string( subsetting, ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_S1U,
(const char **)&enb_ipv4_address_for_S1U)
&& config_setting_lookup_int(subsetting, ENB_CONFIG_STRING_ENB_PORT_FOR_S1U,
&enb_port_for_S1U)
)
) {
g_enb_properties.properties[enb_properties_index]->enb_interface_name_for_S1U = strdup(enb_interface_name_for_S1U);
cidr = enb_ipv4_address_for_S1U;
address = strtok(cidr, "/");
if (address) {
IPV4_STR_ADDR_TO_INT_NWBO ( address, g_enb_properties.properties[enb_properties_index]->enb_ipv4_address_for_S1U, "BAD IP ADDRESS FORMAT FOR eNB S1_U !\n" );
}
g_enb_properties.properties[enb_properties_index]->enb_port_for_S1U = enb_port_for_S1U;
g_enb_properties.properties[enb_properties_index]->enb_interface_name_for_S1_MME = strdup(enb_interface_name_for_S1_MME);
cidr = enb_ipv4_address_for_S1_MME;
address = strtok(cidr, "/");
if (address) {
IPV4_STR_ADDR_TO_INT_NWBO ( address, g_enb_properties.properties[enb_properties_index]->enb_ipv4_address_for_S1_MME, "BAD IP ADDRESS FORMAT FOR eNB S1_MME !\n" );
}
}
} // if (subsetting != NULL) {
enb_properties_index += 1;
} // if (strcmp(active_enb[j], enb_name) == 0)
} // for (j=0; j < num_enb_properties; j++)
} // for (i = 0; i < num_enbs; i++)
} // if (setting != NULL) {
g_enb_properties.number += num_enb_properties;
AssertFatal (parse_errors == 0,
"Failed to parse eNB configuration file %s, found %d error%s !\n",
lib_config_file_name_pP, parse_errors, parse_errors > 1 ? "s" : "");
}
/*------------------------------------------------------------------------------*/
const Enb_properties_array_t *et_enb_config_get(void)
{
return &g_enb_properties;
}

gauthier
committed
/*------------------------------------------------------------------------------*/
void et_eNB_app_register(const Enb_properties_array_t *enb_properties)

gauthier
committed
{
uint32_t enb_id = 0;
uint32_t mme_id = 0;
MessageDef *msg_p = NULL;
char *str = NULL;
struct in_addr addr = {.s_addr = 0};

gauthier
committed
g_scenario->register_enb_pending = 0;

gauthier
committed
for (enb_id = 0; (enb_id < enb_properties->number) ; enb_id++) {
{
s1ap_register_enb_req_t *s1ap_register_eNB = NULL;

gauthier
committed
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
/* 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->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));
}

gauthier
committed
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;

gauthier
committed
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);
g_scenario->register_enb_pending++;
itti_send_msg_to_task (TASK_S1AP, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);

gauthier
committed
}

gauthier
committed
}
/*------------------------------------------------------------------------------*/
void *et_eNB_app_task(void *args_p)
et_scenario_t *scenario = (et_scenario_t*)args_p;

gauthier
committed
MessageDef *msg_p = NULL;
const char *msg_name = NULL;
instance_t instance = 0;
int result = 0;

gauthier
committed
itti_mark_task_ready (TASK_ENB_APP);

gauthier
committed
do {
// Wait for a message
itti_receive_msg (TASK_ENB_APP, &msg_p);

gauthier
committed
msg_name = ITTI_MSG_NAME (msg_p);
instance = ITTI_MSG_INSTANCE (msg_p);

gauthier
committed
switch (ITTI_MSG_ID(msg_p)) {
case TERMINATE_MESSAGE:
itti_exit_task ();
break;

gauthier
committed
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(scenario->register_enb_pending > 0);
scenario->register_enb_pending--;

gauthier
committed
/* Check if at least eNB is registered with one MME */
if (S1AP_REGISTER_ENB_CNF(msg_p).nb_mme > 0) {
scenario->registered_enb++;

gauthier
committed
/* Check if all register eNB requests have been processed */
if (scenario->register_enb_pending == 0) {
timer_remove(scenario->enb_register_retry_timer_id);
if (scenario->registered_enb == scenario->enb_properties->number) {

gauthier
committed
/* If all eNB are registered, start scenario */
LOG_D(ENB_APP, " All eNB are now associated with a MME\n");
et_event_t event;
event.code = ET_EVENT_S1C_CONNECTED;
et_scenario_fsm_notify_event(event);

gauthier
committed
} else {
uint32_t not_associated = scenario->enb_properties->number - scenario->registered_enb;

gauthier
committed
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, &scenario->enb_register_retry_timer_id) < 0) {

gauthier
committed
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 */
scenario->registered_enb = 0;
et_eNB_app_register (scenario->enb_properties);

gauthier
committed
break;

gauthier
committed
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 == scenario->enb_register_retry_timer_id) {

gauthier
committed
/* Restart the registration process */
scenario->registered_enb = 0;
et_eNB_app_register (scenario->enb_properties);

gauthier
committed
break;
default:
LOG_E(ENB_APP, "Received unexpected message %s\n", msg_name);
break;

gauthier
committed
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, const struct shift_packet_s *shifts)
{
et_event_t event;
struct shift_packet_s *shift = shifts;
et_packet_t *packet = NULL;
et_packet_t *next_packet = NULL;
struct timeval shift_all_packets = { .tv_sec = 0, .tv_usec = 0 };
struct timeval relative_last_sent_packet = { .tv_sec = 0, .tv_usec = 0 };
struct timeval relative_last_received_packet = { .tv_sec = 0, .tv_usec = 0 };
struct timeval initial_time = { .tv_sec = 0, .tv_usec = 0 };
char first_packet = 1;
char first_sent_packet = 1;
char first_received_packet = 1;
// first apply timing shifts if requested
while (shift) {
packet = scenario->list_packet;
while (packet) {
fprintf(stdout, "*shift: %p\n", shift);
fprintf(stdout, "\tframe_number: %d\n", shift->frame_number);
fprintf(stdout, "\tshift_seconds: %ld\n", shift->shift_seconds);
fprintf(stdout, "\tshift_microseconds: %ld\n", shift->shift_microseconds);
fprintf(stdout, "\tsingle: %d\n\n", shift->single);
fprintf(stdout, "\tshift_all_packets_seconds: %ld\n", shift_all_packets.tv_sec);
fprintf(stdout, "\tshift_all_packets_microseconds: %ld\n", shift_all_packets.tv_usec);
AssertFatal((packet->time_relative_to_first_packet.tv_sec >= 0) && (packet->time_relative_to_first_packet.tv_usec >= 0),
"Bad timing result time_relative_to_first_packet=%d.%d packet num %u, original frame number %u",
packet->time_relative_to_first_packet.tv_sec,
packet->time_relative_to_first_packet.tv_usec,
packet->packet_number,
packet->original_frame_number);
AssertFatal((packet->time_relative_to_last_received_packet.tv_sec >= 0) && (packet->time_relative_to_last_received_packet.tv_usec >= 0),
"Bad timing result time_relative_to_last_received_packet=%d.%d packet num %u, original frame number %u",
packet->time_relative_to_last_received_packet.tv_sec,
packet->time_relative_to_last_received_packet.tv_usec,
packet->packet_number,
packet->original_frame_number);
AssertFatal((packet->time_relative_to_last_sent_packet.tv_sec >= 0) && (packet->time_relative_to_last_sent_packet.tv_usec >= 0),
"Bad timing result time_relative_to_last_sent_packet=%d.%d packet num %u, original frame number %u",
packet->time_relative_to_last_sent_packet.tv_sec,
packet->time_relative_to_last_sent_packet.tv_usec,
packet->packet_number,
packet->original_frame_number);
// fprintf(stdout, "\tpacket num %u, original frame number %u time_relative_to_first_packet=%d.%d\n",
// packet->packet_number,
// packet->original_frame_number,
// packet->time_relative_to_first_packet.tv_sec,
// packet->time_relative_to_first_packet.tv_usec);
// fprintf(stdout, "\tpacket num %u, original frame number %u time_relative_to_last_received_packet=%d.%d\n",
// packet->packet_number,
// packet->original_frame_number,
// packet->time_relative_to_last_received_packet.tv_sec,
// packet->time_relative_to_last_received_packet.tv_usec);
// fprintf(stdout, "\tpacket num %u, original frame number %u time_relative_to_last_sent_packet=%d.%d\n",
// packet->packet_number,
// packet->original_frame_number,
// packet->time_relative_to_last_sent_packet.tv_sec,
// packet->time_relative_to_last_sent_packet.tv_usec);
if ((shift->single) && (shift->frame_number == packet->original_frame_number)) {
struct timeval t_offset = { .tv_sec = shift->shift_seconds, .tv_usec = shift->shift_microseconds };
et_packet_shift_timing(packet, &t_offset);
next_packet = packet->next;
if (next_packet) {
t_offset.tv_sec = -t_offset.tv_sec;
t_offset.tv_usec = -t_offset.tv_usec;
if (packet->action == ET_PACKET_ACTION_S1C_SEND) {
timeval_add(&next_packet->time_relative_to_last_sent_packet, &next_packet->time_relative_to_last_sent_packet, &t_offset);
} else if (packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
timeval_add(&next_packet->time_relative_to_last_received_packet, &next_packet->time_relative_to_last_received_packet, &t_offset);
}
}
}
if ((0 == shift->single) && (shift->frame_number == packet->original_frame_number)) {
shift_all_packets.tv_sec = shift->shift_seconds;
shift_all_packets.tv_usec = shift->shift_microseconds;
timeval_add(&packet->time_relative_to_first_packet, &packet->time_relative_to_first_packet, &shift_all_packets);
fprintf(stdout, "\tpacket num %u, now original frame number %u time_relative_to_first_packet=%d.%d\n",
packet->packet_number,
packet->original_frame_number,
packet->time_relative_to_first_packet.tv_sec,
packet->time_relative_to_first_packet.tv_usec);
AssertFatal((packet->time_relative_to_first_packet.tv_sec >= 0) && (packet->time_relative_to_first_packet.tv_usec >= 0),
"Bad timing result time_relative_to_first_packet=%d.%d packet num %u, original frame number %u",
packet->time_relative_to_first_packet.tv_sec,
packet->time_relative_to_first_packet.tv_usec,
packet->packet_number,
packet->original_frame_number);
} else if ((0 == shift->single) && (shift->frame_number < packet->original_frame_number)) {
timeval_add(&packet->time_relative_to_first_packet, &packet->time_relative_to_first_packet, &shift_all_packets);
fprintf(stdout, "\tpacket num %u, now original frame number %u time_relative_to_first_packet=%d.%d\n",
packet->packet_number,
packet->original_frame_number,
packet->time_relative_to_first_packet.tv_sec,
packet->time_relative_to_first_packet.tv_usec);
AssertFatal((packet->time_relative_to_first_packet.tv_sec >= 0) && (packet->time_relative_to_first_packet.tv_usec >= 0),
"Bad timing result time_relative_to_first_packet=%d.%d packet num %u, original frame number %u",
packet->time_relative_to_first_packet.tv_sec,
packet->time_relative_to_first_packet.tv_usec,
packet->packet_number,
packet->original_frame_number);
}
packet = packet->next;
}
shift = shift->next;
}
// now recompute time_relative_to_last_received_packet, time_relative_to_last_sent_packet
packet = scenario->list_packet;
while (packet) {
if (first_packet > 0) {
initial_time = packet->time_relative_to_first_packet;
packet->time_relative_to_first_packet.tv_sec = 0;
packet->time_relative_to_first_packet.tv_usec = 0;
first_packet = 0;
} else {
timersub(&packet->time_relative_to_first_packet, &initial_time,
&packet->time_relative_to_first_packet);
}
if (packet->action == ET_PACKET_ACTION_S1C_SEND) {
if (first_sent_packet > 0) {
relative_last_sent_packet = packet->time_relative_to_first_packet;
packet->time_relative_to_last_sent_packet.tv_sec = 0;
packet->time_relative_to_last_sent_packet.tv_usec = 0;
first_sent_packet = 0;
timersub(&packet->time_relative_to_first_packet, &relative_last_sent_packet,
&packet->time_relative_to_last_sent_packet);
relative_last_sent_packet = packet->time_relative_to_first_packet;
if (first_received_packet > 0) {
packet->time_relative_to_last_received_packet.tv_sec = 0;
packet->time_relative_to_last_received_packet.tv_usec = 0;
} else {
timersub(&packet->time_relative_to_first_packet, &relative_last_received_packet,
&packet->time_relative_to_last_received_packet);
}
} else if (packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
if (first_received_packet > 0) {
relative_last_received_packet.tv_sec = packet->time_relative_to_first_packet.tv_sec;
relative_last_received_packet.tv_usec = packet->time_relative_to_first_packet.tv_usec;
packet->time_relative_to_last_received_packet.tv_sec = 0;
packet->time_relative_to_last_received_packet.tv_usec = 0;
first_received_packet = 0;
} else {
timersub(&packet->time_relative_to_first_packet, &relative_last_received_packet,
&packet->time_relative_to_last_received_packet);
relative_last_received_packet = packet->time_relative_to_first_packet;
}
if (first_sent_packet > 0) {
packet->time_relative_to_last_sent_packet.tv_sec = 0;
packet->time_relative_to_last_sent_packet.tv_usec = 0;
} else {
timersub(&packet->time_relative_to_first_packet, &relative_last_sent_packet,
&packet->time_relative_to_last_sent_packet);

gauthier
committed
et_display_scenario(scenario);

gauthier
committed
// 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;
}

gauthier
committed
// 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;
}

gauthier
committed
// create ENB_APP ITTI task: not as same as eNB code
if (itti_create_task (TASK_ENB_APP, et_eNB_app_task, scenario) < 0) {

gauthier
committed
LOG_E(ENB_APP, "Create task for ENB_APP failed\n");
return -1;
}

gauthier
committed
event.code = ET_EVENT_INIT;