Commit 7f9543a9 authored by gauthier's avatar gauthier

Remaining TODO fill assoc_id and mme_ue_id of packets

parent bc374786
......@@ -1667,17 +1667,16 @@ add_executable(test_epc_play_scenario
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario_fsm.c
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario_parse.c
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario_s1ap.c
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario_s1ap_eNB_defs.h
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario_sctp.c
${OPENAIR3_DIR}/TEST/EPC_TEST/play_scenario.h
${OPENAIR2_DIR}/ENB_APP/enb_config.h
${OPENAIR2_DIR}/COMMON/commonDef.h
${OPENAIR2_DIR}/COMMON/messages_def.h
${OPENAIR2_DIR}/COMMON/messages_types.h
${OPENAIR3_DIR}/S1AP/s1ap_eNB_defs.h
${OPENAIR_BIN_DIR}/messages_xml.h
)
target_link_libraries (test_epc_play_scenario
-Wl,--start-group RRC_LIB S1AP_LIB S1AP_ENB X2AP_LIB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} L2 -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
-Wl,--start-group RRC_LIB S1AP_LIB X2AP_LIB GTPV1U LIB_NAS_UE SECU_CN UTIL HASHTABLE SCTP_CLIENT UDP SCHED_LIB PHY LFDS ${ITTI_LIB} ${MSC_LIB} -Wl,--end-group pthread m rt crypt sctp ${LIBXML2_LIBRARIES} ${LIBXSLT_LIBRARIES} ${CRYPTO_LIBRARIES} ${OPENSSL_LIBRARIES} ${NETTLE_LIBRARIES} ${CONFIG_LIBRARIES}
)
......
......@@ -228,7 +228,7 @@ int generate_test_scenario(const char const * test_nameP, const char const * pdm
for (i = 0; i < g_enb_properties.number; i++) {
// eNB S1-C IPv4 address
sprintf(astring, "enb_s1c%d", i);
sprintf(astring, "enb%d_s1c", i);
params[nb_params++] = strdup(astring);
addr.s_addr = g_enb_properties.properties[i]->enb_ipv4_address_for_S1_MME;
sprintf(astring, "\"%s\"", inet_ntoa(addr));
......@@ -236,7 +236,7 @@ int generate_test_scenario(const char const * test_nameP, const char const * pdm
// MME S1-C IPv4 address
for (j = 0; j < g_enb_properties.properties[i]->nb_mme; j++) {
sprintf(astring, "mme_s1c%d_%d", i, j);
sprintf(astring, "mme%d_s1c_%d", i, j);
params[nb_params++] = strdup(astring);
AssertFatal (g_enb_properties.properties[i]->mme_ip_address[j].ipv4_address,
"Only support MME IPv4 address\n");
......
......@@ -8,30 +8,30 @@
/>
<!-- Ugly but no time to find a better way in XSLT 1.0 (map/list)-->
<xsl:param name="enb_s1c0" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c1" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c2" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c3" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c4" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c5" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c6" select="'0.0.0.0'"/>
<xsl:param name="enb_s1c7" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c0_0" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c0_1" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c0_2" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c0_3" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c1_0" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c1_1" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c1_2" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c1_3" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c2_0" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c2_1" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c2_2" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c2_3" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c3_0" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c3_1" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c3_2" select="'0.0.0.0'"/>
<xsl:param name="mme_s1c3_3" select="'0.0.0.0'"/>
<xsl:param name="enb0_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb1_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb2_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb3_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb4_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb5_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb6_s1c" select="'0.0.0.0'"/>
<xsl:param name="enb7_s1c" select="'0.0.0.0'"/>
<xsl:param name="mme0_s1c_0" select="'0.0.0.0'"/>
<xsl:param name="mme0_s1c_1" select="'0.0.0.0'"/>
<xsl:param name="mme0_s1c_2" select="'0.0.0.0'"/>
<xsl:param name="mme0_s1c_3" select="'0.0.0.0'"/>
<xsl:param name="mme1_s1c_0" select="'0.0.0.0'"/>
<xsl:param name="mme1_s1c_1" select="'0.0.0.0'"/>
<xsl:param name="mme1_s1c_2" select="'0.0.0.0'"/>
<xsl:param name="mme1_s1c_3" select="'0.0.0.0'"/>
<xsl:param name="mme2_s1c_0" select="'0.0.0.0'"/>
<xsl:param name="mme2_s1c_1" select="'0.0.0.0'"/>
<xsl:param name="mme2_s1c_2" select="'0.0.0.0'"/>
<xsl:param name="mme2_s1c_3" select="'0.0.0.0'"/>
<xsl:param name="mme3_s1c_0" select="'0.0.0.0'"/>
<xsl:param name="mme3_s1c_1" select="'0.0.0.0'"/>
<xsl:param name="mme3_s1c_2" select="'0.0.0.0'"/>
<xsl:param name="mme3_s1c_3" select="'0.0.0.0'"/>
<xsl:param name="ip_address" select="'0.0.0.0'"/>
......@@ -39,30 +39,30 @@
<xsl:template name="reverse_ip">
<xsl:param name="ip_address"/>
<xsl:choose>
<xsl:when test="$ip_address=$enb_s1c0">enb_s1c0</xsl:when>
<xsl:when test="$ip_address=$enb_s1c1">enb_s1c1</xsl:when>
<xsl:when test="$ip_address=$enb_s1c2">enb_s1c2</xsl:when>
<xsl:when test="$ip_address=$enb_s1c3">enb_s1c3</xsl:when>
<xsl:when test="$ip_address=$enb_s1c4">enb_s1c4</xsl:when>
<xsl:when test="$ip_address=$enb_s1c5">enb_s1c5</xsl:when>
<xsl:when test="$ip_address=$enb_s1c6">enb_s1c6</xsl:when>
<xsl:when test="$ip_address=$enb_s1c7">enb_s1c7</xsl:when>
<xsl:when test="$ip_address=$mme_s1c0_0">mme_s1c0_0</xsl:when>
<xsl:when test="$ip_address=$mme_s1c0_1">mme_s1c0_1</xsl:when>
<xsl:when test="$ip_address=$mme_s1c0_2">mme_s1c0_2</xsl:when>
<xsl:when test="$ip_address=$mme_s1c0_3">mme_s1c0_3</xsl:when>
<xsl:when test="$ip_address=$mme_s1c1_0">mme_s1c1_0</xsl:when>
<xsl:when test="$ip_address=$mme_s1c1_1">mme_s1c1_1</xsl:when>
<xsl:when test="$ip_address=$mme_s1c1_2">mme_s1c1_2</xsl:when>
<xsl:when test="$ip_address=$mme_s1c1_3">mme_s1c1_3</xsl:when>
<xsl:when test="$ip_address=$mme_s1c2_0">mme_s1c2_0</xsl:when>
<xsl:when test="$ip_address=$mme_s1c2_1">mme_s1c2_1</xsl:when>
<xsl:when test="$ip_address=$mme_s1c2_2">mme_s1c2_2</xsl:when>
<xsl:when test="$ip_address=$mme_s1c2_3">mme_s1c2_3</xsl:when>
<xsl:when test="$ip_address=$mme_s1c3_0">mme_s1c3_0</xsl:when>
<xsl:when test="$ip_address=$mme_s1c3_1">mme_s1c3_1</xsl:when>
<xsl:when test="$ip_address=$mme_s1c3_2">mme_s1c3_2</xsl:when>
<xsl:when test="$ip_address=$mme_s1c3_3">mme_s1c3_3</xsl:when>
<xsl:when test="$ip_address=$enb0_s1c">enb0_s1c</xsl:when>
<xsl:when test="$ip_address=$enb1_s1c">enb1_s1c</xsl:when>
<xsl:when test="$ip_address=$enb2_s1c">enb2_s1c</xsl:when>
<xsl:when test="$ip_address=$enb3_s1c">enb3_s1c</xsl:when>
<xsl:when test="$ip_address=$enb4_s1c">enb4_s1c</xsl:when>
<xsl:when test="$ip_address=$enb5_s1c">enb5_s1c</xsl:when>
<xsl:when test="$ip_address=$enb6_s1c">enb6_s1c</xsl:when>
<xsl:when test="$ip_address=$enb7_s1c">enb7_s1c</xsl:when>
<xsl:when test="$ip_address=$mme0_s1c_0">mme0_s1c_0</xsl:when>
<xsl:when test="$ip_address=$mme0_s1c_1">mme0_s1c_1</xsl:when>
<xsl:when test="$ip_address=$mme0_s1c_2">mme0_s1c_2</xsl:when>
<xsl:when test="$ip_address=$mme0_s1c_3">mme0_s1c_3</xsl:when>
<xsl:when test="$ip_address=$mme1_s1c_0">mme1_s1c_0</xsl:when>
<xsl:when test="$ip_address=$mme1_s1c_1">mme1_s1c_1</xsl:when>
<xsl:when test="$ip_address=$mme1_s1c_2">mme1_s1c_2</xsl:when>
<xsl:when test="$ip_address=$mme1_s1c_3">mme1_s1c_3</xsl:when>
<xsl:when test="$ip_address=$mme2_s1c_0">mme2_s1c_0</xsl:when>
<xsl:when test="$ip_address=$mme2_s1c_1">mme2_s1c_1</xsl:when>
<xsl:when test="$ip_address=$mme2_s1c_2">mme2_s1c_2</xsl:when>
<xsl:when test="$ip_address=$mme2_s1c_3">mme2_s1c_3</xsl:when>
<xsl:when test="$ip_address=$mme3_s1c_0">mme3_s1c_0</xsl:when>
<xsl:when test="$ip_address=$mme3_s1c_1">mme3_s1c_1</xsl:when>
<xsl:when test="$ip_address=$mme3_s1c_2">mme3_s1c_2</xsl:when>
<xsl:when test="$ip_address=$mme3_s1c_3">mme3_s1c_3</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">ERROR: Cannot reverse resolv IP <xsl:value-of select="."/> !
</xsl:message>
......@@ -70,6 +70,26 @@
</xsl:choose>
</xsl:template>
<xsl:template name="enb_ip_2_enb_instance">
<xsl:param name="ip_address"/>
<xsl:choose>
<xsl:when test="$ip_address=$enb0_s1c">0</xsl:when>
<xsl:when test="$ip_address=$enb1_s1c">1</xsl:when>
<xsl:when test="$ip_address=$enb2_s1c">2</xsl:when>
<xsl:when test="$ip_address=$enb3_s1c">3</xsl:when>
<xsl:when test="$ip_address=$enb4_s1c">4</xsl:when>
<xsl:when test="$ip_address=$enb5_s1c">5</xsl:when>
<xsl:when test="$ip_address=$enb6_s1c">6</xsl:when>
<xsl:when test="$ip_address=$enb7_s1c">7</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">ERROR: Cannot set eNB instance <xsl:value-of select="."/> !
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="chunktype2str">
<xsl:param name="chunk_type"/>
<xsl:choose>
......@@ -120,8 +140,26 @@
</xsl:variable>
<xsl:variable name="action">
<xsl:choose>
<xsl:when test="starts-with($ip_src,'enb_s1')">SEND</xsl:when>
<xsl:when test="starts-with($ip_src,'mme_s1c')">RECEIVE</xsl:when>
<xsl:when test="starts-with($ip_src,'enb')">SEND</xsl:when>
<xsl:when test="starts-with($ip_src,'mme')">RECEIVE</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">ERROR: UNKNOWN ACTION <xsl:value-of select="."/> !
</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="enb_instance">
<xsl:choose>
<xsl:when test="starts-with($ip_src,'enb')">
<xsl:call-template name="enb_ip_2_enb_instance">
<xsl:with-param name="ip_address" select="$ip/field[@name='ip.src']/@show"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="starts-with($ip_dst,'enb')">
<xsl:call-template name="enb_ip_2_enb_instance">
<xsl:with-param name="ip_address" select="$ip/field[@name='ip.dst']/@show"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message terminate="yes">ERROR: UNKNOWN ACTION <xsl:value-of select="."/> !
</xsl:message>
......@@ -156,6 +194,7 @@
<pos_offset value="{$s1ap_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.data_sid value="{$sctp_data_sid}"/-->
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
......@@ -177,6 +216,7 @@
<pos_offset value="{$sctp_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
<!--sctp.init_nr_in_streams value="{$sctp_init_nr_in_streams}"/-->
......@@ -197,6 +237,7 @@
<pos_offset value="{$sctp_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.data_sid value="{$sctp_data_sid}"/-->
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
......@@ -218,6 +259,7 @@
<pos_offset value="{$sctp_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.data_sid value="{$sctp_data_sid}"/-->
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
......@@ -233,6 +275,7 @@
<pos_offset value="{$sctp_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.data_sid value="{$sctp_data_sid}"/-->
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
......@@ -249,6 +292,7 @@
<pos_offset value="{$sctp_pos_offset}"/>
<ip.src value="{$ip_src}"/>
<ip.dst value="{$ip_dst}"/>
<eNB.instance value="{$enb_instance}"/>
<!--sctp.data_sid value="{$sctp_data_sid}"/-->
<!--sctp.srcport value="{$sctp_srcport}"/-->
<!--sctp.dstport value="{$sctp_dstport}"/-->
......
......@@ -48,6 +48,9 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "intertask_interface_init.h"
......@@ -63,11 +66,19 @@
#define GS_IS_FILE 1
#define GS_IS_DIR 2
//------------------------------------------------------------------------------
char *g_openair_dir = NULL;
char *g_openair_dir = NULL;
Enb_properties_array_t g_enb_properties;
//------------------------------------------------------------------------------
extern int xmlLoadExtDtdDefaultValue;
extern int asn_debug;
extern int asn1_xer_print;
extern et_scenario_t *g_scenario;
extern int xmlLoadExtDtdDefaultValue;
extern int asn_debug;
extern int asn1_xer_print;
//------------------------------------------------------------------------------
// MEMO:
// Scenario with several eNBs: We may have to create ethx.y interfaces
//
//------------------------------------------------------------------------------
......@@ -159,6 +170,7 @@ void et_free_scenario(et_scenario_t* scenario)
packet = next_packet->next;
}
et_free_pointer(scenario);
pthread_mutex_destroy(&scenario->fsm_lock);
}
}
......@@ -290,17 +302,250 @@ void et_ip_str2et_ip(const xmlChar * const ip_str, et_ip_t * const ip)
AssertFatal (0, "ERROR %s() Could not parse ip address %s!\n", __FUNCTION__, ip_str);
}
}
#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;
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;
}
}
// 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;
}
/*------------------------------------------------------------------------------*/
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;
g_scenario->register_enb_pending = 0;
for (enb_id = 0; (enb_id < enb_properties->number) ; enb_id++) {
{
s1ap_register_enb_req_t *s1ap_register_eNB;
......@@ -318,7 +563,6 @@ uint32_t et_eNB_app_register(const Enb_properties_array_t *enb_properties)
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,
......@@ -347,20 +591,16 @@ uint32_t et_eNB_app_register(const Enb_properties_array_t *enb_properties)
itti_send_msg_to_task (TASK_S1AP, ENB_MODULE_ID_TO_INSTANCE(enb_id), msg_p);
register_enb_pending++;
g_scenario->register_enb_pending++;
}
}
return register_enb_pending;
return g_scenario->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;
et_scenario_t *scenario = (et_scenario_t*)args_p;
MessageDef *msg_p = NULL;
const char *msg_name = NULL;
instance_t instance;
......@@ -369,14 +609,6 @@ void *et_eNB_app_task(void *args_p)
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);
......@@ -389,40 +621,40 @@ void *et_eNB_app_task(void *args_p)
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--;
DevAssert(g_scenario->register_enb_pending > 0);
g_scenario->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++;
g_scenario->registered_enb++;
}
/* Check if all register eNB requests have been processed */
if (register_enb_pending == 0) {
if (registered_enb == enb_properties_p->number) {
if (scenario->register_enb_pending == 0) {
if (scenario->registered_enb == scenario->enb_properties->number) {
/* If all eNB are registered, start scenario */
et_event_t event;
event.code = ET_EVENT_S1C_CONNECTED;
et_scenario_fsm_notify_event(event);
} else {
uint32_t not_associated = enb_properties_p->number - registered_enb;
uint32_t not_associated = scenario->enb_properties->number - scenario->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) {
NULL, &scenario->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);
scenario->registered_enb = 0;
scenario->register_enb_pending = et_eNB_app_register (scenario->enb_properties);
}
}
}
......@@ -439,10 +671,10 @@ void *et_eNB_app_task(void *args_p)
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) {
if (TIMER_HAS_EXPIRED (msg_p).timer_id == scenario->enb_register_retry_timer_id) {
/* Restart the registration process */
registered_enb = 0;
register_enb_pending = et_eNB_app_register (enb_properties_p);
scenario->registered_enb = 0;
scenario->register_enb_pending = et_eNB_app_register (scenario->enb_properties);
}
break;
......@@ -476,7 +708,7 @@ int et_play_scenario(et_scenario_t* const scenario)
}
// create ENB_APP ITTI task: not as same as eNB code
if (itti_create_task (TASK_ENB_APP, et_eNB_app_task, NULL) < 0) {
if (itti_create_task (TASK_ENB_APP, et_eNB_app_task, scenario) < 0) {
LOG_E(ENB_APP, "Create task for ENB_APP failed\n");
return -1;
}
......@@ -523,7 +755,6 @@ et_config_parse_opt_line (
{
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 */
......@@ -606,7 +837,7 @@ et_config_parse_opt_line (
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);
et_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);
......
......@@ -28,7 +28,7 @@
*******************************************************************************/
/*
et_scenario.h
play_scenario.h
-------------------
AUTHOR : Lionel GAUTHIER
COMPANY : EURECOM
......@@ -39,14 +39,121 @@
#define PLAY_SCENARIO_H_
# include <time.h>
# include <stdint.h>
# include <pthread.h>
# include <libxml/tree.h>
# include <netinet/in.h>
#include "enb_config.h"
#include "S1AP-PDU.h"
#include "s1ap_ies_defs.h"
#include "play_scenario_s1ap_eNB_defs.h"
#include "hashtable.h"
# define ET_ENB_REGISTER_RETRY_DELAY 3
#define MAX_ENB 16
#define ENB_CONFIG_STRING_ACTIVE_ENBS "Active_eNBs"
#define ENB_CONFIG_STRING_ENB_LIST "eNBs"
#define ENB_CONFIG_STRING_ENB_ID "eNB_ID"
#define ENB_CONFIG_STRING_CELL_TYPE "cell_type"
#define ENB_CONFIG_STRING_ENB_NAME "eNB_name"
#define ENB_CONFIG_STRING_TRACKING_AREA_CODE "tracking_area_code"
#define ENB_CONFIG_STRING_MOBILE_COUNTRY_CODE "mobile_country_code"
#define ENB_CONFIG_STRING_MOBILE_NETWORK_CODE "mobile_network_code"
#define ENB_CONFIG_STRING_MME_IP_ADDRESS "mme_ip_address"
#define ENB_CONFIG_STRING_MME_IPV4_ADDRESS "ipv4"
#define ENB_CONFIG_STRING_MME_IPV6_ADDRESS "ipv6"
#define ENB_CONFIG_STRING_MME_IP_ADDRESS_ACTIVE "active"
#define ENB_CONFIG_STRING_MME_IP_ADDRESS_PREFERENCE "preference"
#define ENB_CONFIG_STRING_SCTP_CONFIG "SCTP"
#define ENB_CONFIG_STRING_SCTP_INSTREAMS "SCTP_INSTREAMS"
#define ENB_CONFIG_STRING_SCTP_OUTSTREAMS "SCTP_OUTSTREAMS"
#define ENB_CONFIG_STRING_NETWORK_INTERFACES_CONFIG "NETWORK_INTERFACES"
#define ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1_MME "ENB_INTERFACE_NAME_FOR_S1_MME"
#define ENB_CONFIG_STRING_ENB_IPV4_ADDRESS_FOR_S1_MME "ENB_IPV4_ADDRESS_FOR_S1_MME"
#define ENB_CONFIG_STRING_ENB_INTERFACE_NAME_FOR_S1U "ENB_INTERFACE_NAME_FOR_S1U"
#define ENB_CONFIG_STRING_ENB_IPV4_ADDR_FOR_S1U "ENB_IPV4_ADDRESS_FOR_S1U"
#define ENB_CONFIG_STRING_ENB_PORT_FOR_S1U "ENB_PORT_FOR_S1U"
typedef struct mme_ip_address_s {
unsigned ipv4:1;
unsigned ipv6:1;
unsigned active:1;
char *ipv4_address;
char *ipv6_address;
} mme_ip_address_t;
#define IPV4_STR_ADDR_TO_INT_NWBO(AdDr_StR,NwBo,MeSsAgE ) do {\
struct in_addr inp;\
if ( inet_aton(AdDr_StR, &inp ) < 0 ) {\
AssertFatal (0, MeSsAgE);\
} else {\
NwBo = inp.s_addr;\
}\
} while (0);
typedef struct Enb_properties_s {
/* Unique eNB_id to identify the eNB within EPC.
* For macro eNB ids this field should be 20 bits long.
* For home eNB ids this field should be 28 bits long.
*/
uint32_t eNB_id;
/* The type of the cell */
enum cell_type_e cell_type;
/* Optional name for the cell
* NOTE: the name can be NULL (i.e no name) and will be cropped to 150
* characters.
*/
char *eNB_name;
/* Tracking area code */
uint16_t tac;
/* Mobile Country Code
* Mobile Network Code
*/
uint16_t mcc;
uint16_t mnc;
uint8_t mnc_digit_length;
/* Physical parameters */
int16_t Nid_cell[1+MAX_NUM_CCs];// for testing, change later
/* Nb of MME to connect to */
uint8_t nb_mme;
/* List of MME to connect to */
mme_ip_address_t mme_ip_address[S1AP_MAX_NB_MME_IP_ADDRESS];
int sctp_in_streams;
int sctp_out_streams;
char *enb_interface_name_for_S1U;
in_addr_t enb_ipv4_address_for_S1U;
tcp_udp_port_t enb_port_for_S1U;
char *enb_interface_name_for_S1_MME;
in_addr_t enb_ipv4_address_for_S1_MME;
} Enb_properties_t;
typedef struct Enb_properties_array_s {
int number;
Enb_properties_t *properties[MAX_ENB];
} Enb_properties_array_t;
# define ET_ENB_REGISTER_RETRY_DELAY 3
# define ET_FSM_STATE_WAITING_RX_EVENT_DELAY_SEC 15
typedef enum {
ET_PACKET_STATUS_START = 0,
......@@ -54,19 +161,17 @@ typedef enum {
ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT,
ET_PACKET_STATUS_SCHEDULED_FOR_SENDING,
ET_PACKET_STATUS_SENT,
ET_PACKET_STATUS_SENT_WITH_ERRORS,
ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING,
ET_PACKET_STATUS_RECEIVED,
ET_PACKET_STATUS_RECEIVED_WITH_ERRORS,
ET_PACKET_STATUS_END
} et_packet_status_t;
typedef enum {
ET_FSM_STATE_START = 0,
ET_FSM_STATE_NULL = ET_FSM_STATE_START,
ET_FSM_STATE_CONNECTING_SCTP,
ET_FSM_STATE_WAITING_TX_EVENT,
ET_FSM_STATE_WAITING_RX_EVENT,
ET_FSM_STATE_CONNECTING_S1C,
ET_FSM_STATE_WAITING_EVENT,
ET_FSM_STATE_RUNNING,
ET_FSM_STATE_END
} et_fsm_state_t;
......@@ -136,6 +241,7 @@ typedef struct sctp_datahdr_s {
uint16_t stream;
uint16_t ssn;
uint32_t ppid;
uint32_t assoc_id; // filled when running scenario
et_s1ap_t payload;
} sctp_datahdr_t;
......@@ -182,11 +288,15 @@ typedef struct et_packet_s {
struct timeval time_relative_to_last_received_packet;
unsigned int original_frame_number;
unsigned int packet_number;
instance_t enb_instance;
et_ip_hdr_t ip_hdr;
et_sctp_hdr_t sctp_hdr;
struct et_packet_s *next;
//scenario running vars
et_packet_status_t status;
long timer_id; // ITTI timer id for waiting rx packets
struct timeval timestamp_packet; // timestamp when rx or tx packet
}et_packet_t;
......@@ -203,24 +313,37 @@ typedef struct sctp_epoll_s {
} thread_desc_t;
typedef struct et_scenario_s {
xmlChar *name;
et_packet_t *list_packet;
xmlChar *name;
et_packet_t *list_packet;
//--------------------------
// playing scenario
et_packet_t *waited_packet;
et_packet_t *current_packet;
//--------------------------
Enb_properties_array_t *enb_properties;
uint32_t register_enb_pending;
uint32_t registered_enb;
long enb_register_retry_timer_id;
pthread_mutex_t fsm_lock;
et_fsm_state_t fsm_state;
hash_table_t *hash_mme2association_id;
hash_table_t *hash_old_ue_mme_id2ue_mme_id;
struct timeval time_last_tx_packet;
struct timeval time_last_rx_packet;
et_packet_t *last_rx_packet;
et_packet_t *last_tx_packet;
et_packet_t *next_packet;
} et_scenario_t;
typedef enum {
ET_EVENT_START = 0,
ET_EVENT_INIT = ET_EVENT_START,
ET_EVENT_S1C_CONNECTED,
ET_EVENT_RX_SCTP_EVENT,
ET_EVENT_RX_S1AP,
ET_EVENT_RX_X2AP,
ET_EVENT_RX_PACKET_TIME_OUT,
ET_EVENT_TX_PACKET,
ET_EVENT_STOP,
ET_EVENT_TX_TIMED_PACKET,
ET_EVENT_END
} et_event_code_t;
......@@ -241,7 +364,8 @@ typedef struct et_event_s {
union {
et_event_init_t init;
et_event_s1ap_data_ind_t s1ap_data_ind;
et_event_s1ap_data_req_t s1ap_data_req;
et_packet_t *tx_timed_packet;
et_packet_t *rx_packet_time_out;
} u;
} et_event_t;
......@@ -266,16 +390,36 @@ int et_s1ap_decode_unsuccessful_outcome(s1ap_message *message, S1ap_Unsuccessful
int et_s1ap_decode_pdu(S1AP_PDU_t * const pdu, s1ap_message * const message, const uint8_t * const buffer, const uint32_t length);
void et_decode_s1ap(et_s1ap_t * const s1ap);
//-------------------------
void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind);
int et_s1ap_eNB_compare_assoc_id( struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_data_s *p2);
uint16_t et_s1ap_eNB_fetch_add_global_cnx_id(void);
void et_s1ap_eNB_prepare_internal_data(void);
void et_s1ap_eNB_insert_new_instance(s1ap_eNB_instance_t *new_instance_p);
struct s1ap_eNB_mme_data_s *et_s1ap_eNB_get_MME(s1ap_eNB_instance_t *instance_p,int32_t assoc_id, uint16_t cnx_id);
s1ap_eNB_instance_t *et_s1ap_eNB_get_instance(instance_t instance);
void et_s1ap_eNB_itti_send_sctp_data_req(instance_t instance, int32_t assoc_id, uint8_t *buffer,uint32_t buffer_length, uint16_t stream);
void et_s1ap_process_rx_packet(et_event_s1ap_data_ind_t * const sctp_data_ind);
void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t * const sctp_data_ind);
void et_s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
net_ip_address_t *mme_ip_address,
net_ip_address_t *local_ip_addr,
uint16_t in_streams,
uint16_t out_streams);
void et_s1ap_handle_s1_setup_message(s1ap_eNB_mme_data_t *mme_desc_p, int sctp_shutdown);
void et_s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB);
void et_s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
void * et_s1ap_eNB_task(void *arg);
//-------------------------
int et_generate_xml_scenario(
const char const * xml_in_dir_name,
const char const * xml_in_scenario_filename,
const char const * enb_config_filename,
char const * tsml_out_scenario_filename);
//-------------------------
int et_scenario_fsm_notify_event_state_null(et_event_t event);
int et_scenario_fsm_notify_event(et_event_t event);
et_fsm_state_t et_scenario_fsm_notify_event_state_running(et_event_t event);
et_fsm_state_t et_scenario_fsm_notify_event_state_waiting(et_event_t event);
et_fsm_state_t et_scenario_fsm_notify_event_state_connecting_s1c(et_event_t event);
et_fsm_state_t et_scenario_fsm_notify_event_state_null(et_event_t event);
et_fsm_state_t et_scenario_fsm_notify_event(et_event_t event);
//-------------------------
void et_parse_s1ap(xmlDocPtr doc, const xmlNode const *s1ap_node, et_s1ap_t * const s1ap);
void et_parse_sctp_data_chunk(xmlDocPtr doc, const xmlNode const *sctp_node, sctp_datahdr_t * const sctp_hdr);
......@@ -297,6 +441,8 @@ sctp_cid_t et_chunk_type_str2cid(const xmlChar * const chunk_type_str);
const char * const et_chunk_type_cid2str(const sctp_cid_t chunk_type);
et_packet_action_t et_action_str2et_action_t(const xmlChar * const action);
void et_ip_str2et_ip(const xmlChar * const ip_str, et_ip_t * const ip);
void et_enb_config_init(const char const * lib_config_file_name_pP);
const Enb_properties_array_t *et_enb_config_get(void);
uint32_t et_eNB_app_register(const Enb_properties_array_t *enb_properties);
void *et_eNB_app_task(void *args_p);
int et_play_scenario(et_scenario_t* const scenario);
......
......@@ -35,74 +35,322 @@
EMAIL : Lionel.Gauthier@eurecom.fr
*/
#include <stdio.h>
#include <sys/time.h>
#include "intertask_interface.h"
#include "platform_types.h"
#include "assertions.h"
#include "play_scenario.h"
#include "s1ap_ies_defs.h"
#include "play_scenario_s1ap_eNB_defs.h"
#include "timer.h"
//------------------------------------------------------------------------------
et_scenario_t *g_scenario = NULL;
et_fsm_state_t g_fsm_state = ET_FSM_STATE_NULL;
//------------------------------------------------------------------------------
int et_scenario_fsm_notify_event_state_null(et_event_t event)
int timeval_subtract (struct timeval * const result, struct timeval * const a, struct timeval * const b)
{
struct timeval b2;
b2.tv_sec = b->tv_sec;
b2.tv_usec = b->tv_usec;
/* Perform the carry for the later subtraction by updating y. */
if (a->tv_usec < b2.tv_usec) {
int nsec = (b2.tv_usec - a->tv_usec) / 1000000 + 1;
b2.tv_usec -= 1000000 * nsec;
b2.tv_sec += nsec;
}
if (a->tv_usec - b2.tv_usec > 1000000) {
int nsec = (a->tv_usec - b2.tv_usec) / 1000000;
b2.tv_usec += 1000000 * nsec;
b2.tv_sec -= nsec;
}
/* Compute the time remaining to wait.
tv_usec is certainly positive. */
result->tv_sec = a->tv_sec - b2.tv_sec;
result->tv_usec = a->tv_usec - b2.tv_usec;
/* Return 1 if result is negative. */
return a->tv_sec < b2.tv_sec;
}
//------------------------------------------------------------------------------
void et_scenario_wait_rx_packet(et_packet_t * const packet)
{
if (timer_setup (ET_FSM_STATE_WAITING_RX_EVENT_DELAY_SEC, 0, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
NULL, &packet->timer_id) < 0) {
AssertFatal(0, " Can not start waiting RX event timer\n");
}
g_scenario->fsm_state = ET_FSM_STATE_WAITING_EVENT;
packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING;
}
//------------------------------------------------------------------------------
void et_scenario_schedule_tx_packet(et_packet_t * const packet)
{
s1ap_eNB_instance_t *s1ap_eNB_instance = NULL;
struct timeval now = { .tv_sec = 0, .tv_usec = 0 };
struct timeval offset_last_tx_packet = { .tv_sec = 0, .tv_usec = 0 };
struct timeval offset_last_rx_packet = { .tv_sec = 0, .tv_usec = 0 };
struct timeval offset_tx_rx = { .tv_sec = 0, .tv_usec = 0 };
struct timeval offset = { .tv_sec = 0, .tv_usec = 0 };
int last_packet_was_tx = 0;
int we_are_too_early = 0;
AssertFatal(NULL != packet, "packet argument is NULL");
s1ap_eNB_instance = et_s1ap_eNB_get_instance(packet->enb_instance);
AssertFatal(NULL != s1ap_eNB_instance, "Cannot get s1ap_eNB_instance_t for eNB instance %d", packet->enb_instance);
g_scenario->fsm_state = ET_FSM_STATE_WAITING_EVENT;
switch (packet->sctp_hdr.chunk_type) {
case SCTP_CID_DATA:
// check if we can send it now
// TODO: BUG we have to discard in scenario all packets that cannot be processed (SACK, COOKIEs, etc)
AssertFatal(gettimeofday(&now, NULL) == 0, "gettimeofday failed");
timeval_subtract(&offset_last_tx_packet,&now,&g_scenario->time_last_tx_packet);
timeval_subtract(&offset_last_rx_packet,&now,&g_scenario->time_last_rx_packet);
last_packet_was_tx = timeval_subtract(&offset_tx_rx,&offset_last_tx_packet,&offset_last_rx_packet);
if (last_packet_was_tx) {
we_are_too_early = timeval_subtract(&offset,&offset_last_tx_packet,&packet->time_relative_to_last_sent_packet);
} else {
we_are_too_early = timeval_subtract(&offset,&offset_last_rx_packet,&packet->time_relative_to_last_received_packet);
}
if (we_are_too_early > 0) {
// set timer
packet->status = ET_PACKET_STATUS_SCHEDULED_FOR_SENDING;
if (timer_setup (offset.tv_sec, offset.tv_usec, TASK_S1AP, INSTANCE_DEFAULT, TIMER_ONE_SHOT,
NULL, &packet->timer_id) < 0) {
AssertFatal(0, " Can not start TX event timer\n");
}
// Done g_scenario->fsm_state = ET_FSM_STATE_WAITING_TX_EVENT;
} else {
// send immediately
et_s1ap_eNB_itti_send_sctp_data_req(
packet->enb_instance,
packet->sctp_hdr.u.data_hdr.assoc_id,
packet->sctp_hdr.u.data_hdr.payload.binary_stream,
packet->sctp_hdr.u.data_hdr.payload.binary_stream_allocated_size,
packet->sctp_hdr.u.data_hdr.stream);
packet->status = ET_PACKET_STATUS_SENT;
g_scenario->fsm_state = ET_FSM_STATE_RUNNING;
}
break;
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
AssertFatal(0, "Invalid case TX packet SCTP_CID_INIT or SCTP_CID_INIT_ACK");
break;
default:
AssertFatal(0, "Invalid case TX packet SCTP_CID %d", packet->sctp_hdr.chunk_type);
}
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_running(et_event_t event)
{
et_packet_t *packet = NULL;
const Enb_properties_array_t *enb_properties_p = NULL;
uint32_t register_enb_pending;
switch (event.code){
case ET_EVENT_RX_PACKET_TIME_OUT:
AssertFatal(0, "Event ET_EVENT_RX_PACKET_TIME_OUT not handled in FSM state ET_FSM_STATE_RUNNING");
break;
case ET_EVENT_TX_TIMED_PACKET:
AssertFatal(0, "Event ET_EVENT_TX_TIMED_PACKET not handled in FSM state ET_FSM_STATE_RUNNING");
break;
case ET_EVENT_RX_S1AP:
AssertFatal(0, "TODO");
break;
default:
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_RUNNING", event.code);
}
pthread_mutex_unlock(&g_scenario->fsm_lock);
return 0;
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_waiting(et_event_t event)
{
switch (event.code){
case ET_EVENT_RX_PACKET_TIME_OUT:
fprintf(stderr, "Error The following packet is not received:\n");
et_display_packet(event.u.rx_packet_time_out);
AssertFatal(0, "Waited packet not received");
break;
case ET_EVENT_RX_S1AP:
et_s1ap_process_rx_packet(&event.u.s1ap_data_ind);
break;
case ET_EVENT_TX_TIMED_PACKET:
// send immediately
et_s1ap_eNB_itti_send_sctp_data_req(
event.u.tx_timed_packet->enb_instance,
event.u.tx_timed_packet->sctp_hdr.u.data_hdr.assoc_id,
event.u.tx_timed_packet->sctp_hdr.u.data_hdr.payload.binary_stream,
event.u.tx_timed_packet->sctp_hdr.u.data_hdr.payload.binary_stream_allocated_size,
event.u.tx_timed_packet->sctp_hdr.u.data_hdr.stream);
event.u.tx_timed_packet->status = ET_PACKET_STATUS_SENT;
g_scenario->fsm_state = ET_FSM_STATE_RUNNING;
break;
default:
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_WAITING", event.code);
}
pthread_mutex_unlock(&g_scenario->fsm_lock);
return 0;
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_connecting_s1c(et_event_t event)
{
switch (event.code){
case ET_EVENT_S1C_CONNECTED:
// hack simulate we have been able to get the right timing values
AssertFatal(gettimeofday(&g_scenario->time_last_tx_packet, NULL) == 0, "gettimeofday failed");
AssertFatal(gettimeofday(&g_scenario->time_last_rx_packet, NULL) == 0, "gettimeofday failed");
while (NULL != g_scenario->next_packet) {
switch (g_scenario->next_packet->sctp_hdr.chunk_type) {
case SCTP_CID_DATA :
// no init in this scenario, may be sub-scenario
if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_SEND) {
et_scenario_schedule_tx_packet(g_scenario->next_packet);
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
} else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
if (g_scenario->next_packet->status == ET_PACKET_STATUS_RECEIVED) {
g_scenario->last_rx_packet = g_scenario->next_packet;
g_scenario->next_packet = g_scenario->next_packet->next;
g_scenario->time_last_rx_packet = g_scenario->last_rx_packet->timestamp_packet;
g_scenario->next_packet = g_scenario->next_packet->next;
} else if (g_scenario->next_packet->status == ET_PACKET_STATUS_NONE) {
et_scenario_wait_rx_packet(g_scenario->next_packet);
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
} else {
AssertFatal(0, "Invalid packet status %d", g_scenario->next_packet->status);
}
} else {
AssertFatal(0, "Invalid packet action %d", g_scenario->next_packet->action);
}
break;
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
case SCTP_CID_HEARTBEAT:
case SCTP_CID_HEARTBEAT_ACK:
case SCTP_CID_COOKIE_ECHO:
case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR:
g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
g_scenario->next_packet = g_scenario->next_packet->next;
break;
case SCTP_CID_ABORT:
case SCTP_CID_SHUTDOWN:
case SCTP_CID_SHUTDOWN_ACK:
case SCTP_CID_ERROR:
case SCTP_CID_SHUTDOWN_COMPLETE:
AssertFatal(0, "The scenario should be cleaned (packet %s cannot be processed at this time)",
et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
break;
default:
g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
g_scenario->next_packet = g_scenario->next_packet->next;
}
}
fprintf(stderr, "No Packet found in this scenario: %s\n", g_scenario->name);
g_scenario->fsm_state = ET_FSM_STATE_NULL;
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
break;
default:
AssertFatal(0, "Case event %d not handled in ET_FSM_STATE_CONNECTING_S1C", event.code);
}
pthread_mutex_unlock(&g_scenario->fsm_lock);
return 0;
}
//------------------------------------------------------------------------------
et_fsm_state_t et_scenario_fsm_notify_event_state_null(et_event_t event)
{
switch (event.code){
case ET_EVENT_INIT:
AssertFatal(NULL == g_scenario, "Current scenario not ended");
g_scenario = event.u.init.scenario;
packet = g_scenario->list_packet;
while (NULL != packet) {
switch (packet->sctp_hdr.chunk_type) {
g_scenario->next_packet = g_scenario->list_packet;
while (NULL != g_scenario->next_packet) {
switch (g_scenario->next_packet->sctp_hdr.chunk_type) {
case SCTP_CID_DATA :
// no init in this scenario, may be sub-scenario
if (packet->action == ET_PACKET_ACTION_S1C_SEND) {
} else if (packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
g_scenario->waited_packet = packet;
if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_SEND) {
et_scenario_schedule_tx_packet(g_scenario->next_packet);
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
} else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
if (g_scenario->next_packet->status == ET_PACKET_STATUS_RECEIVED) {
g_scenario->last_rx_packet = g_scenario->next_packet;
g_scenario->next_packet = g_scenario->next_packet->next;
g_scenario->time_last_rx_packet = g_scenario->last_rx_packet->timestamp_packet;
g_scenario->next_packet = g_scenario->next_packet->next;
} else if (g_scenario->next_packet->status == ET_PACKET_STATUS_NONE) {
et_scenario_wait_rx_packet(g_scenario->next_packet);
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
} else {
AssertFatal(0, "Invalid packet status %d", g_scenario->next_packet->status);
}
} else {
packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
packet = packet->next;
AssertFatal(0, "Invalid packet action %d", g_scenario->next_packet->action);
}
break;
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
enb_properties_p = enb_config_get();
/* Try to register each eNB */
g_fsm_state = ET_FSM_STATE_CONNECTING_SCTP;
register_enb_pending = et_eNB_app_register (enb_properties_p);
g_scenario->enb_properties = (Enb_properties_array_t *)et_enb_config_get();
g_scenario->hash_old_ue_mme_id2ue_mme_id = hashtable_create (256,NULL,NULL);
g_scenario->hash_mme2association_id = hashtable_create (256,NULL,NULL);
// Try to register each eNB
g_scenario->registered_enb = 0;
g_scenario->fsm_state = ET_FSM_STATE_CONNECTING_S1C;
g_scenario->register_enb_pending = et_eNB_app_register (g_scenario->enb_properties);
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
break;
case SCTP_CID_HEARTBEAT:
case SCTP_CID_HEARTBEAT_ACK:
case SCTP_CID_COOKIE_ECHO:
case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR:
packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
packet = packet->next;
g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
g_scenario->next_packet = g_scenario->next_packet->next;
break;
case SCTP_CID_ABORT:
case SCTP_CID_SHUTDOWN:
case SCTP_CID_SHUTDOWN_ACK:
case SCTP_CID_ERROR:
case SCTP_CID_SHUTDOWN_COMPLETE:
AssertFatal(0, "The scenario should be cleaned (packet %s cannot be processed at this time)", et_chunk_type_cid2str(packet->sctp_hdr.chunk_type));
AssertFatal(0, "The scenario should be cleaned (packet %s cannot be processed at this time)",
et_chunk_type_cid2str(g_scenario->next_packet->sctp_hdr.chunk_type));
break;
default:
packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
packet = packet->next;
g_scenario->next_packet->status = ET_PACKET_STATUS_NOT_TAKEN_IN_ACCOUNT;
g_scenario->next_packet = g_scenario->next_packet->next;
}
}
fprintf(stderr, "No Packet found in this scenario: %s\n", g_scenario->name);
return -1;
break;
case ET_EVENT_STOP:
fprintf(stderr, "No Useful packet found in this scenario: %s\n", g_scenario->name);
g_scenario->fsm_state = ET_FSM_STATE_NULL;
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
break;
default:
......@@ -112,16 +360,19 @@ int et_scenario_fsm_notify_event_state_null(et_event_t event)
}
//------------------------------------------------------------------------------
int et_scenario_fsm_notify_event(et_event_t event)
et_fsm_state_t et_scenario_fsm_notify_event(et_event_t event)
{
AssertFatal((event.code >= ET_EVENT_START) && (event.code < ET_EVENT_END), "Unknown et_event_t.code %d", event.code);
switch (g_fsm_state){
pthread_mutex_lock(&g_scenario->fsm_lock);
switch (g_scenario->fsm_state){
case ET_FSM_STATE_NULL: return et_scenario_fsm_notify_event_state_null(event); break;
case ET_FSM_STATE_CONNECTING_SCTP: return et_scenario_fsm_notify_event_state_null(event); break;
case ET_FSM_STATE_WAITING_TX_EVENT: return et_scenario_fsm_notify_event_state_null(event); break;
case ET_FSM_STATE_WAITING_RX_EVENT: return et_scenario_fsm_notify_event_state_null(event); break;
case ET_FSM_STATE_CONNECTING_S1C: return et_scenario_fsm_notify_event_state_connecting_s1c(event); break;
case ET_FSM_STATE_WAITING_EVENT: return et_scenario_fsm_notify_event_state_waiting(event); break;
case ET_FSM_STATE_RUNNING: return et_scenario_fsm_notify_event_state_running(event); break;
default:
return -1;
AssertFatal(0, "Case fsm_state %d not handled", g_scenario->fsm_state);
}
pthread_mutex_unlock(&g_scenario->fsm_lock);
return g_scenario->fsm_state;
}
......@@ -47,17 +47,19 @@
#include <libxslt/transform.h>
#include <libxslt/xsltutils.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "intertask_interface.h"
#include "platform_types.h"
#include "enb_config.h"
#include "assertions.h"
#include "play_scenario.h"
//------------------------------------------------------------------------------
#define ENB_CONFIG_MAX_XSLT_PARAMS 32
//------------------------------------------------------------------------------
extern char *g_openair_dir;
extern Enb_properties_array_t enb_properties;
extern Enb_properties_array_t g_enb_properties;
//------------------------------------------------------------------------------
void et_parse_s1ap(xmlDocPtr doc, const xmlNode const *s1ap_node, et_s1ap_t * const s1ap)
{
......@@ -407,6 +409,10 @@ et_packet_t* et_parse_xml_packet(xmlDocPtr doc, xmlNodePtr node) {
}
xmlFree(xml_char);
}
} else if ((!xmlStrcmp(cur_node->name, (const xmlChar *)"eNB.instance"))) {
xml_char = xmlGetProp((xmlNode *)cur_node, (const xmlChar *)"value");
packet->enb_instance = strtoul((const char *)xml_char, NULL, 0);
xmlFree(xml_char);
}
//}
}
......@@ -439,8 +445,11 @@ et_scenario_t* et_generate_scenario(
if ((!xmlStrcmp(root->name, (const xmlChar *)"scenario"))) {
xml_char = xmlGetProp(root, (const xmlChar *)"name");
printf("scenario name: %s\n", xml_char);
scenario = calloc(1, sizeof(*scenario));
scenario->name = xml_char; // nodup nofree
scenario = calloc(1, sizeof(*scenario));
scenario->name = xml_char; // nodup nofree
scenario->fsm_state = ET_FSM_STATE_NULL;
pthread_mutex_init(&scenario->fsm_lock, NULL);
next_packet = &scenario->list_packet;
for (node = root->children; node != NULL; node = node->next) {
if ((!xmlStrcmp(node->name, (const xmlChar *)"packet"))) {
......@@ -509,21 +518,21 @@ int et_generate_xml_scenario(
fprintf(stdout, "Test scenario file: %s\n", xml_in_scenario_filename);
}
for (i = 0; i < enb_properties.number; i++) {
for (i = 0; i < g_enb_properties.number; i++) {
// eNB S1-C IPv4 address
sprintf(astring, "enb_s1c%d", i);
params[nb_params++] = strdup(astring);
addr.s_addr = enb_properties.properties[i]->enb_ipv4_address_for_S1_MME;
addr.s_addr = g_enb_properties.properties[i]->enb_ipv4_address_for_S1_MME;
sprintf(astring, "\"%s\"", inet_ntoa(addr));
params[nb_params++] = strdup(astring);
// MME S1-C IPv4 address
for (j = 0; j < enb_properties.properties[i]->nb_mme; j++) {
for (j = 0; j < g_enb_properties.properties[i]->nb_mme; j++) {
sprintf(astring, "mme_s1c%d_%d", i, j);
params[nb_params++] = strdup(astring);
AssertFatal (enb_properties.properties[i]->mme_ip_address[j].ipv4_address,
AssertFatal (g_enb_properties.properties[i]->mme_ip_address[j].ipv4_address,
"Only support MME IPv4 address\n");
sprintf(astring, "\"%s\"", enb_properties.properties[i]->mme_ip_address[j].ipv4_address);
sprintf(astring, "\"%s\"", g_enb_properties.properties[i]->mme_ip_address[j].ipv4_address);
params[nb_params++] = strdup(astring);
}
}
......
......@@ -38,29 +38,173 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <crypt.h>
#include "tree.h"
#include "queue.h"
#include "intertask_interface.h"
#include "messages_types.h"
#include "timer.h"
#include "platform_types.h"
#include "assertions.h"
#include "conversions.h"
#include "s1ap_common.h"
#include "s1ap_eNB_defs.h"
#include "s1ap_eNB_default_values.h"
#include "s1ap_eNB_management_procedures.h"
#include "s1ap_eNB.h"
#include "play_scenario_s1ap_eNB_defs.h"
#include "play_scenario.h"
#include "msc.h"
#include "assertions.h"
#include "conversions.h"
//------------------------------------------------------------------------------
s1ap_eNB_internal_data_t s1ap_eNB_internal_data;
RB_GENERATE(s1ap_mme_map, s1ap_eNB_mme_data_s, entry, et_s1ap_eNB_compare_assoc_id);
//------------------------------------------------------------------------------
extern et_scenario_t *g_scenario;
//------------------------------------------------------------------------------
int et_s1ap_eNB_compare_assoc_id(
struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_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;
}
//------------------------------------------------------------------------------
uint32_t et_s1ap_generate_eNB_id(void)
{
char *out;
char hostname[50];
int ret;
uint32_t eNB_id;
/* Retrieve the host name */
ret = gethostname(hostname, sizeof(hostname));
DevAssert(ret == 0);
out = crypt(hostname, "eurecom");
DevAssert(out != NULL);
eNB_id = ((out[0] << 24) | (out[1] << 16) | (out[2] << 8) | out[3]);
return eNB_id;
}
//------------------------------------------------------------------------------
uint16_t et_s1ap_eNB_fetch_add_global_cnx_id(void)
{
return ++s1ap_eNB_internal_data.global_cnx_id;
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_prepare_internal_data(void)
{
memset(&s1ap_eNB_internal_data, 0, sizeof(s1ap_eNB_internal_data));
STAILQ_INIT(&s1ap_eNB_internal_data.s1ap_eNB_instances_head);
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_insert_new_instance(s1ap_eNB_instance_t *new_instance_p)
{
DevAssert(new_instance_p != NULL);
STAILQ_INSERT_TAIL(&s1ap_eNB_internal_data.s1ap_eNB_instances_head,
new_instance_p, s1ap_eNB_entries);
}
//------------------------------------------------------------------------------
struct s1ap_eNB_mme_data_s *et_s1ap_eNB_get_MME(
s1ap_eNB_instance_t *instance_p,
int32_t assoc_id, uint16_t cnx_id)
{
struct s1ap_eNB_mme_data_s temp;
struct s1ap_eNB_mme_data_s *found;
memset(&temp, 0, sizeof(struct s1ap_eNB_mme_data_s));
temp.assoc_id = assoc_id;
temp.cnx_id = cnx_id;
if (instance_p == NULL) {
STAILQ_FOREACH(instance_p, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
s1ap_eNB_entries) {
found = RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
if (found != NULL) {
return found;
}
}
} else {
return RB_FIND(s1ap_mme_map, &instance_p->s1ap_mme_head, &temp);
}
return NULL;
}
//------------------------------------------------------------------------------
s1ap_eNB_instance_t *et_s1ap_eNB_get_instance(instance_t instance)
{
s1ap_eNB_instance_t *temp = NULL;
STAILQ_FOREACH(temp, &s1ap_eNB_internal_data.s1ap_eNB_instances_head,
s1ap_eNB_entries) {
if (temp->instance == instance) {
/* Matching occurence */
return temp;
}
}
return NULL;
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_itti_send_sctp_data_req(instance_t instance, int32_t assoc_id, uint8_t *buffer,
uint32_t buffer_length, uint16_t stream)
{
MessageDef *message_p;
sctp_data_req_t *sctp_data_req;
message_p = itti_alloc_new_message(TASK_S1AP, SCTP_DATA_REQ);
sctp_data_req = &message_p->ittiMsg.sctp_data_req;
sctp_data_req->assoc_id = assoc_id;
sctp_data_req->buffer = buffer;
sctp_data_req->buffer_length = buffer_length;
sctp_data_req->stream = stream;
itti_send_msg_to_task(TASK_SCTP, instance, message_p);
}
//------------------------------------------------------------------------------
extern void s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp);
extern void s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB);
void et_s1ap_process_rx_packet(et_event_s1ap_data_ind_t * const sctp_data_ind)
{
et_packet_t * packet = NULL;
unsigned long int not_found = 1;
packet = g_scenario->next_packet;
// not_found threshold may sure depend on number of mme, may be not sure on number of UE
while ((NULL != packet) && (not_found < 5)) {
if (packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
//TODO
}
not_found += 1;
packet = packet->next;
}
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind)
void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t * const sctp_data_ind)
{
int result = 0;
et_event_t event;
......@@ -73,7 +217,8 @@ void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind)
event.u.s1ap_data_ind.sctp_datahdr.tsn = 0;
event.u.s1ap_data_ind.sctp_datahdr.stream = sctp_data_ind->stream;
event.u.s1ap_data_ind.sctp_datahdr.ssn = 0;
event.u.s1ap_data_ind.sctp_datahdr.ppid = 18; // find constant
event.u.s1ap_data_ind.sctp_datahdr.ppid = S1AP_SCTP_PPID;
event.u.s1ap_data_ind.sctp_datahdr.assoc_id = sctp_data_ind->assoc_id;
event.u.s1ap_data_ind.sctp_datahdr.payload.binary_stream_pos = 0;
event.u.s1ap_data_ind.sctp_datahdr.payload.binary_stream_allocated_size = sctp_data_ind->buffer_length;
......@@ -100,6 +245,253 @@ void et_s1ap_eNB_handle_sctp_data_ind(sctp_data_ind_t *sctp_data_ind)
et_scenario_fsm_notify_event(event);
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_register_mme(s1ap_eNB_instance_t *instance_p,
net_ip_address_t *mme_ip_address,
net_ip_address_t *local_ip_addr,
uint16_t in_streams,
uint16_t out_streams)
{
MessageDef *message_p = NULL;
sctp_new_association_req_t *sctp_new_association_req_p = NULL;
s1ap_eNB_mme_data_t *s1ap_mme_data_p = NULL;
DevAssert(instance_p != NULL);
DevAssert(mme_ip_address != NULL);
message_p = itti_alloc_new_message(TASK_S1AP, SCTP_NEW_ASSOCIATION_REQ);
sctp_new_association_req_p = &message_p->ittiMsg.sctp_new_association_req;
sctp_new_association_req_p->port = S1AP_PORT_NUMBER;
sctp_new_association_req_p->ppid = S1AP_SCTP_PPID;
sctp_new_association_req_p->in_streams = in_streams;
sctp_new_association_req_p->out_streams = out_streams;
memcpy(&sctp_new_association_req_p->remote_address,
mme_ip_address,
sizeof(*mme_ip_address));
memcpy(&sctp_new_association_req_p->local_address,
local_ip_addr,
sizeof(*local_ip_addr));
/* Create new MME descriptor */
s1ap_mme_data_p = calloc(1, sizeof(*s1ap_mme_data_p));
DevAssert(s1ap_mme_data_p != NULL);
s1ap_mme_data_p->cnx_id = et_s1ap_eNB_fetch_add_global_cnx_id();
sctp_new_association_req_p->ulp_cnx_id = s1ap_mme_data_p->cnx_id;
s1ap_mme_data_p->assoc_id = -1;
s1ap_mme_data_p->s1ap_eNB_instance = instance_p;
memcpy((void*)&s1ap_mme_data_p->mme_net_ip_address, mme_ip_address, sizeof(*mme_ip_address));
STAILQ_INIT(&s1ap_mme_data_p->served_gummei);
/* Insert the new descriptor in list of known MME
* but not yet associated.
*/
RB_INSERT(s1ap_mme_map, &instance_p->s1ap_mme_head, s1ap_mme_data_p);
s1ap_mme_data_p->state = S1AP_ENB_STATE_WAITING;
instance_p->s1ap_mme_nb ++;
instance_p->s1ap_mme_pending_nb ++;
itti_send_msg_to_task(TASK_SCTP, instance_p->instance, message_p);
}
//------------------------------------------------------------------------------
void et_s1ap_update_assoc_id_of_packets(const int32_t assoc_id,
struct s1ap_eNB_instance_s * const s1ap_eNB_instance,
s1ap_eNB_mme_data_t * const mme_desc_p)
{
et_packet_t *packet = NULL;
struct in6_addr s1c_mme_ipv6 = IN6ADDR_ANY_INIT;
in_addr_t s1c_mme_ipv4 = INADDR_ANY;
struct in6_addr s1c_enb_ipv6 = IN6ADDR_ANY_INIT;
in_addr_t s1c_enb_ipv4 = INADDR_ANY;
packet = g_scenario->next_packet;
while (NULL != packet) {
switch (packet->sctp_hdr.chunk_type) {
case SCTP_CID_DATA :
if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_SEND) {
// TODO compare addresses and ports
if (packet->ip_hdr.dst)
packet->sctp_hdr.dst_port == 0;
packet->sctp_hdr.src_port == 0;
} else if (g_scenario->next_packet->action == ET_PACKET_ACTION_S1C_RECEIVE) {
}
break;
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
case SCTP_CID_HEARTBEAT:
case SCTP_CID_HEARTBEAT_ACK:
case SCTP_CID_COOKIE_ECHO:
case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR:
break;
case SCTP_CID_ABORT:
case SCTP_CID_SHUTDOWN:
case SCTP_CID_SHUTDOWN_ACK:
case SCTP_CID_ERROR:
case SCTP_CID_SHUTDOWN_COMPLETE:
//TODO
break;
default:
;
}
packet = packet->next;
}
}
//------------------------------------------------------------------------------
void et_s1ap_handle_s1_setup_message(s1ap_eNB_mme_data_t *mme_desc_p, int sctp_shutdown)
{
if (sctp_shutdown) {
/* A previously connected MME has been shutdown */
/* TODO check if it was used by some eNB and send a message to inform these eNB if there is no more associated MME */
if (mme_desc_p->state == S1AP_ENB_STATE_CONNECTED) {
mme_desc_p->state = S1AP_ENB_STATE_DISCONNECTED;
if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb > 0) {
/* Decrease associated MME number */
mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb --;
}
/* If there are no more associated MME, inform eNB app */
if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb == 0) {
MessageDef *message_p;
message_p = itti_alloc_new_message(TASK_S1AP, S1AP_DEREGISTERED_ENB_IND);
S1AP_DEREGISTERED_ENB_IND(message_p).nb_mme = 0;
itti_send_msg_to_task(TASK_ENB_APP, mme_desc_p->s1ap_eNB_instance->instance, message_p);
}
}
} else {
/* Check that at least one setup message is pending */
DevCheck(mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb > 0, mme_desc_p->s1ap_eNB_instance->instance,
mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb, 0);
if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb > 0) {
/* Decrease pending messages number */
mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb --;
}
et_s1ap_update_assoc_id_of_packets(mme_desc_p->assoc_id,
mme_desc_p->s1ap_eNB_instance,
mme_desc_p);
/* If there are no more pending messages, inform eNB app */
if (mme_desc_p->s1ap_eNB_instance->s1ap_mme_pending_nb == 0) {
MessageDef *message_p;
message_p = itti_alloc_new_message(TASK_S1AP, S1AP_REGISTER_ENB_CNF);
S1AP_REGISTER_ENB_CNF(message_p).nb_mme = mme_desc_p->s1ap_eNB_instance->s1ap_mme_associated_nb;
itti_send_msg_to_task(TASK_ENB_APP, mme_desc_p->s1ap_eNB_instance->instance, message_p);
}
}
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_handle_register_eNB(instance_t instance, s1ap_register_enb_req_t *s1ap_register_eNB)
{
s1ap_eNB_instance_t *new_instance;
uint8_t index;
DevAssert(s1ap_register_eNB != NULL);
/* Look if the provided instance already exists */
new_instance = et_s1ap_eNB_get_instance(instance);
if (new_instance != NULL) {
/* Checks if it is a retry on the same eNB */
DevCheck(new_instance->eNB_id == s1ap_register_eNB->eNB_id, new_instance->eNB_id, s1ap_register_eNB->eNB_id, 0);
DevCheck(new_instance->cell_type == s1ap_register_eNB->cell_type, new_instance->cell_type, s1ap_register_eNB->cell_type, 0);
DevCheck(new_instance->tac == s1ap_register_eNB->tac, new_instance->tac, s1ap_register_eNB->tac, 0);
DevCheck(new_instance->mcc == s1ap_register_eNB->mcc, new_instance->mcc, s1ap_register_eNB->mcc, 0);
DevCheck(new_instance->mnc == s1ap_register_eNB->mnc, new_instance->mnc, s1ap_register_eNB->mnc, 0);
DevCheck(new_instance->mnc_digit_length == s1ap_register_eNB->mnc_digit_length, new_instance->mnc_digit_length, s1ap_register_eNB->mnc_digit_length, 0);
} else {
new_instance = calloc(1, sizeof(s1ap_eNB_instance_t));
DevAssert(new_instance != NULL);
RB_INIT(&new_instance->s1ap_ue_head);
RB_INIT(&new_instance->s1ap_mme_head);
/* Copy usefull parameters */
new_instance->instance = instance;
new_instance->eNB_name = s1ap_register_eNB->eNB_name;
new_instance->eNB_id = s1ap_register_eNB->eNB_id;
new_instance->cell_type = s1ap_register_eNB->cell_type;
new_instance->tac = s1ap_register_eNB->tac;
new_instance->mcc = s1ap_register_eNB->mcc;
new_instance->mnc = s1ap_register_eNB->mnc;
new_instance->mnc_digit_length = s1ap_register_eNB->mnc_digit_length;
/* Add the new instance to the list of eNB (meaningfull in virtual mode) */
et_s1ap_eNB_insert_new_instance(new_instance);
S1AP_DEBUG("Registered new eNB[%d] and %s eNB id %u\n",
instance,
s1ap_register_eNB->cell_type == CELL_MACRO_ENB ? "macro" : "home",
s1ap_register_eNB->eNB_id);
}
DevCheck(s1ap_register_eNB->nb_mme <= S1AP_MAX_NB_MME_IP_ADDRESS,
S1AP_MAX_NB_MME_IP_ADDRESS, s1ap_register_eNB->nb_mme, 0);
/* Trying to connect to provided list of MME ip address */
for (index = 0; index < s1ap_register_eNB->nb_mme; index++) {
et_s1ap_eNB_register_mme(new_instance,
&s1ap_register_eNB->mme_ip_address[index],
&s1ap_register_eNB->enb_ip_address,
s1ap_register_eNB->sctp_in_streams,
s1ap_register_eNB->sctp_out_streams);
}
}
//------------------------------------------------------------------------------
void et_s1ap_eNB_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp)
{
s1ap_eNB_instance_t *instance_p = NULL;
s1ap_eNB_mme_data_t *s1ap_mme_data_p = NULL;
DevAssert(sctp_new_association_resp != NULL);
instance_p = et_s1ap_eNB_get_instance(instance);
DevAssert(instance_p != NULL);
s1ap_mme_data_p = et_s1ap_eNB_get_MME(instance_p, -1,
sctp_new_association_resp->ulp_cnx_id);
DevAssert(s1ap_mme_data_p != NULL);
if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) {
S1AP_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);
et_s1ap_handle_s1_setup_message(s1ap_mme_data_p, sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
return;
}
/* Update parameters */
s1ap_mme_data_p->assoc_id = sctp_new_association_resp->assoc_id;
s1ap_mme_data_p->in_streams = sctp_new_association_resp->in_streams;
s1ap_mme_data_p->out_streams = sctp_new_association_resp->out_streams;
et_s1ap_handle_s1_setup_message(s1ap_mme_data_p, sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN);
}
//------------------------------------------------------------------------------
void *et_s1ap_eNB_task(void *arg)
{
......@@ -108,7 +500,7 @@ void *et_s1ap_eNB_task(void *arg)
S1AP_DEBUG("Starting S1AP layer\n");
s1ap_eNB_prepare_internal_data();
et_s1ap_eNB_prepare_internal_data();
itti_mark_task_ready(TASK_S1AP);
MSC_START_USE();
......@@ -127,13 +519,13 @@ void *et_s1ap_eNB_task(void *arg)
* Each eNB has to send an S1AP_REGISTER_ENB message with its
* own parameters.
*/
s1ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
et_s1ap_eNB_handle_register_eNB(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&S1AP_REGISTER_ENB_REQ(received_msg));
}
break;
case SCTP_NEW_ASSOCIATION_RESP: {
s1ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
et_s1ap_eNB_handle_sctp_association_resp(ITTI_MESSAGE_GET_INSTANCE(received_msg),
&received_msg->ittiMsg.sctp_new_association_resp);
}
break;
......@@ -143,6 +535,31 @@ void *et_s1ap_eNB_task(void *arg)
}
break;
case TIMER_HAS_EXPIRED:
LOG_I(S1AP, " Received TIMER_HAS_EXPIRED: timer_id %d\n", TIMER_HAS_EXPIRED(received_msg).timer_id);
{
et_packet_t * packet = (et_packet_t*)TIMER_HAS_EXPIRED (received_msg).arg;
et_event_t event;
if (packet->status == ET_PACKET_STATUS_SCHEDULED_FOR_RECEIVING) {
memset((void*)&event, 0, sizeof(event));
event.code = ET_EVENT_RX_PACKET_TIME_OUT;
event.u.rx_packet_time_out = packet;
et_scenario_fsm_notify_event(event);
} else if (packet->status == ET_PACKET_STATUS_SCHEDULED_FOR_SENDING) {
memset((void*)&event, 0, sizeof(event));
event.code = ET_EVENT_TX_TIMED_PACKET;
event.u.tx_timed_packet = packet;
et_scenario_fsm_notify_event(event);
} else if ((packet->status != ET_PACKET_STATUS_SENT) && ((packet->status != ET_PACKET_STATUS_RECEIVED))) {
AssertFatal (0, "Bad status %d of packet timed out!\n", packet->status);
}
}
if (TIMER_HAS_EXPIRED (received_msg).timer_id == g_scenario->enb_register_retry_timer_id) {
/* Restart the registration process */
g_scenario->registered_enb = 0;
g_scenario->register_enb_pending = et_eNB_app_register (g_scenario->enb_properties);
}
break;
default:
S1AP_ERROR("Received unhandled message: %d:%s\n",
......
/*******************************************************************************
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, Compus SophiaTech 450, route des chappes, 06451 Biot, France.
*******************************************************************************/
#include <stdint.h>
#include "queue.h"
#include "tree.h"
#include "sctp_eNB_defs.h"
#ifndef PLAY_SCENARIO_S1AP_ENB_DEFS_H_
#define PLAY_SCENARIO_S1AP_ENB_DEFS_H_
#define ENB_TAC (1)
#define ENB_MCC (208)
#define ENB_MNC (92)
#define ENB_NAME "Eurecom ENB"
#define ENB_NAME_FORMAT (ENB_NAME" %u")
#define S1AP_PORT_NUMBER (36412)
#define S1AP_SCTP_PPID (18)
#define X2AP_PORT_NUMBER (36422)
#define X2AP_SCTP_PPID (27)
#define S1AP_ENB_NAME_LENGTH_MAX (150)
typedef enum {
/* Disconnected state: initial state for any association. */
S1AP_ENB_STATE_DISCONNECTED = 0x0,
/* State waiting for S1 Setup response message if eNB is MME accepted or
* S1 Setup failure if MME rejects the eNB.
*/
S1AP_ENB_STATE_WAITING = 0x1,
/* The eNB is successfully connected to MME, UE contexts can be created. */
S1AP_ENB_STATE_CONNECTED = 0x2,
/* The MME has sent an overload start message. Once the MME disables the
* OVERLOAD marker, the state of the association will be
* S1AP_ENB_STATE_CONNECTED.
*/
S1AP_ENB_OVERLOAD = 0x3,
/* Max number of states available */
S1AP_ENB_STATE_MAX,
} s1ap_eNB_state_t;
/* If the Overload Action IE in the OVERLOAD START message is set to
* - “reject all RRC connection establishments for non-emergency mobile
* originated data transfer “ (i.e. reject traffic corresponding to RRC cause
* “mo-data “ (TS 36.331 [16])), or
* - “reject all RRC connection establishments for signalling “ (i.e. reject
* traffic corresponding to RRC cause “modata” and “mo-signalling”
* (TS 36.331 [16])),or
* - “only permit RRC connection establishments for emergency sessions and
* mobile terminated services” (i.e. only permit traffic corresponding to RRC
* cause “emergency” and “mt-Access” (TS 36.331 [16])).
*
* NOTE: When the Overload Action IE is set to “only permit RRC connection
* establishments for emergency sessions and mobile terminated services”,
* emergency calls with RRC cause “highPriorityAcess” from high priority users
* are rejected (TS 24.301 [24]).
*/
typedef enum {
S1AP_OVERLOAD_REJECT_MO_DATA = 0x0,
S1AP_OVERLOAD_REJECT_ALL_SIGNALLING = 0x1,
S1AP_OVERLOAD_ONLY_EMERGENCY_AND_MT = 0x2,
S1AP_NO_OVERLOAD = 0x3,
S1AP_OVERLOAD_MAX,
} s1ap_overload_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 mme_group_id;
STAILQ_ENTRY(served_group_id_s) next;
};
/* Served mme code for a particular MME */
struct mme_code_s {
uint8_t mme_code;
STAILQ_ENTRY(mme_code_s) next;
};
/* Served gummei element */
struct served_gummei_s {
/* Number of MME served PLMNs */
uint8_t nb_served_plmns;
/* List of served PLMNs by MME */
STAILQ_HEAD(served_plmns_s, plmn_identity_s) served_plmns;
/* Number of group id in list */
uint8_t nb_group_id;
/* Served group id list */
STAILQ_HEAD(served_group_ids_s, served_group_id_s) served_group_ids;
/* Number of MME code */
uint8_t nb_mme_code;
/* MME Code to uniquely identify an MME within an MME pool area */
STAILQ_HEAD(mme_codes_s, mme_code_s) mme_codes;
/* Next GUMMEI element */
STAILQ_ENTRY(served_gummei_s) next;
};
struct s1ap_eNB_instance_s;
/* This structure describes association of a eNB to a MME */
typedef struct s1ap_eNB_mme_data_s {
/* MME descriptors tree, ordered by sctp assoc id */
RB_ENTRY(s1ap_eNB_mme_data_s) entry;
/* This is the optional name provided by the MME */
char *mme_name;
net_ip_address_t mme_net_ip_address; // useful for joining assoc_id and ip address of packets
/* List of served GUMMEI per MME. There is one GUMMEI per RAT with a max
* number of 8 RATs but in our case only one is used. The LTE related pool
* configuration is included on the first place in the list.
*/
STAILQ_HEAD(served_gummeis_s, served_gummei_s) served_gummei;
/* Relative processing capacity of an MME with respect to the other MMEs
* in the pool in order to load-balance MMEs within a pool as defined
* in TS 23.401.
*/
uint8_t relative_mme_capacity;
/* Current MME overload information (if any). */
s1ap_overload_state_t overload_state;
/* Current eNB->MME S1AP association state */
s1ap_eNB_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/S1AP */
uint16_t cnx_id;
/* SCTP association id */
int32_t assoc_id;
/* Only meaningfull in virtual mode */
struct s1ap_eNB_instance_s *s1ap_eNB_instance;
} s1ap_eNB_mme_data_t;
typedef struct s1ap_eNB_instance_s {
/* Next s1ap eNB association.
* Only used for virtual mode.
*/
STAILQ_ENTRY(s1ap_eNB_instance_s) s1ap_eNB_entries;
/* Number of MME requested by eNB (tree size) */
uint32_t s1ap_mme_nb;
/* Number of MME for which association is pending */
uint32_t s1ap_mme_pending_nb;
/* Number of MME successfully associated to eNB */
uint32_t s1ap_mme_associated_nb;
/* Tree of S1AP MME associations ordered by association ID */
RB_HEAD(s1ap_mme_map, s1ap_eNB_mme_data_s) s1ap_mme_head;
/* TODO: add a map ordered by relative MME capacity */
/* Tree of UE ordered by eNB_ue_s1ap_id's */
RB_HEAD(s1ap_ue_map, s1ap_eNB_ue_context_s) s1ap_ue_head;
/* For virtual mode, mod_id as defined in the rest of the L1/L2 stack */
instance_t instance;
/* Displayable name of eNB */
char *eNB_name;
net_ip_address_t s1c_net_ip_address;
/* Unique eNB_id to identify the eNB within EPC.
* In our case the eNB is a macro eNB so the id will be 20 bits long.
* For Home eNB id, this field should be 28 bits long.
*/
uint32_t eNB_id;
/* The type of the cell */
enum cell_type_e 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;
} s1ap_eNB_instance_t;
typedef struct {
/* List of served eNBs
* Only used for virtual mode
*/
STAILQ_HEAD(s1ap_eNB_instances_head_s, s1ap_eNB_instance_s) s1ap_eNB_instances_head;
/* Nb of registered eNBs */
uint8_t nb_registered_eNBs;
/* Generate a unique connexion id used between S1AP and SCTP */
uint16_t global_cnx_id;
} s1ap_eNB_internal_data_t;
int s1ap_eNB_compare_assoc_id(
struct s1ap_eNB_mme_data_s *p1, struct s1ap_eNB_mme_data_s *p2);
/* Generate the tree management functions */
RB_PROTOTYPE(s1ap_mme_map, s1ap_eNB_mme_data_s, entry,
s1ap_eNB_compare_assoc_id);
#endif /* PLAY_SCENARIO_S1AP_ENB_DEFS_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment