From e58be6bbc14cc1b1bde0a126d9bd9bd9206bca8c Mon Sep 17 00:00:00 2001 From: gauthier <lionel.gauthier@eurecom.fr> Date: Mon, 23 Nov 2015 16:54:44 +0100 Subject: [PATCH] Remaining TODO fill assoc_id and mme_ue_id of packets --- cmake_targets/CMakeLists.txt | 5 +- openair3/TEST/EPC_TEST/generate_scenario.c | 4 +- openair3/TEST/EPC_TEST/generic_scenario.xsl | 144 ++++-- openair3/TEST/EPC_TEST/play_scenario.c | 309 ++++++++++-- openair3/TEST/EPC_TEST/play_scenario.h | 186 +++++++- openair3/TEST/EPC_TEST/play_scenario_fsm.c | 319 +++++++++++-- openair3/TEST/EPC_TEST/play_scenario_parse.c | 27 +- openair3/TEST/EPC_TEST/play_scenario_s1ap.c | 445 +++++++++++++++++- .../EPC_TEST/play_scenario_s1ap_eNB_defs.h | 250 ++++++++++ 9 files changed, 1518 insertions(+), 171 deletions(-) create mode 100644 openair3/TEST/EPC_TEST/play_scenario_s1ap_eNB_defs.h diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt index 148aae24195..bcf90bdb83e 100644 --- a/cmake_targets/CMakeLists.txt +++ b/cmake_targets/CMakeLists.txt @@ -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} ) diff --git a/openair3/TEST/EPC_TEST/generate_scenario.c b/openair3/TEST/EPC_TEST/generate_scenario.c index 8f39b9873e7..9f4e16baefa 100644 --- a/openair3/TEST/EPC_TEST/generate_scenario.c +++ b/openair3/TEST/EPC_TEST/generate_scenario.c @@ -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"); diff --git a/openair3/TEST/EPC_TEST/generic_scenario.xsl b/openair3/TEST/EPC_TEST/generic_scenario.xsl index 1d49b4942e7..592bef83d41 100644 --- a/openair3/TEST/EPC_TEST/generic_scenario.xsl +++ b/openair3/TEST/EPC_TEST/generic_scenario.xsl @@ -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}"/--> diff --git a/openair3/TEST/EPC_TEST/play_scenario.c b/openair3/TEST/EPC_TEST/play_scenario.c index fda38dbcb8b..f29393f0e77 100644 --- a/openair3/TEST/EPC_TEST/play_scenario.c +++ b/openair3/TEST/EPC_TEST/play_scenario.c @@ -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); diff --git a/openair3/TEST/EPC_TEST/play_scenario.h b/openair3/TEST/EPC_TEST/play_scenario.h index 725b5abc0e9..3a52c6a0b81 100644 --- a/openair3/TEST/EPC_TEST/play_scenario.h +++ b/openair3/TEST/EPC_TEST/play_scenario.h @@ -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); diff --git a/openair3/TEST/EPC_TEST/play_scenario_fsm.c b/openair3/TEST/EPC_TEST/play_scenario_fsm.c index 6c73793f4df..8e59a982581 100644 --- a/openair3/TEST/EPC_TEST/play_scenario_fsm.c +++ b/openair3/TEST/EPC_TEST/play_scenario_fsm.c @@ -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; } diff --git a/openair3/TEST/EPC_TEST/play_scenario_parse.c b/openair3/TEST/EPC_TEST/play_scenario_parse.c index 236c17963ab..f6d8aa73ac9 100644 --- a/openair3/TEST/EPC_TEST/play_scenario_parse.c +++ b/openair3/TEST/EPC_TEST/play_scenario_parse.c @@ -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); } } diff --git a/openair3/TEST/EPC_TEST/play_scenario_s1ap.c b/openair3/TEST/EPC_TEST/play_scenario_s1ap.c index cb5571c28b6..7dd80904ae2 100644 --- a/openair3/TEST/EPC_TEST/play_scenario_s1ap.c +++ b/openair3/TEST/EPC_TEST/play_scenario_s1ap.c @@ -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", diff --git a/openair3/TEST/EPC_TEST/play_scenario_s1ap_eNB_defs.h b/openair3/TEST/EPC_TEST/play_scenario_s1ap_eNB_defs.h new file mode 100644 index 00000000000..ac77b88b247 --- /dev/null +++ b/openair3/TEST/EPC_TEST/play_scenario_s1ap_eNB_defs.h @@ -0,0 +1,250 @@ +/******************************************************************************* + 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_ */ -- GitLab