diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 582bfdd74273db91c0a5ef0a4db054255353d23b..e4d3acf4b1c976540a95e6cbd033bfa26b9d9ecd 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -1759,6 +1759,7 @@ add_executable(lte-softmodem
   ${OPENAIR_TARGETS}/ARCH/COMMON/common_lib.c
   ${OPENAIR1_DIR}/SIMULATION/ETH_TRANSPORT/netlink_init.c
   ${OPENAIR3_DIR}/NAS/UE/nas_ue_task.c
+  ${OPENAIR_DIR}/common/utils/utils.c
   ${GTPU_need_ITTI}
   ${HW_SOURCE}
   ${TRANSPORT_SOURCE}
@@ -1889,6 +1890,7 @@ add_executable(oaisim
   ${OPENAIR2_DIR}/RRC/NAS/nas_config.c
   ${OPENAIR2_DIR}/RRC/NAS/rb_config.c
   ${OPENAIR3_DIR}/NAS/UE/nas_ue_task.c
+  ${OPENAIR_DIR}/common/utils/utils.c
   ${GTPU_need_ITTI}
   ${OPENAIR_TARGETS}/COMMON/create_tasks.c
   ${HW_SOURCE}
diff --git a/cmake_targets/at_commands/CMakeLists.txt b/cmake_targets/at_commands/CMakeLists.txt
index ca9a30546324be9cd3fa008e3b6b34bffd4af6b6..61d1565bac912d2e03eb6c183a3d0f479ce161f2 100755
--- a/cmake_targets/at_commands/CMakeLists.txt
+++ b/cmake_targets/at_commands/CMakeLists.txt
@@ -88,31 +88,47 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.2 -std=gnu99 -Wall -Wstrict-prototype
 set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -ggdb -DMALLOC_CHECK_=3")
 set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -ggdb -DMALLOC_CHECK_=3 -O2")
 
-#This is to detect nettle version changes between Ubuntu 14.04/16.04
-set ( nettle_cmd "nettle-hash" )
-set ( nettle_arg "-V" )
-execute_process(COMMAND  ${nettle_cmd} ${nettle_arg}   RESULT_VARIABLE rv OUTPUT_VARIABLE ov ERROR_VARIABLE ev)
+##This is to detect nettle version changes between Ubuntu 14.04/16.04
+#set ( nettle_cmd "nettle-hash" )
+#set ( nettle_arg "-V" )
+#execute_process(COMMAND  ${nettle_cmd} ${nettle_arg}   RESULT_VARIABLE rv OUTPUT_VARIABLE ov ERROR_VARIABLE ev)
+#
+#string(REGEX MATCH "[+-]?[0-9]+([.][0-9]+)?" nv ${ov})
+#
+#message("NETTLE_VERSION = ${nv}")
+#
+## we need to remove decimal as floating point arithematic does not work properly with C preprocessor
+#STRING(REGEX REPLACE "[.]" "" nv ${nv})
+#
+#if ("${nv}" STREQUAL "")
+#  message( FATAL_ERROR "The nettle version not detected properly. Try to run build_oai -I again" )
+#endif()
+#
+#set (NETTLE_VERSION "${nv}")
+#add_definitions("-DNETTLE_VERSION=${NETTLE_VERSION}")
+
+include(FindPkgConfig)
 
-string(REGEX MATCH "[+-]?[0-9]+([.][0-9]+)?" nv ${ov})
+pkg_search_module(NETTLE nettle)
+if(NOT ${NETTLE_FOUND})
+  message( FATAL_ERROR "PACKAGE nettle not found: some targets will fail. Run build_oai -I again!")
+else()
+  include_directories(${NETTLE_INCLUDE_DIRS})
+endif()
 
-message("NETTLE_VERSION = ${nv}")
+message ("NETTLE VERSION_INSTALLED  = ${NETTLE_VERSION}")
 
-# we need to remove decimal as floating point arithematic does not work properly with C preprocessor
-STRING(REGEX REPLACE "[.]" "" nv ${nv})
+string(REGEX REPLACE "([0-9]+).*" "\\1" NETTLE_VERSION_MAJOR ${NETTLE_VERSION})
+string(REGEX REPLACE "[0-9]+\\.([0-9]+).*" "\\1" NETTLE_VERSION_MINOR ${NETTLE_VERSION})
+message ("NETTLE_VERSION_MAJOR = ${NETTLE_VERSION_MAJOR}")
+message ("NETTLE_VERSION_MINOR = ${NETTLE_VERSION_MINOR}")
 
-if ("${nv}" STREQUAL "")
+if ("${NETTLE_VERSION_MAJOR}" STREQUAL "" OR "${NETTLE_VERSION_MINOR}" STREQUAL "")
   message( FATAL_ERROR "The nettle version not detected properly. Try to run build_oai -I again" )
 endif()
 
-set (NETTLE_VERSION "${nv}")
-add_definitions("-DNETTLE_VERSION=${NETTLE_VERSION}")
-
-include(FindPkgConfig)
-
-INCLUDE(FindNettle)
-IF( NOT NETTLE_FOUND )
-  MESSAGE( SEND_ERROR "Nettle is required" )
-ENDIF( NOT NETTLE_FOUND )
+add_definitions("-DNETTLE_VERSION_MAJOR=${NETTLE_VERSION_MAJOR}")
+add_definitions("-DNETTLE_VERSION_MINOR=${NETTLE_VERSION_MINOR}")
 
 
 pkg_search_module(OPENSSL openssl REQUIRED)
@@ -215,8 +231,8 @@ set(api_user_HDR
 
 add_library(api_user ${api_user_SRC} ${api_user_HDR})
 target_include_directories(api_user PRIVATE ${OPENAIR_NAS_DIR}/UE/API/USER
-                                            ${OPENAIR_NAS_DIR}/UE
                                             ${OPENAIR_NAS_DIR}/COMMON
+                                            ${OPENAIR_NAS_DIR}/UE
                                             )
 
 ################################################################################
@@ -397,6 +413,7 @@ target_include_directories(emm PRIVATE
                      ${OPENAIR_DIR}/common/utils/msc 
                      ${OPENAIR_DIR}/common/utils
                      ${OPENAIR_DIR}/openair2/COMMON
+                     ${OPENAIR_NAS_DIR}/UE
                      ${OPENAIR_NAS_DIR}/UE/API/USIM 
                      ${OPENAIR_NAS_DIR}/UE/EMM
                      ${OPENAIR_NAS_DIR}/COMMON/EMM/MSG
@@ -409,7 +426,6 @@ target_include_directories(emm PRIVATE
 # esm LIB
 ################################################################################
 set(esm_SRC
-    ${OPENAIR_NAS_DIR}/UE/ESM/esm_main.c
     ${OPENAIR_NAS_DIR}/UE/ESM/DedicatedEpsBearerContextActivation.c
     ${OPENAIR_NAS_DIR}/UE/ESM/DefaultEpsBearerContextActivation.c
     ${OPENAIR_NAS_DIR}/UE/ESM/EpsBearerContextDeactivation.c
@@ -452,6 +468,7 @@ set(esm_SRC
 )
 
 set(esm_HDR
+    ${OPENAIR_TARGETS}/COMMON/openairinterface5g_limits.h
     ${OPENAIR_NAS_DIR}/UE/ESM/esm_main.h
     ${OPENAIR_NAS_DIR}/COMMON/ESM/MSG/ActivateDedicatedEpsBearerContextAccept.h
     ${OPENAIR_NAS_DIR}/COMMON/ESM/MSG/ActivateDedicatedEpsBearerContextReject.h
@@ -481,13 +498,17 @@ set(esm_HDR
     ${OPENAIR_NAS_DIR}/UE/ESM/SAP/esm_recv.h
     ${OPENAIR_NAS_DIR}/UE/ESM/SAP/esm_sap.h
     ${OPENAIR_NAS_DIR}/UE/ESM/SAP/esm_send.h
+    ${OPENAIR_DIR}/common/utils/utils.h
 )
 
 add_library(esm ${esm_SRC} ${esm_HDR})
 
 target_include_directories(esm PRIVATE 
+                     ${OPENAIR_DIR}/common/utils
+                     ${OPENAIR_NAS_DIR}/UE
                      ${OPENAIR_NAS_DIR}/UE/API/USER 
                      ${OPENAIR_NAS_DIR}/UE/ESM
+                     ${OPENAIR_TARGETS}/COMMON
                      ${OPENAIR_NAS_DIR}/COMMON/ESM/MSG
                      ${OPENAIR_NAS_DIR}/UE/ESM/SAP
                      ${OPENAIR_NAS_DIR}/COMMON/IES
@@ -670,6 +691,9 @@ target_include_directories(ies PRIVATE
 # EXECUTABLE at_nas_ue
 ################################################################################
 include_directories( 
+                     ${OPENAIR_TARGETS}/COMMON
+                     ${OPENAIR_NAS_DIR}/UE
+                     ${OPENAIR_DIR}/common/utils
                      ${OPENAIR_DIR}/common/utils/msc 
                      ${OPENAIR3_DIR}/COMMON 
                      ${OPENAIR3_DIR}/SECU 
@@ -691,6 +715,7 @@ ADD_EXECUTABLE(at_nas_ue  ${OPENAIR_NAS_DIR}/UE/UEprocess.c
                           ${OPENAIR_NAS_DIR}/UE/nas_parser.c
                           ${OPENAIR_NAS_DIR}/UE/nas_proc.c  
                           ${OPENAIR_NAS_DIR}/UE/nas_user.c  
+                          ${OPENAIR_DIR}/common/utils/utils.c
                           )
                           
 target_link_libraries (at_nas_ue 
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index 9565e365e3d63cc76d50dd993f5bdaa04f317def..6a61d36cfa7cb90e8f1c24c63fed2590e1c0b9e4 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -57,17 +57,20 @@ CMAKE_BUILD_TYPE=""
 UE_AUTOTEST_TRACE="False"
 trap handle_ctrl_c INT
 
+gen_nvram_path=$OPENAIR_DIR/targets/bin
+conf_nvram_path=$OPENAIR_DIR/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf
+
 function print_help() {
-  echo_info '
+  echo_info "
 This program installs OpenAirInterface Software
 You should have ubuntu 14.xx, updated, and the Linux kernel >= 3.14
 Options
 -h
    This help
 -c | --clean
-   Erase all files to make a rebuild from start"
+   Erase all files to make a rebuild from start
 -C | --clean-all
-   Erase all files made by previous compilations, installations"
+   Erase all files made by previous compilations, installations
 --clean-kernel
    Erase previously installed features in kernel: iptables, drivers, ...
 -I | --install-external-packages
@@ -82,7 +85,11 @@ Options
 --eNB
    Makes the LTE softmodem
 --UE
-   Makes the UE specific parts (ue_ip, usim, nvram) 
+   Makes the UE specific parts (ue_ip, usim, nvram) from the given configuration file
+--UE-conf-nvram [configuration file]
+   Specify conf_nvram_path (default \"$conf_nvram_path\")
+--UE-gen-nvram [output path]
+   Specify gen_nvram_path (default \"$gen_nvram_path\")
 --RRH
    Makes the RRH
 -a | --agent
@@ -97,7 +104,7 @@ Options
    ETHERNET , None
    Adds this trasport protocol support in compilation
 --oaisim
-   Makes the oaisim simulator. Hardware will be defaulted to "None".
+   Makes the oaisim simulator. Hardware will be defaulted to \"None\".
 --phy_simulators
    Makes the unitary tests Layer 1 simulators
 --core_simulators
@@ -140,11 +147,12 @@ Usage (first build):
 Usage (Regular):
  oaisim : ./build_oai --oaisim -x 
  Eurecom EXMIMO + OAI ENB : ./build_oai --eNB -x 
- NI/ETTUS B201  + OAI ENB : ./build_oai --eNB -x -w USRP'
+ NI/ETTUS B201  + OAI ENB : ./build_oai --eNB -x -w USRP"
 }
 
 
 function main() {
+
   until [ -z "$1" ]
   do
     case "$1" in
@@ -183,6 +191,12 @@ function main() {
             UE=1
             echo_info "Will compile UE"
             shift;;
+       --UE-conf-nvram)
+            conf_nvram_path=$(readlink -f "$1")
+            shift 2;;
+        --UE-gen-nvram)
+            gen_nvram_path=$(readlink -f $2)
+            shift 2;;
        --RRH)
             RRH=1
             echo_info "Will compile RRH"
@@ -517,15 +531,17 @@ function main() {
     compilations \
       nas_sim_tools nvram \
       nvram $dbin/nvram
+    compilations \
+        nas_sim_tools conf2uedata \
+        conf2uedata $dbin/conf2uedata
 
     # generate USIM data
-    if [ -f $dbin/nvram ]; then
-      install_nas_tools $dbin $dconf
+    if [ -f $dbin/conf2uedata ]; then
+      install_nas_tools $conf_nvram_path $gen_nvram_path
       echo_info "Copying UE specific part to $DIR/$lte_build_dir/build"
-      cp -Rvf $dbin/.ue_emm.nvram $DIR/$lte_build_dir/build
-      cp -Rvf $dbin/.ue.nvram $DIR/$lte_build_dir/build
-      cp -Rvf $dbin/.usim.nvram $DIR/$lte_build_dir/build
-
+      cp -Rvf $dbin/.ue_emm.nvram0 $DIR/$lte_build_dir/build
+      cp -Rvf $dbin/.ue.nvram0 $DIR/$lte_build_dir/build
+      cp -Rvf $dbin/.usim.nvram0 $DIR/$lte_build_dir/build
     else
       echo_warning "not generated UE NAS files: binaries not found"
     fi
@@ -647,10 +663,13 @@ function main() {
 	compilations \
 	    nas_sim_tools nvram \
 	    nvram $dbin/nvram
+	compilations \
+        nas_sim_tools conf2uedata \
+        conf2uedata $dbin/conf2uedata
 
 	# generate USIM data
-	if [ -f $dbin/nvram ]; then
-	    install_nas_tools $dbin $dconf
+	if [ -f $dbin/conf2uedata ]; then
+	    install_nas_tools $conf_nvram_path $gen_nvram_path
 	else
 	    echo_warning "not generated UE NAS files: binaries not found"
 	fi
diff --git a/cmake_targets/nas_sim_tools/CMakeLists.txt b/cmake_targets/nas_sim_tools/CMakeLists.txt
index 732ede3f94006466ae8afb9603b4f892f3d19855..81e3fe4b3ce7ac256ed4fea630ee30ec9425b2a1 100644
--- a/cmake_targets/nas_sim_tools/CMakeLists.txt
+++ b/cmake_targets/nas_sim_tools/CMakeLists.txt
@@ -2,70 +2,89 @@ cmake_minimum_required(VERSION 2.8)
 
 project(NAS_SIM_TOOLS)
 
+include(FindPkgConfig)
+pkg_search_module(CONFIG libconfig REQUIRED)
+include_directories(${CONFIG_INCLUDE_DIRS})
+add_definitions(-std=gnu99)
 
 ENABLE_LANGUAGE(C)
-#Sends the -std=c99 flag to the gcc compiler
-add_definitions(-std=c99)
-add_definitions(-DNAS_UE)
+
+set(CMAKE_C_FLAGS
+  "${CMAKE_C_FLAGS} ${C_FLAGS_PROCESSOR} -Werror -Wall -Wstrict-prototypes -Wno-packed-bitfield-compat -g")
 
 set(OPENAIR_DIR     $ENV{OPENAIR_DIR})
-set(OPENAIR1_DIR    $ENV{OPENAIR_DIR}/openair1)
-set(OPENAIR2_DIR    $ENV{OPENAIR_DIR}/openair2)
 set(OPENAIR3_DIR    $ENV{OPENAIR_DIR}/openair3)
-set(OPENAIR3_DIR   $ENV{OPENAIR_DIR}/openair3)
-set(OPENAIR_TARGETS $ENV{OPENAIR_DIR}/targets)
-
-#set(EXECUTABLE_OUTPUT_PATH ${OPENAIR_DIR}/targets/bin)
-
 
-# Add .h files for dependancies
-set(usim_SRC
-    ${OPENAIR_DIR}/openair3/NAS/TOOLS/usim_data.c
+set(CONF2UEDATA_LIB_SRC
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_emm.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_user_data.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_usim.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_network.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_user_plmn.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_parser.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/fs.c
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/display.c
     ${OPENAIR_DIR}/openair3/NAS/UE/API/USIM/usim_api.c
     ${OPENAIR_DIR}/openair3/NAS/UE/API/USIM/aka_functions.c
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/memory.c
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/nas_log.c
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/OctetString.c
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/TLVEncoder.c
+    ${OPENAIR_DIR}/common/utils/utils.c
 )
-set(usim_HDR
-    ${OPENAIR_DIR}/openair3/NAS/TOOLS/network.h
+
+set(conf2uedata_HDR
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf2uedata.h
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf_emm.h
     ${OPENAIR_DIR}/openair3/NAS/UE/API/USIM/usim_api.h
     ${OPENAIR_DIR}/openair3/NAS/UE/API/USIM/aka_functions.h
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/memory.h
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/nas_log.h
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/OctetString.h
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/TLVEncoder.h
+    ${OPENAIR_DIR}/common/utils/utils.h
 )
+
 include_directories(
+    ${OPENAIR_DIR}/common/utils
+    ${OPENAIR_DIR}/openair3/NAS/UE
     ${OPENAIR_DIR}/openair3/NAS/COMMON
+    ${OPENAIR_DIR}/openair3/NAS/UE/API/USER
     ${OPENAIR_DIR}/openair3/NAS/UE/API/USIM
     ${OPENAIR_DIR}/openair3/NAS/UE/EMM/
     ${OPENAIR_DIR}/openair3/NAS/UE/ESM/
     ${OPENAIR_DIR}/openair3/NAS/COMMON/IES/
     ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL
 )
-ADD_EXECUTABLE(usim  ${usim_SRC} ${usim_HDR})
 
+# conf2uedata binary
 
+set(conf2uedata_SRC
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/conf2uedata.c
+	${CONF2UEDATA_LIB_SRC}
+)
 
+add_executable(conf2uedata  ${conf2uedata_SRC} ${conf2uedata_HDR} )
+target_link_libraries(conf2uedata ${CONFIG_LIBRARIES})
 
-set(nvram_SRC
-    ${OPENAIR_DIR}/openair3/NAS/TOOLS/ue_data.c
-    ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/memory.c
-    ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/nas_log.c
-)
+# usim binary
 
-set(nvram_HDR
-    ${OPENAIR_DIR}/openair3/NAS/UE/EMM/emmData.h
-    ${OPENAIR_DIR}/openair3/NAS/COMMON/UTIL/memory.h
-    ${OPENAIR_DIR}/openair3/NAS/COMMON/userDef.h
+set(usim_SRC
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/usim.c
+	${CONF2UEDATA_LIB_SRC}
 )
 
-ADD_EXECUTABLE(nvram  ${nvram_SRC} ${nvram_HDR})
 
-#install (TARGETS usim  DESTINATION ${EXECUTABLE_OUTPUT_PATH})
-#install (TARGETS nvram DESTINATION ${EXECUTABLE_OUTPUT_PATH})
+add_executable(usim  ${usim_SRC} ${conf2uedata_HDR} )
+target_link_libraries(usim ${CONFIG_LIBRARIES})
+
+# nvram binary
+
+set(nvram_SRC
+    ${OPENAIR_DIR}/openair3/NAS/TOOLS/nvram.c
+	${CONF2UEDATA_LIB_SRC}
+)
+
 
-#install(CODE "EXECUTE_PROCESS (COMMAND ${OPENAIR_TARGETS}/bin/nvram --gen WORKING_DIRECTORY ${OPENAIR_TARGETS}/bin)")
-#install(CODE "EXECUTE_PROCESS (COMMAND ${OPENAIR_TARGETS}/bin/usim  --gen WORKING_DIRECTORY ${OPENAIR_TARGETS}/bin)")
+add_executable(nvram  ${nvram_SRC} ${conf2uedata_HDR} )
+target_link_libraries(nvram ${CONFIG_LIBRARIES})
diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper
index f73078afc5e2a1be7075d520a4e5f5f0ed8685c5..c53194747413fd90a2b6cc672c4de17a9a95e803 100755
--- a/cmake_targets/tools/build_helper
+++ b/cmake_targets/tools/build_helper
@@ -450,19 +450,18 @@ install_asn1c_from_source(){
 ################################################
 
 install_nas_tools() {
-  cd $1
-  if [ ! -f .ue.nvram ]; then
+  if [ ! -f .ue.nvram0 ]; then
     echo_success "generate .ue_emm.nvram .ue.nvram"
-    ./nvram --gen
+    ./nvram --gen -c $1 -o $2
   else
-    [ ./nvram -nt .ue.nvram  -o ./nvram -nt .ue_emm.nvram ] && ./nvram --gen
+    [ ./nvram -nt .ue.nvram0 -o ./nvram -nt .ue_emm.nvram0 ] && ./nvram --gen -c $1 -o $2
   fi
 
-  if [ ! -f .usim.nvram ]; then
+  if [ ! -f .usim.nvram0 ]; then
     echo_success "generate .usim.nvram"
-    ./usim --gen
+    ./usim --gen -c $1 -o $2
   else
-    [ ./usim -nt .usim.nvram ] && ./usim --gen
+    [ ./usim -nt .usim.nvram0 ] && ./usim --gen -c $1 -o $2
   fi
 
 }
diff --git a/cmake_targets/tools/run_enb_ue_virt_s1 b/cmake_targets/tools/run_enb_ue_virt_s1
index ecd5993ff47e65769d1867e72d83f875fa3ca757..ff659468038f21939eb5ae9f45fd05343aafcb65 100755
--- a/cmake_targets/tools/run_enb_ue_virt_s1
+++ b/cmake_targets/tools/run_enb_ue_virt_s1
@@ -247,12 +247,6 @@ function main()
   if [ $? -ne 0 ]; then
     echo "200 lte " >> /etc/iproute2/rt_tables
   fi
-  ip rule add fwmark 1 table lte 
-  ifconfig oip1 up
-  ip route add default dev oip1 table lte
-  # the actual IP address depends on the EPC/MME config file for address pool
-  ip route add from 192.188.0.0/24 table lte
-  ip route add to 192.188.0.0/24 table lte
 
   exe_arguments="$exe_arguments -s15 -AAWGN -y1 -b1 -u1 -Q0"
     
diff --git a/common/utils/Makefile.inc b/common/utils/Makefile.inc
deleted file mode 100644
index 33c126b8d73230a685451ff9d86136c608c1982d..0000000000000000000000000000000000000000
--- a/common/utils/Makefile.inc
+++ /dev/null
@@ -1,59 +0,0 @@
-export KERNEL_DIR=/lib/modules/$(shell uname -r)/
-CC=gcc
-CCC=gcc
-linux := $(shell if [ `uname` = "Linux" ] ; then echo "1" ; else echo "0" ; fi)
-
-
-CFLAGS += -std=gnu99 
-CFLAGS += -Wall -g -ggdb -Wstrict-prototypes -fno-strict-aliasing 
-
-# Need to force this option because default kernel module builder is wrong
-CFLAGS += $(call cc-option,-mpreferred-stack-boundary=4)
-
-#For performance, if some option doesn't exist in all gcc versions, use $(call cc-option,MY_OPTION)
-#CFLAGS += -O2
-#CFLAGS +=  -funroll-loops 
-CFLAGS += -Wno-packed-bitfield-compat 
-
-# This is the minimum CPU faetures for OAI
-CFLAGS += -mmmx -msse -msse2 -mssse3 -msse4.1
-# Add CPU features from local CPU
-#CFLAGS += -march=native
-
-ifeq ($(OSTYPE),Cygwin)
-cygwin=1
-CFLAGS += -DCYGWIN
-else
-cygwin=0
-endif
-
-ifeq ($(linux),1)
-CFLAGS += 
-LDFLAGS += 
-endif
-
-ITTI_DIR = $(COMMON_UTILS_DIR)/itti
-
-ITTI_OBJS =  $(ITTI_DIR)/intertask_interface.o
-ITTI_OBJS += $(ITTI_DIR)/intertask_interface_dump.o
-ITTI_OBJS += $(ITTI_DIR)/backtrace.o
-ITTI_OBJS += $(ITTI_DIR)/memory_pools.o
-ITTI_OBJS += $(ITTI_DIR)/signals.o
-ITTI_OBJS += $(ITTI_DIR)/timer.o
-
-
-HASHTABLE_DIR = $(COMMON_UTILS_DIR)/collection/hashtable
-MSC_DIR = $(COMMON_UTILS_DIR)/msc
-
-HASHTABLE_OBJS =  $(HASHTABLE_DIR)/hashtable.o
-HASHTABLE_OBJS += $(HASHTABLE_DIR)/obj_hashtable.o
-
-UTILS_OBJS = $(ITTI_OBJS) $(HASHTABLE_OBJS)  
-
-UTILS_incl = 				\
-    -I$(COMMON_UTILS_DIR)   \
-    -I$(HASHTABLE_DIR)      \
-    -I$(ITTI_DIR) \
-    -I$(MSC_DIR)
-
-print-%  : ; @echo $* = $($*)
diff --git a/common/utils/utils.c b/common/utils/utils.c
new file mode 100644
index 0000000000000000000000000000000000000000..a807a27096f661002d87ab5023291aa62b34fe89
--- /dev/null
+++ b/common/utils/utils.c
@@ -0,0 +1,107 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include "utils.h"
+
+void *calloc_or_fail(size_t size) {
+  void *ptr = calloc(1, size);
+  if (ptr == NULL) {
+    fprintf(stderr, "[UE] Failed to calloc %zu bytes", size);
+    exit(EXIT_FAILURE);
+  }
+  return ptr;
+}
+
+void *malloc_or_fail(size_t size) {
+  void *ptr = malloc(size);
+  if (ptr == NULL) {
+    fprintf(stderr, "[UE] Failed to malloc %zu bytes", size);
+    exit(EXIT_FAILURE);
+  }
+  return ptr;
+}
+
+/****************************************************************************
+ **                                                                        **
+ ** Name:        hex_char_to_hex_value()                                   **
+ **                                                                        **
+ ** Description: Converts an hexadecimal ASCII coded digit into its value. **
+ **                                                                        **
+ ** Inputs:      c:             A char holding the ASCII coded value       **
+ **              Others:        None                                       **
+ **                                                                        **
+ ** Outputs:     None                                                      **
+ **              Return:        Converted value (-1 on error)              **
+ **              Others:        None                                       **
+ **                                                                        **
+ ***************************************************************************/
+int hex_char_to_hex_value (char c)
+{
+  if (!((c >= 'a' && c <= 'f') ||
+        (c >= 'A' && c <= 'F') ||
+        (c >= '0' && c <= '9')))
+    return -1;
+
+  if (c >= 'A') {
+    /* Remove case bit */
+    c &= ~('a' ^ 'A');
+
+    return (c - 'A' + 10);
+  } else {
+    return (c - '0');
+  }
+}
+
+/****************************************************************************
+ **                                                                        **
+ ** Name:        hex_string_to_hex_value()                                 **
+ **                                                                        **
+ ** Description: Converts an hexadecimal ASCII coded string into its value.**
+ **                                                                        **
+ ** Inputs:      hex_value:     A pointer to the location to store the     **
+ **                             conversion result                          **
+ **              size:          The size of hex_value in bytes             **
+ **              Others:        None                                       **
+ **                                                                        **
+ ** Outputs:     hex_value:     Converted value                            **
+ **              Return:        0 on success, -1 on error                  **
+ **              Others:        None                                       **
+ **                                                                        **
+ ***************************************************************************/
+int hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size)
+{
+  int i;
+
+  if (strlen(hex_string) != size*2) {
+    fprintf(stderr, "the string '%s' should be of length %d\n", hex_string, size*2);
+    return -1;
+  }
+
+  for (i=0; i < size; i++) {
+    int a = hex_char_to_hex_value(hex_string[2 * i]);
+    int b = hex_char_to_hex_value(hex_string[2 * i + 1]);
+    if (a == -1 || b == -1) goto error;
+    hex_value[i] = (a << 4) | b;
+  }
+  return 0;
+
+error:
+  fprintf(stderr, "the string '%s' is not a valid hexadecimal string\n", hex_string);
+  for (i=0; i < size; i++)
+    hex_value[i] = 0;
+  return -1;
+}
+
+char *itoa(int i) {
+  char buffer[64];
+  int ret;
+
+  ret = snprintf(buffer, sizeof(buffer), "%d",i);
+  if ( ret <= 0 ) {
+    return NULL;
+  }
+
+  return strdup(buffer);
+}
+
diff --git a/common/utils/utils.h b/common/utils/utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..cfe3df34b9356e02fe33e050198af154b415a472
--- /dev/null
+++ b/common/utils/utils.h
@@ -0,0 +1,17 @@
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+void *calloc_or_fail(size_t size);
+void *malloc_or_fail(size_t size);
+
+// Converts an hexadecimal ASCII coded digit into its value. **
+int hex_char_to_hex_value (char c);
+// Converts an hexadecimal ASCII coded string into its value.**
+int hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size);
+
+char *itoa(int i);
+
+#endif
diff --git a/openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c b/openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c
index df6a55eaf35560a90f619b01e824e145cea2e212..02b5102ae544a16b04e527ac9aed30fc1fc84009 100644
--- a/openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c
+++ b/openair1/PHY/LTE_ESTIMATION/lte_dl_bf_channel_estimation.c
@@ -1,31 +1,24 @@
-/*******************************************************************************
-    OpenAirInterface
-    Copyright(c) 1999 - 2014 Eurecom
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
 
-    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@eurecom.fr
-
-  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
-
- *******************************************************************************/
 #ifdef USER_MODE
 #include <string.h>
 #endif
diff --git a/openair1/PHY/LTE_TRANSPORT/pilots_ue_spec.c b/openair1/PHY/LTE_TRANSPORT/pilots_ue_spec.c
index 4a9010fca0daa712a9db006f0bbf87594283668f..b4199d9b605e36e2f0ca7fa7002d1b9890e25e11 100644
--- a/openair1/PHY/LTE_TRANSPORT/pilots_ue_spec.c
+++ b/openair1/PHY/LTE_TRANSPORT/pilots_ue_spec.c
@@ -1,31 +1,23 @@
-/*******************************************************************************
-    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@eurecom.fr
-
-  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
-
- *******************************************************************************/
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
 
 /*! \file PHY/LTE_TRANSPORT/uespec_pilots.c
 * \brief Top-level routines for generating DL ue-specific reference signals V12.5 2015-03
diff --git a/openair1/PHY/MODULATION/beamforming.c b/openair1/PHY/MODULATION/beamforming.c
index 88ac12b0a711a71bac32e18f0de528cfccd425f5..d2336f6187792869fdd286c94d8831d3b43e6156 100644
--- a/openair1/PHY/MODULATION/beamforming.c
+++ b/openair1/PHY/MODULATION/beamforming.c
@@ -1,31 +1,23 @@
-/*******************************************************************************
-  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@eurecom.fr
-
-Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
-
- *******************************************************************************/
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
 
 /*! \file PHY/MODULATION/beamforming.c
  * \brief 
diff --git a/openair1/PHY/TOOLS/twiddle18432.h b/openair1/PHY/TOOLS/twiddle18432.h
index f21987c4cb2b49e201f5d404bbfd9cb15987aeee..8c6c9822dc69c7c15ef1141b40a5ea4454e323bd 100644
--- a/openair1/PHY/TOOLS/twiddle18432.h
+++ b/openair1/PHY/TOOLS/twiddle18432.h
@@ -1,31 +1,23 @@
-/*******************************************************************************
-    OpenAirInterface
-    Copyright(c) 1999 - 2014 Eurecom
-
-    OpenAirInterface is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-
-    OpenAirInterface is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with OpenAirInterface.The full GNU General Public License is
-   included in this distribution in the file called "COPYING". If not,
-   see <http://www.gnu.org/licenses/>.
-
-  Contact Information
-  OpenAirInterface Admin: openair_admin@eurecom.fr
-  OpenAirInterface Tech : openair_tech@eurecom.fr
-  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr
-
-  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
-
- *******************************************************************************/
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
 
 /* Twiddles generated with
 twa = floor(32767*exp(-sqrt(-1)*2*pi*(0:6143)/18432));
diff --git a/openair1/PHY/impl_defs_top.h b/openair1/PHY/impl_defs_top.h
index 1273fb9cf4f94c64b55c65ace0010c315fd6eb6d..be63ea00cc3a7e5a4d7350651001f642026f33b0 100644
--- a/openair1/PHY/impl_defs_top.h
+++ b/openair1/PHY/impl_defs_top.h
@@ -284,21 +284,7 @@ typedef enum {
 #define NUMBER_OF_HARQ_PID_MAX 8
 
 #define MAX_FRAME_NUMBER 0x400
-#if defined(CBMIMO1) || defined(EXMIMO) || defined(OAI_USRP)
-#define NUMBER_OF_eNB_MAX 1
-#define NUMBER_OF_UE_MAX 16
-#define NUMBER_OF_CONNECTED_eNB_MAX 3
-#else
-#ifdef LARGE_SCALE
-#define NUMBER_OF_eNB_MAX 2
-#define NUMBER_OF_UE_MAX 120
-#define NUMBER_OF_CONNECTED_eNB_MAX 1 // to save some memory
-#else
-#define NUMBER_OF_eNB_MAX 7
-#define NUMBER_OF_UE_MAX 16
-#define NUMBER_OF_CONNECTED_eNB_MAX 3
-#endif
-#endif
+#include "openairinterface5g_limits.h"
 
 #define NUMBER_OF_RN_MAX 3
 typedef enum {no_relay=1,unicast_relay_type1,unicast_relay_type2, multicast_relay} relaying_type_t;
diff --git a/openair1/SCHED/phy_procedures_lte_ue.c b/openair1/SCHED/phy_procedures_lte_ue.c
index 503ba84ea89d1a16b6adfc44d535782bd4344e44..b9662fd5d5491e7380d17741c97f1511bacf1823 100644
--- a/openair1/SCHED/phy_procedures_lte_ue.c
+++ b/openair1/SCHED/phy_procedures_lte_ue.c
@@ -1040,8 +1040,8 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt
   int subframe_tx = proc->subframe_tx;
   int frame_tx = proc->frame_tx;
   int ulsch_start;
-#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
   int overflow=0;
+#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
   int k,l;
   int dummy_tx_buffer[3840*4] __attribute__((aligned(16)));
 #endif
@@ -1069,7 +1069,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt
   ulsch_start = (frame_parms->samples_per_tti*subframe_tx)-ue->N_TA_offset; //-ue->timing_advance;
 #endif //else EXMIMO
 
-#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
+//#if defined(EXMIMO) || defined(OAI_USRP) || defined(OAI_BLADERF) || defined(OAI_LMSSDR)
   if (empty_subframe)
   {
 //#if 1
@@ -1098,7 +1098,7 @@ void ulsch_common_procedures(PHY_VARS_UE *ue, UE_rxtx_proc_t *proc, uint8_t empt
 #endif*/
       return;
   }
-#endif
+//#endif
 
   if ((frame_tx%100) == 0)
     LOG_D(PHY,"[UE %d] Frame %d, subframe %d: ulsch_start = %d (rxoff %d, HW TA %d, timing advance %d, TA_offset %d\n",
diff --git a/openair1/SIMULATION/LTE_PHY/dlsim_tm7.c b/openair1/SIMULATION/LTE_PHY/dlsim_tm7.c
index 874af41f45c3c4cb9cb951c6eb543f0503a47f6f..195f27f72e9960527cc0f8d0c379768ad79b2f23 100644
--- a/openair1/SIMULATION/LTE_PHY/dlsim_tm7.c
+++ b/openair1/SIMULATION/LTE_PHY/dlsim_tm7.c
@@ -1,31 +1,23 @@
- /******************************************************************************
-    OpenAirInterface
-    Copyright(c) 1999 - 2014 Eurecom
-
-    OpenAirInterface is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-
-    OpenAirInterface is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with OpenAirInterface.The full GNU General Public License is
-   included in this distribution in the file called "COPYING". If not,
-   see <http://www.gnu.org/licenses/>.
-
-  Contact Information
-  OpenAirInterface Admin: openair_admin@eurecom.fr
-  OpenAirInterface Tech : openair_tech@eurecom.fr
-  OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr
-
-  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
-
- *******************************************************************************/
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
 
 /*! \file dlsim.c
  \brief Top-level DL simulator
diff --git a/openair2/COMMON/commonDef.h b/openair2/COMMON/commonDef.h
index b1ec47449af1a7f0da1fa7257c16db49ad7eda6d..37d05f3fda8a858737384a3627c6a6306a2d7bbc 100644
--- a/openair2/COMMON/commonDef.h
+++ b/openair2/COMMON/commonDef.h
@@ -316,15 +316,6 @@ typedef struct {
  */
 #define TAI_LIST_T(SIZE) struct {Byte_t n_tais; tai_t tai[SIZE];}
 
-/*
- * User notification callback, executed whenever a change of data with
- * respect of network information (e.g. network registration and/or
- * location change, new PLMN becomes available) is notified by the
- * EPS Mobility Management sublayer
- */
-typedef int (*emm_indication_callback_t) (Stat_t, tac_t, ci_t, AcT_t,
-    const char*, size_t);
-
 typedef enum eps_protocol_discriminator_e {
   /* Protocol discriminator identifier for EPS Mobility Management */
   EPS_MOBILITY_MANAGEMENT_MESSAGE =   0x7,
diff --git a/openair2/COMMON/networkDef.h b/openair2/COMMON/networkDef.h
index c52a58a527e32faaaa187bb6c976f76a3feb3cf7..42c812baada0127cc8c15c2ad014863f7bbc5523 100644
--- a/openair2/COMMON/networkDef.h
+++ b/openair2/COMMON/networkDef.h
@@ -252,13 +252,6 @@ typedef struct {
   network_pkf_t* pkf[NET_PACKET_FILTER_MAX];
 } network_tft_t;
 
-/*
- * User notification callback, executed whenever a change of status with
- * respect of PDN connection or EPS bearer context is notified by the EPS
- * Session Management sublayer
- */
-typedef int (*esm_indication_callback_t) (int, network_pdn_state_t);
-
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
diff --git a/openair2/LAYER2/MAC/main.c b/openair2/LAYER2/MAC/main.c
index d812ec0087a892f0144e11c2172f79f83eecce2d..8553c118dcb92ffee80d0a3ae3dab7fc8801ece2 100644
--- a/openair2/LAYER2/MAC/main.c
+++ b/openair2/LAYER2/MAC/main.c
@@ -62,24 +62,27 @@
 #endif //PHY_EMUL
 
 #include "SCHED/defs.h"
-
+/* TODO: this abstraction_flag here is very hackish - find a proper solution */
+extern uint8_t abstraction_flag;
 void dl_phy_sync_success(module_id_t   module_idP,
                          frame_t       frameP,
                          unsigned char eNB_index,
                          uint8_t            first_sync)   //init as MR
 {
   LOG_D(MAC,"[UE %d] Frame %d: PHY Sync to eNB_index %d successful \n", module_idP, frameP, eNB_index);
-#if ! defined(ENABLE_USE_MME)
+#if defined(ENABLE_USE_MME)
+  int mme_enabled=1;
+#else
+  int mme_enabled=0;
+#endif
 
-  if (first_sync==1) {
+  if (first_sync==1 && !(mme_enabled==1 && abstraction_flag==0)) {
     layer2_init_UE(module_idP);
     openair_rrc_ue_init(module_idP,eNB_index);
   } else
-#endif
   {
     rrc_in_sync_ind(module_idP,frameP,eNB_index);
   }
-
 }
 
 void mrbch_phy_sync_failure(module_id_t module_idP, frame_t frameP, uint8_t free_eNB_index) //init as CH
diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
index 4c67f80e207a84f42a56ed0d7cafbed9f7a1b26c..0c5e4cb9e1e3d4b237c181d9afdb7a691a4e3106 100644
--- a/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
+++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp.c
@@ -816,6 +816,14 @@ pdcp_data_ind(
         ((pdcp_data_ind_header_t *) new_sdu_p->data)->rb_id = rb_id;
 #if defined(OAI_EMU)
         ((pdcp_data_ind_header_t*) new_sdu_p->data)->inst  = ctxt_pP->module_id + oai_emulation.info.nb_enb_local - oai_emulation.info.first_ue_local;
+#else
+#  if defined(ENABLE_USE_MME)
+        /* for the UE compiled in S1 mode, we need 1 here
+         * for the UE compiled in noS1 mode, we need 0
+         * TODO: be sure of this
+         */
+        ((pdcp_data_ind_header_t*) new_sdu_p->data)->inst  = 1;
+#  endif
 #endif
       } else {
         ((pdcp_data_ind_header_t*) new_sdu_p->data)->rb_id = rb_id + (ctxt_pP->module_id * maxDRB);
diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
index e380bc6098bd6e7e98d8d6bb5558435e6fb2fc89..37b38121b9d8b03c9b177839044144fc1cef48a7 100644
--- a/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
+++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
@@ -167,7 +167,8 @@ int pdcp_fifo_flush_sdus(const protocol_ctxt_t* const  ctxt_pP)
         ((pdcp_data_ind_header_t *) sdu_p->data)->data_size);
 #else
 #if ! defined(OAI_EMU)
-    ((pdcp_data_ind_header_t *)(sdu_p->data))->inst = 0;
+    /* TODO: do we have to reset to 0 or not? not for a scenario with 1 UE at least */
+//    ((pdcp_data_ind_header_t *)(sdu_p->data))->inst = 0;
 #endif
 #endif
 
@@ -571,7 +572,8 @@ int pdcp_fifo_read_input_sdus (const protocol_ctxt_t* const  ctxt_pP)
                   pdcp_read_header_g.inst - oai_emulation.info.nb_enb_local+ NB_eNB_INST + oai_emulation.info.first_ue_local :
                   pdcp_read_header_g.inst +  oai_emulation.info.first_enb_local;*/
 #else // OAI_EMU
-          pdcp_read_header_g.inst = 0;
+          /* TODO: do we have to reset to 0 or not? not for a scenario with 1 UE at least */
+//          pdcp_read_header_g.inst = 0;
 //#warning "TO DO CORRCT VALUES FOR ue mod id, enb mod id"
           ctxt.frame         = ctxt_cpy.frame;
           ctxt.enb_flag      = ctxt_cpy.enb_flag;
diff --git a/openair2/NETWORK_DRIVER/UE_IP/common.c b/openair2/NETWORK_DRIVER/UE_IP/common.c
index 86968015975e09e05e37a6639bdcf6eee3cd6cff..1ea5df0d30c9600b0b3e9009639096350fe6d61c 100644
--- a/openair2/NETWORK_DRIVER/UE_IP/common.c
+++ b/openair2/NETWORK_DRIVER/UE_IP/common.c
@@ -345,7 +345,7 @@ void ue_ip_common_wireless2ip(struct nlmsghdr *nlh_pP)
 
   ue_ip_common_class_wireless2ip(pdcph_p->data_size,
                                  (unsigned char *)NLMSG_DATA(nlh_pP) + UE_IP_PDCPH_SIZE,
-                                 1, //pdcph_p->inst,
+                                 pdcph_p->inst,
                                  pdcph_p->rb_id);
 
 }
diff --git a/openair2/NETWORK_DRIVER/UE_IP/constant.h b/openair2/NETWORK_DRIVER/UE_IP/constant.h
index 9b4017773b0c84726ecc1846f50bfbe617b86b51..42fd0b6303c0bcaca9665e0eae52c135a31d6a0b 100644
--- a/openair2/NETWORK_DRIVER/UE_IP/constant.h
+++ b/openair2/NETWORK_DRIVER/UE_IP/constant.h
@@ -19,6 +19,7 @@
  *      contact@openairinterface.org
  */
 
+#include "openairinterface5g_limits.h"
 #ifndef _UE_IP_CST
 #define _UE_IP_CST
 
@@ -51,7 +52,7 @@
 
 
 
-#define UE_IP_NB_INSTANCES_MAX       8
+#define UE_IP_NB_INSTANCES_MAX       NUMBER_OF_UE_MAX
 
 
 #endif
diff --git a/openair2/RRC/LITE/rrc_UE.c b/openair2/RRC/LITE/rrc_UE.c
index 8039fcfb7560f90ba633e2b9c84f6784e7334dea..4c877ebe23d5a194b19738ef13267f47ca1679f8 100644
--- a/openair2/RRC/LITE/rrc_UE.c
+++ b/openair2/RRC/LITE/rrc_UE.c
@@ -4406,7 +4406,6 @@ void *rrc_ue_task( void *args_p )
 
       /* NAS messages */
     case NAS_CELL_SELECTION_REQ:
-      ue_mod_id = 0; /* TODO force ue_mod_id to first UE, NAS UE not virtualized yet */
 
       LOG_D(RRC, "[UE %d] Received %s: state %d, plmnID (%d%d%d.%d%d%d), rat %x\n", ue_mod_id, msg_name, rrc_get_state(ue_mod_id),
             NAS_CELL_SELECTION_REQ (msg_p).plmnID.MCCdigit1,
@@ -4707,7 +4706,6 @@ void *rrc_ue_task( void *args_p )
       break;
 
     case RRC_RAL_CONNECTION_RELEASE_REQ:
-      ue_mod_id = 0; /* TODO force ue_mod_id to first UE, NAS UE not virtualized yet */
       LOG_D(RRC, "[UE %d] Received %s\n", ue_mod_id, msg_name);
       break;
 #endif
diff --git a/openair2/UTIL/OMV/structures.h b/openair2/UTIL/OMV/structures.h
index c50e7b22973c05f7c1c212485685ad955d59a137..434fa4c3836e888697328ae182b422b78fb1d1b0 100644
--- a/openair2/UTIL/OMV/structures.h
+++ b/openair2/UTIL/OMV/structures.h
@@ -31,13 +31,12 @@
 #ifndef STRUCTURES_H
 #define STRUCTURES_H
 
+#include "openairinterface5g_limits.h"
 #ifndef __PHY_IMPLEMENTATION_DEFS_H__
-#define Maxneighbor 64
-#define NUMBER_OF_UE_MAX 64
-#define NUMBER_OF_eNB_MAX 3
-#ifndef NB_ANTENNAS_RX
-#  define NB_ANTENNAS_RX  4
-#endif
+	#define Maxneighbor NUMBER_OF_UE_MAX
+	#ifndef NB_ANTENNAS_RX
+		#define NB_ANTENNAS_RX  4
+	#endif
 #endif
 //
 
diff --git a/openair2/UTIL/OTG/otg_defs.h b/openair2/UTIL/OTG/otg_defs.h
index 0cef777b8f0b98eddeb42b61323bc985ff1e9b6b..ebb3f299897031f17ae140f61091d177121aae60 100644
--- a/openair2/UTIL/OTG/otg_defs.h
+++ b/openair2/UTIL/OTG/otg_defs.h
@@ -33,12 +33,12 @@
 #ifndef __OTG_DEFS_H__
 # define __OTG_DEFS_H__
 
-
+/* \brief To define the NUMBER_OF_eNB_MAX and NUMBER_OF_UE_MAX */
 #if STANDALONE==1
-# define NUMBER_OF_eNB_MAX 3
-# define NUMBER_OF_UE_MAX 3
+	#include "openairinterface5g_limits.h"
 #else
-#include "PHY/impl_defs_top.h" /* \brief To define the NUMBER_OF_eNB_MAX and NUMBER_OF_UE_MAX */
+	// impl_defs_top.h includes openairinterface5g_limits.h
+	#include "PHY/impl_defs_top.h"
 #endif
 
 #include "otg_config.h"
diff --git a/openair3/NAS/COMMON/EMM/MSG/emm_msg.c b/openair3/NAS/COMMON/EMM/MSG/emm_msg.c
index b067b9c0d1584929fdcab190bca92de17e470af9..c357e8784f803827e9e83041033472d1b571bb86 100644
--- a/openair3/NAS/COMMON/EMM/MSG/emm_msg.c
+++ b/openair3/NAS/COMMON/EMM/MSG/emm_msg.c
@@ -464,6 +464,9 @@ int emm_msg_encode(EMM_msg *msg, uint8_t *buffer, uint32_t len)
 #endif
   }
 
+  if (encode_result < 0)
+    LOG_FUNC_RETURN (encode_result);
+
   LOG_FUNC_RETURN (header_result + encode_result);
 }
 
diff --git a/openair3/NAS/COMMON/UTIL/memory.c b/openair3/NAS/COMMON/UTIL/memory.c
index df94d40cfcc7a89d9d1c343e184c8e87b798d775..3b3b6e7f9e1d47caf3e5a2f4736a38eb9f8a1e4b 100644
--- a/openair3/NAS/COMMON/UTIL/memory.c
+++ b/openair3/NAS/COMMON/UTIL/memory.c
@@ -103,6 +103,30 @@ char* memory_get_path(const char* dirname, const char* filename)
   return data_filename;
 }
 
+char* memory_get_path_from_ueid(const char* dirname, const char* filename, int ueid)
+{
+  /* Get non-volatile data directory */
+  const char* path = getenv(dirname);
+  char buffer[2048];
+
+  if (path == NULL) {
+    path = getenv(DEFAULT_NAS_PATH);
+  }
+
+  if (path == NULL) {
+    LOG_TRACE(WARNING, "MEMORY  - %s and %s environment variables are not defined trying local directory", dirname, DEFAULT_NAS_PATH);
+    path = ".";
+  }
+
+  /* Append non-volatile data file name */
+  if ( snprintf(buffer, sizeof(buffer), "%s/%s%d", path, filename, ueid) < 0 ) {
+    return NULL;
+  }
+
+  return strdup(buffer);
+}
+
+
 /****************************************************************************
  **                                                                        **
  ** Name:  memory_read()                                             **
diff --git a/openair3/NAS/COMMON/UTIL/memory.h b/openair3/NAS/COMMON/UTIL/memory.h
index f41f4d586b51b9aaf32df5031700cbb423ba51c9..f7f0733044b50163c262036e5cc0abd9cc85fdc6 100644
--- a/openair3/NAS/COMMON/UTIL/memory.h
+++ b/openair3/NAS/COMMON/UTIL/memory.h
@@ -58,6 +58,8 @@ Description Memory access utilities
 
 char* memory_get_path(const char* dirname, const char* filename);
 
+char* memory_get_path_from_ueid(const char* dirname, const char* filename, int ueid);
+
 int memory_read(const char* datafile, void* data, size_t size);
 
 int memory_write(const char* datafile, const void* data, size_t size);
diff --git a/openair3/NAS/COMMON/commonDef.h b/openair3/NAS/COMMON/commonDef.h
index 64216b009465119c56e0d357dab48875b7c4be08..84d97fc4a8cb954386d0bd7976d5b11630f1d084 100644
--- a/openair3/NAS/COMMON/commonDef.h
+++ b/openair3/NAS/COMMON/commonDef.h
@@ -314,6 +314,7 @@ typedef struct {
  */
 #define TAI_LIST_T(SIZE) struct {Byte_t n_tais; tai_t tai[SIZE];}
 
+#if 0
 /*
  * User notification callback, executed whenever a change of data with
  * respect of network information (e.g. network registration and/or
@@ -322,6 +323,7 @@ typedef struct {
  */
 typedef int (*emm_indication_callback_t) (Stat_t, tac_t, ci_t, AcT_t,
     const char*, size_t);
+#endif
 
 typedef enum eps_protocol_discriminator_e {
   /* Protocol discriminator identifier for EPS Mobility Management */
diff --git a/openair3/NAS/COMMON/networkDef.h b/openair3/NAS/COMMON/networkDef.h
index 46716bee24714ab2273744c4634a0924d9320207..e3e8e1f08b3487d6613dcbbf450b810204f01aaf 100644
--- a/openair3/NAS/COMMON/networkDef.h
+++ b/openair3/NAS/COMMON/networkDef.h
@@ -260,12 +260,14 @@ typedef struct {
   network_pkf_t* pkf[NET_PACKET_FILTER_MAX];
 } network_tft_t;
 
+#if 0
 /*
  * User notification callback, executed whenever a change of status with
  * respect of PDN connection or EPS bearer context is notified by the EPS
  * Session Management sublayer
  */
 typedef int (*esm_indication_callback_t) (int, network_pdn_state_t);
+#endif
 
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
diff --git a/openair3/NAS/TEST/NETWORK/README b/openair3/NAS/TEST/NETWORK/README
index 25a6de5d147600feaa5b5d0f2edabcf73bb27477..465e355e784ff3a7f529d22a8487dac5cd9d486e 100644
--- a/openair3/NAS/TEST/NETWORK/README
+++ b/openair3/NAS/TEST/NETWORK/README
@@ -18,9 +18,9 @@ EURECOM-NAS directory should contain following files:
 
 EURECOM-NAS
 |-- bin
-|   |-- .ue.nvram
-|   |-- .ue_emm.nvram
-|   |-- .usim.nvram
+|   |-- .ue.nvram0
+|   |-- .ue_emm.nvram0
+|   |-- .usim.nvram0
 |   |-- NetworkProcess
 |   |-- UEprocess
 |   `-- UserProcess
@@ -57,10 +57,10 @@ UE for testing purpose.
 When starting up, UEprocess reads configuration data from .nvram binary
 files used as UE's non-volatile memory.
 
-.usim.nvram contains data stored into the USIM
-.ue.nvram contains data related to the UE identification (IMEI,
+.usim.nvram0 contains data stored into the USIM
+.ue.nvram0 contains data related to the UE identification (IMEI,
 manufacturer, model, PIN code)
-.ue_emm.nvram contains data related to EPS Mobility Management (IMSI, last
+.ue_emm.nvram0 contains data related to EPS Mobility Management (IMSI, last
 registered PLMN)
 
 --------------------------------------------------------------------------------
diff --git a/openair3/NAS/TOOLS/Makefile b/openair3/NAS/TOOLS/Makefile
deleted file mode 100644
index ca2e884c1110f9d3fa6e53e0e9df0fd0f2bfb509..0000000000000000000000000000000000000000
--- a/openair3/NAS/TOOLS/Makefile
+++ /dev/null
@@ -1,79 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.0  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-
-export PROCESS = UE
-
-ifndef PROJDIR
-PROJDIR = $(PWD)/..
-endif
-
-include $(PROJDIR)/Makerules
-include $(PROJDIR)/Makefile.inc
-include $(PROJDIR)/../Makefile.tools
-
-export LD_RUN_PATH = $(LIBDIR):$(LIBPROCESS)
-
-LIBS		= -luenas.a -lrt 
-INCLUDES	= -I. -I$(INCDIR) -I$(UTILDIR) -I$(USIMAPIDIR) -I$(EMMDIR) -I$(ESMDIR) -I$(IESDIR)
-
-#LIBSUTIL	= $(LIBDIR)/$(LIBUTIL).a $(LIBDIR)/$(LIBUTIL).so
-
-USIM_OBJ	= usim_data.o
-UE_OBJ		= ue_data.o
-
-USIM_TARGET	= usim_data
-UE_TARGET	= ue_data
-
-TARGETS = $(USIM_TARGET) $(UE_TARGET)
-
-all: $(TARGETS)
-
-#-DIMSI_USA_MNC_3DIGITS
-%.o: %.c Makefile
-	$(CC) $(CFLAGS)   -c $< -o $@
-
-$(USIM_TARGET): $(USIM_OBJ) $(LIBSUTIL)
-	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)  -lnettle -lcrypto -lm
-	@echo Replacing $@ to $(BINDIR)
-	@$(RM) $(BINDIR)/$@
-	@$(CP) $@ $(BINDIR)
-
-$(UE_TARGET): $(UE_OBJ) $(LIBSUTIL)
-	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)  -lnettle -lcrypto -lm
-	@echo Replacing $@ to $(BINDIR)
-	@$(RM) $(BINDIR)/$@
-	@$(CP) $@ $(BINDIR)
-
-clean:
-	$(RM) $(OBJS) *.bak *~
-
-veryclean: clean
-	$(RM) $(TARGETS)
-
-veryveryclean: veryclean
-	$(RM) -Rf *.o $(PROJDIR)
-	$(RM) -Rf *.a $(PROJDIR)
-
-depend:
-	makedepend -- ${CFLAGS} -- ${SRCS}
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
-
diff --git a/openair3/NAS/TOOLS/conf2uedata.c b/openair3/NAS/TOOLS/conf2uedata.c
new file mode 100644
index 0000000000000000000000000000000000000000..80a6933c05b080d7125284efab0f62734fe3e630
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf2uedata.c
@@ -0,0 +1,62 @@
+#include <stdio.h>  // perror, printf, fprintf, snprintf
+#include <stdlib.h> // exit, free
+#include <string.h> // memset, strncpy
+#include <getopt.h>
+
+#include "conf2uedata.h"
+#include "display.h"
+#include "conf_parser.h"
+
+int main(int argc, char**argv) {
+	int option;
+    const char* conf_file = NULL;
+    const char* output_dir = NULL;
+    const char options[]="c:o:h";
+
+    while ((option = getopt(argc, argv, options)) != -1) {
+		switch (option) {
+		case 'c':
+			conf_file = optarg;
+			break;
+		case 'o':
+			output_dir = optarg;
+			break;
+		case 'h':
+			_display_usage();
+			return true;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (output_dir == NULL ) {
+		printf("No output option found\n");
+		_display_usage();
+		exit(1);
+	}
+
+    if ( conf_file == NULL ) {
+		printf("No Configuration file is given\n");
+		_display_usage();
+		exit(1);
+	}
+
+    if ( parse_config_file(output_dir, conf_file, OUTPUT_ALL) == false ) {
+        exit(1);
+    }
+
+    display_data_from_directory(output_dir, DISPLAY_ALL);
+
+	exit(0);
+}
+
+/*
+ * Displays command line usage
+ */
+void _display_usage(void) {
+	fprintf(stderr, "usage: conf2uedata [OPTION]...\n");
+	fprintf(stderr, "\t[-c]\tConfig file to use\n");
+	fprintf(stderr, "\t[-o]\toutput file directory\n");
+	fprintf(stderr, "\t[-h]\tDisplay this usage\n");
+}
diff --git a/openair3/NAS/TOOLS/conf2uedata.h b/openair3/NAS/TOOLS/conf2uedata.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed56a22718fb96ba16c892d089d0ba1560a77a3b
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf2uedata.h
@@ -0,0 +1,6 @@
+#ifndef _CONF2UEDATA_H
+#define _CONF2UEDATA_H
+
+void _display_usage(void);
+
+#endif // _CONF2UEDATA_H
diff --git a/openair3/NAS/TOOLS/conf_emm.c b/openair3/NAS/TOOLS/conf_emm.c
new file mode 100644
index 0000000000000000000000000000000000000000..668b90a0ae0514de07872b092789c68b5a187773
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_emm.c
@@ -0,0 +1,88 @@
+#include <string.h>
+
+#include "memory.h"
+#include "conf_emm.h"
+#include "fs.h"
+
+void gen_emm_data(
+    emm_nvdata_t     *emm_data,
+    const char       *hplmn,
+    const char       *msin,
+    const plmns_list *eplmn,
+    const networks_t networks)
+{
+  memset(emm_data, 0, sizeof(emm_nvdata_t));
+  int hplmn_index = get_plmn_index(hplmn, networks);
+  plmn_conf_param_t *conf = &networks.items[hplmn_index].conf;
+  int i;
+
+  emm_data->imsi.length = 8;
+  emm_data->imsi.u.num.parity = get_msin_parity(msin, conf->mcc, conf->mnc);
+  emm_data->imsi.u.num.digit1 = conf->mcc[0];
+  emm_data->imsi.u.num.digit2 = conf->mcc[1];
+  emm_data->imsi.u.num.digit3 = conf->mcc[2];
+
+  emm_data->imsi.u.num.digit4 = conf->mnc[0];
+  emm_data->imsi.u.num.digit5 = conf->mnc[1];
+
+  if (strlen(conf->mnc) == 3) {
+    emm_data->rplmn.MNCdigit3 = conf->mnc[2];
+
+    emm_data->imsi.u.num.digit6 = conf->mnc[2];
+    emm_data->imsi.u.num.digit7 = msin[0];
+    emm_data->imsi.u.num.digit8 = msin[1];
+    emm_data->imsi.u.num.digit9 = msin[2];
+    emm_data->imsi.u.num.digit10 = msin[3];
+    emm_data->imsi.u.num.digit11 = msin[4];
+    emm_data->imsi.u.num.digit12 = msin[5];
+    emm_data->imsi.u.num.digit13 = msin[6];
+    emm_data->imsi.u.num.digit14 = msin[7];
+    emm_data->imsi.u.num.digit15 = msin[8];
+  } else {
+    emm_data->rplmn.MNCdigit3 = 0xf;
+
+    emm_data->imsi.u.num.digit6 = msin[0];
+    emm_data->imsi.u.num.digit7 = msin[1];
+    emm_data->imsi.u.num.digit8 = msin[2];
+    emm_data->imsi.u.num.digit9 = msin[3];
+    emm_data->imsi.u.num.digit10 = msin[4];
+    emm_data->imsi.u.num.digit11 = msin[5];
+    emm_data->imsi.u.num.digit12 = msin[6];
+    emm_data->imsi.u.num.digit13 = msin[7];
+    emm_data->imsi.u.num.digit14 = msin[8];
+    emm_data->imsi.u.num.digit15 = msin[9];
+  }
+
+  emm_data->rplmn.MCCdigit1 = conf->mcc[0];
+  emm_data->rplmn.MCCdigit2 = conf->mcc[1];
+  emm_data->rplmn.MCCdigit3 = conf->mcc[2];
+  emm_data->rplmn.MNCdigit1 = conf->mnc[0];
+  emm_data->rplmn.MNCdigit2 = conf->mnc[1];
+
+  emm_data->eplmn.n_plmns = eplmn->size;
+  for (i = 0; i < eplmn->size; i++) {
+    emm_data->eplmn.plmn[i] = networks.items[eplmn->items[i]].plmn;
+  }
+}
+
+bool write_emm_data(const char *directory, int user_id, emm_nvdata_t *emm_data) {
+    int rc;
+	char* filename = make_filename(directory, EMM_NVRAM_FILENAME, user_id);
+	rc = memory_write(filename, emm_data, sizeof(emm_nvdata_t));
+	free(filename);
+	if (rc != RETURNok) {
+		perror("ERROR\t: memory_write() failed");
+		exit(false);
+	}
+    return(true);
+}
+
+int get_msin_parity(const char * msin, const char *mcc, const char *mnc) {
+	int imsi_size = strlen(msin) + strlen(mcc)
+			+ strlen(mnc);
+	int result = (imsi_size % 2 == 0) ? 0 : 1;
+	return result;
+
+}
+
+
diff --git a/openair3/NAS/TOOLS/conf_emm.h b/openair3/NAS/TOOLS/conf_emm.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9c30d31c9523f2bb67ab27c6722883de76d52ba
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_emm.h
@@ -0,0 +1,18 @@
+#ifndef _CONF_EMM_H
+#define _CONF_EMM_H
+
+#include "emmData.h"
+#include "conf_network.h"
+#include "conf_user_plmn.h"
+
+void gen_emm_data(
+    emm_nvdata_t     *emm_data,
+    const char       *hplmn,
+    const char       *msin,
+    const plmns_list *eplmn,
+    const networks_t networks);
+
+bool write_emm_data(const char *directory, int user_id, emm_nvdata_t *emm_data);
+int get_msin_parity(const char * msin, const char *mcc, const char *mnc);
+
+#endif
diff --git a/openair3/NAS/TOOLS/conf_network.c b/openair3/NAS/TOOLS/conf_network.c
new file mode 100644
index 0000000000000000000000000000000000000000..96e2183327ad1c8d35215f898566582efe2ee994
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_network.c
@@ -0,0 +1,112 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "conf_network.h"
+
+int get_plmn_index(const char * mccmnc, const networks_t networks) {
+	char mcc[4];
+	char mnc[strlen(mccmnc) - 2];
+	strncpy(mcc, mccmnc, 3);
+	mcc[3] = '\0';
+	strncpy(mnc, mccmnc + 3, 3);
+	mnc[strlen(mccmnc) - 3] = '\0';
+	for (int i = 0; i < networks.size; i++) {
+		if (strcmp(networks.items[i].conf.mcc, mcc) == 0) {
+			if (strcmp(networks.items[i].conf.mnc, mnc) == 0) {
+				return i;
+			}
+		}
+	}
+	return -1;
+}
+
+plmn_t make_plmn_from_conf(const plmn_conf_param_t *plmn_conf) {
+	plmn_t plmn;
+	char num[6];
+
+	memset(&plmn, 0xff, sizeof(plmn));
+
+	snprintf(num, 6, "%s%s", plmn_conf->mcc, plmn_conf->mnc);
+
+	plmn.MCCdigit2 = plmn_conf->mcc[1];
+	plmn.MCCdigit1 = plmn_conf->mcc[0];
+	plmn.MCCdigit3 = plmn_conf->mcc[2];
+	plmn.MNCdigit2 = plmn_conf->mnc[1];
+	plmn.MNCdigit1 = plmn_conf->mnc[0];
+	if (strlen(plmn_conf->mnc) > 2) {
+		plmn.MNCdigit3 = plmn_conf->mnc[2];
+	}
+	return plmn;
+}
+
+void gen_network_record_from_conf(const plmn_conf_param_t *conf, network_record_t *record) {
+		strcpy(record->fullname, conf->fullname);
+		strcpy(record->shortname, conf->shortname);
+
+		char num[6];
+		sprintf(num, "%s%s", conf->mcc, conf->mnc);
+		record->num = atoi(num);
+
+		record->plmn = make_plmn_from_conf(conf);
+		record->tac_end = 0xfffd;
+		record->tac_start = 0x0001;
+}
+
+bool parse_plmn_param(config_setting_t *plmn_setting, plmn_conf_param_t *conf) {
+	int rc = 0;
+	rc = config_setting_lookup_string(plmn_setting, FULLNAME, &conf->fullname);
+	if (rc != 1) {
+		printf("Error on FULLNAME\n");
+		return false;
+	}
+	rc = config_setting_lookup_string(plmn_setting, SHORTNAME, &conf->shortname);
+	if (rc != 1) {
+		printf("Error on SHORTNAME\n");
+		return false;
+	}
+	rc = config_setting_lookup_string(plmn_setting, MNC, &conf->mnc);
+	if (rc != 1 || strlen(conf->mnc) > 3
+			|| strlen(conf->mnc) < 2) {
+		printf("Error ond MNC. Exiting\n");
+		return false;
+	}
+	rc = config_setting_lookup_string(plmn_setting, MCC, &conf->mcc);
+	if (rc != 1 || strlen(conf->mcc) != 3) {
+		printf("Error on MCC\n");
+		return false;
+	}
+	return true;
+}
+
+bool parse_plmns(config_setting_t *all_plmn_setting, networks_t *networks) {
+	config_setting_t *plmn_setting = NULL;
+	char plmn[10];
+	int size = 0;
+
+	size = config_setting_length(all_plmn_setting);
+
+	networks->size = size;
+	networks->items = malloc(sizeof(network_t) * size);
+	for (int i = 0; i < size; i++) {
+		memset(&networks->items[i].record, 0xff, sizeof(network_record_t));
+	}
+
+	for (int i = 0; i < networks->size; i++) {
+		network_t *network = &networks->items[i];
+		sprintf(plmn, "%s%d", PLMN, i);
+		plmn_setting = config_setting_get_member(all_plmn_setting, plmn);
+		if (plmn_setting == NULL) {
+			printf("PLMN%d not fouund\n", i);
+			return false;
+		}
+
+		if ( parse_plmn_param(plmn_setting, &network->conf) == false ) {
+			return false;
+		}
+		gen_network_record_from_conf(&network->conf, &network->record);
+		network->plmn = network->record.plmn;
+	}
+	return true;
+}
+
+
diff --git a/openair3/NAS/TOOLS/conf_network.h b/openair3/NAS/TOOLS/conf_network.h
new file mode 100644
index 0000000000000000000000000000000000000000..a535841a4cfbc7bc4b41a186ab28dc476a793dbc
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_network.h
@@ -0,0 +1,54 @@
+#ifndef _CONF_NETWORK_H
+#define _CONF_NETWORK_H
+
+#include <stdbool.h>
+#include <libconfig.h>
+#include "usim_api.h"
+
+#define PLMN "PLMN"
+
+#define FULLNAME "FULLNAME"
+#define SHORTNAME "SHORTNAME"
+#define MNC "MNC"
+#define MCC "MCC"
+
+#define MIN_TAC     0x0000
+#define MAX_TAC     0xFFFE
+
+/*
+ * PLMN network operator record
+ */
+typedef struct {
+  unsigned int num;
+  plmn_t plmn;
+  char fullname[NET_FORMAT_LONG_SIZE + 1];
+  char shortname[NET_FORMAT_SHORT_SIZE + 1];
+  tac_t tac_start;
+  tac_t tac_end;
+} network_record_t;
+
+typedef struct {
+	const char *fullname;
+	const char *shortname;
+	const char *mnc;
+	const char *mcc;
+} plmn_conf_param_t;
+
+typedef struct {
+    plmn_conf_param_t conf;
+    network_record_t record;
+    plmn_t plmn;
+} network_t;
+
+typedef struct {
+    int size;
+    network_t *items;
+} networks_t;
+
+bool parse_plmn_param(config_setting_t *plmn_setting, plmn_conf_param_t *conf);
+bool parse_plmns(config_setting_t *all_plmn_setting, networks_t *plmns);
+
+void gen_network_record_from_conf(const plmn_conf_param_t *conf, network_record_t *record);
+int get_plmn_index(const char * mccmnc, const networks_t networks);
+
+#endif
diff --git a/openair3/NAS/TOOLS/conf_parser.c b/openair3/NAS/TOOLS/conf_parser.c
new file mode 100644
index 0000000000000000000000000000000000000000..d46142c4614970827c3a050323297480670f848e
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_parser.c
@@ -0,0 +1,119 @@
+#include "conf_parser.h"
+
+#include "conf_network.h"
+#include "conf_emm.h"
+#include "conf_usim.h"
+#include "conf_user_data.h"
+#include "conf_user_plmn.h"
+
+bool parse_config_file(const char *output_dir, const char *conf_filename, int output_flags) {
+	int rc = true;
+    int ret;
+    int ue_nb = 0;
+    config_setting_t *root_setting = NULL;
+    config_setting_t *ue_setting = NULL;
+    config_setting_t *all_plmn_setting = NULL;
+    char user[10];
+    config_t cfg;
+
+	networks_t networks;;
+
+    ret = get_config_from_file(conf_filename, &cfg);
+    if (ret == false) {
+        exit(1);
+    }
+
+    root_setting = config_root_setting(&cfg);
+    ue_nb = config_setting_length(root_setting) - 1;
+
+    all_plmn_setting = config_setting_get_member(root_setting, PLMN);
+    if (all_plmn_setting == NULL) {
+        printf("NO PLMN SECTION...EXITING...\n");
+        return (false);
+    }
+
+    if ( parse_plmns(all_plmn_setting, &networks) == false ) {
+        return false;
+    }
+
+    for (int i = 0; i < ue_nb; i++) {
+	    emm_nvdata_t emm_data;
+
+	    user_nvdata_t user_data;
+	    user_data_conf_t user_data_conf;
+
+	    usim_data_t usim_data;
+	    usim_data_conf_t usim_data_conf;
+
+		user_plmns_t user_plmns;
+
+        sprintf(user, "%s%d", UE, i);
+
+        ue_setting = config_setting_get_member(root_setting, user);
+        if (ue_setting == NULL) {
+            printf("Check UE%d settings\n", i);
+            return false;
+        }
+
+        if ( parse_user_plmns_conf(ue_setting, i, &user_plmns, &usim_data_conf.hplmn, networks) == false ) {
+            return false;
+        }
+
+        rc = parse_ue_user_data(ue_setting, i, &user_data_conf);
+        if (rc != true) {
+            printf("Problem in USER section for UE%d. EXITING...\n", i);
+            return false;
+        }
+        gen_user_data(&user_data_conf, &user_data);
+
+        rc = parse_ue_sim_param(ue_setting, i, &usim_data_conf);
+        if (rc != true) {
+            printf("Problem in SIM section for UE%d. EXITING...\n", i);
+            return false;
+        }
+        gen_usim_data(&usim_data_conf, &usim_data, &user_plmns, networks);
+
+        gen_emm_data(&emm_data, usim_data_conf.hplmn, usim_data_conf.msin,
+                     &user_plmns.equivalents_home, networks);
+
+        if ( output_flags & OUTPUT_UEDATA ) {
+            write_user_data(output_dir, i, &user_data);
+        }
+
+        if ( output_flags & OUTPUT_USIM ) {
+            write_usim_data(output_dir, i, &usim_data);
+        }
+
+        if ( output_flags & OUTPUT_EMM ) {
+            write_emm_data(output_dir, i, &emm_data);
+        }
+
+		user_plmns_free(&user_plmns);
+
+     }
+    free(networks.items);
+	networks.size=0;
+    config_destroy(&cfg);
+	return(true);
+}
+
+bool get_config_from_file(const char *filename, config_t *config) {
+    config_init(config);
+    if (filename == NULL) {
+        // XXX write error message ?
+        return(false);
+    }
+
+    /* Read the file. If there is an error, report it and exit. */
+    if (!config_read_file(config, filename)) {
+        fprintf(stderr, "Cant read config file '%s': %s\n", filename,
+                config_error_text(config));
+        if ( config_error_type(config) == CONFIG_ERR_PARSE ) {
+            fprintf(stderr, "This is line %d\n", config_error_line(config));
+        }
+        config_destroy(config);
+        return (false);
+    }
+    return true;
+}
+
diff --git a/openair3/NAS/TOOLS/conf_parser.h b/openair3/NAS/TOOLS/conf_parser.h
new file mode 100644
index 0000000000000000000000000000000000000000..49d3645faf02a76aa134e5b08ed76c3efbd42bdf
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_parser.h
@@ -0,0 +1,17 @@
+#ifndef _CONF_PARSER_H
+#define _CONF_PARSER_H
+
+#include <stdbool.h>
+#include <libconfig.h>
+
+#define UE "UE"
+
+#define OUTPUT_EMM     1
+#define OUTPUT_USIM    2
+#define OUTPUT_UEDATA  4
+#define OUTPUT_ALL     7
+
+bool get_config_from_file(const char *filename, config_t *config);
+bool parse_config_file(const char *output_dir, const char *filename, int output_flags);
+
+#endif
diff --git a/openair3/NAS/TOOLS/conf_user_data.c b/openair3/NAS/TOOLS/conf_user_data.c
new file mode 100644
index 0000000000000000000000000000000000000000..5e14797e166fad8c3ba5a9c5e85ae3ab79ecf2f1
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_user_data.c
@@ -0,0 +1,86 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include "commonDef.h"
+#include "memory.h"
+#include "fs.h"
+#include "conf_user_data.h"
+
+int parse_ue_user_data(config_setting_t *ue_setting, int user_id, user_data_conf_t *u) {
+	config_setting_t *ue_param_setting = NULL;
+
+	int rc = true;
+	ue_param_setting = config_setting_get_member(ue_setting, USER);
+	if (ue_param_setting == NULL) {
+		printf("Check USER section of UE%d. EXITING...\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, UE_IMEI, &u->imei);
+	if (rc != 1) {
+		printf("Check USER IMEI section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, MANUFACTURER,
+			&u->manufacturer);
+	if (rc != 1) {
+		printf("Check USER MANUFACTURER for UE%d FULLNAME. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, MODEL, &u->model);
+	if (rc != 1) {
+		printf("Check USER MODEL for UE%d FULLNAME. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, PINCODE, &u->pin);
+	if (rc != 1) {
+		printf("Check USER PIN for UE%d FULLNAME. Exiting\n", user_id);
+		return false;
+	}
+	return true;
+}
+
+void gen_user_data(user_data_conf_t *u, user_nvdata_t *user_data) {
+	memset(user_data, 0, sizeof(user_nvdata_t));
+	snprintf(user_data->IMEI, USER_IMEI_SIZE + 1, "%s%d", u->imei, _luhn(u->imei));
+	/*
+	 * Manufacturer identifier
+	 */
+	strncpy(user_data->manufacturer, u->manufacturer, USER_MANUFACTURER_SIZE);
+	/*
+	 * Model identifier
+	 */
+	strncpy(user_data->model, u->model, USER_MODEL_SIZE);
+	/*
+	 * SIM Personal Identification Number
+	 */
+	strncpy(user_data->PIN, u->pin, USER_PIN_SIZE);
+}
+
+bool write_user_data(const char *directory, int user_id, user_nvdata_t *data) {
+    int rc;
+	char* filename = make_filename(directory, USER_NVRAM_FILENAME, user_id);
+	rc = memory_write(filename, data, sizeof(user_nvdata_t));
+    free(filename);
+	if (rc != RETURNok) {
+		perror("ERROR\t: memory_write() failed");
+		return false;
+	}
+	return true;
+}
+
+/*
+ * Computes the check digit using Luhn algorithm
+ */
+int _luhn(const char* cc) {
+	const int m[] = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
+	int odd = 1, sum = 0;
+
+	for (int i = strlen(cc); i--; odd = !odd) {
+		int digit = cc[i] - '0';
+		sum += odd ? m[digit] : digit;
+	}
+
+	return 10 - (sum % 10);
+}
+
+
diff --git a/openair3/NAS/TOOLS/conf_user_data.h b/openair3/NAS/TOOLS/conf_user_data.h
new file mode 100644
index 0000000000000000000000000000000000000000..6b1d641e030c5f4bea3f529ca19531808c504481
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_user_data.h
@@ -0,0 +1,27 @@
+#ifndef _CONF_USER_DATA_H
+#define _CONF_USER_DATA_H
+
+#include <stdbool.h>
+#include <libconfig.h>
+#include "userDef.h"
+
+#define USER "USER"
+#define MANUFACTURER "MANUFACTURER"
+#define MODEL "MODEL"
+#define UE_IMEI "IMEI"
+#define PINCODE "PIN"
+
+typedef struct {
+	const char* imei;
+	const char* manufacturer;
+	const char* model;
+	const char* pin;
+} user_data_conf_t;
+
+void gen_user_data(user_data_conf_t *u, user_nvdata_t *user_data);
+bool write_user_data(const char *directory, int user_id, user_nvdata_t *data);
+int parse_ue_user_data(config_setting_t *ue_setting, int user_id, user_data_conf_t *u);
+
+int _luhn(const char* cc);
+
+#endif
diff --git a/openair3/NAS/TOOLS/conf_user_plmn.c b/openair3/NAS/TOOLS/conf_user_plmn.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b3c6dd3e6c1f2759d06e39274d711de512f0e96
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_user_plmn.c
@@ -0,0 +1,81 @@
+#include <stdlib.h>
+#include <string.h>
+#include "conf_user_plmn.h"
+
+bool parse_user_plmns_conf(config_setting_t *ue_setting, int user_id,
+                          user_plmns_t *user_plmns, const char **h,
+                          const networks_t networks) {
+	int nb_errors = 0;
+	const char *hplmn;
+
+	if ( config_setting_lookup_string(ue_setting, HPLMN, h) != 1 ) {
+		printf("Check HPLMN section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	hplmn = *h;
+	if (get_plmn_index(hplmn, networks) == -1) {
+		printf("HPLMN for UE%d is not defined in PLMN section. Exiting\n",
+				user_id);
+		return false;
+	}
+
+	if ( parse_Xplmn(ue_setting, UCPLMN, user_id, &user_plmns->users_controlled, networks) == false )
+		nb_errors++;
+	if ( parse_Xplmn(ue_setting, OPLMN, user_id, &user_plmns->operators, networks) == false )
+		nb_errors++;
+	if ( parse_Xplmn(ue_setting, OCPLMN, user_id, &user_plmns->operators_controlled, networks) == false )
+		nb_errors++;
+	if ( parse_Xplmn(ue_setting, FPLMN, user_id, &user_plmns->forbiddens, networks) == false )
+		nb_errors++;
+	if ( parse_Xplmn(ue_setting, EHPLMN, user_id, &user_plmns->equivalents_home, networks) == false )
+		nb_errors++;
+
+	if ( nb_errors > 0 )
+		return false;
+	return true;
+}
+
+bool parse_Xplmn(config_setting_t *ue_setting, const char *section,
+               int user_id, plmns_list *plmns, const networks_t networks) {
+	int rc;
+	int item_count;
+	config_setting_t *setting;
+
+	setting = config_setting_get_member(ue_setting, section);
+	if (setting == NULL) {
+		printf("Check %s section for UE%d. Exiting\n", section, user_id);
+		return false;
+	}
+
+	item_count = config_setting_length(setting);
+	int *datas = malloc(item_count * sizeof(int));
+	for (int i = 0; i < item_count; i++) {
+		const char *mccmnc = config_setting_get_string_elem(setting, i);
+		if (mccmnc == NULL) {
+			printf("Check %s section for UE%d. Exiting\n", section, user_id);
+			free(datas);
+			return false;
+		}
+		rc = get_plmn_index(mccmnc, networks);
+		if (rc == -1) {
+			printf("The PLMN %s is not defined in PLMN section. Exiting...\n",
+					mccmnc);
+			free(datas);
+			return false;
+		}
+		datas[i] = rc;
+	}
+
+	plmns->size = item_count;
+	plmns->items = datas;
+	return true;
+}
+
+void user_plmns_free(user_plmns_t *user_plmns) {
+	free(user_plmns->users_controlled.items);
+	free(user_plmns->operators.items);
+	free(user_plmns->operators_controlled.items);
+	free(user_plmns->forbiddens.items);
+	free(user_plmns->equivalents_home.items);
+	memset(user_plmns, 0, sizeof(user_plmns_t));
+}
diff --git a/openair3/NAS/TOOLS/conf_user_plmn.h b/openair3/NAS/TOOLS/conf_user_plmn.h
new file mode 100644
index 0000000000000000000000000000000000000000..561767b6c4823471171e1bd5e628707d88581b23
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_user_plmn.h
@@ -0,0 +1,37 @@
+#ifndef _CONF_USER_H
+#define _CONF_USER_H
+
+#include <stdbool.h>
+#include <libconfig.h>
+#include "conf_network.h"
+
+#define HPLMN "HPLMN"
+#define UCPLMN "UCPLMN_LIST"
+#define OPLMN "OPLMN_LIST"
+#define OCPLMN "OCPLMN_LIST"
+#define FPLMN "FPLMN_LIST"
+#define EHPLMN "EHPLMN_LIST"
+
+typedef struct {
+    int size;
+    int *items;
+} plmns_list;
+
+typedef struct {
+    plmns_list users_controlled;
+    plmns_list operators;
+    plmns_list operators_controlled;
+    plmns_list forbiddens;
+    plmns_list equivalents_home;
+} user_plmns_t;
+
+bool parse_user_plmns_conf(config_setting_t *ue_setting, int user_id,
+                          user_plmns_t *user_plmns, const char **h,
+                          const networks_t networks);
+
+bool parse_Xplmn(config_setting_t *ue_setting, const char *section,
+               int user_id, plmns_list *plmns, const networks_t networks);
+
+void user_plmns_free(user_plmns_t *user_plmns);
+
+#endif
diff --git a/openair3/NAS/TOOLS/conf_usim.c b/openair3/NAS/TOOLS/conf_usim.c
new file mode 100644
index 0000000000000000000000000000000000000000..3a5c6dbf6d146285fd0e3dedc37b5b8bf6d07ecf
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_usim.c
@@ -0,0 +1,308 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include "userDef.h"
+#include "utils.h"
+#include "conf_emm.h"
+#include "fs.h"
+#include "conf_usim.h"
+
+bool parse_ue_sim_param(config_setting_t *ue_setting, int user_id, usim_data_conf_t *u) {
+	int rc = true;
+	config_setting_t *ue_param_setting = NULL;
+	ue_param_setting = config_setting_get_member(ue_setting, SIM);
+	if (ue_param_setting == NULL) {
+		printf("Check SIM section for UE%d. EXITING...\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, MSIN, &u->msin);
+	if (rc != 1 || strlen(u->msin) > 10 || strlen(u->msin) < 9) {
+		printf("Check SIM MSIN section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, USIM_API_K,
+			&u->usim_api_k);
+	if (rc != 1) {
+		printf("Check SIM USIM_API_K  section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, OPC, &u->opc);
+	if (rc != 1) {
+		printf("Check SIM OPC section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	rc = config_setting_lookup_string(ue_param_setting, MSISDN, &u->msisdn);
+	if (rc != 1) {
+		printf("Check SIM MSISDN section for UE%d. Exiting\n", user_id);
+		return false;
+	}
+	return true;
+}
+
+void gen_usim_data(usim_data_conf_t *u, usim_data_t *usim_data,
+                   const user_plmns_t *user_plmns, const networks_t networks) {
+    int hplmn_index = get_plmn_index(u->hplmn, networks);
+	const plmn_conf_param_t *conf = &networks.items[hplmn_index].conf;
+	memset(usim_data, 0, sizeof(usim_data_t));
+	usim_data->imsi.length = 8;
+	usim_data->imsi.u.num.parity = get_msin_parity(u->msin,
+		conf->mcc,
+		conf->mnc);
+
+	usim_data->imsi.u.num.digit1 = conf->mcc[0];
+	usim_data->imsi.u.num.digit2 = conf->mcc[1];
+	usim_data->imsi.u.num.digit3 = conf->mcc[2];
+
+	usim_data->imsi.u.num.digit4 = conf->mnc[0];
+	usim_data->imsi.u.num.digit5 = conf->mnc[1];
+
+	if (strlen(conf->mnc) == 2) {
+		usim_data->imsi.u.num.digit6 = u->msin[0];
+		usim_data->imsi.u.num.digit7 = u->msin[1];
+		usim_data->imsi.u.num.digit8 = u->msin[2];
+		usim_data->imsi.u.num.digit9 = u->msin[3];
+		usim_data->imsi.u.num.digit10 = u->msin[4];
+		usim_data->imsi.u.num.digit11 = u->msin[5];
+		usim_data->imsi.u.num.digit12 = u->msin[6];
+		usim_data->imsi.u.num.digit13 = u->msin[7];
+		usim_data->imsi.u.num.digit14 = u->msin[8];
+		usim_data->imsi.u.num.digit15 = u->msin[9];
+	} else {
+		usim_data->imsi.u.num.digit6 = conf->mnc[2];
+		usim_data->imsi.u.num.digit7 = u->msin[0];
+		usim_data->imsi.u.num.digit8 = u->msin[1];
+		usim_data->imsi.u.num.digit9 = u->msin[2];
+		usim_data->imsi.u.num.digit10 = u->msin[3];
+		usim_data->imsi.u.num.digit11 = u->msin[4];
+		usim_data->imsi.u.num.digit12 = u->msin[5];
+		usim_data->imsi.u.num.digit13 = u->msin[6];
+		usim_data->imsi.u.num.digit14 = u->msin[7];
+		usim_data->imsi.u.num.digit15 = u->msin[8];
+	}
+
+	/*
+	 * Ciphering and Integrity Keys
+	 */
+	usim_data->keys.ksi = KSI;
+	memset(&usim_data->keys.ck, 0, USIM_CK_SIZE);
+	memset(&usim_data->keys.ik, 0, USIM_IK_SIZE);
+	/*
+	 * Higher Priority PLMN search period
+	 */
+	usim_data->hpplmn = 0x00; /* Disable timer */
+
+	/*
+	 * List of Forbidden PLMNs
+	 */
+	for (int i = 0; i < USIM_FPLMN_MAX; i++) {
+		memset(&usim_data->fplmn[i], 0xff, sizeof(plmn_t));
+	}
+	if (user_plmns->forbiddens.size > 0) {
+		for (int i = 0; i < user_plmns->forbiddens.size; i++) {
+			usim_data->fplmn[i] = networks.items[user_plmns->forbiddens.items[i]].plmn;
+		}
+	}
+
+	/*
+	 * Location Information
+	 */
+	usim_data->loci.tmsi = DEFAULT_TMSI;
+	usim_data->loci.lai.plmn = networks.items[hplmn_index].plmn;
+	usim_data->loci.lai.lac = DEFAULT_LAC;
+	usim_data->loci.status = USIM_LOCI_NOT_UPDATED;
+	/*
+	 * Packet Switched Location Information
+	 */
+	usim_data->psloci.p_tmsi = DEFAULT_P_TMSI;
+	usim_data->psloci.signature[0] = 0x01;
+	usim_data->psloci.signature[1] = 0x02;
+	usim_data->psloci.signature[2] = 0x03;
+	usim_data->psloci.rai.plmn = networks.items[hplmn_index].plmn;
+	usim_data->psloci.rai.lac = DEFAULT_LAC;
+	usim_data->psloci.rai.rac = DEFAULT_RAC;
+	usim_data->psloci.status = USIM_PSLOCI_NOT_UPDATED;
+	/*
+	 * Administrative Data
+	 */
+	usim_data->ad.UE_Operation_Mode = USIM_NORMAL_MODE;
+	usim_data->ad.Additional_Info = 0xffff;
+	usim_data->ad.MNC_Length = strlen(conf->mnc);
+	/*
+	 * EPS NAS security context
+	 */
+	usim_data->securityctx.length = 52;
+	usim_data->securityctx.KSIasme.type = USIM_KSI_ASME_TAG;
+	usim_data->securityctx.KSIasme.length = 1;
+	usim_data->securityctx.KSIasme.value[0] = KSI_ASME;
+	usim_data->securityctx.Kasme.type = USIM_K_ASME_TAG;
+	usim_data->securityctx.Kasme.length = USIM_K_ASME_SIZE;
+	memset(usim_data->securityctx.Kasme.value, 0,
+			usim_data->securityctx.Kasme.length);
+	usim_data->securityctx.ulNAScount.type = USIM_UL_NAS_COUNT_TAG;
+	usim_data->securityctx.ulNAScount.length = USIM_UL_NAS_COUNT_SIZE;
+	memset(usim_data->securityctx.ulNAScount.value, 0,
+			usim_data->securityctx.ulNAScount.length);
+	usim_data->securityctx.dlNAScount.type = USIM_DL_NAS_COUNT_TAG;
+	usim_data->securityctx.dlNAScount.length = USIM_DL_NAS_COUNT_SIZE;
+	memset(usim_data->securityctx.dlNAScount.value, 0,
+			usim_data->securityctx.dlNAScount.length);
+	usim_data->securityctx.algorithmID.type = USIM_INT_ENC_ALGORITHMS_TAG;
+	usim_data->securityctx.algorithmID.length = 1;
+	usim_data->securityctx.algorithmID.value[0] = SECURITY_ALGORITHMS;
+	/*
+	 * Subcriber's Number
+	 */
+	usim_data->msisdn.length = 7;
+	usim_data->msisdn.number.ext = 1;
+	usim_data->msisdn.number.ton = MSISDN_TON_UNKNOWKN;
+	usim_data->msisdn.number.npi = MSISDN_NPI_ISDN_TELEPHONY;
+	usim_data->msisdn.conf1_record_id = 0xff; /* Not used */
+	usim_data->msisdn.ext1_record_id = 0xff; /* Not used */
+	int j = 0;
+	for (int i = 0; i < strlen(u->msisdn); i += 2) {
+		usim_data->msisdn.number.digit[j].msb = u->msisdn[i];
+		j++;
+	}
+	j = 0;
+	for (int i = 1; i < strlen(u->msisdn); i += 2) {
+		usim_data->msisdn.number.digit[j].lsb = u->msisdn[i];
+		j++;
+
+	}
+	if (strlen(u->msisdn) % 2 == 0) {
+		for (int i = strlen(u->msisdn) / 2; i < 10; i++) {
+			usim_data->msisdn.number.digit[i].msb = 0xf;
+			usim_data->msisdn.number.digit[i].lsb = 0xf;
+		}
+	} else {
+		usim_data->msisdn.number.digit[strlen(u->msisdn) / 2].lsb = 0xf;
+		for (int i = (strlen(u->msisdn) / 2) + 1; i < 10; i++) {
+			usim_data->msisdn.number.digit[i].msb = 0xf;
+			usim_data->msisdn.number.digit[i].lsb = 0xf;
+		}
+	}
+	/*
+	 * PLMN Network Name and Operator PLMN List
+	 */
+	for (int i = 0; i < user_plmns->operators.size; i++) {
+		network_record_t record = networks.items[user_plmns->operators.items[i]].record;
+		usim_data->pnn[i].fullname.type = USIM_PNN_FULLNAME_TAG;
+		usim_data->pnn[i].fullname.length = strlen(record.fullname);
+		strncpy((char*) usim_data->pnn[i].fullname.value, record.fullname,
+				usim_data->pnn[i].fullname.length);
+		usim_data->pnn[i].shortname.type = USIM_PNN_SHORTNAME_TAG;
+		usim_data->pnn[i].shortname.length = strlen(record.shortname);
+		strncpy((char*) usim_data->pnn[i].shortname.value, record.shortname,
+				usim_data->pnn[i].shortname.length);
+		usim_data->opl[i].plmn = record.plmn;
+		usim_data->opl[i].start = record.tac_start;
+		usim_data->opl[i].end = record.tac_end;
+		usim_data->opl[i].record_id = i;
+	}
+	if (user_plmns->operators.size < USIM_OPL_MAX) {
+		for (int i = user_plmns->operators.size; i < USIM_OPL_MAX; i++) {
+			memset(&usim_data->opl[i].plmn, 0xff, sizeof(plmn_t));
+		}
+	}
+
+	/*
+	 * List of Equivalent HPLMNs
+	 */
+	for (int i = 0; i < user_plmns->equivalents_home.size; i++) {
+		usim_data->ehplmn[i] = networks.items[user_plmns->equivalents_home.items[i]].plmn;
+	}
+	if (user_plmns->equivalents_home.size < USIM_EHPLMN_MAX) {
+		for (int i = user_plmns->equivalents_home.size; i < USIM_EHPLMN_MAX; i++) {
+			memset(&usim_data->ehplmn[i], 0xff, sizeof(plmn_t));
+		}
+	}
+	/*
+	 * Home PLMN Selector with Access Technology
+	 */
+	usim_data->hplmn.plmn = networks.items[hplmn_index].plmn;
+	usim_data->hplmn.AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
+
+	/*
+	 * List of user controlled PLMN selector with Access Technology
+	 */
+	for (int i = 0; i < USIM_PLMN_MAX; i++) {
+		memset(&usim_data->plmn[i], 0xff, sizeof(plmn_t));
+	}
+	if (user_plmns->users_controlled.size > 0) {
+		for (int i = 0; i < user_plmns->users_controlled.size; i++) {
+			usim_data->plmn[i].plmn = networks.items[user_plmns->users_controlled.items[i]].plmn;
+		}
+	}
+
+	// List of operator controlled PLMN selector with Access Technology
+
+	for (int i = 0; i < USIM_OPLMN_MAX; i++) {
+		memset(&usim_data->oplmn[i], 0xff, sizeof(plmn_t));
+	}
+	if (user_plmns->operators_controlled.size > 0) {
+		for (int i = 0; i < user_plmns->operators_controlled.size; i++) {
+			usim_data->oplmn[i].plmn = networks.items[user_plmns->operators_controlled.items[i]].plmn;
+			usim_data->oplmn[i].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN
+					| USIM_ACT_EUTRAN);
+		}
+	}
+	/*
+	 * EPS Location Information
+	 */
+	usim_data->epsloci.guti.gummei.plmn =
+			networks.items[hplmn_index].plmn;
+	usim_data->epsloci.guti.gummei.MMEgid = DEFAULT_MME_ID;
+	usim_data->epsloci.guti.gummei.MMEcode = DEFAULT_MME_CODE;
+	usim_data->epsloci.guti.m_tmsi = DEFAULT_M_TMSI;
+	usim_data->epsloci.tai.plmn = usim_data->epsloci.guti.gummei.plmn;
+	usim_data->epsloci.tai.tac = DEFAULT_TAC;
+	usim_data->epsloci.status = USIM_EPSLOCI_UPDATED;
+	/*
+	 * Non-Access Stratum configuration
+	 */
+	usim_data->nasconfig.NAS_SignallingPriority.type =
+	USIM_NAS_SIGNALLING_PRIORITY_TAG;
+	usim_data->nasconfig.NAS_SignallingPriority.length = 1;
+	usim_data->nasconfig.NAS_SignallingPriority.value[0] = 0x00;
+	usim_data->nasconfig.NMO_I_Behaviour.type = USIM_NMO_I_BEHAVIOUR_TAG;
+	usim_data->nasconfig.NMO_I_Behaviour.length = 1;
+	usim_data->nasconfig.NMO_I_Behaviour.value[0] = 0x00;
+	usim_data->nasconfig.AttachWithImsi.type = USIM_ATTACH_WITH_IMSI_TAG;
+	usim_data->nasconfig.AttachWithImsi.length = 1;
+#if defined(START_WITH_GUTI)
+	usim_data->nasconfig.AttachWithImsi.value[0] = 0x00;
+#else
+	usim_data->nasconfig.AttachWithImsi.value[0] = 0x01;
+#endif
+	usim_data->nasconfig.MinimumPeriodicSearchTimer.type =
+	USIM_MINIMUM_PERIODIC_SEARCH_TIMER_TAG;
+	usim_data->nasconfig.MinimumPeriodicSearchTimer.length = 1;
+	usim_data->nasconfig.MinimumPeriodicSearchTimer.value[0] = 0x00;
+	usim_data->nasconfig.ExtendedAccessBarring.type =
+	USIM_EXTENDED_ACCESS_BARRING_TAG;
+	usim_data->nasconfig.ExtendedAccessBarring.length = 1;
+	usim_data->nasconfig.ExtendedAccessBarring.value[0] = 0x00;
+	usim_data->nasconfig.Timer_T3245_Behaviour.type =
+	USIM_TIMER_T3245_BEHAVIOUR_TAG;
+	usim_data->nasconfig.Timer_T3245_Behaviour.length = 1;
+	usim_data->nasconfig.Timer_T3245_Behaviour.value[0] = 0x00;
+
+        /* initialize the subscriber authentication security key */
+        if (hex_string_to_hex_value(usim_data->keys.usim_api_k,
+                                    u->usim_api_k, USIM_API_K_SIZE) == -1 ||
+            hex_string_to_hex_value(usim_data->keys.opc,
+                                    u->opc, OPC_SIZE) == -1) {
+          fprintf(stderr, "fix your configuration file\n");
+          exit(1);
+        }
+}
+
+bool write_usim_data(const char *directory, int user_id, usim_data_t *usim_data){
+    int rc;
+    char *filename = make_filename(directory, USIM_API_NVRAM_FILENAME, user_id);
+    rc = usim_api_write(filename, usim_data);
+    free(filename);
+    return rc;
+}
+
+
diff --git a/openair3/NAS/TOOLS/conf_usim.h b/openair3/NAS/TOOLS/conf_usim.h
new file mode 100644
index 0000000000000000000000000000000000000000..4768a6d8c0c9239a639fa82b5946ca013d0ad98b
--- /dev/null
+++ b/openair3/NAS/TOOLS/conf_usim.h
@@ -0,0 +1,48 @@
+#ifndef _CONF_USIM_H
+#define _CONF_USIM_H
+
+#include <libconfig.h>
+#include "usim_api.h"
+#include "conf_user_plmn.h"
+
+#define SIM "SIM"
+#define MSIN "MSIN"
+#define USIM_API_K "USIM_API_K"
+#define OPC "OPC"
+#define MSISDN "MSISDN"
+
+#define KSI               USIM_KSI_NOT_AVAILABLE
+#define KSI_ASME          USIM_KSI_NOT_AVAILABLE
+
+#define OPC_SIZE          16
+
+#define DEFAULT_TMSI      0x0000000D
+#define DEFAULT_P_TMSI    0x0000000D
+#define DEFAULT_M_TMSI    0x0000000D
+
+#define DEFAULT_RAC       0x01
+#define DEFAULT_TAC       0x0001
+#define DEFAULT_LAC       0xFFFE
+
+#define DEFAULT_MME_ID    0x0102
+#define DEFAULT_MME_CODE  0x0F
+
+// TODO add this setting in configuration file
+#define INT_ALGO          USIM_INT_EIA2
+#define ENC_ALGO          USIM_ENC_EEA0
+#define SECURITY_ALGORITHMS (ENC_ALGO | INT_ALGO)
+
+typedef struct {
+    const char *msin;
+    const char *usim_api_k;
+    const char *msisdn;
+    const char *opc;
+    const char *hplmn;
+} usim_data_conf_t;
+
+bool parse_ue_sim_param(config_setting_t *ue_setting, int user_id, usim_data_conf_t *u);
+bool write_usim_data(const char *directory, int user_id, usim_data_t *usim_data);
+void gen_usim_data(usim_data_conf_t *u, usim_data_t *usim_data,
+                   const user_plmns_t *user_plmns, const networks_t networks);
+
+#endif
diff --git a/openair3/NAS/TOOLS/display.c b/openair3/NAS/TOOLS/display.c
new file mode 100644
index 0000000000000000000000000000000000000000..7d145d1f3b6dd648e229bb41c7900132dbe86b2d
--- /dev/null
+++ b/openair3/NAS/TOOLS/display.c
@@ -0,0 +1,372 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "userDef.h"
+#include "usim_api.h"
+#include "emmData.h"
+#include "display.h"
+#include "memory.h"
+#include "fs.h"
+
+#define PRINT_PLMN_DIGIT(d) if ((d) != 0xf) printf("%u", (d))
+
+#define PRINT_PLMN(plmn)    \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit1); \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit2); \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit3); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit1); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit2); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit3)
+
+#define PRINT_PLMN_DIGIT(d) if ((d) != 0xf) printf("%u", (d))
+
+#define PRINT_PLMN(plmn)    \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit1); \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit2); \
+    PRINT_PLMN_DIGIT((plmn).MCCdigit3); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit1); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit2); \
+    PRINT_PLMN_DIGIT((plmn).MNCdigit3)
+
+// return number of files displayed
+int display_data_from_directory(const char *directory, int flags) {
+	int user_id = 0;
+	char *filename;
+	bool found = true;
+  int displayed_count = 0;
+
+	while ( found ) {
+		found = false;
+
+		if ( flags & DISPLAY_UEDATA ) {
+			filename = get_ue_filename(directory, user_id);
+			if ( file_exist_and_is_readable(filename) ) {
+				display_ue_data(filename);
+				displayed_count += 1;
+				found = true;
+				printf("UE identity data file: %s\n", filename);
+			}
+			free(filename);
+		}
+
+		if ( flags & DISPLAY_EMM ) {
+			filename = get_emm_filename(directory, user_id);
+			if ( file_exist_and_is_readable(filename) ) {
+				display_emm_data(filename);
+				displayed_count += 1;
+				found = true;
+				printf("EPS Mobility Management data file: %s\n", filename);
+			}
+			free(filename);
+		}
+
+		if ( flags & DISPLAY_USIM ) {
+			filename = get_usim_filename(directory, user_id);
+			if ( file_exist_and_is_readable(filename) ) {
+				display_usim_data(filename);
+				displayed_count += 1;
+				found = true;
+				printf("USIM data file: %s\n", filename);
+			}
+			free(filename);
+		}
+
+		user_id += 1;
+	}
+	return displayed_count;
+}
+
+void display_ue_data(const char *filename) {
+	user_nvdata_t data;
+	int rc;
+	/*
+	 * Read UE's non-volatile data
+	 */
+	memset(&data, 0, sizeof(user_nvdata_t));
+	rc = memory_read(filename, &data, sizeof(user_nvdata_t));
+
+	if (rc != RETURNok) {
+		perror("ERROR\t: memory_read() failed");
+		exit(false);
+	}
+
+	/*
+	 * Display UE's non-volatile data
+	 */
+	printf("\nUE's non-volatile data:\n\n");
+	printf("IMEI\t\t= %s\n", data.IMEI);
+	printf("manufacturer\t= %s\n", data.manufacturer);
+	printf("model\t\t= %s\n", data.model);
+	printf("PIN\t\t= %s\n", data.PIN);
+}
+
+/*
+ * Displays UE's non-volatile EMM data
+ */
+void display_emm_data(const char *filename) {
+
+	int rc;
+	emm_nvdata_t data;
+
+	/*
+	 * Read EMM non-volatile data
+	 */
+	memset(&data, 0, sizeof(emm_nvdata_t));
+	rc = memory_read(filename, &data, sizeof(emm_nvdata_t));
+	if (rc != RETURNok) {
+		perror("ERROR\t: memory_read() failed ");
+		exit(false);
+	}
+
+	/*
+	 * Display EMM non-volatile data
+	 */
+	printf("\nEMM non-volatile data:\n\n");
+
+	printf("IMSI\t\t= ");
+
+	if (data.imsi.u.num.digit6 == 0b1111) {
+		if (data.imsi.u.num.digit15 == 0b1111) {
+			printf("%u%u%u.%u%u.%u%u%u%u%u%u%u%u\n", data.imsi.u.num.digit1,
+					data.imsi.u.num.digit2, data.imsi.u.num.digit3,
+					data.imsi.u.num.digit4, data.imsi.u.num.digit5,
+
+					data.imsi.u.num.digit7, data.imsi.u.num.digit8,
+					data.imsi.u.num.digit9, data.imsi.u.num.digit10,
+					data.imsi.u.num.digit11, data.imsi.u.num.digit12,
+					data.imsi.u.num.digit13, data.imsi.u.num.digit14);
+		} else {
+			printf("%u%u%u.%u%u.%u%u%u%u%u%u%u%u%u\n", data.imsi.u.num.digit1,
+					data.imsi.u.num.digit2, data.imsi.u.num.digit3,
+					data.imsi.u.num.digit4, data.imsi.u.num.digit5,
+
+					data.imsi.u.num.digit7, data.imsi.u.num.digit8,
+					data.imsi.u.num.digit9, data.imsi.u.num.digit10,
+					data.imsi.u.num.digit11, data.imsi.u.num.digit12,
+					data.imsi.u.num.digit13, data.imsi.u.num.digit14,
+					data.imsi.u.num.digit15);
+		}
+	} else {
+		if (data.imsi.u.num.digit15 == 0b1111) {
+			printf("%u%u%u.%u%u%u.%u%u%u%u%u%u%u%u\n", data.imsi.u.num.digit1,
+					data.imsi.u.num.digit2, data.imsi.u.num.digit3,
+					data.imsi.u.num.digit4, data.imsi.u.num.digit5,
+					data.imsi.u.num.digit6,
+
+					data.imsi.u.num.digit7, data.imsi.u.num.digit8,
+					data.imsi.u.num.digit9, data.imsi.u.num.digit10,
+					data.imsi.u.num.digit11, data.imsi.u.num.digit12,
+					data.imsi.u.num.digit13, data.imsi.u.num.digit14);
+		} else {
+			printf("%u%u%u.%u%u%u.%u%u%u%u%u%u%u%u%u\n", data.imsi.u.num.digit1,
+					data.imsi.u.num.digit2, data.imsi.u.num.digit3,
+					data.imsi.u.num.digit4, data.imsi.u.num.digit5,
+					data.imsi.u.num.digit6,
+
+					data.imsi.u.num.digit7, data.imsi.u.num.digit8,
+					data.imsi.u.num.digit9, data.imsi.u.num.digit10,
+					data.imsi.u.num.digit11, data.imsi.u.num.digit12,
+					data.imsi.u.num.digit13, data.imsi.u.num.digit14,
+					data.imsi.u.num.digit15);
+		}
+	}
+
+	printf("RPLMN\t\t= ");
+	PRINT_PLMN(data.rplmn);
+	printf("\n");
+
+	for (int i = 0; i < data.eplmn.n_plmns; i++) {
+		printf("EPLMN[%d]\t= ", i);
+		PRINT_PLMN(data.eplmn.plmn[i]);
+		printf("\n");
+	}
+}
+
+void display_usim_data(const char *filename) {
+
+	int rc;
+	usim_data_t data = { };
+
+    rc = usim_api_read(filename, &data);
+
+	if (rc != RETURNok) {
+		perror("ERROR\t: usim_api_read() failed");
+		exit(2);
+	}
+
+	/*
+	 * Display USIM application data
+	 */
+	printf("\nUSIM data:\n\n");
+	int digits;
+
+	printf("Administrative Data:\n");
+	printf("\tUE_Operation_Mode\t= 0x%.2x\n", data.ad.UE_Operation_Mode);
+	printf("\tAdditional_Info\t\t= 0x%.4x\n", data.ad.Additional_Info);
+	printf("\tMNC_Length\t\t= %d\n\n", data.ad.MNC_Length);
+
+	printf("IMSI:\n");
+	printf("\tlength\t= %d\n", data.imsi.length);
+	printf("\tparity\t= %s\n",
+			data.imsi.u.num.parity == EVEN_PARITY ? "Even" : "Odd");
+	digits = (data.imsi.length * 2) - 1
+			- (data.imsi.u.num.parity == EVEN_PARITY ? 1 : 0);
+	printf("\tdigits\t= %d\n", digits);
+	printf("\tdigits\t= %u%u%u%u%u%x%u%u%u%u",
+			data.imsi.u.num.digit1, // MCC digit 1
+			data.imsi.u.num.digit2, // MCC digit 2
+			data.imsi.u.num.digit3, // MCC digit 3
+			data.imsi.u.num.digit4, // MNC digit 1
+			data.imsi.u.num.digit5, // MNC digit 2
+			data.imsi.u.num.digit6, // MNC digit 3
+			data.imsi.u.num.digit7, data.imsi.u.num.digit8,
+			data.imsi.u.num.digit9, data.imsi.u.num.digit10);
+
+	if (digits >= 11)
+		printf("%x", data.imsi.u.num.digit11);
+
+	if (digits >= 12)
+		printf("%x", data.imsi.u.num.digit12);
+
+	if (digits >= 13)
+		printf("%x", data.imsi.u.num.digit13);
+
+	if (digits >= 14)
+		printf("%x", data.imsi.u.num.digit14);
+
+	if (digits >= 15)
+		printf("%x", data.imsi.u.num.digit15);
+
+	printf("\n\n");
+
+	printf("Ciphering and Integrity Keys:\n");
+	printf("\tKSI\t: 0x%.2x\n", data.keys.ksi);
+	char key[USIM_CK_SIZE + 1];
+	key[USIM_CK_SIZE] = '\0';
+	memcpy(key, data.keys.ck, USIM_CK_SIZE);
+	printf("\tCK\t: \"%s\"\n", key);
+	memcpy(key, data.keys.ik, USIM_IK_SIZE);
+	printf("\tIK\t: \"%s\"\n", key);
+
+	printf("EPS NAS security context:\n");
+	printf("\tKSIasme\t: 0x%.2x\n", data.securityctx.KSIasme.value[0]);
+	char kasme[USIM_K_ASME_SIZE + 1];
+	kasme[USIM_K_ASME_SIZE] = '\0';
+	memcpy(kasme, data.securityctx.Kasme.value, USIM_K_ASME_SIZE);
+	printf("\tKasme\t: \"%s\"\n", kasme);
+	printf("\tulNAScount\t: 0x%.8x\n",
+			*(uint32_t*) data.securityctx.ulNAScount.value);
+	printf("\tdlNAScount\t: 0x%.8x\n",
+			*(uint32_t*) data.securityctx.dlNAScount.value);
+	printf("\talgorithmID\t: 0x%.2x\n\n",
+			data.securityctx.algorithmID.value[0]);
+
+	printf("MSISDN\t= %u%u%u %u%u%u%u %u%u%u%u\n\n",
+			data.msisdn.number.digit[0].msb, data.msisdn.number.digit[0].lsb,
+			data.msisdn.number.digit[1].msb, data.msisdn.number.digit[1].lsb,
+			data.msisdn.number.digit[2].msb, data.msisdn.number.digit[2].lsb,
+			data.msisdn.number.digit[3].msb, data.msisdn.number.digit[3].lsb,
+			data.msisdn.number.digit[4].msb, data.msisdn.number.digit[4].lsb,
+			data.msisdn.number.digit[5].msb);
+
+	for (int i = 0; i < USIM_PNN_MAX; i++) {
+		printf("PNN[%d]\t= {%s, %s}\n", i, data.pnn[i].fullname.value,
+				data.pnn[i].shortname.value);
+	}
+
+	printf("\n");
+
+	for (int i = 0; i < USIM_OPL_MAX; i++) {
+		printf("OPL[%d]\t= ", i);
+		PRINT_PLMN(data.opl[i].plmn);
+		printf(", TAC = [%.4x - %.4x], record_id = %d\n", data.opl[i].start,
+				data.opl[i].end, data.opl[i].record_id);
+	}
+
+	printf("\n");
+
+	printf("HPLMN\t\t= ");
+	PRINT_PLMN(data.hplmn.plmn);
+	printf(", AcT = 0x%x\n\n", data.hplmn.AcT);
+
+	for (int i = 0; i < USIM_FPLMN_MAX; i++) {
+		printf("FPLMN[%d]\t= ", i);
+		PRINT_PLMN(data.fplmn[i]);
+		printf("\n");
+	}
+
+	printf("\n");
+
+	for (int i = 0; i < USIM_EHPLMN_MAX; i++) {
+		printf("EHPLMN[%d]\t= ", i);
+		PRINT_PLMN(data.ehplmn[i]);
+		printf("\n");
+	}
+
+	printf("\n");
+
+	for (int i = 0; i < USIM_PLMN_MAX; i++) {
+		printf("PLMN[%d]\t\t= ", i);
+		PRINT_PLMN(data.plmn[i].plmn);
+		printf(", AcTPLMN = 0x%x", data.plmn[i].AcT);
+		printf("\n");
+	}
+
+	printf("\n");
+
+	for (int i = 0; i < USIM_OPLMN_MAX; i++) {
+		printf("OPLMN[%d]\t= ", i);
+		PRINT_PLMN(data.oplmn[i].plmn);
+		printf(", AcTPLMN = 0x%x", data.oplmn[i].AcT);
+		printf("\n");
+	}
+
+	printf("\n");
+
+	printf("HPPLMN\t\t= 0x%.2x (%d minutes)\n\n", data.hpplmn, data.hpplmn);
+
+	printf("LOCI:\n");
+	printf("\tTMSI = 0x%.4x\n", data.loci.tmsi);
+	printf("\tLAI\t: PLMN = ");
+	PRINT_PLMN(data.loci.lai.plmn);
+	printf(", LAC = 0x%.2x\n", data.loci.lai.lac);
+	printf("\tstatus\t= %d\n\n", data.loci.status);
+
+	printf("PSLOCI:\n");
+	printf("\tP-TMSI = 0x%.4x\n", data.psloci.p_tmsi);
+	printf("\tsignature = 0x%x 0x%x 0x%x\n", data.psloci.signature[0],
+			data.psloci.signature[1], data.psloci.signature[2]);
+	printf("\tRAI\t: PLMN = ");
+	PRINT_PLMN(data.psloci.rai.plmn);
+	printf(", LAC = 0x%.2x, RAC = 0x%.1x\n", data.psloci.rai.lac,
+			data.psloci.rai.rac);
+	printf("\tstatus\t= %d\n\n", data.psloci.status);
+
+	printf("EPSLOCI:\n");
+	printf("\tGUTI\t: GUMMEI\t: (PLMN = ");
+	PRINT_PLMN(data.epsloci.guti.gummei.plmn);
+	printf(", MMEgid = 0x%.2x, MMEcode = 0x%.1x)",
+			data.epsloci.guti.gummei.MMEgid, data.epsloci.guti.gummei.MMEcode);
+	printf(", M-TMSI = 0x%.4x\n", data.epsloci.guti.m_tmsi);
+	printf("\tTAI\t: PLMN = ");
+	PRINT_PLMN(data.epsloci.tai.plmn);
+	printf(", TAC = 0x%.2x\n", data.epsloci.tai.tac);
+	printf("\tstatus\t= %d\n\n", data.epsloci.status);
+
+	printf("NASCONFIG:\n");
+	printf("\tNAS_SignallingPriority\t\t: 0x%.2x\n",
+			data.nasconfig.NAS_SignallingPriority.value[0]);
+	printf("\tNMO_I_Behaviour\t\t\t: 0x%.2x\n",
+			data.nasconfig.NMO_I_Behaviour.value[0]);
+	printf("\tAttachWithImsi\t\t\t: 0x%.2x\n",
+			data.nasconfig.AttachWithImsi.value[0]);
+	printf("\tMinimumPeriodicSearchTimer\t: 0x%.2x\n",
+			data.nasconfig.MinimumPeriodicSearchTimer.value[0]);
+	printf("\tExtendedAccessBarring\t\t: 0x%.2x\n",
+			data.nasconfig.ExtendedAccessBarring.value[0]);
+	printf("\tTimer_T3245_Behaviour\t\t: 0x%.2x\n",
+			data.nasconfig.Timer_T3245_Behaviour.value[0]);
+
+}
+
+
diff --git a/openair3/NAS/TOOLS/display.h b/openair3/NAS/TOOLS/display.h
new file mode 100644
index 0000000000000000000000000000000000000000..fef721c490b8cf82b70416d493e86340f7d16d3e
--- /dev/null
+++ b/openair3/NAS/TOOLS/display.h
@@ -0,0 +1,16 @@
+#ifndef _DISPLAY_H
+#define _DISPLAY_H
+
+#define DISPLAY_EMM 1
+#define DISPLAY_USIM 2
+#define DISPLAY_UEDATA 4
+#define DISPLAY_ALL 7
+
+// return number of files displayed
+int display_data_from_directory(const char *directory, int flags);
+
+void display_ue_data(const char *filename);
+void display_emm_data(const char *filename);
+void display_usim_data(const char *filename);
+
+#endif
diff --git a/openair3/NAS/TOOLS/fs.c b/openair3/NAS/TOOLS/fs.c
new file mode 100644
index 0000000000000000000000000000000000000000..c25f10a1e790d8c9d34051e32831781948ebf77f
--- /dev/null
+++ b/openair3/NAS/TOOLS/fs.c
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fs.h"
+#include "user_api.h"
+#include "utils.h"
+
+bool file_exist_and_is_readable(const char *filename) {
+    FILE *file ;
+    file = fopen(filename, "r");
+    if ( file == NULL )
+        return false;
+    fclose(file);
+    return true;
+}
+
+char *get_ue_filename(const char *output_dir, int user_id) {
+    return make_filename(output_dir, USER_NVRAM_FILENAME, user_id);
+}
+
+char *get_emm_filename(const char *output_dir, int user_id) {
+    return make_filename(output_dir, EMM_NVRAM_FILENAME, user_id);
+}
+
+char *get_usim_filename(const char *output_dir, int user_id) {
+	return make_filename(output_dir, USIM_API_NVRAM_FILENAME, user_id);
+}
+
+char *make_filename(const char *output_dir, const char *filename, int ueid) {
+	size_t size;
+    char *str_ueid, *str;
+
+    str_ueid = itoa(ueid);
+
+    if (str_ueid == NULL) {
+        perror("ERROR\t: itoa() failed");
+        exit(EXIT_FAILURE);
+    }
+
+    size = strlen(output_dir)+strlen(filename) + sizeof(ueid) + 1 + 1; // for \0 and for '/'
+    str = malloc(size);
+    if (str == NULL) {
+        perror("ERROR\t: make_filename() failed");
+        exit(EXIT_FAILURE);
+    }
+
+    snprintf(str, size, "%s/%s%s",output_dir, filename, str_ueid);
+    free(str_ueid);
+
+ return str;
+}
diff --git a/openair3/NAS/TOOLS/fs.h b/openair3/NAS/TOOLS/fs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ecfca559550a2a440727f39045637a165209222d
--- /dev/null
+++ b/openair3/NAS/TOOLS/fs.h
@@ -0,0 +1,12 @@
+#ifndef _FS_H
+#define _FS_H
+
+#include <stdbool.h>
+
+bool file_exist_and_is_readable(const char *filename);
+char *get_ue_filename(const char *output_dir, int user_id);
+char *get_emm_filename(const char *output_dir, int user_id);
+char *get_usim_filename(const char *output_dir, int user_id);
+char *make_filename(const char *output_dir, const char *filename, int ueid);
+
+#endif
diff --git a/openair3/NAS/TOOLS/nvram.c b/openair3/NAS/TOOLS/nvram.c
new file mode 100644
index 0000000000000000000000000000000000000000..b10511f2fc94ca8f6acc92d2f08e235e54e61638
--- /dev/null
+++ b/openair3/NAS/TOOLS/nvram.c
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*****************************************************************************
+Source    usim_data.c
+
+Version   0.1
+
+Date    2012/10/31
+
+Product   NVRAM data generator
+
+Subsystem NVRAM data generator main process
+
+Author    Frederic Maurel
+
+Description Implements the utility used to generate data stored in the
+    NVRAM application
+
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+#include "conf_parser.h"
+#include "display.h"
+
+#define DEFAULT_NAS_PATH "PWD"
+#define OUTPUT_DIR_ENV "NVRAM_DIR"
+void _display_usage(const char* command);
+
+int main (int argc, char * const argv[])
+{
+  enum usim_command {
+    NVRAM_COMMAND_NONE,
+    NVRAM_COMMAND_PRINT,
+    NVRAM_COMMAND_GEN,
+  } command = NVRAM_COMMAND_NONE;
+
+  char *output_dir = NULL;
+  char *conf_file = NULL;
+  const char options[]="gpc:o:h";
+  const struct option options_long_option[] = {
+    {"gen",    no_argument, NULL, 'g'},
+    {"print",  no_argument, NULL, 'p'},
+    {"conf",   required_argument, NULL, 'c'},
+    {"output", required_argument, NULL, 'o'},
+    {"help",   no_argument, NULL, 'h'},
+    {NULL,     0,           NULL, 0}
+  };
+  int option_index;
+  char option_short;
+
+  /*
+   * Read command line parameters
+   */
+  while ( true ) {
+    option_short = getopt_long(argc, argv, options, options_long_option, &option_index );
+
+    if ( option_short == -1 )
+      break;
+
+    switch (option_short) {
+      case 'c':
+        conf_file = optarg;
+        break;
+      case 'g':
+        command = NVRAM_COMMAND_GEN;
+        break;
+      case 'p':
+        command = NVRAM_COMMAND_PRINT;
+        break;
+      case 'o':
+        output_dir = optarg;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if ( command == NVRAM_COMMAND_NONE ) {
+    _display_usage(argv[0]);
+    exit(EXIT_SUCCESS);
+  }
+
+  /* compute default data directory if no output_dir is given */
+  if ( output_dir == NULL ) {
+    output_dir = getenv(OUTPUT_DIR_ENV);
+
+    if (output_dir == NULL) {
+      output_dir = getenv(DEFAULT_NAS_PATH);
+    }
+
+    if (output_dir == NULL) {
+      fprintf(stderr, "%s and %s environment variables are not defined trying local directory",
+              OUTPUT_DIR_ENV, DEFAULT_NAS_PATH);
+      output_dir = ".";
+    }
+  }
+
+  if ( command == NVRAM_COMMAND_GEN ) {
+    if ( conf_file == NULL ) {
+      printf("No Configuration file is given\n");
+      _display_usage(argv[0]);
+      exit(EXIT_FAILURE);
+    }
+
+    if ( parse_config_file(output_dir, conf_file, OUTPUT_UEDATA|OUTPUT_EMM) == false ) {
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  if ( display_data_from_directory(output_dir, DISPLAY_UEDATA|DISPLAY_EMM) == 0) {
+    fprintf(stderr, "No NVRAM files found in %s\n", output_dir);
+  }
+
+  exit(EXIT_SUCCESS);
+}
+
+/****************************************************************************/
+/*********************  L O C A L    F U N C T I O N S  *********************/
+/****************************************************************************/
+
+/*
+ * Displays command line usage
+ */
+void _display_usage(const char* command)
+{
+  fprintf(stderr, "usage: %s [OPTION]\n", command);
+  fprintf(stderr, "\t[--gen|-g]\tGenerate the NVRAM data file\n");
+  fprintf(stderr, "\t[--print|-p]\tDisplay the content of the NVRAM data file\n");
+	fprintf(stderr, "\t[-c]\tConfig file to use\n");
+	fprintf(stderr, "\t[-o]\toutput file directory\n");
+  fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
+  const char* path = getenv("NVRAM_DIR");
+
+  if (path != NULL) {
+    fprintf(stderr, "NVRAM_DIR = %s\n", path);
+  } else {
+    fprintf(stderr, "NVRAM_DIR environment variable is not defined\n");
+  }
+}
diff --git a/openair3/NAS/TOOLS/ue_bcom_test.conf b/openair3/NAS/TOOLS/ue_bcom_test.conf
new file mode 100644
index 0000000000000000000000000000000000000000..c60b628941dbdc0090a567ec0ad67e575efc7b03
--- /dev/null
+++ b/openair3/NAS/TOOLS/ue_bcom_test.conf
@@ -0,0 +1,2334 @@
+PLMN:
+{
+	PLMN0:
+	{
+		FULLNAME="B<>COM Cubiq";
+		SHORTNAME="BCOM";
+		MNC="89";
+		MCC="208";
+	};
+};
+
+UE0:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000000";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000000";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE1:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000001";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000001";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE2:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000002";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000002";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE3:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000003";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000003";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE4:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000004";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000004";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE5:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000005";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000005";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE6:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000006";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000006";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE7:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000007";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000007";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE8:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000008";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000008";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE9:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000009";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000009";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE10:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000010";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000010";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE11:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000011";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000011";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE12:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000012";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000012";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE13:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000013";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000013";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE14:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000014";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000014";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE15:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000015";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000015";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE16:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000016";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000016";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE17:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000017";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000017";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE18:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000018";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000018";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE19:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000019";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000019";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE20:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000020";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000020";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE21:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000021";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000021";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE22:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000022";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000022";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE23:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000023";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000023";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE24:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000024";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000024";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE25:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000025";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000025";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE26:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000026";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000026";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE27:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000027";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000027";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE28:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000028";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000028";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE29:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000029";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000029";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE30:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000030";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000030";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE31:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000031";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000031";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE32:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000032";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000032";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE33:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000033";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000033";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE34:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000034";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000034";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE35:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000035";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000035";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE36:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000036";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000036";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE37:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000037";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000037";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE38:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000038";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000038";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE39:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000039";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000039";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE40:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000040";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000040";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE41:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000041";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000041";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE42:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000042";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000042";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE43:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000043";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000043";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE44:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000044";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000044";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE45:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000045";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000045";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE46:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000046";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000046";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE47:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000047";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000047";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE48:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000048";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000048";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE49:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000049";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000049";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE50:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000050";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000050";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE51:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000051";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000051";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE52:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000052";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000052";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE53:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000053";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000053";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE54:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000054";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000054";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE55:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000055";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000055";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE56:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000056";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000056";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE57:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000057";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000057";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE58:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000058";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000058";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE59:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000059";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000059";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE60:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000060";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000060";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE61:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000061";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000061";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE62:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000062";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000062";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE63:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000063";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000063";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE64:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000064";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000064";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE65:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000065";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000065";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE66:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000066";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000066";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE67:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000067";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000067";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE68:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000068";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000068";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE69:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000069";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000069";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE70:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000070";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000070";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE71:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000071";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000071";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE72:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000072";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000072";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE73:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000073";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000073";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE74:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000074";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000074";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE75:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000075";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000075";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE76:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000076";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000076";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE77:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000077";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000077";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE78:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000078";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000078";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE79:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000079";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000079";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE80:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000080";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000080";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE81:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000081";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000081";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE82:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000082";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000082";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE83:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000083";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000083";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE84:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000084";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000084";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE85:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000085";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000085";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE86:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000086";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000086";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE87:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000087";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000087";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE88:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000088";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000088";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE89:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000089";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000089";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE90:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000090";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000090";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE91:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000091";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000091";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE92:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000092";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000092";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE93:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000093";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000093";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE94:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000094";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000094";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE95:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000095";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000095";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE96:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000096";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000096";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE97:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000097";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000097";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE98:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000098";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000098";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE99:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000099";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000099";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
+UE100:
+{
+USER:
+	{
+	IMEI="35609204079299";
+	MANUFACTURER="EURECOM";
+	MODEL="LTE Android PC";
+	PIN="0000";
+	};
+SIM:
+	{
+	MSIN="1000000100";
+	USIM_API_K="00112233445566778899AABBCCDDEEFF";
+	OPC="21C6FD3F84AA71BAC34FA8FCA0EAC64F";
+	MSISDN="33600000100";
+	};
+HPLMN= "20889";
+UCPLMN_LIST=();
+OPLMN_LIST=("20889");
+OCPLMN_LIST = ();
+FPLMN_LIST = ();
+EHPLMN_LIST= ();
+};
diff --git a/openair3/NAS/TOOLS/ue_data.c b/openair3/NAS/TOOLS/ue_data.c
deleted file mode 100644
index 3636ad6ee3d5519ecb61e81976f84a671be15bbe..0000000000000000000000000000000000000000
--- a/openair3/NAS/TOOLS/ue_data.c
+++ /dev/null
@@ -1,536 +0,0 @@
-/*
- * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The OpenAirInterface Software Alliance licenses this file to You under
- * the OAI Public License, Version 1.0  (the "License"); you may not use this file
- * except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.openairinterface.org/?page_id=698
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *-------------------------------------------------------------------------------
- * For more information about the OpenAirInterface (OAI) Software Alliance:
- *      contact@openairinterface.org
- */
-
-/*****************************************************************************
-Source    ue_data.c
-
-Version   0.1
-
-Date    2012/11/02
-
-Product   UE data generator
-
-Subsystem UE data generator main process
-
-Author    Frederic Maurel
-
-Description Implements the utility used to generate data stored in the
-    UE's non-volatile memory device
-
-*****************************************************************************/
-
-#include "userDef.h"
-#include "memory.h"
-
-#include "emmData.h"
-#include "network.h"
-
-#include <stdio.h>  // perror, printf, fprintf, snprintf
-#include <stdlib.h> // exit, free
-#include <string.h> // memset, strncpy
-
-/****************************************************************************/
-/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
-/****************************************************************************/
-
-#define USER_IMEI   "35611302209414"
-#define USER_MANUFACTURER "EURECOM"
-#define USER_MODEL    "LTE Android PC"
-//#define USER_MANUFACTURER "SAGEM"
-//#define USER_MODEL    "my225x"
-#define USER_PIN    "0000"
-
-#define PRINT_PLMN_DIGIT(d) if ((d) != 0xf) printf("%u", (d))
-
-#define PRINT_PLMN(plmn)    \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit1); \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit2); \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit3); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit1); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit2); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit3)
-
-/****************************************************************************/
-/*******************  L O C A L    D E F I N I T I O N S  *******************/
-/****************************************************************************/
-
-static void _display_usage(const char* command);
-
-static void _gen_user_data(user_nvdata_t* data);
-static void _gen_emm_data(emm_nvdata_t* data);
-
-static int _luhn(const char* cc);
-static void _display_ue_data(const user_nvdata_t* data);
-static void _display_emm_data(const emm_nvdata_t* data);
-
-/****************************************************************************/
-/******************  E X P O R T E D    F U N C T I O N S  ******************/
-/****************************************************************************/
-
-int main (int argc, const char* argv[])
-{
-  int rc;
-  char* path;
-  user_nvdata_t user_data;
-  emm_nvdata_t emm_data;
-
-  unsigned char gen_data;
-
-  /*
-   * ----------------------------
-   * Read command line parameters
-   * ----------------------------
-   */
-  if (argc != 2) {
-    fprintf(stderr, "Invalid parameter\n");
-    _display_usage(argv[0]);
-    exit(EXIT_FAILURE);
-  } else if ( (strcmp(argv[1], "--gen") == 0) ||
-              (strcmp(argv[1], "-g") == 0) ) {
-    /* Generate UE data files */
-    gen_data = TRUE;
-  } else if ( (strcmp(argv[1], "--print") == 0) ||
-              (strcmp(argv[1], "-p") == 0) ) {
-    /* Display content of UE data files */
-    gen_data = FALSE;
-  } else {
-    /* Display usage */
-    _display_usage(argv[0]);
-    exit(EXIT_SUCCESS);
-  }
-  /*
-   * ----------------------
-   * UE's non-volatile data
-   * ----------------------
-   */
-  path = memory_get_path(USER_NVRAM_DIRNAME, USER_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    perror("ERROR\t: memory_get_path() failed");
-    exit(EXIT_FAILURE);
-  }
-
-  if (gen_data) {
-    /*
-     * Initialize UE's non-volatile data
-     */
-    memset(&user_data, 0, sizeof(user_nvdata_t));
-    _gen_user_data(&user_data);
-    /*
-     * Write UE's non-volatile data
-     */
-    rc = memory_write(path, &user_data, sizeof(user_nvdata_t));
-
-    if (rc != RETURNok) {
-      perror("ERROR\t: memory_write() failed");
-      free(path);
-      exit(EXIT_FAILURE);
-    }
-  }
-
-  /*
-   * Read UE's non-volatile data
-   */
-  memset(&user_data, 0, sizeof(user_nvdata_t));
-  rc = memory_read(path, &user_data, sizeof(user_nvdata_t));
-
-  if (rc != RETURNok) {
-    perror("ERROR\t: memory_read() failed");
-    free(path);
-    exit(EXIT_FAILURE);
-  }
-
-  free(path);
-  /*
-   * Display UE's non-volatile data
-   */
-  printf("\nUE's non-volatile data:\n\n");
-  _display_ue_data(&user_data);
-
-  /*
-   * ---------------------
-   * EMM non-volatile data
-   * ---------------------
-   */
-  path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    perror("ERROR\t: memory_get_path() failed");
-    exit(EXIT_FAILURE);
-  }
-
-  if (gen_data) {
-    /*
-     * Initialize EMM non-volatile data
-     */
-    memset(&emm_data, 0, sizeof(emm_nvdata_t));
-    _gen_emm_data(&emm_data);
-    /*
-     * Write EMM non-volatile data
-     */
-    rc = memory_write(path, &emm_data, sizeof(emm_nvdata_t));
-
-    if (rc != RETURNok) {
-      perror("ERROR\t: memory_write() failed");
-      free(path);
-      exit(EXIT_FAILURE);
-    }
-  }
-
-  /*
-   * Read EMM non-volatile data
-   */
-  memset(&emm_data, 0, sizeof(emm_nvdata_t));
-  rc = memory_read(path, &emm_data, sizeof(emm_nvdata_t));
-
-  if (rc != RETURNok) {
-    perror("ERROR\t: memory_read() failed ");
-    free(path);
-    exit(EXIT_FAILURE);
-  }
-
-  free(path);
-  /*
-   * Display EMM non-volatile data
-   */
-  printf("\nEMM non-volatile data:\n\n");
-  _display_emm_data(&emm_data);
-
-  /*
-   *---------------
-   * Files location
-   *---------------
-   */
-  path = memory_get_path(USER_NVRAM_DIRNAME, USER_NVRAM_FILENAME);
-  printf("\nUE identity data file: %s\n", path);
-  free(path);
-  path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME);
-  printf("EPS Mobility Management data file: %s\n", path);
-  free(path);
-
-  exit(EXIT_SUCCESS);
-}
-
-/****************************************************************************/
-/*********************  L O C A L    F U N C T I O N S  *********************/
-/****************************************************************************/
-
-/*
- * Displays command line usage
- */
-static void _display_usage(const char* command)
-{
-  fprintf(stderr, "usage: %s [OPTION]\n", command);
-  fprintf(stderr, "\t[--gen|-g]\tGenerate the UE data files\n");
-  fprintf(stderr, "\t[--print|-p]\tDisplay the content of the UE data files\n");
-  fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
-  const char* path = getenv("NVRAM_DIR");
-
-  if (path != NULL) {
-    fprintf(stderr, "NVRAM_DIR = %s\n", path);
-  } else {
-    fprintf(stderr, "NVRAM_DIR environment variable is not defined\n");
-  }
-}
-
-/*
- * Generates UE's non-volatile data
- */
-static void _gen_user_data(user_nvdata_t* data)
-{
-  /*
-   * Product Serial Number Identification
-   * IMEI = AA-BBBBBB-CCCCCC-D
-   * AA-BBBBBB: Type Allocation Code (TAC)
-   * CCCCCC: Serial Number
-   * D: Luhn check digit
-   */
-  snprintf(data->IMEI, USER_IMEI_SIZE+1, "%s%d",
-           USER_IMEI, _luhn(USER_IMEI));
-  /*
-   * Manufacturer identifier
-   */
-  strncpy(data->manufacturer, USER_MANUFACTURER, USER_MANUFACTURER_SIZE);
-  /*
-   * Model identifier
-   */
-  strncpy(data->model, USER_MODEL, USER_MODEL_SIZE);
-  /*
-   * SIM Personal Identification Number
-   */
-  strncpy(data->PIN, USER_PIN, USER_PIN_SIZE);
-}
-
-/*
- * Generates UE's non-volatile EMM data
- */
-static void _gen_emm_data(emm_nvdata_t* data)
-{
-#if (SELECTED_PLMN == FCT1)
-  /*
-   * International Mobile Subscriber Identity
-   * IMSI = MCC + MNC + MSIN = 310 (USA) + 028 (UNKNOWN) + 90832150
-   */
-#warning "IMSI 310.028.90832150"
-  data->imsi.length = 8;
-  data->imsi.u.num.parity = 0x0;  // Type of identity = IMSI, even
-  data->imsi.u.num.digit1 = 3;    // MCC digit 1
-  data->imsi.u.num.digit2 = 1;    // MCC digit 2
-  data->imsi.u.num.digit3 = 0;    // MCC digit 3
-  data->imsi.u.num.digit4 = 0;    // MNC digit 1
-  data->imsi.u.num.digit5 = 2;    // MNC digit 2
-  data->imsi.u.num.digit6 = 8;    // MNC digit 3
-  data->imsi.u.num.digit7 = 9;
-  data->imsi.u.num.digit8 = 0;
-  data->imsi.u.num.digit9 = 8;
-  data->imsi.u.num.digit10 = 3;
-  data->imsi.u.num.digit11 = 2;
-  data->imsi.u.num.digit12 = 1;
-  data->imsi.u.num.digit13 = 5;
-  data->imsi.u.num.digit14 = 0;
-  data->imsi.u.num.digit15 = 0xF;
-  /*
-   * Last registered home PLMN
-   */
-  data->rplmn.MCCdigit1 = 3;
-  data->rplmn.MCCdigit2 = 1;
-  data->rplmn.MCCdigit3 = 0;
-  data->rplmn.MNCdigit1 = 0;
-  data->rplmn.MNCdigit2 = 2;
-  data->rplmn.MNCdigit3 = 8;
-#endif
-#if (SELECTED_PLMN == SFR1)
-  /*
-   * International Mobile Subscriber Identity
-   * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
-   */
-     data->imsi.length = 8;
-     data->imsi.u.num.parity = 0x0;  // Type of identity = IMSI, even
-     data->imsi.u.num.digit1 = 2;  // MCC digit 1
-     data->imsi.u.num.digit2 = 0;  // MCC digit 2
-     data->imsi.u.num.digit3 = 8;  // MCC digit 3
-     data->imsi.u.num.digit4 = 1;  // MNC digit 1
-     data->imsi.u.num.digit5 = 0;  // MNC digit 2
-     data->imsi.u.num.digit6 = 0;//0xF;  // MNC digit 3
-     data->imsi.u.num.digit7 = 0;
-     data->imsi.u.num.digit8 = 0;
-     data->imsi.u.num.digit9 = 0;
-     data->imsi.u.num.digit10 = 0;
-     data->imsi.u.num.digit11 = 1;
-     data->imsi.u.num.digit12 = 2;
-     data->imsi.u.num.digit13 = 3;
-     data->imsi.u.num.digit14 = 4;
-     data->imsi.u.num.digit15 = 0xF;
-
-     data->rplmn.MCCdigit1 = 2;
-     data->rplmn.MCCdigit2 = 0;
-     data->rplmn.MCCdigit3 = 8;
-     data->rplmn.MNCdigit1 = 1;
-     data->rplmn.MNCdigit2 = 0;
-     data->rplmn.MNCdigit3 = 0xf;
-#endif
-#if (SELECTED_PLMN == OAI_LTEBOX)
-  /*
-   * International Mobile Subscriber Identity
-   * IMSI = MCC + MNC + MSIN = 208 (France) + 93 (OAI) + 00001110
-   */
-     data->imsi.length = 8;
-     data->imsi.u.num.parity = ODD_PARITY;  // Type of identity = IMSI, even
-     data->imsi.u.num.digit1 = 2;  // MCC digit 1
-     data->imsi.u.num.digit2 = 0;  // MCC digit 2
-     data->imsi.u.num.digit3 = 8;  // MCC digit 3
-     data->imsi.u.num.digit4 = 9;  // MNC digit 1
-     data->imsi.u.num.digit5 = 3;  // MNC digit 2
-     data->imsi.u.num.digit6 = 0;  // MNC digit 3
-     data->imsi.u.num.digit7 = 1;
-     data->imsi.u.num.digit8 = 0;
-     data->imsi.u.num.digit9 = 0;
-     data->imsi.u.num.digit10 = 0;
-     data->imsi.u.num.digit11 = 0;
-     data->imsi.u.num.digit12 = 1;
-     data->imsi.u.num.digit13 = 1;
-     data->imsi.u.num.digit14 = 1;
-     data->imsi.u.num.digit15 = 1;
-
-     data->rplmn.MCCdigit1 = 2;
-     data->rplmn.MCCdigit2 = 0;
-     data->rplmn.MCCdigit3 = 8;
-     data->rplmn.MNCdigit1 = 9;
-     data->rplmn.MNCdigit2 = 3;
-     data->rplmn.MNCdigit3 = 0xf;
-#endif
-#if (SELECTED_PLMN == TEST1)
-  /*
-   * International Mobile Subscriber Identity
-   * IMSI = MCC + MNC + MSIN = 001  + 01  + 00001234
-   */
-  data->imsi.length = 8;
-  data->imsi.u.num.parity = 0x0;  // Type of identity = IMSI, even
-  data->imsi.u.num.digit1 = 0;    // MCC digit 1
-  data->imsi.u.num.digit2 = 0;    // MCC digit 2
-  data->imsi.u.num.digit3 = 1;    // MCC digit 3
-  data->imsi.u.num.digit4 = 0;    // MNC digit 1
-  data->imsi.u.num.digit5 = 1;    // MNC digit 2
-  data->imsi.u.num.digit6 = 0;
-  data->imsi.u.num.digit7 = 0;
-  data->imsi.u.num.digit8 = 0;
-  data->imsi.u.num.digit9 = 0;
-  data->imsi.u.num.digit10 = 0;
-  data->imsi.u.num.digit11 = 1;
-  data->imsi.u.num.digit12 = 2;
-  data->imsi.u.num.digit13 = 3;
-  data->imsi.u.num.digit14 = 4;
-  data->imsi.u.num.digit15 = 0xF;
-
-  /*
-   * Last registered home PLMN
-   */
-  data->rplmn.MCCdigit1 = 0;
-  data->rplmn.MCCdigit2 = 0;
-  data->rplmn.MCCdigit3 = 1;
-  data->rplmn.MNCdigit1 = 0;
-  data->rplmn.MNCdigit2 = 1;
-  data->rplmn.MNCdigit3 = 0xf;
-#endif
-  /*
-   * List of Equivalent PLMNs
-   */
-  data->eplmn.n_plmns = 0;
-}
-
-/*
- * Computes the check digit using Luhn algorithm
- */
-static int _luhn(const char* cc)
-{
-  const int m[] = {0,2,4,6,8,1,3,5,7,9};
-  int odd = 1, sum = 0;
-
-  for (int i = strlen(cc); i--; odd = !odd) {
-    int digit = cc[i] - '0';
-    sum += odd ? m[digit] : digit;
-  }
-
-  return 10 - (sum % 10);
-}
-
-/*
- * Displays UE's non-volatile data
- */
-static void _display_ue_data(const user_nvdata_t* data)
-{
-  printf("IMEI\t\t= %s\n", data->IMEI);
-  printf("manufacturer\t= %s\n", data->manufacturer);
-  printf("model\t\t= %s\n", data->model);
-  printf("PIN\t\t= %s\n", data->PIN);
-}
-
-/*
- * Displays UE's non-volatile EMM data
- */
-static void _display_emm_data(const emm_nvdata_t* data)
-{
-  printf("IMSI\t\t= ");
-
-  if (data->imsi.u.num.digit6 == 0b1111) {
-    if (data->imsi.u.num.digit15 == 0b1111) {
-      printf("%u%u%u.%u%u.%u%u%u%u%u%u%u%u\n",
-             data->imsi.u.num.digit1,
-             data->imsi.u.num.digit2,
-             data->imsi.u.num.digit3,
-             data->imsi.u.num.digit4,
-             data->imsi.u.num.digit5,
-
-             data->imsi.u.num.digit7,
-             data->imsi.u.num.digit8,
-             data->imsi.u.num.digit9,
-             data->imsi.u.num.digit10,
-             data->imsi.u.num.digit11,
-             data->imsi.u.num.digit12,
-             data->imsi.u.num.digit13,
-             data->imsi.u.num.digit14);
-    } else {
-      printf("%u%u%u.%u%u.%u%u%u%u%u%u%u%u%u\n",
-             data->imsi.u.num.digit1,
-             data->imsi.u.num.digit2,
-             data->imsi.u.num.digit3,
-             data->imsi.u.num.digit4,
-             data->imsi.u.num.digit5,
-
-             data->imsi.u.num.digit7,
-             data->imsi.u.num.digit8,
-             data->imsi.u.num.digit9,
-             data->imsi.u.num.digit10,
-             data->imsi.u.num.digit11,
-             data->imsi.u.num.digit12,
-             data->imsi.u.num.digit13,
-             data->imsi.u.num.digit14,
-             data->imsi.u.num.digit15);
-    }
-  } else {
-    if (data->imsi.u.num.digit15 == 0b1111) {
-      printf("%u%u%u.%u%u%u.%u%u%u%u%u%u%u%u\n",
-             data->imsi.u.num.digit1,
-             data->imsi.u.num.digit2,
-             data->imsi.u.num.digit3,
-             data->imsi.u.num.digit4,
-             data->imsi.u.num.digit5,
-             data->imsi.u.num.digit6,
-
-             data->imsi.u.num.digit7,
-             data->imsi.u.num.digit8,
-             data->imsi.u.num.digit9,
-             data->imsi.u.num.digit10,
-             data->imsi.u.num.digit11,
-             data->imsi.u.num.digit12,
-             data->imsi.u.num.digit13,
-             data->imsi.u.num.digit14);
-    } else {
-      printf("%u%u%u.%u%u%u.%u%u%u%u%u%u%u%u%u\n",
-             data->imsi.u.num.digit1,
-             data->imsi.u.num.digit2,
-             data->imsi.u.num.digit3,
-             data->imsi.u.num.digit4,
-             data->imsi.u.num.digit5,
-             data->imsi.u.num.digit6,
-
-             data->imsi.u.num.digit7,
-             data->imsi.u.num.digit8,
-             data->imsi.u.num.digit9,
-             data->imsi.u.num.digit10,
-             data->imsi.u.num.digit11,
-             data->imsi.u.num.digit12,
-             data->imsi.u.num.digit13,
-             data->imsi.u.num.digit14,
-             data->imsi.u.num.digit15);
-    }
-  }
-
-  printf("RPLMN\t\t= ");
-  PRINT_PLMN(data->rplmn);
-  printf("\n");
-
-  for (int i = 0; i < data->eplmn.n_plmns; i++) {
-    printf("EPLMN[%d]\t= ", i);
-    PRINT_PLMN(data->eplmn.plmn[i]);
-    printf("\n");
-  }
-}
diff --git a/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf b/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf
new file mode 100644
index 0000000000000000000000000000000000000000..84ec59a36bcb9906c8f9010a7dabfbfb2e62412e
--- /dev/null
+++ b/openair3/NAS/TOOLS/ue_eurecom_test_sfr.conf
@@ -0,0 +1,114 @@
+# List of known PLMNS
+PLMN: {
+    PLMN0: {
+           FULLNAME="Test network";
+           SHORTNAME="OAI4G";
+           MNC="01";
+           MCC="001";
+
+    };
+    PLMN1: {
+           FULLNAME="SFR France";
+           SHORTNAME="SFR";
+           MNC="10";
+           MCC="208";
+
+    };
+    PLMN2: {
+           FULLNAME="SFR France";
+           SHORTNAME="SFR";
+           MNC="11";
+           MCC="208";
+    };
+    PLMN3: {
+           FULLNAME="SFR France";
+           SHORTNAME="SFR";
+           MNC="13";
+           MCC="208";
+    };
+    PLMN4: {
+           FULLNAME="OAI LTEBOX";
+           SHORTNAME="OAIALU";
+           MNC="93";
+           MCC="208";
+    };
+    PLMN5: {
+           FULLNAME="T-Mobile USA";
+           SHORTNAME="T-Mobile";
+           MNC="280";
+           MCC="310";
+    };
+    PLMN6: {
+           FULLNAME="FICTITIOUS USA";
+           SHORTNAME="FICTITIO";
+           MNC="028";
+           MCC="310";
+    };
+    PLMN7: {
+           FULLNAME="Vodafone Italia";
+           SHORTNAME="VODAFONE";
+           MNC="10";
+           MCC="222";
+    };
+    PLMN8: {
+           FULLNAME="Vodafone Spain";
+           SHORTNAME="VODAFONE";
+           MNC="01";
+           MCC="214";
+    };
+    PLMN9: {
+           FULLNAME="Vodafone Spain";
+           SHORTNAME="VODAFONE";
+           MNC="06";
+           MCC="214";
+    };
+    PLMN10: {
+           FULLNAME="Vodafone Germ";
+           SHORTNAME="VODAFONE";
+           MNC="02";
+           MCC="262";
+    };
+    PLMN11: {
+           FULLNAME="Vodafone Germ";
+           SHORTNAME="VODAFONE";
+           MNC="04";
+           MCC="262";
+    };
+};
+
+UE0:
+{
+    USER: {
+        IMEI="356113022094149";
+        MANUFACTURER="EURECOM";
+        MODEL="LTE Android PC";
+        PIN="0000";
+    };
+
+    SIM: {
+        MSIN="0100001111";
+        USIM_API_K="fec86ba6eb707ed08905757b1bb44b8f";
+        OPC="C42449363BBAD02B66D16BC975D77CC1";
+        MSISDN="33611123456";
+    };
+
+    # Home PLMN Selector with Access Technology
+    HPLMN= "20893";
+
+    # User controlled PLMN Selector with Access Technology
+    UCPLMN_LIST = ();
+
+    # Operator PLMN List
+    OPLMN_LIST = ("00101", "20810", "20811", "20813", "20893", "310280", "310028");
+
+    # Operator controlled PLMN Selector with Access Technology
+    OCPLMN_LIST = ("22210", "21401", "21406", "26202", "26204");
+
+    # Forbidden plmns
+    FPLMN_LIST = ();
+
+    # List of Equivalent HPLMNs
+#TODO: UE does not connect if set, to be fixed in the UE
+#    EHPLMN_LIST= ("20811", "20813");
+    EHPLMN_LIST= ();
+};
diff --git a/openair3/NAS/TOOLS/usim.c b/openair3/NAS/TOOLS/usim.c
new file mode 100644
index 0000000000000000000000000000000000000000..242b30d65558e3a1c4f31aa89f61267bcac533a0
--- /dev/null
+++ b/openair3/NAS/TOOLS/usim.c
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*****************************************************************************
+Source    usim_data.c
+
+Version   0.1
+
+Date    2012/10/31
+
+Product   USIM data generator
+
+Subsystem USIM data generator main process
+
+Author    Frederic Maurel
+
+Description Implements the utility used to generate data stored in the
+    USIM application
+
+ *****************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+#include "conf_parser.h"
+#include "display.h"
+
+#define DEFAULT_NAS_PATH "PWD"
+#define OUTPUT_DIR_ENV "USIM_DIR"
+void _display_usage(const char* command);
+
+int main (int argc, char * const argv[])
+{
+  enum usim_command {
+    USIM_COMMAND_NONE,
+    USIM_COMMAND_PRINT,
+    USIM_COMMAND_GEN,
+  } command = USIM_COMMAND_NONE;
+
+  char *output_dir = NULL;
+  char *conf_file = NULL;
+  const char options[]="gpc:o:h";
+  const struct option options_long_option[] = {
+    {"gen",    no_argument, NULL, 'g'},
+    {"print",  no_argument, NULL, 'p'},
+    {"conf",   required_argument, NULL, 'c'},
+    {"output", required_argument, NULL, 'o'},
+    {"help",   no_argument, NULL, 'h'},
+    {NULL,     0,           NULL, 0}
+  };
+  int option_index;
+  char option_short;
+
+  /*
+   * Read command line parameters
+   */
+  while ( true ) {
+    option_short = getopt_long(argc, argv, options, options_long_option, &option_index );
+
+    if ( option_short == -1 )
+      break;
+
+    switch (option_short) {
+      case 'c':
+        conf_file = optarg;
+        break;
+      case 'g':
+        command = USIM_COMMAND_GEN;
+        break;
+      case 'p':
+        command = USIM_COMMAND_PRINT;
+        break;
+      case 'o':
+        output_dir = optarg;
+        break;
+      default:
+        break;
+    }
+  }
+
+  if ( command == USIM_COMMAND_NONE ) {
+    _display_usage(argv[0]);
+    exit(EXIT_SUCCESS);
+  }
+
+  /* compute default data directory if no output_dir is given */
+  if ( output_dir == NULL ) {
+    output_dir = getenv(OUTPUT_DIR_ENV);
+
+    if (output_dir == NULL) {
+      output_dir = getenv(DEFAULT_NAS_PATH);
+    }
+
+    if (output_dir == NULL) {
+      fprintf(stderr, "%s and %s environment variables are not defined trying local directory",
+              OUTPUT_DIR_ENV, DEFAULT_NAS_PATH);
+      output_dir = ".";
+    }
+  }
+
+  if ( command == USIM_COMMAND_GEN ) {
+    if ( conf_file == NULL ) {
+      printf("No Configuration file is given\n");
+      _display_usage(argv[0]);
+      exit(EXIT_FAILURE);
+    }
+
+    if ( parse_config_file(output_dir, conf_file, OUTPUT_USIM) == false ) {
+      exit(EXIT_FAILURE);
+    }
+  }
+
+  if ( display_data_from_directory(output_dir, DISPLAY_USIM) == 0) {
+    fprintf(stderr, "No USIM files found in %s\n", output_dir);
+  }
+
+  exit(EXIT_SUCCESS);
+}
+
+/****************************************************************************/
+/*********************  L O C A L    F U N C T I O N S  *********************/
+/****************************************************************************/
+
+/*
+ * Displays command line usage
+ */
+void _display_usage(const char* command)
+{
+  fprintf(stderr, "usage: %s [OPTION]\n", command);
+  fprintf(stderr, "\t[--gen|-g]\tGenerate the USIM data file\n");
+  fprintf(stderr, "\t[--print|-p]\tDisplay the content of the USIM data file\n");
+	fprintf(stderr, "\t[-c]\tConfig file to use\n");
+	fprintf(stderr, "\t[-o]\toutput file directory\n");
+  fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
+  const char* path = getenv("USIM_DIR");
+
+  if (path != NULL) {
+    fprintf(stderr, "USIM_DIR = %s\n", path);
+  } else {
+    fprintf(stderr, "USIM_DIR environment variable is not defined\n");
+  }
+}
diff --git a/openair3/NAS/TOOLS/usim_data.c b/openair3/NAS/TOOLS/usim_data.c
deleted file mode 100644
index 23eba29bb134cf1fa3ad8a9bacec4f546d23cd96..0000000000000000000000000000000000000000
--- a/openair3/NAS/TOOLS/usim_data.c
+++ /dev/null
@@ -1,659 +0,0 @@
-/*
- * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The OpenAirInterface Software Alliance licenses this file to You under
- * the OAI Public License, Version 1.0  (the "License"); you may not use this file
- * except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.openairinterface.org/?page_id=698
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *-------------------------------------------------------------------------------
- * For more information about the OpenAirInterface (OAI) Software Alliance:
- *      contact@openairinterface.org
- */
-
-/*****************************************************************************
-Source    usim_data.c
-
-Version   0.1
-
-Date    2012/10/31
-
-Product   USIM data generator
-
-Subsystem USIM data generator main process
-
-Author    Frederic Maurel
-
-Description Implements the utility used to generate data stored in the
-    USIM application
-
- *****************************************************************************/
-
-#include "network.h"
-
-#include "usim_api.h"
-#include "memory.h"
-#include "network.h"
-
-#include <stdio.h>  // perror, printf
-#include <stdlib.h> // exit
-#include <string.h> // memset, memcpy, strncpy
-
-
-/****************************************************************************/
-/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
-/****************************************************************************/
-
-#define KSI     USIM_KSI_NOT_AVAILABLE
-#define KSI_ASME    USIM_KSI_NOT_AVAILABLE
-#define INT_ALGO    USIM_INT_EIA2
-#define ENC_ALGO    USIM_ENC_EEA0
-#define SECURITY_ALGORITHMS (ENC_ALGO | INT_ALGO)
-
-#define MIN_TAC     0x0000
-#define MAX_TAC     0xFFFE
-
-#define DEFAULT_TMSI    0x0000000D
-#define DEFAULT_P_TMSI    0x0000000D
-#define DEFAULT_M_TMSI    0x0000000D
-#define DEFAULT_LAC   0xFFFE
-#define DEFAULT_RAC   0x01
-#define DEFAULT_TAC   0x0001
-
-#define DEFAULT_MME_ID    0x0102
-#define DEFAULT_MME_CODE  0x0F
-
-#define PRINT_PLMN_DIGIT(d) if ((d) != 0xf) printf("%u", (d))
-
-#define PRINT_PLMN(plmn)    \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit1); \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit2); \
-    PRINT_PLMN_DIGIT((plmn).MCCdigit3); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit1); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit2); \
-    PRINT_PLMN_DIGIT((plmn).MNCdigit3)
-
-/****************************************************************************/
-/*******************  L O C A L    D E F I N I T I O N S  *******************/
-/****************************************************************************/
-
-static void _display_usage(const char* command);
-
-static void _display_usim_data(const usim_data_t* data);
-
-/****************************************************************************/
-/******************  E X P O R T E D    F U N C T I O N S  ******************/
-/****************************************************************************/
-
-int main (int argc, const char* argv[])
-{
-  int rc;
-  usim_data_t usim_data;
-
-  unsigned char gen_data;
-
-  /*
-   * Read command line parameters
-   */
-  if (argc != 2) {
-    fprintf(stderr, "Invalid parameter\n");
-    _display_usage(argv[0]);
-    exit(EXIT_FAILURE);
-  } else if ( (strcmp(argv[1], "--gen") == 0) ||
-              (strcmp(argv[1], "-g") == 0) ) {
-    /* Generate USIM data files */
-    gen_data = TRUE;
-  } else if ( (strcmp(argv[1], "--print") == 0) ||
-              (strcmp(argv[1], "-p") == 0) ) {
-    /* Display content of USIM data files */
-    gen_data = FALSE;
-  } else {
-    /* Display usage */
-    _display_usage(argv[0]);
-    exit(EXIT_SUCCESS);
-  }
-
-  if (gen_data) {
-    /*
-     * Initialize USIM data
-     */
-    memset(&usim_data, 0, sizeof(usim_data_t));
-
-#if (SELECTED_PLMN == FCT1)
-    /*
-     * International Mobile Subscriber Identity
-     * IMSI = MCC + MNC + MSIN = 310 (USA) + 028 (UNKNOWN) + 90832150
-     */
-#warning "IMSI 310.028.90832150"
-    usim_data.imsi.length = 8;
-    usim_data.imsi.u.num.parity = EVEN_PARITY;      // Parity: even
-    usim_data.imsi.u.num.digit1 = 3;                // MCC digit 1
-    usim_data.imsi.u.num.digit2 = 1;                // MCC digit 2
-    usim_data.imsi.u.num.digit3 = 0;                // MCC digit 3
-    usim_data.imsi.u.num.digit4 = 0;                // MNC digit 1
-    usim_data.imsi.u.num.digit5 = 2;                // MNC digit 2
-    usim_data.imsi.u.num.digit6 = 8;                // MNC digit 3
-    usim_data.imsi.u.num.digit7 = 9;
-    usim_data.imsi.u.num.digit8 = 0;
-    usim_data.imsi.u.num.digit9 = 8;
-    usim_data.imsi.u.num.digit10 = 3;
-    usim_data.imsi.u.num.digit11 = 2;
-    usim_data.imsi.u.num.digit12 = 1;
-    usim_data.imsi.u.num.digit13 = 5;
-    usim_data.imsi.u.num.digit14 = 0;
-    usim_data.imsi.u.num.digit15 = 0b1111;
-#endif
-#if (SELECTED_PLMN == SFR1)
-
-    /*
-     * International Mobile Subscriber Identity
-     * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
-     */
-#warning "IMSI 208.10.00001234"
-
-    usim_data.imsi.length = 8;
-    usim_data.imsi.u.num.parity = EVEN_PARITY;      // Parity: even
-    usim_data.imsi.u.num.digit1 = 2;                // MCC digit 1
-    usim_data.imsi.u.num.digit2 = 0;                // MCC digit 2
-    usim_data.imsi.u.num.digit3 = 8;                // MCC digit 3
-    usim_data.imsi.u.num.digit4 = 1;                // MNC digit 1
-    usim_data.imsi.u.num.digit5 = 0;                // MNC digit 2
-    usim_data.imsi.u.num.digit6 = 0;//0b1111;     // MNC digit 3
-    usim_data.imsi.u.num.digit7 = 0;
-    usim_data.imsi.u.num.digit8 = 0;
-    usim_data.imsi.u.num.digit9 = 0;
-    usim_data.imsi.u.num.digit10 = 0;
-    usim_data.imsi.u.num.digit11 = 1;
-    usim_data.imsi.u.num.digit12 = 2;
-    usim_data.imsi.u.num.digit13 = 3;
-    usim_data.imsi.u.num.digit14 = 4;
-    usim_data.imsi.u.num.digit15 = 0b1111;
-#endif
-#if (SELECTED_PLMN == OAI_LTEBOX)
-    /*
-     * International Mobile Subscriber Identity
-     * IMSI = MCC + MNC + MSIN = 208 (France) + 10 (SFR) + 00001234
-     */
-#warning "IMSI 208.93.0100001111"
-    usim_data.imsi.length = 8;
-    usim_data.imsi.u.num.parity = ODD_PARITY;      // Parity: even
-    usim_data.imsi.u.num.digit1 = 2;                // MCC digit 1
-    usim_data.imsi.u.num.digit2 = 0;                // MCC digit 2
-    usim_data.imsi.u.num.digit3 = 8;                // MCC digit 3
-    usim_data.imsi.u.num.digit4 = 9;                // MNC digit 1
-    usim_data.imsi.u.num.digit5 = 3;                // MNC digit 2
-    usim_data.imsi.u.num.digit6 = 0;     // MNC digit 3
-    usim_data.imsi.u.num.digit7 = 1;
-    usim_data.imsi.u.num.digit8 = 0;
-    usim_data.imsi.u.num.digit9 = 0;
-    usim_data.imsi.u.num.digit10 = 0;
-    usim_data.imsi.u.num.digit11 = 0;
-    usim_data.imsi.u.num.digit12 = 1;
-    usim_data.imsi.u.num.digit13 = 1;
-    usim_data.imsi.u.num.digit14 = 1;
-    usim_data.imsi.u.num.digit15 = 1;
-#endif
-#if (SELECTED_PLMN == TEST1)
-#warning "IMSI 001.01.000001234"
-    usim_data.imsi.length = 8;
-    usim_data.imsi.u.num.parity = 0x0;  // Type of identity = IMSI, even
-    usim_data.imsi.u.num.digit1 = 0;    // MCC digit 1
-    usim_data.imsi.u.num.digit2 = 0;    // MCC digit 2
-    usim_data.imsi.u.num.digit3 = 1;    // MCC digit 3
-    usim_data.imsi.u.num.digit4 = 0;    // MNC digit 1
-    usim_data.imsi.u.num.digit5 = 1;    // MNC digit 2
-    usim_data.imsi.u.num.digit6 = 0;
-    usim_data.imsi.u.num.digit7 = 0;
-    usim_data.imsi.u.num.digit8 = 0;
-    usim_data.imsi.u.num.digit9 = 0;
-    usim_data.imsi.u.num.digit10 = 0;
-    usim_data.imsi.u.num.digit11 = 1;
-    usim_data.imsi.u.num.digit12 = 2;
-    usim_data.imsi.u.num.digit13 = 3;
-    usim_data.imsi.u.num.digit14 = 4;
-    usim_data.imsi.u.num.digit15 = 0xF;
-    usim_data.usimtestmode = 1; // set usim in test mode in order to get the CMW500 K key
-#endif
-    /*
-     * Ciphering and Integrity Keys
-     */
-    usim_data.keys.ksi = KSI;
-    memset(&usim_data.keys.ck, 0, USIM_CK_SIZE);
-    memset(&usim_data.keys.ik, 0, USIM_IK_SIZE);
-
-    /*
-     * Higher Priority PLMN search period
-     */
-    usim_data.hpplmn = 0x00;  /* Disable timer */
-
-    /*
-     * List of Forbidden PLMNs
-     */
-    for (int i = 0; i < USIM_FPLMN_MAX; i++) {
-      memset(&usim_data.fplmn[i], 0xff, sizeof(plmn_t));
-    }
-
-    /*
-     * Location Information
-     */
-    usim_data.loci.tmsi = DEFAULT_TMSI;
-    usim_data.loci.lai.plmn = network_records[SELECTED_PLMN].plmn;
-    usim_data.loci.lai.lac = DEFAULT_LAC;
-    usim_data.loci.status = USIM_LOCI_NOT_UPDATED;
-    /*
-     * Packet Switched Location Information
-     */
-    usim_data.psloci.p_tmsi = DEFAULT_P_TMSI;
-    usim_data.psloci.signature[0] = 0x01;
-    usim_data.psloci.signature[1] = 0x02;
-    usim_data.psloci.signature[2] = 0x03;
-    usim_data.psloci.rai.plmn = network_records[SELECTED_PLMN].plmn;
-    usim_data.psloci.rai.lac = DEFAULT_LAC;
-    usim_data.psloci.rai.rac = DEFAULT_RAC;
-    usim_data.psloci.status = USIM_PSLOCI_NOT_UPDATED;
-    /*
-     * Administrative Data
-     */
-    usim_data.ad.UE_Operation_Mode = USIM_NORMAL_MODE;
-    usim_data.ad.Additional_Info = 0xffff;
-    usim_data.ad.MNC_Length = 2;
-    /*
-     * EPS NAS security context
-     */
-    usim_data.securityctx.length = 52;
-    usim_data.securityctx.KSIasme.type = USIM_KSI_ASME_TAG;
-    usim_data.securityctx.KSIasme.length = 1;
-    usim_data.securityctx.KSIasme.value[0] = KSI_ASME;
-    usim_data.securityctx.Kasme.type = USIM_K_ASME_TAG;
-    usim_data.securityctx.Kasme.length = USIM_K_ASME_SIZE;
-    memset(usim_data.securityctx.Kasme.value, 0,
-           usim_data.securityctx.Kasme.length);
-    usim_data.securityctx.ulNAScount.type = USIM_UL_NAS_COUNT_TAG;
-    usim_data.securityctx.ulNAScount.length = USIM_UL_NAS_COUNT_SIZE;
-    memset(usim_data.securityctx.ulNAScount.value, 0,
-           usim_data.securityctx.ulNAScount.length);
-    usim_data.securityctx.dlNAScount.type = USIM_DL_NAS_COUNT_TAG;
-    usim_data.securityctx.dlNAScount.length = USIM_DL_NAS_COUNT_SIZE;
-    memset(usim_data.securityctx.dlNAScount.value, 0,
-           usim_data.securityctx.dlNAScount.length);
-    usim_data.securityctx.algorithmID.type = USIM_INT_ENC_ALGORITHMS_TAG;
-    usim_data.securityctx.algorithmID.length = 1;
-    usim_data.securityctx.algorithmID.value[0] = SECURITY_ALGORITHMS;
-    /*
-     * Subcriber's Number
-     */
-    usim_data.msisdn.length = 7;
-    usim_data.msisdn.number.ext = 1;
-    usim_data.msisdn.number.ton = MSISDN_TON_UNKNOWKN;
-    usim_data.msisdn.number.npi = MSISDN_NPI_ISDN_TELEPHONY;
-    usim_data.msisdn.number.digit[0].msb = 3;
-    usim_data.msisdn.number.digit[0].lsb = 3;
-    usim_data.msisdn.number.digit[1].msb = 6;
-    usim_data.msisdn.number.digit[1].lsb = 1;
-    usim_data.msisdn.number.digit[2].msb = 1;
-    usim_data.msisdn.number.digit[2].lsb = 1;
-    usim_data.msisdn.number.digit[3].msb = 2;
-    usim_data.msisdn.number.digit[3].lsb = 3;
-    usim_data.msisdn.number.digit[4].msb = 4;
-    usim_data.msisdn.number.digit[4].lsb = 5;
-    usim_data.msisdn.number.digit[5].msb = 6;
-    usim_data.msisdn.number.digit[5].lsb = 0xf;
-    usim_data.msisdn.number.digit[6].msb = 0xf;
-    usim_data.msisdn.number.digit[6].lsb = 0xf;
-    usim_data.msisdn.number.digit[7].msb = 0xf;
-    usim_data.msisdn.number.digit[7].lsb = 0xf;
-    usim_data.msisdn.number.digit[8].msb = 0xf;
-    usim_data.msisdn.number.digit[8].lsb = 0xf;
-    usim_data.msisdn.number.digit[9].msb = 0xf;
-    usim_data.msisdn.number.digit[9].lsb = 0xf;
-    usim_data.msisdn.conf1_record_id = 0xff;  /* Not used */
-    usim_data.msisdn.ext1_record_id = 0xff;   /* Not used */
-
-    /*
-     * PLMN Network Name and Operator PLMN List
-     */
-    for (int i = TEST1; i < VDF1; i++) {
-      network_record_t record = network_records[i];
-      usim_data.pnn[i].fullname.type = USIM_PNN_FULLNAME_TAG;
-      usim_data.pnn[i].fullname.length = strlen(record.fullname);
-      strncpy((char*)usim_data.pnn[i].fullname.value, record.fullname,
-              usim_data.pnn[i].fullname.length);
-      usim_data.pnn[i].shortname.type = USIM_PNN_SHORTNAME_TAG;
-      usim_data.pnn[i].shortname.length = strlen(record.shortname);
-      strncpy((char*)usim_data.pnn[i].shortname.value, record.shortname,
-              usim_data.pnn[i].shortname.length);
-      usim_data.opl[i].plmn = record.plmn;
-      usim_data.opl[i].start = record.tac_start;
-      usim_data.opl[i].end = record.tac_end;
-      usim_data.opl[i].record_id = i;
-    }
-
-    for (int i = VDF2; i < USIM_OPL_MAX; i++) {
-      memset(&usim_data.opl[i].plmn, 0xff, sizeof(plmn_t));
-    }
-
-    /*
-     * List of Equivalent HPLMNs
-     */
-    usim_data.ehplmn[0] = network_records[SFR2].plmn;
-    usim_data.ehplmn[1] = network_records[SFR3].plmn;
-    /*
-     * Home PLMN Selector with Access Technology
-     */
-    usim_data.hplmn.plmn = network_records[SELECTED_PLMN].plmn;
-    usim_data.hplmn.AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-
-    /*
-     * List of user controlled PLMN selector with Access Technology
-     */
-    for (int i = 0; i < USIM_PLMN_MAX; i++) {
-      memset(&usim_data.plmn[i], 0xff, sizeof(plmn_t));
-    }
-
-    /*
-     * List of operator controlled PLMN selector with Access Technology
-     */
-    usim_data.oplmn[0].plmn = network_records[VDF1].plmn;
-    usim_data.oplmn[0].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-    usim_data.oplmn[1].plmn = network_records[VDF2].plmn;
-    usim_data.oplmn[1].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-    usim_data.oplmn[2].plmn = network_records[VDF3].plmn;
-    usim_data.oplmn[2].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-    usim_data.oplmn[3].plmn = network_records[VDF4].plmn;
-    usim_data.oplmn[3].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-    usim_data.oplmn[4].plmn = network_records[VDF5].plmn;
-    usim_data.oplmn[4].AcT = (USIM_ACT_GSM | USIM_ACT_UTRAN | USIM_ACT_EUTRAN);
-
-    for (int i = 5; i < USIM_OPLMN_MAX; i++) {
-      memset(&usim_data.oplmn[i], 0xff, sizeof(plmn_t));
-    }
-
-    /*
-     * EPS Location Information
-     */
-    usim_data.epsloci.guti.gummei.plmn = network_records[SELECTED_PLMN].plmn;
-    usim_data.epsloci.guti.gummei.MMEgid = DEFAULT_MME_ID;
-    usim_data.epsloci.guti.gummei.MMEcode = DEFAULT_MME_CODE;
-    usim_data.epsloci.guti.m_tmsi = DEFAULT_M_TMSI;
-    usim_data.epsloci.tai.plmn = usim_data.epsloci.guti.gummei.plmn;
-    usim_data.epsloci.tai.tac = DEFAULT_TAC;
-    usim_data.epsloci.status = USIM_EPSLOCI_UPDATED;
-    /*
-     * Non-Access Stratum configuration
-     */
-    usim_data.nasconfig.NAS_SignallingPriority.type = USIM_NAS_SIGNALLING_PRIORITY_TAG;
-    usim_data.nasconfig.NAS_SignallingPriority.length = 1;
-    usim_data.nasconfig.NAS_SignallingPriority.value[0] = 0x00;
-    usim_data.nasconfig.NMO_I_Behaviour.type = USIM_NMO_I_BEHAVIOUR_TAG;
-    usim_data.nasconfig.NMO_I_Behaviour.length = 1;
-    usim_data.nasconfig.NMO_I_Behaviour.value[0] = 0x00;
-    usim_data.nasconfig.AttachWithImsi.type = USIM_ATTACH_WITH_IMSI_TAG;
-    usim_data.nasconfig.AttachWithImsi.length = 1;
-#if defined(START_WITH_GUTI)
-    usim_data.nasconfig.AttachWithImsi.value[0] = 0x00;
-#else
-    usim_data.nasconfig.AttachWithImsi.value[0] = 0x01;
-#endif
-    usim_data.nasconfig.MinimumPeriodicSearchTimer.type = USIM_MINIMUM_PERIODIC_SEARCH_TIMER_TAG;
-    usim_data.nasconfig.MinimumPeriodicSearchTimer.length = 1;
-    usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0] = 0x00;
-    usim_data.nasconfig.ExtendedAccessBarring.type = USIM_EXTENDED_ACCESS_BARRING_TAG;
-    usim_data.nasconfig.ExtendedAccessBarring.length = 1;
-    usim_data.nasconfig.ExtendedAccessBarring.value[0] = 0x00;
-    usim_data.nasconfig.Timer_T3245_Behaviour.type = USIM_TIMER_T3245_BEHAVIOUR_TAG;
-    usim_data.nasconfig.Timer_T3245_Behaviour.length = 1;
-    usim_data.nasconfig.Timer_T3245_Behaviour.value[0] = 0x00;
-
-    /*
-     * Write USIM application data
-     */
-    rc = usim_api_write(&usim_data);
-
-    if (rc != RETURNok) {
-      perror("ERROR\t: usim_api_write() failed");
-      exit(EXIT_FAILURE);
-    }
-  }
-
-  /*
-   * Read USIM application data
-   */
-  memset(&usim_data, 0, sizeof(usim_data_t));
-  rc = usim_api_read(&usim_data);
-
-  if (rc != RETURNok) {
-    perror("ERROR\t: usim_api_read() failed");
-    exit(EXIT_FAILURE);
-  }
-
-  /*
-   * Display USIM application data
-   */
-  printf("\nUSIM data:\n\n");
-  _display_usim_data(&usim_data);
-
-  /*
-   * Display USIM file location
-   */
-  char* path = memory_get_path("USIM_DIR", ".usim.nvram");
-  printf("\nUSIM data file: %s\n", path);
-  free(path);
-
-  exit(EXIT_SUCCESS);
-}
-
-/****************************************************************************/
-/*********************  L O C A L    F U N C T I O N S  *********************/
-/****************************************************************************/
-
-/*
- * Displays command line usage
- */
-static void _display_usage(const char* command)
-{
-  fprintf(stderr, "usage: %s [OPTION]\n", command);
-  fprintf(stderr, "\t[--gen|-g]\tGenerate the USIM data file\n");
-  fprintf(stderr, "\t[--print|-p]\tDisplay the content of the USIM data file\n");
-  fprintf(stderr, "\t[--help|-h]\tDisplay this usage\n");
-  const char* path = getenv("USIM_DIR");
-
-  if (path != NULL) {
-    fprintf(stderr, "USIM_DIR = %s\n", path);
-  } else {
-    fprintf(stderr, "USIM_DIR environment variable is not defined\n");
-  }
-}
-
-/*
- * Displays USIM application data
- */
-static void _display_usim_data(const usim_data_t* data)
-{
-  int digits;
-
-  printf("Administrative Data:\n");
-  printf("\tUE_Operation_Mode\t= 0x%.2x\n", data->ad.UE_Operation_Mode);
-  printf("\tAdditional_Info\t\t= 0x%.4x\n", data->ad.Additional_Info);
-  printf("\tMNC_Length\t\t= %d\n\n", data->ad.MNC_Length);
-
-  printf("IMSI:\n");
-  printf("\tlength\t= %d\n", data->imsi.length);
-  printf("\tparity\t= %s\n", data->imsi.u.num.parity == EVEN_PARITY ? "Even" : "Odd");
-  digits = (data->imsi.length * 2) - 1 - (data->imsi.u.num.parity == EVEN_PARITY ? 1 : 0);
-  printf("\tdigits\t= %d\n", digits);
-  
-  printf("\tdigits\t= %u%u%u%u%u%x%u%u%u%u",
-         data->imsi.u.num.digit1, // MCC digit 1
-         data->imsi.u.num.digit2, // MCC digit 2
-         data->imsi.u.num.digit3, // MCC digit 3
-         data->imsi.u.num.digit4, // MNC digit 1
-         data->imsi.u.num.digit5, // MNC digit 2
-         data->imsi.u.num.digit6==0xf?0:data->imsi.u.num.digit6, // MNC digit 3
-         data->imsi.u.num.digit7,
-         data->imsi.u.num.digit8,
-         data->imsi.u.num.digit9,
-         data->imsi.u.num.digit10);
-
-  if (digits >= 11)
-    printf("%x", data->imsi.u.num.digit11);
-
-  if (digits >= 12)
-    printf("%x", data->imsi.u.num.digit12);
-
-  if (digits >= 13)
-    printf("%x", data->imsi.u.num.digit13);
-
-  if (digits >= 14)
-    printf("%x", data->imsi.u.num.digit14);
-
-  if (digits >= 15)
-    printf("%x", data->imsi.u.num.digit15);
-
-  printf("\n\n");
-
-  printf("Ciphering and Integrity Keys:\n");
-  printf("\tKSI\t: 0x%.2x\n", data->keys.ksi);
-  char key[USIM_CK_SIZE + 1];
-  key[USIM_CK_SIZE] = '\0';
-  memcpy(key, data->keys.ck, USIM_CK_SIZE);
-  printf("\tCK\t: \"%s\"\n", key);
-  memcpy(key, data->keys.ik, USIM_IK_SIZE);
-  printf("\tIK\t: \"%s\"\n", key);
-
-  printf("EPS NAS security context:\n");
-  printf("\tKSIasme\t: 0x%.2x\n", data->securityctx.KSIasme.value[0]);
-  char kasme[USIM_K_ASME_SIZE + 1];
-  kasme[USIM_K_ASME_SIZE] = '\0';
-  memcpy(kasme, data->securityctx.Kasme.value, USIM_K_ASME_SIZE);
-  printf("\tKasme\t: \"%s\"\n", kasme);
-  printf("\tulNAScount\t: 0x%.8x\n",
-         *(uint32_t*)data->securityctx.ulNAScount.value);
-  printf("\tdlNAScount\t: 0x%.8x\n",
-         *(uint32_t*)data->securityctx.dlNAScount.value);
-  printf("\talgorithmID\t: 0x%.2x\n\n",
-         data->securityctx.algorithmID.value[0]);
-
-  printf("MSISDN\t= %u%u%u %u%u%u%u %u%u%u%u\n\n",
-         data->msisdn.number.digit[0].msb,
-         data->msisdn.number.digit[0].lsb,
-         data->msisdn.number.digit[1].msb,
-         data->msisdn.number.digit[1].lsb,
-         data->msisdn.number.digit[2].msb,
-         data->msisdn.number.digit[2].lsb,
-         data->msisdn.number.digit[3].msb,
-         data->msisdn.number.digit[3].lsb,
-         data->msisdn.number.digit[4].msb,
-         data->msisdn.number.digit[4].lsb,
-         data->msisdn.number.digit[5].msb);
-
-  for (int i = 0; i < USIM_PNN_MAX; i++) {
-    printf("PNN[%d]\t= {%s, %s}\n", i,
-           data->pnn[i].fullname.value, data->pnn[i].shortname.value);
-  }
-
-  printf("\n");
-
-  for (int i = 0; i < USIM_OPL_MAX; i++) {
-    printf("OPL[%d]\t= ", i);
-    PRINT_PLMN(data->opl[i].plmn);
-    printf(", TAC = [%.4x - %.4x], record_id = %d\n",
-           data->opl[i].start, data->opl[i].end, data->opl[i].record_id);
-  }
-
-  printf("\n");
-
-  printf("HPLMN\t\t= ");
-  PRINT_PLMN(data->hplmn.plmn);
-  printf(", AcT = 0x%x\n\n", data->hplmn.AcT);
-
-  for (int i = 0; i < USIM_FPLMN_MAX; i++) {
-    printf("FPLMN[%d]\t= ", i);
-    PRINT_PLMN(data->fplmn[i]);
-    printf("\n");
-  }
-
-  printf("\n");
-
-  for (int i = 0; i < USIM_EHPLMN_MAX; i++) {
-    printf("EHPLMN[%d]\t= ", i);
-    PRINT_PLMN(data->ehplmn[i]);
-    printf("\n");
-  }
-
-  printf("\n");
-
-  for (int i = 0; i < USIM_PLMN_MAX; i++) {
-    printf("PLMN[%d]\t\t= ", i);
-    PRINT_PLMN(data->plmn[i].plmn);
-    printf(", AcTPLMN = 0x%x", data->plmn[i].AcT);
-    printf("\n");
-  }
-
-  printf("\n");
-
-  for (int i = 0; i < USIM_OPLMN_MAX; i++) {
-    printf("OPLMN[%d]\t= ", i);
-    PRINT_PLMN(data->oplmn[i].plmn);
-    printf(", AcTPLMN = 0x%x", data->oplmn[i].AcT);
-    printf("\n");
-  }
-
-  printf("\n");
-
-  printf("HPPLMN\t\t= 0x%.2x (%d minutes)\n\n", data->hpplmn, data->hpplmn);
-
-  printf("LOCI:\n");
-  printf("\tTMSI = 0x%.4x\n", data->loci.tmsi);
-  printf("\tLAI\t: PLMN = ");
-  PRINT_PLMN(data->loci.lai.plmn);
-  printf(", LAC = 0x%.2x\n", data->loci.lai.lac);
-  printf("\tstatus\t= %d\n\n", data->loci.status);
-
-  printf("PSLOCI:\n");
-  printf("\tP-TMSI = 0x%.4x\n", data->psloci.p_tmsi);
-  printf("\tsignature = 0x%x 0x%x 0x%x\n",
-         data->psloci.signature[0],
-         data->psloci.signature[1],
-         data->psloci.signature[2]);
-  printf("\tRAI\t: PLMN = ");
-  PRINT_PLMN(data->psloci.rai.plmn);
-  printf(", LAC = 0x%.2x, RAC = 0x%.1x\n",
-         data->psloci.rai.lac, data->psloci.rai.rac);
-  printf("\tstatus\t= %d\n\n", data->psloci.status);
-
-  printf("EPSLOCI:\n");
-  printf("\tGUTI\t: GUMMEI\t: (PLMN = ");
-  PRINT_PLMN(data->epsloci.guti.gummei.plmn);
-  printf(", MMEgid = 0x%.2x, MMEcode = 0x%.1x)",
-         data->epsloci.guti.gummei.MMEgid,
-         data->epsloci.guti.gummei.MMEcode);
-  printf(", M-TMSI = 0x%.4x\n", data->epsloci.guti.m_tmsi);
-  printf("\tTAI\t: PLMN = ");
-  PRINT_PLMN(data->epsloci.tai.plmn);
-  printf(", TAC = 0x%.2x\n",
-         data->epsloci.tai.tac);
-  printf("\tstatus\t= %d\n\n", data->epsloci.status);
-
-  printf("NASCONFIG:\n");
-  printf("\tNAS_SignallingPriority\t\t: 0x%.2x\n",
-         data->nasconfig.NAS_SignallingPriority.value[0]);
-  printf("\tNMO_I_Behaviour\t\t\t: 0x%.2x\n",
-         data->nasconfig.NMO_I_Behaviour.value[0]);
-  printf("\tAttachWithImsi\t\t\t: 0x%.2x\n",
-         data->nasconfig.AttachWithImsi.value[0]);
-  printf("\tMinimumPeriodicSearchTimer\t: 0x%.2x\n",
-         data->nasconfig.MinimumPeriodicSearchTimer.value[0]);
-  printf("\tExtendedAccessBarring\t\t: 0x%.2x\n",
-         data->nasconfig.ExtendedAccessBarring.value[0]);
-  printf("\tTimer_T3245_Behaviour\t\t: 0x%.2x\n",
-         data->nasconfig.Timer_T3245_Behaviour.value[0]);
-}
-
diff --git a/openair3/NAS/UE/API/USER/Makefile b/openair3/NAS/UE/API/USER/Makefile
deleted file mode 100644
index d5f4d6fffba2deb77a25a7739e06ac10e10b858e..0000000000000000000000000000000000000000
--- a/openair3/NAS/UE/API/USER/Makefile
+++ /dev/null
@@ -1,42 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.0  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-
-ifndef PROJDIR
-PROJDIR  = $(PWD)/../../..
-INCLUDES = -I. -I$(INCDIR) -I$(UTILDIR) -I$(IESDIR) -I$(EMMMSGDIR) -I$(ESMMSGDIR)
-endif
-
-include $(PROJDIR)/Makerules
-include $(PROJDIR)/Makefile.inc
-
-all: $(OBJS)
-
-%.o: %.c Makefile
-	@echo Compiling $<
-	@$(CC) $(CFLAGS) -c $< -o $@
-
-clean:
-	$(RM) $(OBJS) *.bak *~
-
-depend:
-	makedepend -- ${CFLAGS} -- ${SRCS}
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/openair3/NAS/UE/API/USER/at_command.c b/openair3/NAS/UE/API/USER/at_command.c
index 89793a55ae3bdb38ba1b61f5390678628ec053e8..8f40ba93c434f7f06e7765ff09774318db28f13e 100644
--- a/openair3/NAS/UE/API/USER/at_command.c
+++ b/openair3/NAS/UE/API/USER/at_command.c
@@ -54,6 +54,7 @@ Description Defines the ATtention (AT) command set supported by the NAS
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
+// FIXME Put this in .h
 extern int at_response_format_v1;
 extern int at_error_code_suppression_q1;
 extern at_error_format_t at_error_format;
@@ -278,6 +279,7 @@ int at_command_decode(const char* buffer, int length, at_command_t* at_command)
   char* buf = strdup(buffer+2);
   char* cmd = strtok(buf, ";");
 
+  // FIXME check overflow
   for (i=0; cmd && (rc != RETURNerror); i++) {
     rc = ParseString(cmd, &at_command[i]);
     cmd = strtok(NULL, ";");
diff --git a/openair3/NAS/UE/API/USER/at_error.c b/openair3/NAS/UE/API/USER/at_error.c
index 443c6a3300c5b2f2e336906f1b43fc37e739c954..7548fb513effb3d502cac4ce593b1f55e7be55f1 100644
--- a/openair3/NAS/UE/API/USER/at_error.c
+++ b/openair3/NAS/UE/API/USER/at_error.c
@@ -49,6 +49,7 @@ Description Defines error codes returned when execution of AT command
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
+// FIXME put this in .h
 extern int at_response_format_v1;
 
 /*
diff --git a/openair3/NAS/UE/API/USER/tst/Makefile b/openair3/NAS/UE/API/USER/tst/Makefile
deleted file mode 100644
index 48681f31a515265263b44c36873018bfb7ed1dec..0000000000000000000000000000000000000000
--- a/openair3/NAS/UE/API/USER/tst/Makefile
+++ /dev/null
@@ -1,58 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.0  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-
-ifndef PROJDIR
-PROJDIR = $(PWD)/../../../..
-endif
-
-include $(PROJDIR)/Makerules
-include $(PROJDIR)/Makefile.inc
-
-LIBS		= -lutil -lapi -lEMMmsg -lESMmsg -lies
-INCLUDES	= -I. -I$(INCDIR) -I$(UTILDIR) -I$(USERAPIDIR)
-
-LIBSAPI		= $(LIBDIR)/$(LIBAPI).a $(LIBDIR)/$(LIBAPI).so
-
-TST_OBJ		= at_parser.o
-
-TST_TARGET	= at_parser
-
-TARGETS = $(TST_TARGET)
-
-all: $(TARGETS)
-
-%.o: %.c Makefile
-	$(CC) $(CFLAGS) -c $< -o $@ 
-
-$(TST_TARGET): $(TST_OBJ)
-	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
-
-clean:
-	$(RM) $(OBJS) $(TARGETS) *.bak *~
-
-depend:
-	makedepend -- ${CFLAGS} -- ${SRCS}
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
-
-at_parser.o: $(USERAPIDIR)/at_command.h
-at_parser.o: $(INCDIR)/commonDef.h $(INCDIR)/userDef.h
-at_parser.o: $(INCDIR)/networkDef.h $(UTILDIR)/log.h
diff --git a/openair3/NAS/UE/API/USER/user_api.c b/openair3/NAS/UE/API/USER/user_api.c
index 9c2364629276e16338711ae481a8f9e601b7fe9d..a1300ea059d9dac26cfe107c3c6d9c0e02fc4f38 100644
--- a/openair3/NAS/UE/API/USER/user_api.c
+++ b/openair3/NAS/UE/API/USER/user_api.c
@@ -45,9 +45,9 @@ Description     Implements the API used by the NAS layer running in the UE
 #include "device.h"
 #include "nas_user.h"
 
-#include "at_command.h"
 #include "at_response.h"
 #include "at_error.h"
+#include "esm_ebr.h"
 
 #include "user_indication.h"
 
@@ -68,63 +68,12 @@ Description     Implements the API used by the NAS layer running in the UE
 /*
  * Asynchronous notification procedure handlers
  */
-static int _user_api_registration_handler(unsigned char id, const void* data, size_t size);
-static int _user_api_location_handler(unsigned char id, const void* data, size_t size);
-static int _user_api_network_handler(unsigned char id, const void* data, size_t size);
-static int _user_api_pdn_connection_handler(unsigned char id, const void* data, size_t size);
-
-static int _user_api_send(at_response_t* data);
-
-/* -------------------
- * Connection endpoint
- * -------------------
- *      The connection endpoint is used to send/receive data to/from the
- *      user application layer. Its definition depends on the underlaying
- *      mechanism chosen to communicate (network socket, I/O terminal device).
- *      A connection endpoint is handled using an identifier, and functions
- *      used to retreive the file descriptor actually allocated by the system,
- *      to receive data, to send data, and to perform clean up when connection
- *      is shut down.
- *      Only one single end to end connection with the user is managed at a
- *      time.
- */
-static struct {
-  /* Connection endpoint reference  */
-  void* endpoint;
-  /* Connection endpoint handlers */
-  void*   (*open) (int, const char*, const char*);
-  int     (*getfd)(const void*);
-  ssize_t (*recv) (void*, char*, size_t);
-  ssize_t (*send) (const void*, const char*, size_t);
-  void    (*close)(void*);
-} _user_api_id;
-
-#define USER_API_OPEN(a, b, c)  _user_api_id.open(a, b, c)
-#define USER_API_GETFD()  _user_api_id.getfd(_user_api_id.endpoint)
-#define USER_API_RECV(a, b) _user_api_id.recv(_user_api_id.endpoint, a, b)
-#define USER_API_SEND(a, b) _user_api_id.send(_user_api_id.endpoint, a, b)
-#define USER_API_CLOSE()  _user_api_id.close(_user_api_id.endpoint)
+static int _user_api_registration_handler(user_api_id_t *user_api_id, unsigned char id, const void* data, size_t size);
+static int _user_api_location_handler(user_api_id_t *user_api_id, unsigned char id, const void* data, size_t size);
+static int _user_api_network_handler(user_api_id_t *user_api_id, unsigned char id, const void* data, size_t size);
+static int _user_api_pdn_connection_handler(user_api_id_t *user_api_id, unsigned char id, const void* data, size_t size);
 
-/*
- * The buffer used to receive data from the user application layer
- */
-#define USER_API_RECV_BUFFER_SIZE 4096
-static char _user_api_recv_buffer[USER_API_RECV_BUFFER_SIZE];
-
-/*
- * The buffer used to send data to the user application layer
- */
-#define USER_API_SEND_BUFFER_SIZE USER_API_RECV_BUFFER_SIZE
-static char _user_api_send_buffer[USER_API_SEND_BUFFER_SIZE];
-
-/*
- * The decoded data received from the user application layer
- */
-static struct {
-  int n_cmd;    /* number of user data to be processed    */
-#define USER_DATA_MAX 10
-  at_command_t cmd[USER_DATA_MAX];  /* user data to be processed  */
-} _user_data = {};
+static int _user_api_send(user_api_id_t *user_api_id, at_response_t* data);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -147,44 +96,44 @@ static struct {
  **              Others:        _user_api_id                               **
  **                                                                        **
  ***************************************************************************/
-int user_api_initialize(const char* host, const char* port,
+int user_api_initialize(user_api_id_t *user_api_id, const char* host, const char* port,
                         const char* devname, const char* devparams)
 {
   LOG_FUNC_IN;
 
-  gethostname(_user_api_send_buffer, USER_API_SEND_BUFFER_SIZE);
+  gethostname(user_api_id->send_buffer, USER_API_SEND_BUFFER_SIZE);
 
   if (devname != NULL) {
     /* Initialize device handlers */
-    _user_api_id.open  = device_open;
-    _user_api_id.getfd = device_get_fd;
-    _user_api_id.recv  = device_read;
-    _user_api_id.send  = device_write;
-    _user_api_id.close = device_close;
+    user_api_id->open  = device_open;
+    user_api_id->getfd = device_get_fd;
+    user_api_id->recv  = device_read;
+    user_api_id->send  = device_write;
+    user_api_id->close = device_close;
 
     /* Initialize communication channel */
-    _user_api_id.endpoint = USER_API_OPEN(DEVICE, devname, devparams);
+    user_api_id->endpoint = user_api_id->open(DEVICE, devname, devparams);
 
-    if (_user_api_id.endpoint == NULL) {
+    if (user_api_id->endpoint == NULL) {
       LOG_TRACE(ERROR, "USR-API   - Failed to open connection endpoint, "
                 "%s", strerror(errno));
       LOG_FUNC_RETURN (RETURNerror);
     }
 
     LOG_TRACE(INFO, "USR-API   - User's communication device %d is OPENED "
-              "on %s/%s", user_api_get_fd(), _user_api_send_buffer, devname);
+              "on %s/%s", user_api_get_fd(user_api_id), user_api_id->send_buffer, devname);
   } else {
     /* Initialize network socket handlers */
-    _user_api_id.open  = socket_udp_open;
-    _user_api_id.getfd = socket_get_fd;
-    _user_api_id.recv  = socket_recv;
-    _user_api_id.send  = socket_send;
-    _user_api_id.close = socket_close;
+    user_api_id->open  = socket_udp_open;
+    user_api_id->getfd = socket_get_fd;
+    user_api_id->recv  = socket_recv;
+    user_api_id->send  = socket_send;
+    user_api_id->close = socket_close;
 
     /* Initialize communication channel */
-    _user_api_id.endpoint = USER_API_OPEN(SOCKET_SERVER, host, port);
+    user_api_id->endpoint = user_api_id->open(SOCKET_SERVER, host, port);
 
-    if (_user_api_id.endpoint == NULL) {
+    if (user_api_id->endpoint == NULL) {
       const char* error = ( (errno < 0) ?
                             gai_strerror(errno) : strerror(errno) );
       LOG_TRACE(ERROR, "USR-API   - Failed to open connection endpoint, "
@@ -193,7 +142,7 @@ int user_api_initialize(const char* host, const char* port,
     }
 
     LOG_TRACE(INFO, "USR-API   - User's UDP socket %d is BOUND to %s/%s",
-              user_api_get_fd(), _user_api_send_buffer, port);
+              user_api_get_fd(user_api_id), user_api_id->send_buffer, port);
   }
 
   /* Register the asynchronous notification handlers */
@@ -238,10 +187,10 @@ int user_api_initialize(const char* host, const char* port,
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-int user_api_get_fd(void)
+int user_api_get_fd(user_api_id_t *user_api_id)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (USER_API_GETFD());
+  LOG_FUNC_RETURN (user_api_id->getfd(user_api_id->endpoint));
 }
 
 /****************************************************************************
@@ -253,19 +202,18 @@ int user_api_get_fd(void)
  **              before its usage.                                         **
  **                                                                        **
  ** Inputs:      index:         Index of the user data structure to get    **
- **              Others:        _user_data                                 **
  **                                                                        **
  ** Outputs:     Return:        A generic pointer to the user data         **
  **                             structure                                  **
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-const void* user_api_get_data(int index)
+const void* user_api_get_data(user_at_commands_t *commands, int index)
 {
   LOG_FUNC_IN;
 
-  if (index < _user_data.n_cmd) {
-    LOG_FUNC_RETURN ((void*)(&_user_data.cmd[index]));
+  if (index < commands->n_cmd) {
+    LOG_FUNC_RETURN ((void*)(&commands->cmd[index]));
   }
 
   LOG_FUNC_RETURN (NULL);
@@ -277,33 +225,23 @@ const void* user_api_get_data(int index)
  **                                                                        **
  ** Description: Read data received from the user application layer        **
  **                                                                        **
- ** Inputs:      fd:            File descriptor of the connection endpoint **
- **                             from which data have been received         **
  **          Others:            _user_api_id                               **
  **                                                                        **
  ** Outputs: Return:            The number of bytes read when success;     **
  **                             RETURNerror Otherwise                      **
- **          Others:            _user_api_recv_buffer, _user_api_id        **
+ **          Others:            user_api_id->recv_buffer, _user_api_id        **
  **                                                                        **
  ***************************************************************************/
-int user_api_read_data(int fd)
+int user_api_read_data(user_api_id_t *user_api_id)
 {
   LOG_FUNC_IN;
 
   int rbytes;
 
-  /* Sanity check */
-  int sfd = user_api_get_fd();
-
-  if (fd != sfd) {
-    LOG_TRACE(ERROR, "USR-API   - Endpoint %d is not the one created for communication with the user application layer (%d)", fd, sfd);
-    LOG_FUNC_RETURN (RETURNerror);
-  }
-
-  memset(_user_api_recv_buffer, 0, USER_API_RECV_BUFFER_SIZE);
+  memset(user_api_id->recv_buffer, 0, USER_API_RECV_BUFFER_SIZE);
 
   /* Receive data from the user application layer */
-  rbytes = USER_API_RECV(_user_api_recv_buffer, USER_API_RECV_BUFFER_SIZE);
+  rbytes = user_api_id->recv(user_api_id->endpoint, user_api_id->recv_buffer, USER_API_RECV_BUFFER_SIZE);
 
   if (rbytes == RETURNerror) {
     LOG_TRACE(ERROR, "USR-API   - recv() failed, %s", strerror(errno));
@@ -313,7 +251,7 @@ int user_api_read_data(int fd)
   } else {
     LOG_TRACE(INFO, "USR-API   - %d bytes received "
               "from the user application layer", rbytes);
-    LOG_DUMP(_user_api_recv_buffer, rbytes);
+    LOG_DUMP(user_api_id->recv_buffer, rbytes);
   }
 
   LOG_FUNC_RETURN (rbytes);
@@ -329,45 +267,42 @@ int user_api_read_data(int fd)
  **                                                                        **
  ** Outputs:         Return:    The number of bytes write when success;    **
  **                             RETURNerror Otherwise                      **
- **                  Others:    _user_api_recv_buffer                      **
+ **                  Others:    user_api_id->recv_buffer                      **
  **                                                                        **
  ***************************************************************************/
-int user_api_set_data(char *message)
+int user_api_set_data(user_api_id_t *user_api_id, char *message)
 {
   LOG_FUNC_IN;
 
   int rbytes;
 
-  memset(_user_api_recv_buffer, 0, USER_API_RECV_BUFFER_SIZE);
+  memset(user_api_id->recv_buffer, 0, USER_API_RECV_BUFFER_SIZE);
 
-  strncpy(_user_api_recv_buffer, message, USER_API_RECV_BUFFER_SIZE);
-  rbytes = strlen(_user_api_recv_buffer);
+  strncpy(user_api_id->recv_buffer, message, USER_API_RECV_BUFFER_SIZE);
+  rbytes = strlen(user_api_id->recv_buffer);
 
   LOG_TRACE(INFO, "USR-API   - %d bytes write", rbytes);
-  LOG_DUMP(_user_api_recv_buffer, rbytes);
+  LOG_DUMP(user_api_id->recv_buffer, rbytes);
 
   LOG_FUNC_RETURN (rbytes);
 }
 
 /****************************************************************************
  **                                                                        **
- ** Name:        user_api_send_data()                                      **
+** Name:        user_api_send_data()                                      **
  **                                                                        **
  ** Description: Send data to the user application layer                   **
  **                                                                        **
- ** Inputs:      fd:            File descriptor of the connection endpoint **
- **                             to which data have to be sent              **
  **              length:        Number of bytes to send                    **
- **              Others:        _user_api_send_buffer, _user_api_id        **
  **                                                                        **
  ** Outputs:     Return:        The number of bytes sent when success;     **
  **                             RETURNerror Otherwise                      **
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_send_data(int length)
+static int _user_api_send_data(user_api_id_t *user_api_id, int length)
 {
-  int sbytes = USER_API_SEND(_user_api_send_buffer, length);
+  int sbytes = user_api_id->send(user_api_id->endpoint, user_api_id->send_buffer, length);
 
   if (sbytes == RETURNerror) {
     LOG_TRACE(ERROR, "USR-API   - send() failed, %s", strerror(errno));
@@ -377,28 +312,25 @@ static int _user_api_send_data(int length)
   } else {
     LOG_TRACE(INFO, "USR-API   - %d bytes sent "
               "to the user application layer", sbytes);
-    LOG_DUMP(_user_api_send_buffer, sbytes);
+    LOG_DUMP(user_api_id->send_buffer, sbytes);
   }
 
   return sbytes;
 }
-int user_api_send_data(int fd, int length)
+
+/****************************************************************************
+ **                                                                        **
+ ** Name:        user_api_close()                                          **
+ ***************************************************************************/
+ int user_api_send_data(user_api_id_t *user_api_id, int length)
 {
   LOG_FUNC_IN;
 
-  /* Sanity check */
-  int sfd = user_api_get_fd();
-
-  if (fd != sfd) {
-    LOG_TRACE(ERROR, "USR-API   - Endpoint %d is not the one created for communication with the user application layer (%d)", fd, sfd);
-    LOG_FUNC_RETURN (RETURNerror);
-  }
-
   /* Send data to the user application layer */
   int sbytes = 0;
 
   if (length > 0) {
-    sbytes = _user_api_send_data(length);
+    sbytes = _user_api_send_data(user_api_id, length);
   }
 
   LOG_FUNC_RETURN (sbytes);
@@ -411,31 +343,18 @@ int user_api_send_data(int fd, int length)
  ** Description: Close the user API from which the NAS layer sent/received **
  **              messages to/from the user application layer               **
  **                                                                        **
- ** Inputs:      fd:            File descriptor of the connection endpoint **
- **                             allocated by the system to communicate     **
- **                             with the user application layer            **
  **              Others:        None                                       **
  **                                                                        **
  ** Outputs:     Return:        None                                       **
- **              Others:        _user_api_id                               **
  **                                                                        **
  ***************************************************************************/
-void user_api_close(int fd)
+void user_api_close(user_api_id_t *user_api_id)
 {
   LOG_FUNC_IN;
 
-  /* Sanity check */
-  int sfd = user_api_get_fd();
-
-  if (fd != sfd) {
-    LOG_TRACE(ERROR, "USR-API   - Endpoint %d is not the one created for communication with the user application layer (%d)", fd, sfd);
-    LOG_FUNC_OUT;
-    return;
-  }
-
   /* Cleanup the connection endpoint */
-  USER_API_CLOSE();
-  _user_api_id.endpoint = NULL;
+  user_api_id->close(user_api_id->endpoint) ;
+  user_api_id->endpoint = NULL;
 
   LOG_FUNC_OUT;
 }
@@ -450,46 +369,45 @@ void user_api_close(int fd)
  **    layer when the AT command failed to be decoded.           **
  **                                                                        **
  ** Inputs:  length:  Number of bytes to decode                  **
- **      Others:  _user_api_recv_buffer                      **
  **                                                                        **
  ** Outputs:   Return:  The number of AT commands succeessfully    **
  **       decoded                                    **
- **      Others:  _user_api_send_buffer, _user_data          **
  **                                                                        **
  ***************************************************************************/
-int user_api_decode_data(int length)
+int user_api_decode_data(user_api_id_t *user_api_id, user_at_commands_t *commands, int length)
 {
   LOG_FUNC_IN;
 
   /* Parse the AT command line */
-  LOG_TRACE(INFO, "USR-API   - Decode user data: %s", _user_api_recv_buffer);
-  _user_data.n_cmd = at_command_decode(_user_api_recv_buffer, length,
-                                       _user_data.cmd);
+  LOG_TRACE(INFO, "USR-API   - Decode user data: %s", user_api_id->recv_buffer);
+  commands->n_cmd = at_command_decode(user_api_id->recv_buffer, length,
+                                       commands->cmd);
 
-  if (_user_data.n_cmd > 0) {
+  if (commands->n_cmd > 0) {
     /* AT command data received from the user application layer
      * has been successfully decoded */
     LOG_TRACE(INFO, "USR-API   - %d AT command%s ha%s been successfully "
-              "decoded", _user_data.n_cmd,
-              (_user_data.n_cmd > 1) ? "s" : "",
-              (_user_data.n_cmd > 1) ? "ve" : "s");
+              "decoded", commands->n_cmd,
+              (commands->n_cmd > 1) ? "s" : "",
+              (commands->n_cmd > 1) ? "ve" : "s");
   } else {
     int bytes;
 
     /* Failed to decode AT command data received from the user
      * application layer; Return syntax error code message */
     LOG_TRACE(ERROR, "USR-API   - Syntax error: Failed to decode "
-              "AT command data %s", _user_api_recv_buffer);
+              "AT command data %s", user_api_id->recv_buffer);
 
     /* Encode the syntax error code message */
-    bytes = at_error_encode(_user_api_send_buffer, AT_ERROR_SYNTAX,
+    bytes = at_error_encode(user_api_id->send_buffer, AT_ERROR_SYNTAX,
                             AT_ERROR_OPERATION_NOT_SUPPORTED);
 
+    // FIXME move _user_data call
     /* Send the syntax error code message */
-    (void) _user_api_send_data(bytes);
+    _user_api_send_data(user_api_id, bytes);
   }
 
-  LOG_FUNC_RETURN (_user_data.n_cmd);
+  LOG_FUNC_RETURN (commands->n_cmd);
 }
 
 /****************************************************************************
@@ -508,10 +426,9 @@ int user_api_decode_data(int length)
  ** Outputs:   Return:  The number of characters that have been    **
  **       successfully encoded;                      **
  **       RETURNerror otherwise.                     **
- **      Others:  _user_api_send_buffer                      **
  **                                                                        **
  ***************************************************************************/
-int user_api_encode_data(const void* data, int success_code)
+int user_api_encode_data(user_api_id_t *user_api_id, const void* data, int success_code)
 {
   LOG_FUNC_IN;
 
@@ -520,16 +437,16 @@ int user_api_encode_data(const void* data, int success_code)
 
   /* Encode AT command error message */
   if (user_data->cause_code != AT_ERROR_SUCCESS) {
-    bytes = at_error_encode(_user_api_send_buffer, AT_ERROR_CME,
+    bytes = at_error_encode(user_api_id->send_buffer, AT_ERROR_CME,
                             user_data->cause_code);
   }
   /* Encode AT command response message */
   else {
-    bytes = at_response_encode(_user_api_send_buffer, user_data);
+    bytes = at_response_encode(user_api_id->send_buffer, user_data);
 
     /* Add success result code */
     if ( (success_code) && (bytes != RETURNerror) ) {
-      bytes += at_error_encode(&_user_api_send_buffer[bytes],
+      bytes += at_error_encode(&user_api_id->send_buffer[bytes],
                                AT_ERROR_OK, 0);
     }
   }
@@ -567,7 +484,7 @@ int user_api_encode_data(const void* data, int success_code)
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-int user_api_emm_callback(Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT,
+int user_api_emm_callback(user_api_id_t *user_api_id, Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT,
                           const char* data, size_t size)
 {
   LOG_FUNC_IN;
@@ -579,14 +496,14 @@ int user_api_emm_callback(Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT,
      * The list of available operators present in the network has to be
      * displayed to the user application
      */
-    rc = user_ind_notify(USER_IND_PLMN, (void*)data, size);
+    rc = user_ind_notify(user_api_id, USER_IND_PLMN, (void*)data, size);
   } else {
     user_indication_t ind;
     ind.notification.reg.status = stat;
 
     if (size > 0) {
       /* The UE's network registration status has changed */
-      rc = user_ind_notify(USER_IND_REG, (void*)&ind, 0);
+      rc = user_ind_notify(user_api_id, USER_IND_REG, (void*)&ind, 0);
     }
 
     if (rc != RETURNerror) {
@@ -596,7 +513,7 @@ int user_api_emm_callback(Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT,
       ind.notification.loc.tac = tac;
       ind.notification.loc.ci  = ci;
       ind.notification.loc.AcT = AcT;
-      rc = user_ind_notify(USER_IND_LOC, (void*)&ind, 0);
+      rc = user_ind_notify(user_api_id, USER_IND_LOC, (void*)&ind, 0);
     }
   }
 
@@ -624,7 +541,7 @@ int user_api_emm_callback(Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT,
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-int user_api_esm_callback(int cid, network_pdn_state_t state)
+int user_api_esm_callback(user_api_id_t *user_api_id, int cid, network_pdn_state_t state)
 {
   LOG_FUNC_IN;
 
@@ -634,7 +551,7 @@ int user_api_esm_callback(int cid, network_pdn_state_t state)
   ind.notification.pdn.cid = cid;
   ind.notification.pdn.status = state;
   /* The status of the specified PDN connection has changed */
-  rc = user_ind_notify(USER_IND_PDN, (void*)&ind, 0);
+  rc = user_ind_notify(user_api_id, USER_IND_PDN, (void*)&ind, 0);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -650,23 +567,22 @@ int user_api_esm_callback(int cid, network_pdn_state_t state)
  ** Description: Encodes and sends data to the user application layer      **
  **                                                                        **
  ** Inputs:  data:    The data to send                           **
- **      Others:  _user_api_send_buffer, _user_api_id        **
  **                                                                        **
  ** Outputs:   Return:  The number of bytes sent when success;     **
  **       RETURNerror Otherwise                      **
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_send(at_response_t* data)
+static int _user_api_send(user_api_id_t *user_api_id, at_response_t* data)
 {
   LOG_FUNC_IN;
 
   /* Encode AT command response message */
-  int bytes = at_response_encode(_user_api_send_buffer, data);
+  int bytes = at_response_encode(user_api_id->send_buffer, data);
 
   /* Send the AT command response message to the user application */
   if (bytes != RETURNerror) {
-    bytes = _user_api_send_data(bytes);
+    bytes = _user_api_send_data(user_api_id, bytes);
   }
 
   LOG_FUNC_RETURN (bytes);
@@ -693,7 +609,7 @@ static int _user_api_send(at_response_t* data)
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_registration_handler(unsigned char id, const void* data,
+static int _user_api_registration_handler(user_api_id_t *user_api_id, unsigned char id, const void* data,
     size_t size)
 {
   LOG_FUNC_IN;
@@ -710,7 +626,7 @@ static int _user_api_registration_handler(unsigned char id, const void* data,
   at_response.response.cereg.stat = reg->status;
 
   /* Encode and send the AT command response message to the user */
-  int bytes = _user_api_send(&at_response);
+  int bytes = _user_api_send(user_api_id, &at_response);
 
   LOG_FUNC_RETURN (bytes);
 }
@@ -735,7 +651,7 @@ static int _user_api_registration_handler(unsigned char id, const void* data,
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_location_handler(unsigned char id, const void* data,
+static int _user_api_location_handler(user_api_id_t *user_api_id, unsigned char id, const void* data,
                                       size_t size)
 {
   LOG_FUNC_IN;
@@ -759,7 +675,7 @@ static int _user_api_location_handler(unsigned char id, const void* data,
   }
 
   /* Encode and send the AT command response message to the user */
-  int bytes = _user_api_send(&at_response);
+  int bytes = _user_api_send(user_api_id, &at_response);
 
   LOG_FUNC_RETURN (bytes);
 }
@@ -782,7 +698,7 @@ static int _user_api_location_handler(unsigned char id, const void* data,
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_network_handler(unsigned char id, const void* data,
+static int _user_api_network_handler(user_api_id_t *user_api_id, unsigned char id, const void* data,
                                      size_t size)
 {
   LOG_FUNC_IN;
@@ -795,7 +711,7 @@ static int _user_api_network_handler(unsigned char id, const void* data,
   at_response.response.cops.tst.size = size;
 
   /* Encode and send the AT command response message to the user */
-  int bytes = _user_api_send(&at_response);
+  int bytes = _user_api_send(user_api_id, &at_response);
 
   LOG_FUNC_RETURN (bytes);
 }
@@ -820,7 +736,7 @@ static int _user_api_network_handler(unsigned char id, const void* data,
  **      Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _user_api_pdn_connection_handler(unsigned char id, const void* data,
+static int _user_api_pdn_connection_handler(user_api_id_t *user_api_id, unsigned char id, const void* data,
     size_t size)
 {
   LOG_FUNC_IN;
@@ -837,7 +753,7 @@ static int _user_api_pdn_connection_handler(unsigned char id, const void* data,
   at_response.response.cgev.code = pdn->status;
 
   /* Encode and send the AT command response message to the user */
-  int bytes = _user_api_send(&at_response);
+  int bytes = _user_api_send(user_api_id, &at_response);
 
   LOG_FUNC_RETURN (bytes);
 }
diff --git a/openair3/NAS/UE/API/USER/user_api.h b/openair3/NAS/UE/API/USER/user_api.h
index 9597c0f1d1bbcedb1908abf4bde53cf9247fb2cd..6536144165b540bd9bf186d888aaf14c41eeb00b 100644
--- a/openair3/NAS/UE/API/USER/user_api.h
+++ b/openair3/NAS/UE/API/USER/user_api.h
@@ -42,15 +42,14 @@ Description Implements the API used by the NAS layer running in the UE
 
 #include "commonDef.h"
 #include "networkDef.h"
+#include "at_command.h"
+#include "user_api_defs.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
 
-/****************************************************************************/
-/************************  G L O B A L    T Y P E S  ************************/
-/****************************************************************************/
-
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -59,20 +58,20 @@ Description Implements the API used by the NAS layer running in the UE
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-int user_api_initialize(const char* host, const char* port, const char* devname, const char* devparams);
+int user_api_initialize(user_api_id_t *user_api_id, const char* host, const char* port, const char* devname, const char* devparams);
 
-int user_api_emm_callback(Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT, const char* data, size_t size);
-int user_api_esm_callback(int cid, network_pdn_state_t state);
+int user_api_emm_callback(user_api_id_t *user_api_id, Stat_t stat, tac_t tac, ci_t ci, AcT_t AcT, const char* data, size_t size);
+int user_api_esm_callback(user_api_id_t *user_api_id, int cid, network_pdn_state_t state);
 
-int user_api_get_fd(void);
-const void* user_api_get_data(int index);
+int user_api_get_fd(user_api_id_t *user_api_id);
+const void* user_api_get_data(user_at_commands_t *commands, int index);
 
-int user_api_read_data(int fd);
-int user_api_set_data(char *message);
-int user_api_send_data(int fd, int length);
-void user_api_close(int fd);
+int user_api_read_data(user_api_id_t *user_api_id);
+int user_api_set_data(user_api_id_t *user_api_id, char *message);
+int user_api_send_data(user_api_id_t *user_api_id, int length);
+void user_api_close(user_api_id_t *user_api_id);
 
-int user_api_decode_data(int length);
-int user_api_encode_data(const void* data, int add_success_code);
+int user_api_decode_data(user_api_id_t *user_api_id, user_at_commands_t *commands, int length);
+int user_api_encode_data(user_api_id_t *user_api_id, const void* data, int add_success_code);
 
 #endif /* __USER_API_H__ */
diff --git a/openair3/NAS/UE/API/USER/user_api_defs.h b/openair3/NAS/UE/API/USER/user_api_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..ef4d8cd383b93d49fad48011d87ac1531886578d
--- /dev/null
+++ b/openair3/NAS/UE/API/USER/user_api_defs.h
@@ -0,0 +1,50 @@
+#ifndef _USER_API_DEFS_H
+#define _USER_API_DEFS_H
+
+#include <sys/types.h>
+#include "at_command.h"
+
+/****************************************************************************/
+/************************  G L O B A L    T Y P E S  ************************/
+/****************************************************************************/
+
+#define USER_API_RECV_BUFFER_SIZE 4096
+#define USER_API_SEND_BUFFER_SIZE USER_API_RECV_BUFFER_SIZE
+#define USER_DATA_MAX 10
+
+/*
+ * The decoded data received from the user application layer
+ */
+typedef struct {
+  int n_cmd;    /* number of user data to be processed    */
+  at_command_t cmd[USER_DATA_MAX];  /* user data to be processed  */
+} user_at_commands_t;
+
+/* -------------------
+ * Connection endpoint
+ * -------------------
+ *      The connection endpoint is used to send/receive data to/from the
+ *      user application layer. Its definition depends on the underlaying
+ *      mechanism chosen to communicate (network socket, I/O terminal device).
+ *      A connection endpoint is handled using an identifier, and functions
+ *      used to retreive the file descriptor actually allocated by the system,
+ *      to receive data, to send data, and to perform clean up when connection
+ *      is shut down.
+ *      Only one single end to end connection with the user is managed at a
+ *      time.
+ */
+typedef struct {
+  /* Connection endpoint reference  */
+  void* endpoint;
+  /* Connection endpoint handlers */
+  void*   (*open) (int, const char*, const char*);
+  int     (*getfd)(const void*);
+  ssize_t (*recv) (void*, char*, size_t);
+  ssize_t (*send) (const void*, const char*, size_t);
+  void    (*close)(void*);
+  char    recv_buffer[USER_API_RECV_BUFFER_SIZE];
+  char    send_buffer[USER_API_SEND_BUFFER_SIZE];
+} user_api_id_t;
+
+
+#endif
diff --git a/openair3/NAS/UE/API/USER/user_indication.c b/openair3/NAS/UE/API/USER/user_indication.c
index d7495f46e767ffbe61ad9cd9133163fc2f76d496..620596005df5d3fdd1513a85d947486d242d7ac8 100644
--- a/openair3/NAS/UE/API/USER/user_indication.c
+++ b/openair3/NAS/UE/API/USER/user_indication.c
@@ -146,7 +146,7 @@ int user_ind_deregister(user_ind_t ind)
  **    Others:  None                                       **
  **                                                                        **
  ***************************************************************************/
-int user_ind_notify(user_ind_t ind, const void* data, size_t size)
+int user_ind_notify(user_api_id_t *user_api_id, user_ind_t ind, const void* data, size_t size)
 {
   LOG_FUNC_IN;
 
@@ -158,7 +158,7 @@ int user_ind_notify(user_ind_t ind, const void* data, size_t size)
       user_ind_callback_t notify = _user_ind_handler.callback[ind];
 
       if (notify != NULL) {
-        rc = (*notify)(_user_ind_handler.id, data, size);
+        rc = (*notify)(user_api_id, _user_ind_handler.id, data, size);
       }
     } else {
       /* Silently discard not registered notification */
diff --git a/openair3/NAS/UE/API/USER/user_indication.h b/openair3/NAS/UE/API/USER/user_indication.h
index d31a8754e49dc7ca85834d0d3f566124fb1741fb..39aba424e7a755f88ce91b3980d405dd5f819cba 100644
--- a/openair3/NAS/UE/API/USER/user_indication.h
+++ b/openair3/NAS/UE/API/USER/user_indication.h
@@ -43,6 +43,7 @@ Description Defines functions which allow the user application to register
 
 #include "commonDef.h"
 #include "networkDef.h"
+#include "user_api_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -101,7 +102,7 @@ typedef struct {
 /*
  * Type of procedure executed upon receiving registered notification
  */
-typedef int (*user_ind_callback_t) (unsigned char, const void*, size_t);
+typedef int (*user_ind_callback_t) (user_api_id_t *user_api_id, unsigned char, const void*, size_t);
 
 /****************************************************************************/
 /************************  G L O B A L    T Y P E S  ************************/
@@ -117,6 +118,6 @@ typedef int (*user_ind_callback_t) (unsigned char, const void*, size_t);
 
 int user_ind_register(user_ind_t ind, unsigned char id, user_ind_callback_t cb);
 int user_ind_deregister(user_ind_t ind);
-int user_ind_notify(user_ind_t ind, const void* data, size_t size);
+int user_ind_notify(user_api_id_t *user_api_id, user_ind_t ind, const void* data, size_t size);
 
 #endif /* __USER_IND_H__*/
diff --git a/openair3/NAS/UE/API/USIM/Makefile b/openair3/NAS/UE/API/USIM/Makefile
deleted file mode 100644
index d5f4d6fffba2deb77a25a7739e06ac10e10b858e..0000000000000000000000000000000000000000
--- a/openair3/NAS/UE/API/USIM/Makefile
+++ /dev/null
@@ -1,42 +0,0 @@
-#/*
-# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
-# * contributor license agreements.  See the NOTICE file distributed with
-# * this work for additional information regarding copyright ownership.
-# * The OpenAirInterface Software Alliance licenses this file to You under
-# * the OAI Public License, Version 1.0  (the "License"); you may not use this file
-# * except in compliance with the License.
-# * You may obtain a copy of the License at
-# *
-# *      http://www.openairinterface.org/?page_id=698
-# *
-# * Unless required by applicable law or agreed to in writing, software
-# * distributed under the License is distributed on an "AS IS" BASIS,
-# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# * See the License for the specific language governing permissions and
-# * limitations under the License.
-# *-------------------------------------------------------------------------------
-# * For more information about the OpenAirInterface (OAI) Software Alliance:
-# *      contact@openairinterface.org
-# */
-
-ifndef PROJDIR
-PROJDIR  = $(PWD)/../../..
-INCLUDES = -I. -I$(INCDIR) -I$(UTILDIR) -I$(IESDIR) -I$(EMMMSGDIR) -I$(ESMMSGDIR)
-endif
-
-include $(PROJDIR)/Makerules
-include $(PROJDIR)/Makefile.inc
-
-all: $(OBJS)
-
-%.o: %.c Makefile
-	@echo Compiling $<
-	@$(CC) $(CFLAGS) -c $< -o $@
-
-clean:
-	$(RM) $(OBJS) *.bak *~
-
-depend:
-	makedepend -- ${CFLAGS} -- ${SRCS}
-
-# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/openair3/NAS/UE/API/USIM/aka_functions.c b/openair3/NAS/UE/API/USIM/aka_functions.c
index 415ba695f21b4812bcd4828e296ee201499e8aaa..4d8f4cd1d31b33370056cea6e4b9556319e5a369 100644
--- a/openair3/NAS/UE/API/USIM/aka_functions.c
+++ b/openair3/NAS/UE/API/USIM/aka_functions.c
@@ -39,26 +39,6 @@
 #include "aka_functions.h"
 #include "nas_log.h"
 
-/*--------- Operator Variant Algorithm Configuration Field --------*/
-/*------- Insert your value of OP here -------*/
-/* PFT OP used currently in HSS (OPENAIRHSS/auc/kdf.c) */
-#define OAI_LTEBOX
-
-#ifdef OAI_LTEBOX
-//1006020f0a478bf6b699f15c062e42b3
-/*u8 OP[16] = {0xb3, 0x42, 0x2e, 0x06, 0x5c, 0xf1, 0x99, 0xb6,
-             0xf6, 0x8b, 0x47, 0x0a, 0x0f, 0x02, 0x06, 0x10
-	     };*/
-u8 OP[16] = {0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6,
-             0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3
-};
-#else
-u8 OP[16] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
-             0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11
-            };
-#endif
-/*------- Insert your value of OP here -------*/
-
 /*-------------------------------------------------------------------
  *                            Algorithm f1
  *-------------------------------------------------------------------
@@ -69,16 +49,14 @@ u8 OP[16] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
  *
  *-----------------------------------------------------------------*/
 void f1    ( u8 k_pP[16], u8 rand_pP[16], u8 sqn_pP[6], u8 amf_pP[2],
-             u8 mac_a_pP[8] )
+             u8 mac_a_pP[8], const u8 op_c[16])
 {
-  u8 op_c[16];
   u8 temp[16];
   u8 in1[16];
   u8 out1[16];
   u8 rijndaelInput[16];
   u8 i;
   RijndaelKeySchedule( k_pP );
-  ComputeOPc( op_c );
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = rand_pP[i] ^ op_c[i];
@@ -124,9 +102,8 @@ void f1    ( u8 k_pP[16], u8 rand_pP[16], u8 sqn_pP[6], u8 amf_pP[2],
  *
  *-----------------------------------------------------------------*/
 void f2345 ( u8 k_pP[16], u8 rand_pP[16],
-             u8 res_pP[8], u8 ck_pP[16], u8 ik_pP[16], u8 ak_pP[6] )
+             u8 res_pP[8], u8 ck_pP[16], u8 ik_pP[16], u8 ak_pP[6],const u8 op_c[16])
 {
-  u8 op_c[16];
   u8 temp[16];
   u8 out[16];
   u8 rijndaelInput[16];
@@ -142,7 +119,6 @@ void f2345 ( u8 k_pP[16], u8 rand_pP[16],
             rand_pP[8],rand_pP[9],rand_pP[10],rand_pP[11],rand_pP[12],rand_pP[13],rand_pP[14],rand_pP[15]);
 
   RijndaelKeySchedule( k_pP );
-  ComputeOPc( op_c );
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = rand_pP[i] ^ op_c[i];
@@ -225,16 +201,14 @@ void f2345 ( u8 k_pP[16], u8 rand_pP[16],
  *
  *-----------------------------------------------------------------*/
 void f1star( u8 k_pP[16], u8 rand_pP[16], u8 sqn_pP[6], u8 amf_pP[2],
-             u8 mac_s_pP[8] )
+             u8 mac_s_pP[8],const u8 op_c[16])
 {
-  u8 op_c[16];
   u8 temp[16];
   u8 in1[16];
   u8 out1[16];
   u8 rijndaelInput[16];
   u8 i;
   RijndaelKeySchedule( k_pP );
-  ComputeOPc( op_c );
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = rand_pP[i] ^ op_c[i];
@@ -280,15 +254,13 @@ void f1star( u8 k_pP[16], u8 rand_pP[16], u8 sqn_pP[6], u8 amf_pP[2],
  *
  *-----------------------------------------------------------------*/
 void f5star( u8 k_pP[16], u8 rand_pP[16],
-             u8 ak_pP[6] )
+             u8 ak_pP[6], const u8 op_c[16])
 {
-  u8 op_c[16];
   u8 temp[16];
   u8 out[16];
   u8 rijndaelInput[16];
   u8 i;
   RijndaelKeySchedule( k_pP );
-  ComputeOPc( op_c );
 
   for (i=0; i<16; i++)
     rijndaelInput[i] = rand_pP[i] ^ op_c[i];
@@ -316,22 +288,17 @@ void f5star( u8 k_pP[16], u8 rand_pP[16],
  * Function to compute OPc from OP and K. Assumes key schedule has
     already been performed.
  *-----------------------------------------------------------------*/
-void ComputeOPc( u8 op_c_pP[16] )
+void ComputeOPc(const u8 op[16], u8 op_c_pP[16])
 {
   u8 i;
   LOG_TRACE(DEBUG,
-            "USIM-API  - ComputeOPc : OP[0..15]=%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
-            OP[0],OP[1],OP[2], OP[3], OP[4], OP[5], OP[6], OP[7],
-            OP[8],OP[9],OP[10],OP[11],OP[12],OP[13],OP[14],OP[15]);
-  RijndaelEncrypt( OP, op_c_pP );
+            "USIM-API  - ComputeOPc : op[0..15]=%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+            op[0],op[1],op[2], op[3], op[4], op[5], op[6], op[7],
+            op[8],op[9],op[10],op[11],op[12],op[13],op[14],op[15]);
+  RijndaelEncrypt( op, op_c_pP );
 
   for (i=0; i<16; i++)
-    op_c_pP[i] ^= OP[i];
-  LOG_TRACE(DEBUG,
-            "USIM-API  - OPc[0..15]=%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
-            op_c_pP[0],op_c_pP[1],op_c_pP[2], op_c_pP[3], op_c_pP[4], op_c_pP[5], op_c_pP[6], op_c_pP[7],
-            op_c_pP[8],op_c_pP[9],op_c_pP[10],op_c_pP[11],op_c_pP[12],op_c_pP[13],op_c_pP[14],op_c_pP[15]);
-
+    op_c_pP[i] ^= op[i];
 
   return;
 } /* end of function ComputeOPc */
@@ -489,7 +456,7 @@ void MixColumn(u8 state[4][4])
  * 16-byte output (using round keys already derived from 16-byte
  * key).
  *-----------------------------------------------------------------*/
-void RijndaelEncrypt( u8 input[16], u8 output[16] )
+void RijndaelEncrypt(const u8 input[16], u8 output[16] )
 {
   u8 state[4][4];
   int i, r;
diff --git a/openair3/NAS/UE/API/USIM/aka_functions.h b/openair3/NAS/UE/API/USIM/aka_functions.h
index 66b5a90fbca6c356f057a733b4b5a2d72db7b43e..8c370bb76c8df239839cf6ab7f810495418301a7 100644
--- a/openair3/NAS/UE/API/USIM/aka_functions.h
+++ b/openair3/NAS/UE/API/USIM/aka_functions.h
@@ -23,13 +23,13 @@ typedef unsigned char u8;
 
 /*--------------------------- prototypes --------------------------*/
 void f1    ( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2],
-             u8 mac_a[8] );
+             u8 mac_a[8], const u8 op[16]);
 void f2345 ( u8 k[16], u8 rand[16],
-             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6] );
+             u8 res[8], u8 ck[16], u8 ik[16], u8 ak[6], const u8 op[16]);
 void f1star( u8 k[16], u8 rand[16], u8 sqn[6], u8 amf[2],
-             u8 mac_s[8] );
+             u8 mac_s[8], const u8 op[16]);
 void f5star( u8 k[16], u8 rand[16],
-             u8 ak[6] );
-void ComputeOPc( u8 op_c[16] );
+             u8 ak[6], const u8 op[16]);
+void ComputeOPc(const u8 op[16], u8 op_c_pP[16] );
 void RijndaelKeySchedule( u8 key[16] );
-void RijndaelEncrypt( u8 input[16], u8 output[16] );
+void RijndaelEncrypt(const u8 input[16], u8 output[16] );
diff --git a/openair3/NAS/UE/API/USIM/usim_api.c b/openair3/NAS/UE/API/USIM/usim_api.c
index 58ca9aa279e95e66375647e3760bf490f833dded..efb0383596dfc885bf20137c2673c25aa48909ea 100644
--- a/openair3/NAS/UE/API/USIM/usim_api.c
+++ b/openair3/NAS/UE/API/USIM/usim_api.c
@@ -41,11 +41,13 @@ Description Implements the API used by the NAS layer to read/write
 
 #include "usim_api.h"
 #include "nas_log.h"
+#include "utils.h"
 #include "memory.h"
 #include <stdio.h>
 #include "aka_functions.h"
 #include <string.h> // memcpy, memset
 #include <stdlib.h> // malloc, free
+#include <stdio.h>
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
@@ -55,46 +57,6 @@ Description Implements the API used by the NAS layer to read/write
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
 
-/*
- * The name of the file where are stored data of the USIM application
- */
-#define USIM_API_NVRAM_FILENAME ".usim.nvram"
-
-/*
- * The name of the environment variable which defines the directory
- * where the USIM application file is located
- */
-#define USIM_API_NVRAM_DIRNAME  "USIM_DIR"
-
-/*
- * Subscriber authentication security key
- */
-#define USIM_API_K_SIZE         16
-//#define USIM_API_K_VALUE        "fec86ba6eb707ed08905757b1bb44b8f"
-#define USIM_API_K_VALUE        "8BAF473F2F8FD09487CCCBD7097C6862"
-#define TEST_USIM_API_K_VALUE   "000102030405060708090a0b0c0d0e0f" // CMW500 K key
-
-static uint8_t _usim_api_k[USIM_API_K_SIZE];
-
-
-/*
- * List of last used Sequence Numbers SQN
- */
-#define USIM_API_AK_SIZE 6
-#define USIM_API_SQN_SIZE USIM_API_AK_SIZE
-#define USIM_API_SQNMS_SIZE USIM_API_SQN_SIZE
-
-static struct _usim_api_data_s {
-  /* Highest sequence number the USIM has ever accepted */
-  uint8_t sqn_ms[USIM_API_SQNMS_SIZE];
-  /* List of the last used sequence numbers   */
-#define USIM_API_SQN_LIST_SIZE  32
-  uint8_t n_sqns;
-  uint32_t sqn[USIM_API_SQN_LIST_SIZE];
-} _usim_api_data;
-
-static uint8_t _usim_api_hex_char_to_hex_value (char c);
-static void _usim_api_hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size);
 static int _usim_api_check_sqn(uint32_t seq, uint8_t ind);
 
 /****************************************************************************/
@@ -115,38 +77,17 @@ static int _usim_api_check_sqn(uint32_t seq, uint8_t ind);
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-int usim_api_read(usim_data_t* data)
+int usim_api_read(const char *filename, usim_data_t* data)
 {
   LOG_FUNC_IN;
 
-  /* Get USIM application pathname */
-  char* path = memory_get_path(USIM_API_NVRAM_DIRNAME,
-                               USIM_API_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    LOG_TRACE(ERROR, "USIM-API  - Failed to get USIM pathname");
-    LOG_FUNC_RETURN (RETURNerror);
-  }
-
   /* Read USIM application data */
-  if (memory_read(path, data, sizeof(usim_data_t)) != RETURNok) {
+  if (memory_read(filename, data, sizeof(usim_data_t)) != RETURNok) {
     LOG_TRACE(ERROR, "USIM-API  - %s file is either not valid "
-              "or not present", path);
-    free(path);
+              "or not present", filename);
     LOG_FUNC_RETURN (RETURNerror);
   }
 
-  /* initialize the subscriber authentication security key */
-  if(data->usimtestmode == 0)
-  {
-    _usim_api_hex_string_to_hex_value(_usim_api_k, USIM_API_K_VALUE, USIM_API_K_SIZE);
-  }
-  else
-  {
-    _usim_api_hex_string_to_hex_value(_usim_api_k, TEST_USIM_API_K_VALUE, USIM_API_K_SIZE);
-  }
-
-  free(path);
   LOG_FUNC_RETURN (RETURNok);
 }
 
@@ -164,28 +105,17 @@ int usim_api_read(usim_data_t* data)
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-int usim_api_write(const usim_data_t* data)
+int usim_api_write(const char *filename, const usim_data_t* data)
 {
   LOG_FUNC_IN;
 
-  /* Get USIM application pathname */
-  char* path = memory_get_path(USIM_API_NVRAM_DIRNAME,
-                               USIM_API_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    LOG_TRACE(ERROR, "USIM-API  - Failed to get USIM pathname");
-    LOG_FUNC_RETURN (RETURNerror);
-  }
-
   /* Write USIM application data */
-  if (memory_write(path, data, sizeof(usim_data_t)) != RETURNok) {
+  if (memory_write(filename, data, sizeof(usim_data_t)) != RETURNok) {
 
-    LOG_TRACE(ERROR, "USIM-API  - Unable to write USIM file %s", path);
-    free(path);
+    LOG_TRACE(ERROR, "USIM-API  - Unable to write USIM file %s", filename);
     LOG_FUNC_RETURN (RETURNerror);
   }
 
-  free(path);
   LOG_FUNC_RETURN (RETURNok);
 }
 
@@ -220,7 +150,8 @@ int usim_api_write(const usim_data_t* data)
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* autn_pP,
+int usim_api_authenticate_test(usim_data_t *usim_data,
+                               const OctetString* rand_pP, const OctetString* autn_pP,
                                OctetString* auts_pP, OctetString* res_pP,
                                OctetString* ck_pP, OctetString* ik_pP)
 {
@@ -236,7 +167,7 @@ int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* au
   //       RES = XDOUT
   for (i=0; i<USIM_API_K_SIZE; i++)
   {
-      res_pP->value[i] = rand_pP->value[i] ^ _usim_api_k[i];
+      res_pP->value[i] = rand_pP->value[i] ^ usim_data->keys.usim_api_k[i];
   }
 
   //step2: res = f2(xdout,n)
@@ -327,19 +258,19 @@ int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* au
 
     /* Concealed value of the counter SQNms in the USIM:
      * Conc(SQNMS) = SQNMS ⊕ f5*K(RAND) */
-    f5star(_usim_api_k, rand_pP->value, ak);
+    f5star(usim_data->keys.usim_api_k, rand_pP->value, ak, usim_data->keys.opc);
 
 
     u8 sqn_ms[USIM_API_SQNMS_SIZE];
     memset(sqn_ms, 0, USIM_API_SQNMS_SIZE);
 
     //#define USIM_API_SQN_MS_SIZE  3
-    printf("_usim_api_data.sqn_ms %p\n",_usim_api_data.sqn_ms);
+    printf("usim_data->usim_sqn_data.sqn_ms %p\n", usim_data->usim_sqn_data.sqn_ms);
     for (i = 0; i < USIM_API_SQNMS_SIZE; i++) {
       //#warning "LG:BUG HERE TODO"
-      printf("i %d:  ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i] %d\n",i, ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i]);
+      printf("i %d:  ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i] %d\n",i, ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i]);
       sqn_ms[USIM_API_SQNMS_SIZE - i] =
-        ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i];
+        ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i];
     }
 
     u8 sqnms[USIM_API_SQNMS_SIZE];
@@ -355,8 +286,8 @@ int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* au
      * MACS = f1*K(SQNMS || RAND || AMF) */
 #define USIM_API_MACS_SIZE USIM_API_XMAC_SIZE
     u8 macs[USIM_API_MACS_SIZE];
-    f1star(_usim_api_k, rand_pP->value, sqn_ms,
-           &rand_pP->value[USIM_API_SQN_SIZE], macs);
+    f1star(usim_data->keys.usim_api_k, rand_pP->value, sqn_ms,
+           &rand_pP->value[USIM_API_SQN_SIZE], macs, usim_data->keys.opc);
     LOG_TRACE(DEBUG, "USIM-API  - MACS %02X%02X%02X%02X%02X%02X%02X%02X",
               macs[0],macs[1],macs[2],macs[3],
               macs[4],macs[5],macs[6],macs[7]);
@@ -392,7 +323,6 @@ int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* au
  **              autn_pP:          Authentication token                       **
  **                             AUTN = (SQN xor AK) || AMF || MAC          **
  **                                         48          16     64 bits     **
- **              Others:        Security key                               **
  **                                                                        **
  ** Outputs:     auts_pP:          Re-synchronization token                   **
  **              res_pP:           Authentication response                    **
@@ -403,7 +333,7 @@ int usim_api_authenticate_test(const OctetString* rand_pP, const OctetString* au
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP,
+int usim_api_authenticate(usim_data_t *usim_data, const OctetString* rand_pP, const OctetString* autn_pP,
                           OctetString* auts_pP, OctetString* res_pP,
                           OctetString* ck_pP, OctetString* ik_pP)
 {
@@ -421,8 +351,8 @@ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP
   /* Compute the anonymity key AK = f5K (RAND) */
 
   u8 ak[USIM_API_AK_SIZE];
-  f2345(_usim_api_k, rand_pP->value,
-        res_pP->value, ck_pP->value, ik_pP->value, ak);
+  f2345(usim_data->keys.usim_api_k, rand_pP->value,
+        res_pP->value, ck_pP->value, ik_pP->value, ak, usim_data->keys.opc);
   LOG_TRACE(INFO, "USIM-API  - res(f2)  :%s",dump_octet_string(res_pP));
   LOG_TRACE(INFO, "USIM-API  - ck(f3)   :%s",dump_octet_string(ck_pP));
   LOG_TRACE(INFO, "USIM-API  - ik(f4)   :%s",dump_octet_string(ik_pP));
@@ -443,7 +373,7 @@ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP
   /* Compute XMAC = f1K (SQN || RAND || AMF) */
 #define USIM_API_XMAC_SIZE 8
   u8 xmac[USIM_API_XMAC_SIZE];
-  f1(_usim_api_k, rand_pP->value, sqn, &autn_pP->value[USIM_API_SQN_SIZE], xmac);
+  f1(usim_data->keys.usim_api_k, rand_pP->value, sqn, &autn_pP->value[USIM_API_SQN_SIZE], xmac, usim_data->keys.opc);
   LOG_TRACE(DEBUG,
             "USIM-API  - Computed XMAC %02X%02X%02X%02X%02X%02X%02X%02X",
             xmac[0],xmac[1],xmac[2],xmac[3],
@@ -471,19 +401,19 @@ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP
 
     /* Concealed value of the counter SQNms in the USIM:
      * Conc(SQNMS) = SQNMS ⊕ f5*K(RAND) */
-    f5star(_usim_api_k, rand_pP->value, ak);
+    f5star(usim_data->keys.usim_api_k, rand_pP->value, ak, usim_data->keys.opc);
 
 
     u8 sqn_ms[USIM_API_SQNMS_SIZE];
     memset(sqn_ms, 0, USIM_API_SQNMS_SIZE);
 
     //#define USIM_API_SQN_MS_SIZE  3
-    printf("_usim_api_data.sqn_ms %p\n",_usim_api_data.sqn_ms);
+    printf("usim_data->usim_sqn.sqn_ms %p\n",usim_data->usim_sqn_data.sqn_ms);
     for (i = 0; i < USIM_API_SQNMS_SIZE; i++) {
       //#warning "LG:BUG HERE TODO"
-      printf("i %d:  ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i] %d\n",i, ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i]);
+      printf("i %d:  ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i] %d\n",i, ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i]);
       sqn_ms[USIM_API_SQNMS_SIZE - i] =
-        ((uint8_t*)(_usim_api_data.sqn_ms))[USIM_API_SQNMS_SIZE - i];
+        ((uint8_t*)(usim_data->usim_sqn_data.sqn_ms))[USIM_API_SQNMS_SIZE - i];
     }
 
     u8 sqnms[USIM_API_SQNMS_SIZE];
@@ -499,8 +429,8 @@ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP
      * MACS = f1*K(SQNMS || RAND || AMF) */
 #define USIM_API_MACS_SIZE USIM_API_XMAC_SIZE
     u8 macs[USIM_API_MACS_SIZE];
-    f1star(_usim_api_k, rand_pP->value, sqn_ms,
-           &rand_pP->value[USIM_API_SQN_SIZE], macs);
+    f1star(usim_data->keys.usim_api_k, rand_pP->value, sqn_ms,
+           &rand_pP->value[USIM_API_SQN_SIZE], macs, usim_data->keys.opc);
     LOG_TRACE(DEBUG, "USIM-API  - MACS %02X%02X%02X%02X%02X%02X%02X%02X",
               macs[0],macs[1],macs[2],macs[3],
               macs[4],macs[5],macs[6],macs[7]);
@@ -520,57 +450,6 @@ int usim_api_authenticate(const OctetString* rand_pP, const OctetString* autn_pP
 /*********************  L O C A L    F U N C T I O N S  *********************/
 /****************************************************************************/
 
-/****************************************************************************
- **                                                                        **
- ** Name:        _usim_api_hex_char_to_hex_value()                         **
- **                                                                        **
- ** Description: Converts an hexadecimal ASCII coded digit into its value. **
- **                                                                        **
- ** Inputs:      c:             A char holding the ASCII coded value       **
- **              Others:        None                                       **
- **                                                                        **
- ** Outputs:     None                                                      **
- **              Return:        Converted value                            **
- **              Others:        None                                       **
- **                                                                        **
- ***************************************************************************/
-static uint8_t _usim_api_hex_char_to_hex_value (char c)
-{
-  if (c >= 'A') {
-    /* Remove case bit */
-    c &= ~('a' ^ 'A');
-
-    return (c - 'A' + 10);
-  } else {
-    return (c - '0');
-  }
-}
-
-/****************************************************************************
- **                                                                        **
- ** Name:        _usim_api_hex_string_to_hex_value()                       **
- **                                                                        **
- ** Description: Converts an hexadecimal ASCII coded string into its value.**
- **                                                                        **
- ** Inputs:      hex_value:     A pointer to the location to store the     **
- **                             conversion result                          **
- **              size:          The size of hex_value in bytes             **
- **              Others:        None                                       **
- **                                                                        **
- ** Outputs:     hex_value:     Converted value                            **
- **              Return:        None                                       **
- **              Others:        None                                       **
- **                                                                        **
- ***************************************************************************/
-static void _usim_api_hex_string_to_hex_value (uint8_t *hex_value, const char *hex_string, int size)
-{
-  int i;
-
-  for (i=0; i < size; i++) {
-    hex_value[i] = (_usim_api_hex_char_to_hex_value(hex_string[2 * i]) << 4) | _usim_api_hex_char_to_hex_value(hex_string[2 * i + 1]);
-  }
-}
-
 /****************************************************************************
  **                                                                        **
  ** Name:        _usim_api_check_sqn()                                     **
diff --git a/openair3/NAS/UE/API/USIM/usim_api.h b/openair3/NAS/UE/API/USIM/usim_api.h
index 8cfaeb8aed67d14a0ca3cecde85eeba9433ce3d1..f2f80d610c5d0601be4baa837125f2d2bff6a8d5 100644
--- a/openair3/NAS/UE/API/USIM/usim_api.h
+++ b/openair3/NAS/UE/API/USIM/usim_api.h
@@ -48,6 +48,23 @@ Description Implements the API used by the NAS layer to read/write
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
 
+/*
+ * Subscriber authentication security key
+ */
+#define USIM_API_K_SIZE         16
+#define USIM_API_K_VALUE        "fec86ba6eb707ed08905757b1bb44b8f"
+
+/*
+ * The name of the file where are stored data of the USIM application
+ */
+#define USIM_API_NVRAM_FILENAME ".usim.nvram"
+
+/*
+ * The name of the environment variable which defines the directory
+ * where the USIM application file is located
+ */
+#define USIM_API_NVRAM_DIRNAME  "USIM_DIR"
+
 /****************************************************************************/
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
@@ -106,8 +123,27 @@ typedef struct {
   /* Integrity key  */
 #define USIM_IK_SIZE  16
   Byte_t ik[USIM_IK_SIZE];
+  uint8_t usim_api_k[USIM_API_K_SIZE];
+  uint8_t opc[16];
 } usim_keys_t;
 
+/*
+ * List of last used Sequence Numbers SQN
+ */
+#define USIM_API_AK_SIZE 6
+#define USIM_API_SQN_SIZE USIM_API_AK_SIZE
+#define USIM_API_SQNMS_SIZE USIM_API_SQN_SIZE
+
+typedef struct {
+  /* Highest sequence number the USIM has ever accepted */
+  uint8_t sqn_ms[USIM_API_SQNMS_SIZE];
+  /* List of the last used sequence numbers   */
+#define USIM_API_SQN_LIST_SIZE  32
+  uint8_t n_sqns;
+  uint32_t sqn[USIM_API_SQN_LIST_SIZE];
+} usim_sqn_data_t;
+
+
 /*
  * EPS NAS Security Context
  * ------------------------
@@ -329,6 +365,7 @@ typedef struct {
   usim_nasconfig_t nasconfig;
   /* usim test mode */
   uint8_t usimtestmode;
+  usim_sqn_data_t usim_sqn_data;
 } usim_data_t;
 
 /****************************************************************************/
@@ -339,14 +376,15 @@ typedef struct {
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-int usim_api_read(usim_data_t* data);
+int usim_api_read(const char *filename, usim_data_t* data);
 
-int usim_api_write(const usim_data_t* data);
+int usim_api_write(const char *filename, const usim_data_t* data);
 
-int usim_api_authenticate(const OctetString* rand, const OctetString* autn,
+int usim_api_authenticate(usim_data_t *usim_data, const OctetString* rand_pP, const OctetString* autn_pP,
                           OctetString* auts, OctetString* res,
                           OctetString* ck, OctetString* ik);
-int usim_api_authenticate_test(const OctetString* rand, const OctetString* autn,
+int usim_api_authenticate_test(usim_data_t *usim_data,
+                               const OctetString* rand, const OctetString* autn,
                                OctetString* auts, OctetString* res,
                                OctetString* ck, OctetString* ik);
 
diff --git a/openair3/NAS/UE/EMM/Attach.c b/openair3/NAS/UE/EMM/Attach.c
index 939c5b4afdd7796e58ab66b76372e446336674ff..c0c82b9b4ccf258567f414882fa368e513b1bcb9 100644
--- a/openair3/NAS/UE/EMM/Attach.c
+++ b/openair3/NAS/UE/EMM/Attach.c
@@ -59,6 +59,7 @@ Description Defines the attach related EMM procedure executed by the
 #include "nas_timer.h"
 
 #include "emmData.h"
+#include "emm_timers.h"
 
 #include "emm_sap.h"
 #include "esm_sap.h"
@@ -90,24 +91,12 @@ static const char *_emm_attach_type_str[] = {
 /*
  * Timer handlers
  */
-void *_emm_attach_t3410_handler(void *);
-static void *_emm_attach_t3411_handler(void *);
-static void *_emm_attach_t3402_handler(void *);
+static void *_emm_attach_t3411_handler(void *args);
 
 /*
  * Abnormal case attach procedure
  */
-static void _emm_attach_abnormal_cases_bcd(emm_sap_t *);
-
-/*
- * Internal data used for attach procedure
- */
-static struct {
-#define EMM_ATTACH_COUNTER_MAX  5
-  unsigned int attempt_count; /* Counter used to limit the number of
-                 * subsequently rejected attach attempts */
-} _emm_attach_data = {0};
-
+static void _emm_attach_abnormal_cases_bcd(nas_user_t *user, emm_sap_t *);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -133,14 +122,14 @@ static struct {
  **      INITIATED.                                                **
  **                                                                        **
  ** Inputs:  type:      Type of the requested attach               **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    T3402, T3410, T3411                        **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach(emm_proc_attach_type_t type)
+int emm_proc_attach(nas_user_t *user, emm_proc_attach_type_t type)
 {
   LOG_FUNC_IN;
 
@@ -148,13 +137,14 @@ int emm_proc_attach(emm_proc_attach_type_t type)
   emm_as_establish_t *emm_as = &emm_sap.u.emm_as.u.establish;
   esm_sap_t esm_sap;
   int rc;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach type = %s (%d)",
             _emm_attach_type_str[type], type);
 
   /* Update the emergency bearer service indicator */
   if (type == EMM_ATTACH_TYPE_EMERGENCY) {
-    _emm_data.is_emergency = TRUE;
+    user->emm_data->is_emergency = TRUE;
   }
 
   /* Setup initial NAS information message to transfer */
@@ -163,7 +153,7 @@ int emm_proc_attach(emm_proc_attach_type_t type)
   emm_as->type = type;
 
   /* Set the RRC connection establishment cause */
-  if (_emm_data.is_emergency) {
+  if (user->emm_data->is_emergency) {
     emm_as->RRCcause = NET_ESTABLISH_CAUSE_EMERGENCY;
     emm_as->RRCtype = NET_ESTABLISH_TYPE_EMERGENCY_CALLS;
   } else {
@@ -172,7 +162,7 @@ int emm_proc_attach(emm_proc_attach_type_t type)
   }
 
   /* Set the PLMN identifier of the selected PLMN */
-  emm_as->plmnID = &_emm_data.splmn;
+  emm_as->plmnID = &user->emm_data->splmn;
   /*
    * Process the EPS mobile identity
    */
@@ -182,55 +172,55 @@ int emm_proc_attach(emm_proc_attach_type_t type)
   emm_as->UEid.imei = NULL;
 
   /* Check whether the UE is configured for "AttachWithIMSI" */
-  if (_emm_data.AttachWithImsi) {
+  if (user->emm_data->AttachWithImsi) {
     /* Check whether the selected PLMN is neither the registered PLMN
      * nor in the list of equivalent PLMNs */
-    if ( (!_emm_data.is_rplmn) && (!_emm_data.is_eplmn) ) {
+    if ( (!user->emm_data->is_rplmn) && (!user->emm_data->is_eplmn) ) {
       LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach with IMSI");
       /* Include the IMSI */
-      emm_as->UEid.imsi = _emm_data.imsi;
+      emm_as->UEid.imsi = user->emm_data->imsi;
     } else {
       LOG_TRACE(INFO,
                 "EMM-PROC  - Initiate EPS attach with NO IMSI, is registered PLMN %d, is equivalent PLMN %d",
-                _emm_data.is_rplmn,
-                _emm_data.is_eplmn);
+                user->emm_data->is_rplmn,
+                user->emm_data->is_eplmn);
     }
-  } else if (_emm_data.guti) {
+  } else if (user->emm_data->guti) {
     LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach with GUTI");
     /* Include a valid GUTI and the last visited registered TAI */
-    emm_as->UEid.guti = _emm_data.guti;
-    emm_as->UEid.tai = _emm_data.tai;
-  } else if (!_emm_data.is_emergency) {
+    emm_as->UEid.guti = user->emm_data->guti;
+    emm_as->UEid.tai = user->emm_data->tai;
+  } else if (!user->emm_data->is_emergency) {
     LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach with IMSI cause is no emergency and no GUTI");
     /* Include the IMSI if no valid GUTI is available */
-    emm_as->UEid.imsi = _emm_data.imsi;
+    emm_as->UEid.imsi = user->emm_data->imsi;
   } else {
     /* The UE is attaching for emergency bearer services and
      * does not hold a valid GUTI */
-    if (_emm_data.imsi) {
+    if (user->emm_data->imsi) {
       /* Include the IMSI if valid (USIM is present) */
       LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach with IMSI cause is emergency and no GUTI");
-      emm_as->UEid.imsi = _emm_data.imsi;
+      emm_as->UEid.imsi = user->emm_data->imsi;
     } else {
       LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS attach with IMSI cause is emergency and no GUTI and no IMSI");
       /* Include the IMEI if the IMSI is not valid */
-      emm_as->UEid.imei = _emm_data.imei;
+      emm_as->UEid.imei = user->emm_data->imei;
     }
   }
 
   /* Setup EPS NAS security data */
-  emm_as_set_security_data(&emm_as->sctx, _emm_data.security, FALSE, FALSE);
+  emm_as_set_security_data(&emm_as->sctx, user->emm_data->security, FALSE, FALSE);
   emm_as->ksi = EMM_AS_NO_KEY_AVAILABLE;
 
-  if (_emm_data.security) {
-    if (_emm_data.security->type != EMM_KSI_NOT_AVAILABLE) {
-      emm_as->ksi = _emm_data.security->eksi;
+  if (user->emm_data->security) {
+    if (user->emm_data->security->type != EMM_KSI_NOT_AVAILABLE) {
+      emm_as->ksi = user->emm_data->security->eksi;
     }
 
-    LOG_TRACE(INFO, "EMM-PROC  - eps_encryption 0x%X", _emm_data.security->capability.eps_encryption);
-    LOG_TRACE(INFO, "EMM-PROC  - eps_integrity  0x%X", _emm_data.security->capability.eps_integrity);
-    emm_as->encryption = _emm_data.security->capability.eps_encryption;
-    emm_as->integrity = _emm_data.security->capability.eps_integrity;
+    LOG_TRACE(INFO, "EMM-PROC  - eps_encryption 0x%X", user->emm_data->security->capability.eps_encryption);
+    LOG_TRACE(INFO, "EMM-PROC  - eps_integrity  0x%X", user->emm_data->security->capability.eps_integrity);
+    emm_as->encryption = user->emm_data->security->capability.eps_encryption;
+    emm_as->integrity = user->emm_data->security->capability.eps_integrity;
   }
 
   /*
@@ -244,15 +234,15 @@ int emm_proc_attach(emm_proc_attach_type_t type)
   /* TODO: PDN type should be set according to the IP capability of the UE */
   esm_sap.data.pdn_connect.pdn_type = NET_PDN_TYPE_IPV4;
   esm_sap.data.pdn_connect.apn = NULL;
-  esm_sap.data.pdn_connect.is_emergency = _emm_data.is_emergency;
-  rc = esm_sap_send(&esm_sap);
+  esm_sap.data.pdn_connect.is_emergency = user->emm_data->is_emergency;
+  rc = esm_sap_send(user, &esm_sap);
 
   if (rc != RETURNerror) {
     /* Setup EMM procedure handler to be executed upon receiving
      * lower layer notification */
-    rc = emm_proc_lowerlayer_initialize(emm_proc_attach_request,
+    rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, emm_proc_attach_request,
                                         emm_proc_attach_failure,
-                                        emm_proc_attach_release, NULL);
+                                        emm_proc_attach_release, user);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "Failed to initialize EMM procedure handler");
@@ -260,12 +250,12 @@ int emm_proc_attach(emm_proc_attach_type_t type)
     }
 
     /* Start T3410 timer */
-    T3410.id = nas_timer_start(T3410.sec, _emm_attach_t3410_handler, NULL);
+    emm_timers->T3410.id = nas_timer_start(emm_timers->T3410.sec, emm_attach_t3410_handler, user);
     LOG_TRACE(INFO,"EMM-PROC  - Timer T3410 (%d) expires in %ld seconds",
-              T3410.id, T3410.sec);
+              emm_timers->T3410.id, emm_timers->T3410.sec);
     /* Stop T3402 and T3411 timers if running */
-    T3402.id = nas_timer_stop(T3402.id);
-    T3411.id = nas_timer_stop(T3411.id);
+    emm_timers->T3402.id = nas_timer_stop(emm_timers->T3402.id);
+    emm_timers->T3411.id = nas_timer_stop(emm_timers->T3411.id);
 
     /*
      * Notify EMM-AS SAP that a RRC connection establishment procedure
@@ -276,7 +266,7 @@ int emm_proc_attach(emm_proc_attach_type_t type)
     /* Get the PDN connectivity request to transfer within the ESM
      * container of the initial attach request message */
     emm_sap.u.emm_as.u.establish.NASmsg = esm_sap.send;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN(rc);
@@ -301,7 +291,7 @@ int emm_proc_attach(emm_proc_attach_type_t type)
 int emm_proc_attach_request(void *args)
 {
   LOG_FUNC_IN;
-
+  nas_user_t *user=args;
   emm_sap_t emm_sap;
   int rc;
 
@@ -309,7 +299,7 @@ int emm_proc_attach_request(void *args)
    * Notify EMM that Attach Request has been sent to the network
    */
   emm_sap.primitive = EMMREG_ATTACH_REQ;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -342,10 +332,10 @@ int emm_proc_attach_request(void *args)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data, T3412, T3402, T3423             **
+ **      Others:    user->emm_data-> T3412, T3402, T3423             **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach_accept(long t3412, long t3402, long t3423,
+int emm_proc_attach_accept(nas_user_t *user, long t3412, long t3402, long t3423,
                            int n_tais, tai_t *tai, GUTI_t *guti,
                            int n_eplmns, plmn_t *eplmn,
                            const OctetString *esm_msg_pP)
@@ -357,51 +347,52 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
   int rc;
   int i;
   int j;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(INFO, "EMM-PROC  - EPS attach accepted by the network");
 
   /* Stop timer T3410 */
-  LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-  T3410.id = nas_timer_stop(T3410.id);
+  LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+  emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
 
   /* Delete old TAI list and store the received TAI list */
-  _emm_data.ltai.n_tais = n_tais;
+  user->emm_data->ltai.n_tais = n_tais;
 
   for (i = 0; (i < n_tais) && (i < EMM_DATA_TAI_MAX); i++) {
-    _emm_data.ltai.tai[i] = tai[i];
+    user->emm_data->ltai.tai[i] = tai[i];
   }
 
   /* Update periodic tracking area update timer value */
-  T3412.sec = t3412;
+  emm_timers->T3412.sec = t3412;
 
   /* Update attach failure timer value */
   if ( !(t3402 < 0) ) {
-    T3402.sec = t3402;
+    emm_timers->T3402.sec = t3402;
   }
 
   /* Update E-UTRAN deactivate ISR timer value */
   if ( !(t3423 < 0) ) {
-    T3423.sec = t3423;
+    emm_timers->T3423.sec = t3423;
   }
 
   /* Delete old GUTI and store the new assigned GUTI if provided */
   if (guti) {
-    *_emm_data.guti = *guti;
+    *user->emm_data->guti = *guti;
   }
 
   /* Update the stored list of equivalent PLMNs */
-  _emm_data.nvdata.eplmn.n_plmns = 0;
+  user->emm_data->nvdata.eplmn.n_plmns = 0;
 
   if (n_eplmns > 0) {
     for (i = 0; (i < n_eplmns) && (i < EMM_DATA_EPLMN_MAX); i++) {
       int is_forbidden = FALSE;
 
-      if (!_emm_data.is_emergency) {
+      if (!user->emm_data->is_emergency) {
         /* If the attach procedure is not for emergency bearer
          * services, the UE shall remove from the list any PLMN
          * code that is already in the list of forbidden PLMNs */
-        for (j = 0; j < _emm_data.fplmn.n_plmns; j++) {
-          if (PLMNS_ARE_EQUAL(eplmn[i], _emm_data.fplmn.plmn[j])) {
+        for (j = 0; j < user->emm_data->fplmn.n_plmns; j++) {
+          if (PLMNS_ARE_EQUAL(eplmn[i], user->emm_data->fplmn.plmn[j])) {
             is_forbidden = TRUE;
             break;
           }
@@ -409,15 +400,15 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
       }
 
       if ( !is_forbidden ) {
-        _emm_data.nvdata.eplmn.plmn[_emm_data.nvdata.eplmn.n_plmns++] =
+        user->emm_data->nvdata.eplmn.plmn[user->emm_data->nvdata.eplmn.n_plmns++] =
           eplmn[i];
       }
     }
 
     /* Add the PLMN code of the registered PLMN that sent the list */
-    if (_emm_data.nvdata.eplmn.n_plmns < EMM_DATA_EPLMN_MAX) {
-      _emm_data.nvdata.eplmn.plmn[_emm_data.nvdata.eplmn.n_plmns++] =
-        _emm_data.splmn;
+    if (user->emm_data->nvdata.eplmn.n_plmns < EMM_DATA_EPLMN_MAX) {
+      user->emm_data->nvdata.eplmn.plmn[user->emm_data->nvdata.eplmn.n_plmns++] =
+        user->emm_data->splmn;
     }
   }
 
@@ -427,14 +418,14 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
   esm_sap.primitive = ESM_DEFAULT_EPS_BEARER_CONTEXT_ACTIVATE_REQ;
   esm_sap.is_standalone = FALSE;
   esm_sap.recv = esm_msg_pP;
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   if ( (rc != RETURNerror) && (esm_sap.err == ESM_SAP_SUCCESS) ) {
     /* Setup EMM procedure handler to be executed upon receiving
      * lower layer notification */
-    rc = emm_proc_lowerlayer_initialize(emm_proc_attach_complete,
+    rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, emm_proc_attach_complete,
                                         emm_proc_attach_failure,
-                                        NULL, NULL);
+                                        NULL, user);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING,
@@ -448,17 +439,17 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
      * be sent to the network
      */
     emm_sap.primitive = EMMAS_DATA_REQ;
-    emm_sap.u.emm_as.u.data.guti = _emm_data.guti;
-    emm_sap.u.emm_as.u.data.ueid = 0;
+    emm_sap.u.emm_as.u.data.guti = user->emm_data->guti;
+    emm_sap.u.emm_as.u.data.ueid = user->ueid;
     /* Setup EPS NAS security data */
     emm_as_set_security_data(&emm_sap.u.emm_as.u.data.sctx,
-                             _emm_data.security, FALSE, TRUE);
+                             user->emm_data->security, FALSE, TRUE);
     /* Get the activate default EPS bearer context accept message
      * to be transfered within the ESM container of the attach
      * complete message */
     emm_sap.u.emm_as.u.data.NASinfo = EMM_AS_NAS_DATA_ATTACH;
     emm_sap.u.emm_as.u.data.NASmsg = esm_sap.send;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   } else if (esm_sap.err != ESM_SAP_DISCARDED) {
     /* 3GPP TS 24.301, section 5.5.1.2.6, case j
      * If the ACTIVATE DEFAULT BEARER CONTEXT REQUEST message combined
@@ -467,7 +458,7 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
      * procedure by sending a DETACH REQUEST message to the network.
      */
     emm_sap.primitive = EMMREG_DETACH_INIT;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   } else {
     /*
      * ESM procedure failed and, received message has been discarded or
@@ -497,22 +488,23 @@ int emm_proc_attach_accept(long t3412, long t3402, long t3423,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data, _emm_attach_data, T3410         **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
+int emm_proc_attach_reject(nas_user_t *user, int emm_cause, const OctetString *esm_msg_pP)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
+  emm_attach_data_t *emm_attach_data = user->emm_data->emm_attach_data;
 
   LOG_TRACE(WARNING, "EMM-PROC  - EPS attach rejected by the network, "
             "EMM cause = %d", emm_cause);
 
   /* Stop timer T3410 */
-  LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-  T3410.id = nas_timer_stop(T3410.id);
+  LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+  emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
 
   /* Update the EPS update status, the GUTI, the visited registered TAI and
    * the eKSI */
@@ -528,15 +520,15 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
   case EMM_CAUSE_ROAMING_NOT_ALLOWED:
   case EMM_CAUSE_NO_SUITABLE_CELLS:
     /* Set the EPS update status to EU3 ROAMING NOT ALLOWED */
-    _emm_data.status = EU3_ROAMING_NOT_ALLOWED;
+    user->emm_data->status = EU3_ROAMING_NOT_ALLOWED;
     /* Delete the GUTI */
-    _emm_data.guti = NULL;
+    user->emm_data->guti = NULL;
     /* Delete the last visited registered TAI */
-    _emm_data.tai = NULL;
+    user->emm_data->tai = NULL;
 
     /* Delete the eKSI */
-    if (_emm_data.security) {
-      _emm_data.security->type = EMM_KSI_NOT_AVAILABLE;
+    if (user->emm_data->security) {
+      user->emm_data->security->type = EMM_KSI_NOT_AVAILABLE;
     }
 
     break;
@@ -552,34 +544,34 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
   case EMM_CAUSE_EPS_NOT_ALLOWED:
   case EMM_CAUSE_BOTH_NOT_ALLOWED:
     /* Consider the USIM as invalid for EPS services */
-    _emm_data.usim_is_valid = FALSE;
+    user->emm_data->usim_is_valid = FALSE;
     /* Delete the list of equivalent PLMNs */
-    _emm_data.nvdata.eplmn.n_plmns = 0;
+    user->emm_data->nvdata.eplmn.n_plmns = 0;
     break;
 
   case EMM_CAUSE_PLMN_NOT_ALLOWED:
   case EMM_CAUSE_NOT_AUTHORIZED_IN_PLMN:
   case EMM_CAUSE_ROAMING_NOT_ALLOWED:
     /* Delete the list of equivalent PLMNs */
-    _emm_data.nvdata.eplmn.n_plmns = 0;
+    user->emm_data->nvdata.eplmn.n_plmns = 0;
     /* Reset the attach attempt counter */
-    _emm_attach_data.attempt_count = 0;
+    emm_attach_data->attempt_count = 0;
     break;
 
   case EMM_CAUSE_TA_NOT_ALLOWED:
   case EMM_CAUSE_EPS_NOT_ALLOWED_IN_PLMN:
   case EMM_CAUSE_NO_SUITABLE_CELLS:
     /* Reset the attach attempt counter */
-    _emm_attach_data.attempt_count = 0;
+    emm_attach_data->attempt_count = 0;
     break;
 
   case EMM_CAUSE_ESM_FAILURE:
 
     /* 3GPP TS 24.301, section 5.5.1.2.6, case d */
-    if (_emm_data.NAS_SignallingPriority != 1) {
+    if (user->emm_data->NAS_SignallingPriority != 1) {
       /* The UE is not configured for NAS signalling low priority;
        * set the attach attempt counter to 5 */
-      _emm_attach_data.attempt_count = EMM_ATTACH_COUNTER_MAX;
+      emm_attach_data->attempt_count = EMM_ATTACH_COUNTER_MAX;
     }
 
     break;
@@ -591,7 +583,7 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
   case EMM_CAUSE_PROTOCOL_ERROR:
     /* 3GPP TS 24.301, section 5.5.1.2.6, case d
      * Set the attach attempt counter to 5 */
-    _emm_attach_data.attempt_count = EMM_ATTACH_COUNTER_MAX;
+    emm_attach_data->attempt_count = EMM_ATTACH_COUNTER_MAX;
     break;
 
   default :
@@ -603,25 +595,25 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
   case EMM_CAUSE_PLMN_NOT_ALLOWED:
   case EMM_CAUSE_NOT_AUTHORIZED_IN_PLMN:
     /* Store the PLMN identity in the "forbidden PLMN list" */
-    _emm_data.fplmn.plmn[_emm_data.fplmn.n_plmns++] = _emm_data.splmn;
+    user->emm_data->fplmn.plmn[user->emm_data->fplmn.n_plmns++] = user->emm_data->splmn;
     break;
 
   case EMM_CAUSE_TA_NOT_ALLOWED:
     /* Store the current TAI in the list of "forbidden tracking
      * areas for regional provision of service" */
-    _emm_data.ftai.tai[_emm_data.ftai.n_tais++] = *_emm_data.tai;
+    user->emm_data->ftai.tai[user->emm_data->ftai.n_tais++] = *user->emm_data->tai;
     break;
 
   case EMM_CAUSE_ROAMING_NOT_ALLOWED:
     /* Store the current TAI in the list of "forbidden tracking
      * areas for roaming" */
-    _emm_data.ftai_roaming.tai[_emm_data.ftai_roaming.n_tais++] = *_emm_data.tai;
+    user->emm_data->ftai_roaming.tai[user->emm_data->ftai_roaming.n_tais++] = *user->emm_data->tai;
     break;
 
   case EMM_CAUSE_EPS_NOT_ALLOWED_IN_PLMN:
     /* Store the PLMN identity in the "forbidden PLMNs for GPRS
      * service" list */
-    _emm_data.fplmn_gprs.plmn[_emm_data.fplmn_gprs.n_plmns++] = _emm_data.splmn;
+    user->emm_data->fplmn_gprs.plmn[user->emm_data->fplmn_gprs.n_plmns++] = user->emm_data->splmn;
     break;
 
   default :
@@ -662,7 +654,7 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
     break;
 
   case EMM_CAUSE_IMEI_NOT_ACCEPTED:
-    if (_emm_data.is_emergency) {
+    if (user->emm_data->is_emergency) {
       /*
        * Notify EMM that the UE failed to register to the network
        * for emergency bearer services because "IMEI not accepted"
@@ -676,11 +668,11 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
   default :
     /* Other values are considered as abnormal cases
      * 3GPP TS 24.301, section 5.5.1.2.6, case d */
-    _emm_attach_abnormal_cases_bcd(&emm_sap);
+    _emm_attach_abnormal_cases_bcd(user, &emm_sap);
     break;
   }
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   /*
    * Notify ESM that the network rejected connectivity to the PDN
@@ -690,7 +682,7 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
     esm_sap.primitive = ESM_PDN_CONNECTIVITY_REJ;
     esm_sap.is_standalone = FALSE;
     esm_sap.recv = esm_msg_pP;
-    rc = esm_sap_send(&esm_sap);
+    rc = esm_sap_send(user, &esm_sap);
   }
 
   LOG_FUNC_RETURN(rc);
@@ -714,13 +706,14 @@ int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg_pP)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data, _emm_attach_data                **
  **                                                                        **
  ***************************************************************************/
 int emm_proc_attach_complete(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user = args;
+  emm_attach_data_t *emm_attach_data = user->emm_data->emm_attach_data;
   emm_sap_t emm_sap;
   esm_sap_t esm_sap;
   int rc;
@@ -728,22 +721,22 @@ int emm_proc_attach_complete(void *args)
   LOG_TRACE(INFO, "EMM-PROC  - EPS attach complete");
 
   /* Reset EMM procedure handler */
-  (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   /* Reset the attach attempt counter */
-  _emm_attach_data.attempt_count = 0;
+  emm_attach_data->attempt_count = 0;
   /* TODO: Reset the tracking area updating attempt counter */
 
   /* Set the EPS update status to EU1 UPDATED */
-  _emm_data.status = EU1_UPDATED;
-  _emm_data.is_attached = TRUE;
+  user->emm_data->status = EU1_UPDATED;
+  user->emm_data->is_attached = TRUE;
 
   /*
    * Notify EMM that network attach complete message has been delivered
    * to the network
    */
   emm_sap.primitive = EMMREG_ATTACH_CNF;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /*
@@ -753,7 +746,7 @@ int emm_proc_attach_complete(void *args)
      */
     esm_sap.primitive = ESM_DEFAULT_EPS_BEARER_CONTEXT_ACTIVATE_CNF;
     esm_sap.is_standalone = FALSE;
-    rc = esm_sap_send(&esm_sap);
+    rc = esm_sap_send(user, &esm_sap);
   }
 
   LOG_FUNC_RETURN(rc);
@@ -786,19 +779,20 @@ int emm_proc_attach_complete(void *args)
 int emm_proc_attach_failure(int is_initial, void *args)
 {
   LOG_FUNC_IN;
-
   int rc = RETURNok;
   esm_sap_t esm_sap;
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - EPS attach failure");
 
   /* Reset EMM procedure handler */
-  (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   /* Stop timer T3410 if still running */
-  if (T3410.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-    T3410.id = nas_timer_stop(T3410.id);
+  if (emm_timers->T3410.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+    emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
   }
 
   if (is_initial) {
@@ -821,13 +815,13 @@ int emm_proc_attach_failure(int is_initial, void *args)
     esm_sap.recv = NULL;
   }
 
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   if (rc != RETURNerror) {
     /* Start T3411 timer */
-    T3411.id = nas_timer_start(T3411.sec, _emm_attach_t3411_handler, NULL);
+    emm_timers->T3411.id = nas_timer_start(emm_timers->T3411.sec, _emm_attach_t3411_handler, NULL);
     LOG_TRACE(INFO, "EMM-PROC  - Timer T3411 (%d) expires in %ld seconds",
-              T3411.id, T3411.sec);
+              emm_timers->T3411.id, emm_timers->T3411.sec);
   }
 
   LOG_FUNC_RETURN(rc);
@@ -855,16 +849,16 @@ int emm_proc_attach_failure(int is_initial, void *args)
 int emm_proc_attach_release(void *args)
 {
   LOG_FUNC_IN;
-
+  nas_user_t *user=args;
   emm_sap_t emm_sap;
   int rc;
 
   LOG_TRACE(WARNING, "EMM-PROC  - NAS signalling connection released");
 
   /* Execute abnormal case attach procedure */
-  _emm_attach_abnormal_cases_bcd(&emm_sap);
+  _emm_attach_abnormal_cases_bcd(user, &emm_sap);
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -883,7 +877,7 @@ int emm_proc_attach_release(void *args)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach_restart(void)
+int emm_proc_attach_restart(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -896,8 +890,8 @@ int emm_proc_attach_restart(void)
    * Notify EMM that the attach procedure has to be restarted
    */
   emm_sap.primitive = EMMREG_ATTACH_INIT;
-  emm_sap.u.emm_reg.u.attach.is_emergency = _emm_data.is_emergency;
-  rc = emm_sap_send(&emm_sap);
+  emm_sap.u.emm_reg.u.attach.is_emergency = user->emm_data->is_emergency;
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -913,17 +907,17 @@ int emm_proc_attach_restart(void)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach_set_emergency(void)
+int emm_proc_attach_set_emergency(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
 
   LOG_TRACE(WARNING, "EMM-PROC  - UE is now attached to the network for "
             "emergency bearer services only");
 
-  _emm_data.is_emergency = TRUE;
+  emm_data->is_emergency = TRUE;
 
   LOG_FUNC_RETURN(RETURNok);
 }
@@ -940,26 +934,27 @@ int emm_proc_attach_set_emergency(void)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_attach_set_detach(void)
+int emm_proc_attach_set_detach(void *nas_user)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user=nas_user;
   int rc;
 
   LOG_TRACE(WARNING,
             "EMM-PROC  - UE is now locally detached from the network");
 
   /* Reset the network attachment indicator */
-  _emm_data.is_attached = FALSE;
+  user->emm_data->is_attached = FALSE;
   /*
    * Notify that the UE is locally detached from the network
    */
   emm_sap_t emm_sap;
   emm_sap.primitive = EMMREG_DETACH_CNF;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -995,25 +990,27 @@ int emm_proc_attach_set_detach(void)
  **      Others:    T3410                                      **
  **                                                                        **
  ***************************************************************************/
-void *_emm_attach_t3410_handler(void *args)
+void *emm_attach_t3410_handler(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   emm_sap_t emm_sap;
   int rc;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3410 timer expired");
 
   /* Stop T3410 timer */
-  T3410.id = nas_timer_stop(T3410.id);
+  emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
   /* Execute abnormal case attach procedure */
-  _emm_attach_abnormal_cases_bcd(&emm_sap);
+  _emm_attach_abnormal_cases_bcd(user, &emm_sap);
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /* Locally release the NAS signalling connection */
-    _emm_data.ecm_status = ECM_IDLE;
+    user->emm_data->ecm_status = ECM_IDLE;
   }
 
   LOG_FUNC_RETURN(NULL);
@@ -1041,20 +1038,22 @@ static void *_emm_attach_t3411_handler(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   emm_sap_t emm_sap;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3411 timer expired");
 
   /* Stop T3411 timer */
-  T3411.id = nas_timer_stop(T3411.id);
+  emm_timers->T3411.id = nas_timer_stop(emm_timers->T3411.id);
   /*
    * Notify EMM that timer T3411 expired and attach procedure has to be
    * restarted
    */
   emm_sap.primitive = EMMREG_ATTACH_INIT;
-  emm_sap.u.emm_reg.u.attach.is_emergency = _emm_data.is_emergency;
+  emm_sap.u.emm_reg.u.attach.is_emergency = user->emm_data->is_emergency;
 
-  (void) emm_sap_send(&emm_sap);
+  (void) emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(NULL);
 }
@@ -1078,29 +1077,31 @@ static void *_emm_attach_t3411_handler(void *args)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _emm_attach_data, T3402                    **
  **                                                                        **
  ***************************************************************************/
 static void *_emm_attach_t3402_handler(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user = args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
+  emm_attach_data_t *emm_attach_data = user->emm_data->emm_attach_data;
   emm_sap_t emm_sap;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3402 timer expired");
 
   /* Stop T3402 timer */
-  T3402.id = nas_timer_stop(T3402.id);
+  emm_timers->T3402.id = nas_timer_stop(emm_timers->T3402.id);
   /* Reset the attach attempt counter */
-  _emm_attach_data.attempt_count = 0;
+  emm_attach_data->attempt_count = 0;
   /*
    * Notify EMM that timer T3402 expired and attach procedure has to be
    * restarted
    */
   emm_sap.primitive = EMMREG_ATTACH_INIT;
-  emm_sap.u.emm_reg.u.attach.is_emergency = _emm_data.is_emergency;
+  emm_sap.u.emm_reg.u.attach.is_emergency = user->emm_data->is_emergency;
 
-  (void) emm_sap_send(&emm_sap);
+  (void) emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(NULL);
 }
@@ -1128,30 +1129,30 @@ static void *_emm_attach_t3402_handler(void *args)
  **                                                                        **
  ** Outputs:     emm_sap:   EMM service access point                   **
  **      Return:    None                                       **
- **      Others:    _emm_data, _emm_attach_data, T3402, T3410, **
  **             T3411                                      **
  **                                                                        **
  ***************************************************************************/
-static void _emm_attach_abnormal_cases_bcd(emm_sap_t *emm_sap)
+static void _emm_attach_abnormal_cases_bcd(nas_user_t *user, emm_sap_t *emm_sap)
 {
   LOG_FUNC_IN;
-
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
+  emm_attach_data_t *emm_attach_data = user->emm_data->emm_attach_data;
   LOG_TRACE(WARNING, "EMM-PROC  - Abnormal case, attach counter = %d",
-            _emm_attach_data.attempt_count);
+            emm_attach_data->attempt_count);
 
   /* Stop timer T3410 */
-  if (T3410.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-    T3410.id = nas_timer_stop(T3410.id);
+  if (emm_timers->T3410.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+    emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
   }
 
-  if (_emm_attach_data.attempt_count < EMM_ATTACH_COUNTER_MAX) {
+  if (emm_attach_data->attempt_count < EMM_ATTACH_COUNTER_MAX) {
     /* Increment the attach attempt counter */
-    _emm_attach_data.attempt_count += 1;
+    emm_attach_data->attempt_count += 1;
     /* Start T3411 timer */
-    T3411.id = nas_timer_start(T3411.sec, _emm_attach_t3411_handler, NULL);
+    emm_timers->T3411.id = nas_timer_start(emm_timers->T3411.sec, _emm_attach_t3411_handler, NULL);
     LOG_TRACE(INFO, "EMM-PROC  - Timer T3411 (%d) expires in %ld seconds",
-              T3411.id, T3411.sec);
+              emm_timers->T3411.id, emm_timers->T3411.sec);
     /*
      * Notify EMM that the attempt to attach for EPS services failed and
      * the attach attempt counter didn't reach its maximum value; network
@@ -1160,26 +1161,26 @@ static void _emm_attach_abnormal_cases_bcd(emm_sap_t *emm_sap)
     emm_sap->primitive = EMMREG_ATTACH_FAILED;
   } else {
     /* Delete the GUTI */
-    _emm_data.guti = NULL;
+    user->emm_data->guti = NULL;
     /* Delete the TAI list */
-    _emm_data.ltai.n_tais = 0;
+    user->emm_data->ltai.n_tais = 0;
     /* Delete the last visited registered TAI */
-    _emm_data.tai = NULL;
+    user->emm_data->tai = NULL;
     /* Delete the list of equivalent PLMNs */
-    _emm_data.nvdata.eplmn.n_plmns = 0;
+    user->emm_data->nvdata.eplmn.n_plmns = 0;
 
     /* Delete the eKSI */
-    if (_emm_data.security) {
-      _emm_data.security->type = EMM_KSI_NOT_AVAILABLE;
+    if (user->emm_data->security) {
+      user->emm_data->security->type = EMM_KSI_NOT_AVAILABLE;
     }
 
     /* Set the EPS update status to EU2 NOT UPDATED */
-    _emm_data.status = EU2_NOT_UPDATED;
+    user->emm_data->status = EU2_NOT_UPDATED;
 
     /* Start T3402 timer */
-    T3402.id = nas_timer_start(T3402.sec, _emm_attach_t3402_handler, NULL);
+    emm_timers->T3402.id = nas_timer_start(emm_timers->T3402.sec, _emm_attach_t3402_handler, user);
     LOG_TRACE(INFO, "EMM-PROC  - Timer T3402 (%d) expires in %ld seconds",
-              T3402.id, T3402.sec);
+              emm_timers->T3402.id, emm_timers->T3402.sec);
     /*
      * Notify EMM that the attempt to attach for EPS services failed and
      * the attach attempt counter reached its maximum value.
diff --git a/openair3/NAS/UE/EMM/Authentication.c b/openair3/NAS/UE/EMM/Authentication.c
index 9022acbdac8d1a3cc4e6f9602dacb2572b79a323..e49bdd2db7e5e36aa52835dd58b8f477bd3317a9 100644
--- a/openair3/NAS/UE/EMM/Authentication.c
+++ b/openair3/NAS/UE/EMM/Authentication.c
@@ -59,26 +59,21 @@ Description Defines the authentication EMM procedure executed by the
 #include "nas_timer.h"
 
 #include "emmData.h"
+#include "emm_timers.h"
 
 #include "emm_sap.h"
 #include "emm_cause.h"
+#include "emm_timers.h"
 
 #include "usim_api.h"
 #include "secu_defs.h"
+#include "Authentication.h"
 
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
-/*
- * Retransmission timer handlers
- */
-extern void *_emm_attach_t3410_handler(void *);
-extern void *_emm_service_t3417_handler(void *);
-extern void *_emm_detach_t3421_handler(void *);
-extern void *_emm_tau_t3430_handler(void *);
-
 extern uint8_t usim_test;
 
 /****************************************************************************/
@@ -97,34 +92,15 @@ static void *_authentication_t3416_handler(void *);
 static void *_authentication_t3418_handler(void *);
 static void *_authentication_t3420_handler(void *);
 
-/*
- * Internal data used for authentication procedure
- */
-static struct {
-  uint8_t rand[AUTH_RAND_SIZE];   /* Random challenge number  */
-  uint8_t res[AUTH_RES_SIZE];     /* Authentication response  */
-  uint8_t ck[AUTH_CK_SIZE];       /* Ciphering key        */
-  uint8_t ik[AUTH_IK_SIZE];       /* Integrity key        */
-#define AUTHENTICATION_T3410    0x01
-#define AUTHENTICATION_T3417    0x02
-#define AUTHENTICATION_T3421    0x04
-#define AUTHENTICATION_T3430    0x08
-  unsigned char timers;       /* Timer restart bitmap     */
-#define AUTHENTICATION_COUNTER_MAX 3
-  unsigned char mac_count:2;  /* MAC failure counter (#20)        */
-  unsigned char umts_count:2; /* UMTS challenge failure counter (#26) */
-  unsigned char sync_count:2; /* Sync failure counter (#21)       */
-} _authentication_data;
-
 /*
  * Abnormal case authentication procedure
  */
-static int _authentication_abnormal_cases_cde(int emm_cause,
+static int _authentication_abnormal_cases_cde(nas_user_t *user, int emm_cause,
     const OctetString *auts);
-static int _authentication_abnormal_case_f(void);
+static int _authentication_abnormal_case_f(nas_user_t *user);
 
-static int _authentication_stop_timers(void);
-static int _authentication_start_timers(void);
+static int _authentication_stop_timers(nas_user_t *user);
+static int _authentication_start_timers(nas_user_t *user);
 static int _authentication_kasme(const OctetString *autn,
                                  const OctetString *ck, const OctetString *ik, const plmn_t *plmn,
                                  OctetString *kasme);
@@ -163,21 +139,21 @@ static int _authentication_kasme(const OctetString *autn,
  **      ksi:       The NAS ket sey identifier                 **
  **      rand:      Authentication parameter RAND              **
  **      autn:      Authentication parameter AUTN              **
- **      Others:    _emm_data, _authentication_data            **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data, _authentication_data, T3416,    **
  **             T3418, T3420                               **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_authentication_request(int native_ksi, int ksi,
+int emm_proc_authentication_request(nas_user_t *user, int native_ksi, int ksi,
                                     const OctetString *rand,
                                     const OctetString *autn)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(INFO, "EMM-PROC  - Authentication requested ksi type = %s, ksi = %d", native_ksi ? "native" : "mapped", ksi);
 
@@ -185,29 +161,29 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
    * The UE shall proceed with an EPS authentication challenge only if a
    * USIM is present
    */
-  if (!_emm_data.usim_is_valid) {
+  if (!user->emm_data->usim_is_valid) {
     LOG_TRACE(WARNING, "EMM-PROC  - USIM is not present or not valid");
     LOG_FUNC_RETURN (RETURNerror);
   }
 
   /* Stop timer T3418, if running */
-  if (T3418.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3418 (%d)", T3418.id);
-    T3418.id = nas_timer_stop(T3418.id);
+  if (emm_timers->T3418.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3418 (%d)", emm_timers->T3418.id);
+    emm_timers->T3418.id = nas_timer_stop(emm_timers->T3418.id);
   }
 
   /* Stop timer T3420, if running */
-  if (T3420.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3420 (%d)", T3420.id);
-    T3420.id = nas_timer_stop(T3420.id);
+  if (emm_timers->T3420.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3420 (%d)", emm_timers->T3420.id);
+    emm_timers->T3420.id = nas_timer_stop(emm_timers->T3420.id);
   }
 
   /* Setup security keys */
-  OctetString ck = {AUTH_CK_SIZE, _authentication_data.ck};
-  OctetString ik = {AUTH_IK_SIZE, _authentication_data.ik};
-  OctetString res = {AUTH_RES_SIZE, _authentication_data.res};
+  OctetString ck = {AUTH_CK_SIZE, authentication_data->ck};
+  OctetString ik = {AUTH_IK_SIZE, authentication_data->ik};
+  OctetString res = {AUTH_RES_SIZE, authentication_data->res};
 
-  if (memcmp(_authentication_data.rand, rand->value, AUTH_CK_SIZE) != 0) {
+  if (memcmp(authentication_data->rand, rand->value, AUTH_CK_SIZE) != 0) {
     /*
      * There is no valid stored RAND in the ME or the stored RAND is
      * different from the new received value in the AUTHENTICATION
@@ -237,11 +213,11 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
        */
       if(usim_test == 0)
       {
-        rc = usim_api_authenticate(rand, autn, &auts, &res, &ck, &ik);
+        rc = usim_api_authenticate(&user->usim_data, rand, autn, &auts, &res, &ck, &ik);
       }
       else
       {
-        rc = usim_api_authenticate_test(rand, autn, &auts, &res, &ck, &ik); // XOR algo for autentication on usim test mode
+        rc = usim_api_authenticate_test(&user->usim_data, rand, autn, &auts, &res, &ck, &ik);
       }
     }
 
@@ -254,24 +230,24 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
                 (sbit == 0) ? "Non-EPS authentication unacceptable" :
                 "MAC code failure");
       /* Delete any previously stored RAND and RES and stop timer T3416 */
-      (void) emm_proc_authentication_delete();
+      emm_proc_authentication_delete(user);
 
       /* Proceed authentication abnormal cases procedure */
       if (auts.length > 0) {
         /* 3GPP TS 24.301, section 5.4.2.6, case e
          * SQN failure */
         rc = _authentication_abnormal_cases_cde(
-               EMM_CAUSE_SYNCH_FAILURE, &auts);
+               user, EMM_CAUSE_SYNCH_FAILURE, &auts);
       } else if (sbit == 0) {
         /* 3GPP TS 24.301, section 5.4.2.6, case d
          * Non-EPS authentication unacceptable */
         rc = _authentication_abnormal_cases_cde(
-               EMM_CAUSE_NON_EPS_AUTH_UNACCEPTABLE, NULL);
+               user, EMM_CAUSE_NON_EPS_AUTH_UNACCEPTABLE, NULL);
       } else {
         /* 3GPP TS 24.301, section 5.4.2.6, case c
          * MAC code failure */
         rc = _authentication_abnormal_cases_cde(
-               EMM_CAUSE_MAC_FAILURE, NULL);
+               user, EMM_CAUSE_MAC_FAILURE, NULL);
       }
 
       /* Free the AUTS parameter */
@@ -284,18 +260,18 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
 
     /* Store the new RAND in the volatile memory */
     if (rand->length <= AUTH_RAND_SIZE) {
-      memcpy(_authentication_data.rand, rand->value, rand->length);
+      memcpy(authentication_data->rand, rand->value, rand->length);
     }
 
     /* Start, or reset and restart timer T3416 */
-    if (T3416.id != NAS_TIMER_INACTIVE_ID) {
-      LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3416 (%d)", T3416.id);
-      T3416.id = nas_timer_stop(T3416.id);
+    if (emm_timers->T3416.id != NAS_TIMER_INACTIVE_ID) {
+      LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3416 (%d)", emm_timers->T3416.id);
+      emm_timers->T3416.id = nas_timer_stop(emm_timers->T3416.id);
     }
 
-    T3416.id = nas_timer_start(T3416.sec, _authentication_t3416_handler, NULL);
+    emm_timers->T3416.id = nas_timer_start(emm_timers->T3416.sec, _authentication_t3416_handler, NULL);
     LOG_TRACE(INFO, "EMM-PROC  - Timer T3416 (%d) expires in  %ld seconds",
-              T3416.id, T3416.sec);
+              emm_timers->T3416.id, emm_timers->T3416.sec);
   }
 
   /*
@@ -304,7 +280,7 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
    * the authenticity of the core network
    */
   /* Start any retransmission timers */
-  rc = _authentication_start_timers();
+  rc = _authentication_start_timers(user);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING, "EMM-PROC  - Failed to start retransmission timers");
@@ -313,7 +289,7 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
 
   /* Setup EMM procedure handler to be executed upon receiving
    * lower layer notification */
-  rc = emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING,
@@ -327,46 +303,46 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
    */
   emm_sap_t emm_sap;
   emm_sap.primitive = EMMAS_SECURITY_RES;
-  emm_sap.u.emm_as.u.security.guti = _emm_data.guti;
-  emm_sap.u.emm_as.u.security.ueid = 0;
+  emm_sap.u.emm_as.u.security.guti = user->emm_data->guti;
+  emm_sap.u.emm_as.u.security.ueid = user->ueid;
   emm_sap.u.emm_as.u.security.msgType = EMM_AS_MSG_TYPE_AUTH;
   emm_sap.u.emm_as.u.security.emm_cause = EMM_CAUSE_SUCCESS;
   emm_sap.u.emm_as.u.security.res = &res;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.security.sctx,
-                           _emm_data.security, FALSE, TRUE);
-  rc = emm_sap_send(&emm_sap);
+                           user->emm_data->security, FALSE, TRUE);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /* Reset the authentication failure counters */
-    _authentication_data.mac_count = 0;
-    _authentication_data.umts_count = 0;
-    _authentication_data.sync_count = 0;
+    authentication_data->mac_count = 0;
+    authentication_data->umts_count = 0;
+    authentication_data->sync_count = 0;
 
     /* Create non-current EPS security context */
-    if (_emm_data.non_current == NULL) {
-      _emm_data.non_current =
+    if (user->emm_data->non_current == NULL) {
+      user->emm_data->non_current =
         (emm_security_context_t *)malloc(sizeof(emm_security_context_t));
     }
 
-    if (_emm_data.non_current) {
-      memset(_emm_data.non_current, 0, sizeof(emm_security_context_t));
+    if (user->emm_data->non_current) {
+      memset(user->emm_data->non_current, 0, sizeof(emm_security_context_t));
 
       /* Set the security context type */
       if (native_ksi) {
-        _emm_data.non_current->type = EMM_KSI_NATIVE;
+        user->emm_data->non_current->type = EMM_KSI_NATIVE;
       } else {
-        _emm_data.non_current->type = EMM_KSI_MAPPED;
+        user->emm_data->non_current->type = EMM_KSI_MAPPED;
       }
 
       /* Set the EPS key set identifier */
-      _emm_data.non_current->eksi = ksi;
+      user->emm_data->non_current->eksi = ksi;
       /* Derive the Kasme from the authentication challenge using
        * the PLMN identity of the selected PLMN */
-      _emm_data.non_current->kasme.length = AUTH_KASME_SIZE;
-      _emm_data.non_current->kasme.value  = malloc(32);
-      _authentication_kasme(autn, &ck, &ik, &_emm_data.splmn,
-                            &_emm_data.non_current->kasme);
+      user->emm_data->non_current->kasme.length = AUTH_KASME_SIZE;
+      user->emm_data->non_current->kasme.value  = malloc(32);
+      _authentication_kasme(autn, &ck, &ik, &user->emm_data->splmn,
+                            &user->emm_data->non_current->kasme);
       /* NAS integrity and cyphering keys are not yet available */
     }
   }
@@ -392,66 +368,67 @@ int emm_proc_authentication_request(int native_ksi, int ksi,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data, _authentication_data, T3410,    **
  **             T3417, T3430                               **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_authentication_reject(void)
+int emm_proc_authentication_reject(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - Authentication not accepted by the network");
 
   /* Delete any previously stored RAND and RES and stop timer T3416 */
-  (void) emm_proc_authentication_delete();
+  (void) emm_proc_authentication_delete(user);
 
   /* Set the EPS update status to EU3 ROAMING NOT ALLOWED */
-  _emm_data.status = EU3_ROAMING_NOT_ALLOWED;
+  user->emm_data->status = EU3_ROAMING_NOT_ALLOWED;
   /* Delete the stored GUTI */
-  _emm_data.guti = NULL;
+  user->emm_data->guti = NULL;
   /* Delete the TAI list */
-  _emm_data.ltai.n_tais = 0;
+  user->emm_data->ltai.n_tais = 0;
   /* Delete the last visited registered TAI */
-  _emm_data.tai = NULL;
+  user->emm_data->tai = NULL;
 
   /* Delete the eKSI */
-  if (_emm_data.security) {
-    _emm_data.security->type = EMM_KSI_NOT_AVAILABLE;
+  if (user->emm_data->security) {
+    user->emm_data->security->type = EMM_KSI_NOT_AVAILABLE;
   }
 
   /* Consider the USIM invalid */
-  _emm_data.usim_is_valid = FALSE;
+  user->emm_data->usim_is_valid = FALSE;
 
   /* Stop timer T3410 */
-  if (T3410.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-    T3410.id = nas_timer_stop(T3410.id);
+  if (emm_timers->T3410.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+    emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
   }
 
   /* Stop timer T3417 */
-  if (T3417.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3417 (%d)", T3417.id);
-    T3417.id = nas_timer_stop(T3417.id);
+  if (emm_timers->T3417.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3417 (%d)", emm_timers->T3417.id);
+    emm_timers->T3417.id = nas_timer_stop(emm_timers->T3417.id);
   }
 
   /* Stop timer T3430 */
-  if (T3430.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3430 (%d)", T3430.id);
-    T3430.id = nas_timer_stop(T3430.id);
+  if (emm_timers->T3430.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3430 (%d)", emm_timers->T3430.id);
+    emm_timers->T3430.id = nas_timer_stop(emm_timers->T3430.id);
   }
 
   /* Abort any EMM signalling procedure (prevent the retransmission timers to
    * be restarted) */
-  _authentication_data.timers = 0x00;
+  authentication_data->timers = 0x00;
 
   /*
    * Notify EMM that authentication is not accepted by the network
    */
   emm_sap.primitive = EMMREG_AUTH_REJ;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -475,24 +452,25 @@ int emm_proc_authentication_reject(void)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _authentication_data, T3416                **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_authentication_delete(void)
+int emm_proc_authentication_delete(nas_user_t *user)
 {
   LOG_FUNC_IN;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(INFO, "EMM-PROC  - Delete authentication data RAND and RES");
 
   /* Stop timer T3416, if running */
-  if (T3416.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3416 (%d)", T3416.id);
-    T3416.id = nas_timer_stop(T3416.id);
+  if (emm_timers->T3416.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3416 (%d)", emm_timers->T3416.id);
+    emm_timers->T3416.id = nas_timer_stop(emm_timers->T3416.id);
   }
 
   /* Delete any previously stored RAND and RES */
-  memset(_authentication_data.rand, 0, AUTH_RAND_SIZE);
-  memset(_authentication_data.res, 0, AUTH_RES_SIZE);
+  memset(authentication_data->rand, 0, AUTH_RAND_SIZE);
+  memset(authentication_data->res, 0, AUTH_RES_SIZE);
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -529,13 +507,15 @@ int emm_proc_authentication_delete(void)
 static void *_authentication_t3416_handler(void *args)
 {
   LOG_FUNC_IN;
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3416 timer expired");
 
   /* Stop timer T3416 */
-  T3416.id = nas_timer_stop(T3416.id);
+  emm_timers->T3416.id = nas_timer_stop(emm_timers->T3416.id);
   /* Delete previouly stored RAND and RES authentication data */
-  (void) emm_proc_authentication_delete();
+  (void) emm_proc_authentication_delete(user);
 
   LOG_FUNC_RETURN (NULL);
 }
@@ -556,7 +536,6 @@ static void *_authentication_t3416_handler(void *args)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _authentication_data, T3418                **
  **                                                                        **
  ***************************************************************************/
 static void *_authentication_t3418_handler(void *args)
@@ -564,16 +543,19 @@ static void *_authentication_t3418_handler(void *args)
   LOG_FUNC_IN;
 
   int rc;
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
+  authentication_data_t *authentication_data = user->authentication_data;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3418 timer expired");
 
   /* Stop timer T3418 */
-  T3418.id = nas_timer_stop(T3418.id);
+  emm_timers->T3418.id = nas_timer_stop(emm_timers->T3418.id);
   /* Reset the MAC failure and UMTS challenge failure counters */
-  _authentication_data.mac_count = 0;
-  _authentication_data.umts_count = 0;
+  authentication_data->mac_count = 0;
+  authentication_data->umts_count = 0;
   /* 3GPP TS 24.301, section 5.4.2.7, case f */
-  rc = _authentication_abnormal_case_f();
+  rc = _authentication_abnormal_case_f(user);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING, "EMM-PROC  - Failed to proceed abnormal case f");
@@ -597,7 +579,6 @@ static void *_authentication_t3418_handler(void *args)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _authentication_data, T3420                **
  **                                                                        **
  ***************************************************************************/
 static void *_authentication_t3420_handler(void *args)
@@ -605,15 +586,18 @@ static void *_authentication_t3420_handler(void *args)
   LOG_FUNC_IN;
 
   int rc;
+  nas_user_t *user=args;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3420 timer expired");
 
   /* Stop timer T3420 */
-  T3420.id = nas_timer_stop(T3420.id);
+  emm_timers->T3420.id = nas_timer_stop(emm_timers->T3420.id);
   /* Reset the sync failure counter */
-  _authentication_data.sync_count = 0;
+  authentication_data->sync_count = 0;
   /* 3GPP TS 24.301, section 5.4.2.7, case f */
-  rc = _authentication_abnormal_case_f();
+  rc = _authentication_abnormal_case_f(user);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING, "EMM-PROC  - Failed to proceed abnormal case f");
@@ -641,20 +625,21 @@ static void *_authentication_t3420_handler(void *args)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _authentication_data, T3418, T3420         **
  **                                                                        **
  ***************************************************************************/
-static int _authentication_abnormal_cases_cde(int emm_cause,
+static int _authentication_abnormal_cases_cde(nas_user_t *user, int emm_cause,
     const OctetString *auts)
 {
   LOG_FUNC_IN;
 
   int rc;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - "
             "Abnormal case, authentication counters c/d/e = %d/%d/%d",
-            _authentication_data.mac_count, _authentication_data.umts_count,
-            _authentication_data.sync_count);
+            authentication_data->mac_count, authentication_data->umts_count,
+            authentication_data->sync_count);
 
   /*
    * Notify EMM-AS SAP that Authentication Failure message has to be sent
@@ -662,15 +647,15 @@ static int _authentication_abnormal_cases_cde(int emm_cause,
    */
   emm_sap_t emm_sap;
   emm_sap.primitive = EMMAS_SECURITY_RES;
-  emm_sap.u.emm_as.u.security.guti = _emm_data.guti;
-  emm_sap.u.emm_as.u.security.ueid = 0;
+  emm_sap.u.emm_as.u.security.guti = user->emm_data->guti;
+  emm_sap.u.emm_as.u.security.ueid = user->ueid;
   emm_sap.u.emm_as.u.security.msgType = EMM_AS_MSG_TYPE_AUTH;
   emm_sap.u.emm_as.u.security.emm_cause = emm_cause;
   emm_sap.u.emm_as.u.security.auts = auts;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.security.sctx,
-                           _emm_data.security, FALSE, TRUE);
-  rc = emm_sap_send(&emm_sap);
+                           user->emm_data->security, FALSE, TRUE);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /*
@@ -680,34 +665,34 @@ static int _authentication_abnormal_cases_cde(int emm_cause,
     case EMM_CAUSE_MAC_FAILURE:
       /* 3GPP TS 24.301, section 5.4.2.6, case c
        * Update the MAC failure counter */
-      _authentication_data.mac_count += 1;
+      authentication_data->mac_count += 1;
       /* Start timer T3418 */
-      T3418.id = nas_timer_start(T3418.sec,
-                                 _authentication_t3418_handler, NULL);
+      emm_timers->T3418.id = nas_timer_start(emm_timers->T3418.sec,
+                                 _authentication_t3418_handler, user);
       LOG_TRACE(INFO,"EMM-PROC  - Timer T3418 (%d) expires in "
-                "%ld seconds", T3418.id, T3418.sec);
+                "%ld seconds", emm_timers->T3418.id, emm_timers->T3418.sec);
       break;
 
     case EMM_CAUSE_NON_EPS_AUTH_UNACCEPTABLE:
       /* 3GPP TS 24.301, section 5.4.2.6, case d
        * Update the UMTS challenge failure counter */
-      _authentication_data.umts_count += 1;
+      authentication_data->umts_count += 1;
       /* Start timer T3418 */
-      T3418.id = nas_timer_start(T3418.sec,
-                                 _authentication_t3418_handler, NULL);
+      emm_timers->T3418.id = nas_timer_start(emm_timers->T3418.sec,
+                                 _authentication_t3418_handler, user);
       LOG_TRACE(INFO,"EMM-PROC  - Timer T3418 (%d) expires in "
-                "%ld seconds", T3418.id, T3418.sec);
+                "%ld seconds", emm_timers->T3418.id, emm_timers->T3418.sec);
       break;
 
     case EMM_CAUSE_SYNCH_FAILURE:
       /* 3GPP TS 24.301, section 5.4.2.6, case e
        * Update the synch failure counter */
-      _authentication_data.sync_count += 1;
+      authentication_data->sync_count += 1;
       /* Start timer T3420 */
-      T3420.id = nas_timer_start(T3420.sec,
-                                 _authentication_t3420_handler, NULL);
+      emm_timers->T3420.id = nas_timer_start(emm_timers->T3420.sec,
+                                 _authentication_t3420_handler, user);
       LOG_TRACE(INFO,"EMM-PROC  - Timer T3420 (%d) expires in "
-                "%ld seconds", T3420.id, T3420.sec);
+                "%ld seconds", emm_timers->T3420.id, emm_timers->T3420.sec);
       break;
 
     default:
@@ -719,7 +704,7 @@ static int _authentication_abnormal_cases_cde(int emm_cause,
     /*
      * Stop any retransmission timers that are running
      */
-    rc = _authentication_stop_timers();
+    rc = _authentication_stop_timers(user);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "EMM-PROC  - "
@@ -733,17 +718,17 @@ static int _authentication_abnormal_cases_cde(int emm_cause,
     int failure_counter = 0;
 
     if (emm_cause == EMM_CAUSE_MAC_FAILURE) {
-      failure_counter = _authentication_data.mac_count
-                        + _authentication_data.sync_count;
+      failure_counter = authentication_data->mac_count
+                        + authentication_data->sync_count;
     } else if (emm_cause == EMM_CAUSE_SYNCH_FAILURE) {
-      failure_counter = _authentication_data.mac_count
-                        + _authentication_data.umts_count
-                        + _authentication_data.sync_count;
+      failure_counter = authentication_data->mac_count
+                        + authentication_data->umts_count
+                        + authentication_data->sync_count;
     }
 
     if (failure_counter >= AUTHENTICATION_COUNTER_MAX) {
       /* 3GPP TS 24.301, section 5.4.2.6, case f */
-      rc = _authentication_abnormal_case_f();
+      rc = _authentication_abnormal_case_f(user);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "EMM-PROC  - "
@@ -771,7 +756,7 @@ static int _authentication_abnormal_cases_cde(int emm_cause,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _authentication_abnormal_case_f(void)
+static int _authentication_abnormal_case_f(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -785,16 +770,16 @@ static int _authentication_abnormal_case_f(void)
    */
   emm_sap_t emm_sap;
   emm_sap.primitive = EMMAS_RELEASE_REQ;
-  emm_sap.u.emm_as.u.release.guti = _emm_data.guti;
+  emm_sap.u.emm_as.u.release.guti = user->emm_data->guti;
   emm_sap.u.emm_as.u.release.cause = EMM_AS_CAUSE_AUTHENTICATION;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /* Start any retransmission timers (e.g. T3410, T3417, T3421 or
      * T3430), if they were running and stopped when the UE received
      * the first AUTHENTICATION REQUEST message containing an invalid
      * MAC or SQN */
-    rc = _authentication_start_timers();
+    rc = _authentication_start_timers(user);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -818,40 +803,41 @@ static int _authentication_abnormal_case_f(void)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _authentication_data, T3410, T3417, T3421, **
  **             T3430                                      **
  **                                                                        **
  ***************************************************************************/
-static int _authentication_stop_timers(void)
+static int _authentication_stop_timers(nas_user_t *user)
 {
   LOG_FUNC_IN;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   /* Stop attach timer */
-  if (T3410.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", T3410.id);
-    T3410.id = nas_timer_stop(T3410.id);
-    _authentication_data.timers |= AUTHENTICATION_T3410;
+  if (emm_timers->T3410.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3410 (%d)", emm_timers->T3410.id);
+    emm_timers->T3410.id = nas_timer_stop(emm_timers->T3410.id);
+    authentication_data->timers |= AUTHENTICATION_T3410;
   }
 
   /* Stop service request timer */
-  if (T3417.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3417 (%d)", T3417.id);
-    T3417.id = nas_timer_stop(T3417.id);
-    _authentication_data.timers |= AUTHENTICATION_T3417;
+  if (emm_timers->T3417.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3417 (%d)", emm_timers->T3417.id);
+    emm_timers->T3417.id = nas_timer_stop(emm_timers->T3417.id);
+    authentication_data->timers |= AUTHENTICATION_T3417;
   }
 
   /* Stop detach timer */
-  if (T3421.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3421 (%d)", T3421.id);
-    T3421.id = nas_timer_stop(T3421.id);
-    _authentication_data.timers |= AUTHENTICATION_T3421;
+  if (emm_timers->T3421.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3421 (%d)", emm_timers->T3421.id);
+    emm_timers->T3421.id = nas_timer_stop(emm_timers->T3421.id);
+    authentication_data->timers |= AUTHENTICATION_T3421;
   }
 
   /* Stop tracking area update timer */
-  if (T3430.id != NAS_TIMER_INACTIVE_ID) {
-    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3430 (%d)", T3430.id);
-    T3430.id = nas_timer_stop(T3430.id);
-    _authentication_data.timers |= AUTHENTICATION_T3430;
+  if (emm_timers->T3430.id != NAS_TIMER_INACTIVE_ID) {
+    LOG_TRACE(INFO, "EMM-PROC  - Stop timer T3430 (%d)", emm_timers->T3430.id);
+    emm_timers->T3430.id = nas_timer_stop(emm_timers->T3430.id);
+    authentication_data->timers |= AUTHENTICATION_T3430;
   }
 
   LOG_FUNC_RETURN (RETURNok);
@@ -869,43 +855,44 @@ static int _authentication_stop_timers(void)
  **      3GPP TS 24.301, section 5.4.2.7, case f                   **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _authentication_data                       **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    T3410, T3417, T3421, T3430                 **
  **                                                                        **
  ***************************************************************************/
-static int _authentication_start_timers(void)
+static int _authentication_start_timers(nas_user_t *user)
 {
   LOG_FUNC_IN;
+  authentication_data_t *authentication_data = user->authentication_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
-  if (_authentication_data.timers & AUTHENTICATION_T3410) {
+  if (authentication_data->timers & AUTHENTICATION_T3410) {
     /* Start attach timer */
-    T3410.id = nas_timer_start(T3410.sec, _emm_attach_t3410_handler, NULL);
+    emm_timers->T3410.id = nas_timer_start(emm_timers->T3410.sec, emm_attach_t3410_handler, NULL);
     LOG_TRACE(INFO,"EMM-PROC  - Timer T3410 (%d) expires in "
-              "%ld seconds", T3410.id, T3410.sec);
+              "%ld seconds", emm_timers->T3410.id, emm_timers->T3410.sec);
   }
 
-  if (_authentication_data.timers & AUTHENTICATION_T3417) {
+  if (authentication_data->timers & AUTHENTICATION_T3417) {
     /* Start service request timer */
-    T3417.id = nas_timer_start(T3417.sec, _emm_service_t3417_handler, NULL);
+    emm_timers->T3417.id = nas_timer_start(emm_timers->T3417.sec, emm_service_t3417_handler, NULL);
     LOG_TRACE(INFO,"EMM-PROC  - Timer T3417 (%d) expires in "
-              "%ld seconds", T3417.id, T3417.sec);
+              "%ld seconds", emm_timers->T3417.id, emm_timers->T3417.sec);
   }
 
-  if (_authentication_data.timers & AUTHENTICATION_T3421) {
+  if (authentication_data->timers & AUTHENTICATION_T3421) {
     /* Start detach timer */
-    T3421.id = nas_timer_start(T3421.sec, _emm_detach_t3421_handler, NULL);
+    emm_timers->T3421.id = nas_timer_start(emm_timers->T3421.sec, emm_detach_t3421_handler, NULL);
     LOG_TRACE(INFO,"EMM-PROC  - Timer T3421 (%d) expires in "
-              "%ld seconds", T3421.id, T3421.sec);
+              "%ld seconds", emm_timers->T3421.id, emm_timers->T3421.sec);
   }
 
-  if (_authentication_data.timers & AUTHENTICATION_T3430) {
+  if (authentication_data->timers & AUTHENTICATION_T3430) {
     /* Start tracking area update timer */
-    T3430.id = nas_timer_start(T3430.sec, _emm_tau_t3430_handler, NULL);
+    emm_timers->T3430.id = nas_timer_start(emm_timers->T3430.sec, emm_tau_t3430_handler, NULL);
     LOG_TRACE(INFO,"EMM-PROC  - Timer T3430 (%d) expires in "
-              "%ld seconds", T3430.id, T3430.sec);
+              "%ld seconds", emm_timers->T3430.id, emm_timers->T3430.sec);
   }
 
   LOG_FUNC_RETURN (RETURNok);
diff --git a/openair3/NAS/UE/EMM/Authentication.h b/openair3/NAS/UE/EMM/Authentication.h
new file mode 100644
index 0000000000000000000000000000000000000000..50b2d990aa5ba8628a19550bc393b8b0a88604c2
--- /dev/null
+++ b/openair3/NAS/UE/EMM/Authentication.h
@@ -0,0 +1,23 @@
+#ifndef _AUTHENTICATION_H
+#define _AUTHENTICATION_H
+
+/*
+ * Internal data used for authentication procedure
+ */
+typedef struct {
+  uint8_t rand[AUTH_RAND_SIZE];   /* Random challenge number  */
+  uint8_t res[AUTH_RES_SIZE];     /* Authentication response  */
+  uint8_t ck[AUTH_CK_SIZE];       /* Ciphering key        */
+  uint8_t ik[AUTH_IK_SIZE];       /* Integrity key        */
+#define AUTHENTICATION_T3410    0x01
+#define AUTHENTICATION_T3417    0x02
+#define AUTHENTICATION_T3421    0x04
+#define AUTHENTICATION_T3430    0x08
+  unsigned char timers;       /* Timer restart bitmap     */
+#define AUTHENTICATION_COUNTER_MAX 3
+  unsigned char mac_count:2;  /* MAC failure counter (#20)        */
+  unsigned char umts_count:2; /* UMTS challenge failure counter (#26) */
+  unsigned char sync_count:2; /* Sync failure counter (#21)       */
+} authentication_data_t;
+
+#endif
diff --git a/openair3/NAS/UE/EMM/Detach.c b/openair3/NAS/UE/EMM/Detach.c
index 7fd73305246ffddd6568cc148037ada478a40f91..fd2db1b6174ae7a9624f875531eaf8b68a2d07b4 100644
--- a/openair3/NAS/UE/EMM/Detach.c
+++ b/openair3/NAS/UE/EMM/Detach.c
@@ -49,6 +49,7 @@ Description Defines the detach related EMM procedure executed by the
 #include "nas_timer.h"
 
 #include "emmData.h"
+#include "emm_timers.h"
 
 #include "emm_sap.h"
 #include "esm_sap.h"
@@ -75,29 +76,11 @@ static const char *_emm_detach_type_str[] = {
  *      Internal data handled by the detach procedure in the UE
  * --------------------------------------------------------------------------
  */
-/*
- * Timer handlers
- */
-void *_emm_detach_t3421_handler(void *);
 
 /*
  * Abnormal case detach procedures
  */
-static int _emm_detach_abort(emm_proc_detach_type_t type);
-
-/*
- * Internal data used for detach procedure
- */
-static struct {
-#define EMM_DETACH_COUNTER_MAX  5
-  unsigned int count;      /* Counter used to limit the number of
-                  * subsequently detach attempts    */
-  int switch_off;      /* UE switch-off indicator     */
-  emm_proc_detach_type_t type; /* Type of the detach procedure
-                  * currently in progress       */
-} _emm_detach_data = {0, FALSE, EMM_DETACH_TYPE_RESERVED};
-
-
+static int _emm_detach_abort(nas_user_t *user, emm_proc_detach_type_t type);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -124,34 +107,34 @@ static struct {
  ** Inputs:  type:      Type of the requested detach               **
  **      switch_off:    Indicates whether the detach is required   **
  **             because the UE is switched off or not      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_detach_data                           **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_detach(emm_proc_detach_type_t type, int switch_off)
+int emm_proc_detach(nas_user_t *user, emm_proc_detach_type_t type, int switch_off)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   emm_as_data_t *emm_as = &emm_sap.u.emm_as.u.data;
+  emm_detach_data_t *emm_detach_data = user->emm_data->emm_detach_data;
   int rc;
 
   LOG_TRACE(INFO, "EMM-PROC  - Initiate EPS detach type = %s (%d)",
             _emm_detach_type_str[type], type);
 
   /* Initialize the detach procedure internal data */
-  _emm_detach_data.count = 0;
-  _emm_detach_data.switch_off = switch_off;
-  _emm_detach_data.type = type;
+  emm_detach_data->count = 0;
+  emm_detach_data->switch_off = switch_off;
+  emm_detach_data->type = type;
 
   /* Setup EMM procedure handler to be executed upon receiving
    * lower layer notification */
-  rc = emm_proc_lowerlayer_initialize(emm_proc_detach_request,
+  rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, emm_proc_detach_request,
                                       emm_proc_detach_failure,
-                                      emm_proc_detach_release, NULL);
+                                      emm_proc_detach_release, user);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING, "Failed to initialize EMM procedure handler");
@@ -167,17 +150,17 @@ int emm_proc_detach(emm_proc_detach_type_t type, int switch_off)
   /* Set the switch-off indicator */
   emm_as->switch_off = switch_off;
   /* Set the EPS mobile identity */
-  emm_as->guti = _emm_data.guti;
-  emm_as->ueid = 0;
+  emm_as->guti = user->emm_data->guti;
+  emm_as->ueid = user->ueid;
   /* Setup EPS NAS security data */
-  emm_as_set_security_data(&emm_as->sctx, _emm_data.security, FALSE, TRUE);
+  emm_as_set_security_data(&emm_as->sctx, user->emm_data->security, FALSE, TRUE);
 
   /*
    * Notify EMM-AS SAP that Detach Request message has to
    * be sent to the network
    */
   emm_sap.primitive = EMMAS_DATA_REQ;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -202,21 +185,24 @@ int emm_proc_detach_request(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user = args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
+  emm_detach_data_t *emm_detach_data = user->emm_data->emm_detach_data;
   emm_sap_t emm_sap;
   int rc;
 
-  if ( !_emm_detach_data.switch_off ) {
+  if ( !emm_detach_data->switch_off ) {
     /* Start T3421 timer */
-    T3421.id = nas_timer_start(T3421.sec, _emm_detach_t3421_handler, NULL);
+    emm_timers->T3421.id = nas_timer_start(emm_timers->T3421.sec, emm_detach_t3421_handler, user);
     LOG_TRACE(INFO, "EMM-PROC  - Timer T3421 (%d) expires in %ld seconds",
-              T3421.id, T3421.sec);
+              emm_timers->T3421.id, emm_timers->T3421.sec);
   }
 
   /*
    * Notify EMM that Detach Request has been sent to the network
    */
   emm_sap.primitive = EMMREG_DETACH_REQ;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -243,19 +229,21 @@ int emm_proc_detach_request(void *args)
  **      Others:    T3421                                      **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_detach_accept(void)
+int emm_proc_detach_accept(void* args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user=args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   int rc;
 
   LOG_TRACE(INFO, "EMM-PROC  - UE initiated detach procedure completion");
 
   /* Reset EMM procedure handler */
-  (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  (void) emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   /* Stop timer T3421 */
-  T3421.id = nas_timer_stop(T3421.id);
+  emm_timers->T3421.id = nas_timer_stop(emm_timers->T3421.id);
 
   /*
    * Notify ESM that all EPS bearer contexts have to be locally deactivated
@@ -263,7 +251,7 @@ int emm_proc_detach_accept(void)
   esm_sap_t esm_sap;
   esm_sap.primitive = ESM_EPS_BEARER_CONTEXT_DEACTIVATE_REQ;
   esm_sap.data.eps_bearer_context_deactivate.ebi = ESM_SAP_ALL_EBI;
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   /*
    * XXX - Upon receiving notification from ESM that all EPS bearer
@@ -285,7 +273,6 @@ int emm_proc_detach_accept(void)
  **                                                                        **
  ** Inputs:  is_initial:    Not used                                   **
  **          args:      Not used                                   **
- **      Others:    _emm_detach_data                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
@@ -296,23 +283,26 @@ int emm_proc_detach_failure(int is_initial, void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user = args;
+  emm_detach_data_t *emm_detach_data = user->emm_data->emm_detach_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   emm_sap_t emm_sap;
   int rc;
 
   LOG_TRACE(WARNING, "EMM-PROC  - Network detach failure");
 
   /* Reset EMM procedure handler */
-  (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  (void) emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   /* Stop timer T3421 */
-  T3421.id = nas_timer_stop(T3421.id);
+  emm_timers->T3421.id = nas_timer_stop(emm_timers->T3421.id);
 
   /*
    * Notify EMM that detach procedure has to be restarted
    */
   emm_sap.primitive = EMMREG_DETACH_INIT;
-  emm_sap.u.emm_reg.u.detach.switch_off = _emm_detach_data.switch_off;
-  rc = emm_sap_send(&emm_sap);
+  emm_sap.u.emm_reg.u.detach.switch_off = emm_detach_data->switch_off;
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -329,7 +319,6 @@ int emm_proc_detach_failure(int is_initial, void *args)
  **      The  detach procedure shall be aborted.                   **
  **                                                                        **
  ** Inputs:  args:      not used                                   **
- **      Others:    _emm_detach_data                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
@@ -342,8 +331,10 @@ int emm_proc_detach_release(void *args)
 
   LOG_TRACE(WARNING, "EMM-PROC  - NAS signalling connection released");
 
+  nas_user_t *user = args;
+  emm_detach_data_t *emm_detach_data = user->emm_data->emm_detach_data;
   /* Abort the detach procedure */
-  int rc = _emm_detach_abort(_emm_detach_data.type);
+  int rc = _emm_detach_abort(user, emm_detach_data->type);
 
   LOG_FUNC_RETURN(rc);
 }
@@ -368,50 +359,52 @@ int emm_proc_detach_release(void *args)
  **              3GPP TS 24.301, section 5.5.2.2.4 case c                  **
  **      On the first four expiries of the timer, the UE shall re- **
  **      transmit the DETACH REQUEST message and shall reset and   **
- **      restart timer T3421. On the fifth expiry of timer T3421,  **
+ **      restart timer emm_timers->T3421. On the fifth expiry of timer T3421,  **
  **      the detach procedure shall be aborted.                    **
  **                                                                        **
  ** Inputs:  args:      handler parameters                         **
- **      Others:    _emm_detach_data                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void *_emm_detach_t3421_handler(void *args)
+void *emm_detach_t3421_handler(void *args)
 {
   LOG_FUNC_IN;
 
+  nas_user_t *user = args;
+  emm_detach_data_t *emm_detach_data = user->emm_data->emm_detach_data;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   int rc;
 
   /* Increment the retransmission counter */
-  _emm_detach_data.count += 1;
+  emm_detach_data->count += 1;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3421 timer expired, "
-            "retransmission counter = %d", _emm_detach_data.count);
+            "retransmission counter = %d", emm_detach_data->count);
 
-  if (_emm_detach_data.count < EMM_DETACH_COUNTER_MAX) {
+  if (emm_detach_data->count < EMM_DETACH_COUNTER_MAX) {
     /* Retransmit the Detach Request message */
     emm_sap_t emm_sap;
     emm_as_data_t *emm_as = &emm_sap.u.emm_as.u.data;
 
     /* Stop timer T3421 */
-    T3421.id = nas_timer_stop(T3421.id);
+    emm_timers->T3421.id = nas_timer_stop(emm_timers->T3421.id);
 
     /* Setup NAS information message to transfer */
     emm_as->NASinfo = EMM_AS_NAS_INFO_DETACH;
     emm_as->NASmsg.length = 0;
     emm_as->NASmsg.value = NULL;
     /* Set the detach type */
-    emm_as->type = _emm_detach_data.type;
+    emm_as->type = emm_detach_data->type;
     /* Set the switch-off indicator */
-    emm_as->switch_off = _emm_detach_data.switch_off;
+    emm_as->switch_off = emm_detach_data->switch_off;
     /* Set the EPS mobile identity */
-    emm_as->guti = _emm_data.guti;
-    emm_as->ueid = 0;
+    emm_as->guti = user->emm_data->guti;
+    emm_as->ueid = user->ueid;
     /* Setup EPS NAS security data */
-    emm_as_set_security_data(&emm_as->sctx, _emm_data.security,
+    emm_as_set_security_data(&emm_as->sctx, user->emm_data->security,
                              FALSE, TRUE);
 
     /*
@@ -419,17 +412,17 @@ void *_emm_detach_t3421_handler(void *args)
      * be sent to the network
      */
     emm_sap.primitive = EMMAS_DATA_REQ;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
     if (rc != RETURNerror) {
       /* Start T3421 timer */
-      T3421.id = nas_timer_start(T3421.sec, _emm_detach_t3421_handler, NULL);
+      emm_timers->T3421.id = nas_timer_start(emm_timers->T3421.sec, emm_detach_t3421_handler, user);
       LOG_TRACE(INFO, "EMM-PROC  - Timer T3421 (%d) expires in %ld "
-                "seconds", T3421.id, T3421.sec);
+                "seconds", emm_timers->T3421.id, emm_timers->T3421.sec);
     }
   } else {
     /* Abort the detach procedure */
-    rc = _emm_detach_abort(_emm_detach_data.type);
+    rc = _emm_detach_abort(user, emm_detach_data->type);
   }
 
   LOG_FUNC_RETURN(NULL);
@@ -448,34 +441,34 @@ void *_emm_detach_t3421_handler(void *args)
  ** Description: Aborts the detach procedure                               **
  **                                                                        **
  ** Inputs:  type:      not used                                   **
- **      Others:    _emm_detach_data                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    T3421                                      **
  **                                                                        **
  ***************************************************************************/
-static int _emm_detach_abort(emm_proc_detach_type_t type)
+static int _emm_detach_abort(nas_user_t *user, emm_proc_detach_type_t type)
 {
   LOG_FUNC_IN;
 
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
   emm_sap_t emm_sap;
   int rc ;
 
   LOG_TRACE(WARNING, "EMM-PROC  - Abort the detach procedure");
 
   /* Reset EMM procedure handler */
-  (void) emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   /* Stop timer T3421 */
-  T3421.id = nas_timer_stop(T3421.id);
+  emm_timers->T3421.id = nas_timer_stop(emm_timers->T3421.id);
 
   /*
    * Notify EMM that detach procedure failed
    */
   emm_sap.primitive = EMMREG_DETACH_FAILED;
   emm_sap.u.emm_reg.u.detach.type = type;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/EmmStatusHdl.c b/openair3/NAS/UE/EMM/EmmStatusHdl.c
index c95441d37065e316a7869185860c98be8babfe48..89dd50207f05b80d454de9899d54434f255d6499 100644
--- a/openair3/NAS/UE/EMM/EmmStatusHdl.c
+++ b/openair3/NAS/UE/EMM/EmmStatusHdl.c
@@ -115,7 +115,7 @@ int emm_proc_status_ind(unsigned int ueid, int emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_status(unsigned int ueid, int emm_cause)
+int emm_proc_status(nas_user_t *user, int emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -132,15 +132,15 @@ int emm_proc_status(unsigned int ueid, int emm_cause)
    */
   emm_sap.primitive = EMMAS_STATUS_IND;
   emm_sap.u.emm_as.u.status.emm_cause = emm_cause;
-  emm_sap.u.emm_as.u.status.ueid = ueid;
+  emm_sap.u.emm_as.u.status.ueid = user->ueid;
 
-  emm_sap.u.emm_as.u.status.guti = _emm_data.guti;
-  sctx = _emm_data.security;
+  emm_sap.u.emm_as.u.status.guti = user->emm_data->guti;
+  sctx = user->emm_data->security;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.status.sctx, sctx,
                            FALSE, TRUE);
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/Identification.c b/openair3/NAS/UE/EMM/Identification.c
index a9ceb7a43bd522669c23ede0ab31fe74f8a8f871..7cf2e765c61322a185618566632227270f083e6f 100644
--- a/openair3/NAS/UE/EMM/Identification.c
+++ b/openair3/NAS/UE/EMM/Identification.c
@@ -49,6 +49,7 @@ Description Defines the identification EMM procedure executed by the
 
 #include "emm_sap.h"
 #include "msc.h"
+#include "user_defs.h"
 
 #include <stdlib.h> // malloc, free
 #include <string.h> // memcpy
@@ -102,7 +103,7 @@ static const char *_emm_identity_type_str[] = {
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_identification_request(emm_proc_identity_type_t type)
+int emm_proc_identification_request(nas_user_t *user, emm_proc_identity_type_t type)
 {
   LOG_FUNC_IN;
 
@@ -114,7 +115,7 @@ int emm_proc_identification_request(emm_proc_identity_type_t type)
 
   /* Setup EMM procedure handler to be executed upon receiving
    * lower layer notification */
-  rc = emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING,
@@ -129,8 +130,8 @@ int emm_proc_identification_request(emm_proc_identity_type_t type)
     imsi_t modified_imsi;
 
     /* International Mobile Subscriber Identity is requested */
-    if (_emm_data.imsi) {
-      memcpy (&modified_imsi, _emm_data.imsi, sizeof (modified_imsi));
+    if (user->emm_data->imsi) {
+      memcpy (&modified_imsi, user->emm_data->imsi, sizeof (modified_imsi));
 
       /* LW: Eventually replace the 0xF value set in MNC digit 3 by a 0 to avoid IMSI to be truncated before reaching HSS */
       if (modified_imsi.u.num.digit6 == 0xF) {
@@ -166,9 +167,9 @@ int emm_proc_identification_request(emm_proc_identity_type_t type)
   case EMM_IDENT_TYPE_IMEI:
 
     /* International Mobile Equipment Identity is requested */
-    if (_emm_data.imei) {
+    if (user->emm_data->imei) {
       emm_sap.u.emm_as.u.security.identType = EMM_IDENT_TYPE_IMEI;
-      emm_sap.u.emm_as.u.security.imei = _emm_data.imei;
+      emm_sap.u.emm_as.u.security.imei = user->emm_data->imei;
     }
 
     break;
@@ -176,9 +177,9 @@ int emm_proc_identification_request(emm_proc_identity_type_t type)
   case EMM_IDENT_TYPE_TMSI:
 
     /* Temporary Mobile Subscriber Identity is requested */
-    if (_emm_data.guti) {
+    if (user->emm_data->guti) {
       emm_sap.u.emm_as.u.security.identType = EMM_IDENT_TYPE_TMSI;
-      emm_sap.u.emm_as.u.security.tmsi = _emm_data.guti->m_tmsi;
+      emm_sap.u.emm_as.u.security.tmsi = user->emm_data->guti->m_tmsi;
     }
 
     break;
@@ -193,13 +194,13 @@ int emm_proc_identification_request(emm_proc_identity_type_t type)
    * to the MME
    */
   emm_sap.primitive = EMMAS_SECURITY_RES;
-  emm_sap.u.emm_as.u.security.guti = _emm_data.guti;
-  emm_sap.u.emm_as.u.security.ueid = 0;
+  emm_sap.u.emm_as.u.security.guti = user->emm_data->guti;
+  emm_sap.u.emm_as.u.security.ueid = user->ueid;
   emm_sap.u.emm_as.u.security.msgType = EMM_AS_MSG_TYPE_IDENT;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.security.sctx,
-                           _emm_data.security, FALSE, TRUE);
-  rc = emm_sap_send(&emm_sap);
+                           user->emm_data->security, FALSE, TRUE);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/IdleMode.c b/openair3/NAS/UE/EMM/IdleMode.c
index a5a2c3070894305392cc2ef0b760f49837d1f9b6..159a3b2a2ceba18bd0e76c2d32d1494f72b36a29 100644
--- a/openair3/NAS/UE/EMM/IdleMode.c
+++ b/openair3/NAS/UE/EMM/IdleMode.c
@@ -59,6 +59,7 @@ Description Defines EMM procedures executed by the Non-Access Stratum
 
 #include "emm_proc.h"
 #include "nas_log.h"
+#include "utils.h"
 
 #include "emm_sap.h"
 
@@ -78,56 +79,8 @@ Description Defines EMM procedures executed by the Non-Access Stratum
 /****************************************************************************/
 
 static int _IdleMode_plmn_str(char *plmn_str, const plmn_t *plmn);
-static int _IldlMode_get_opnn_id(const plmn_t *plmn);
-static int _IdleMode_get_suitable_cell(int index);
-
-/*
- * A list of PLMN identities in priority order is maintained locally
- * to perform the PLMN selection procedure.
- *
- * In automatic mode of operation, this list is used for PLMN selection when
- * the UE is switched on, or upon recovery from lack of coverage, or when the
- * user requests the UE to initiate PLMN reselection, and registration.
- * In manual mode of operation, this list is displayed to the user that may
- * select an available PLMN and initiate registration.
- *
- * The list may contain PLMN identifiers in the following order:
- * - The last registered PLMN or each equivalent PLMN present in the list of
- *   "equivalent PLMNs" (EPLMN_MAX), when UE is switched on or following
- *   recovery from lack of coverage;
- * - The highest priority PLMN in the list of "equivalent HPLMNs" or the
- *   HPLMN derived from the IMSI (1)
- * - Each PLMN/access technology combination in the "User Controlled PLMN
- *   Selector with Access Technology" (PLMN_MAX)
- * - Each PLMN/access technology combination in the "Operator Controlled PLMN
- *   Selector with Access Technology" (OPLMN_MAX)
- * - Other PLMN/access technology combinations with received high quality
- *   signal in random order (TODO)
- * - Other PLMN/access technology combinations in order of decreasing signal
- *   quality (TODO)
- * - The last selected PLMN again (1)
- */
-static struct {
-  int n_plmns;
-#define EMM_PLMN_LIST_SIZE (EMM_DATA_EPLMN_MAX + EMM_DATA_PLMN_MAX +    \
-                            EMM_DATA_OPLMN_MAX + 2)
-  plmn_t *plmn[EMM_PLMN_LIST_SIZE];
-  int index;    /* Index of the PLMN for which selection is ongoing        */
-  int hplmn;    /* Index of the home PLMN or the highest priority
-           * equivalent home PLMN                    */
-  int fplmn;    /* Index of the first forbidden PLMN               */
-  int splmn;    /* Index of the currently selected PLMN            */
-  int rplmn;    /* Index of the currently registered PLMN          */
-  struct plmn_param_t {
-    char fullname[NET_FORMAT_LONG_SIZE+1];   /* PLMN full identifier     */
-    char shortname[NET_FORMAT_SHORT_SIZE+1]; /* PLMN short identifier    */
-    char num[NET_FORMAT_NUM_SIZE+1];     /* PLMN numeric identifier  */
-    int stat; /* Indication of the PLMN availability             */
-    int tac;  /* Location/Tracking Area Code                 */
-    int ci;   /* Serving cell identifier                     */
-    int rat;  /* Radio Access Technology supported by the serving cell   */
-  } param[EMM_PLMN_LIST_SIZE];
-} _emm_plmn_list;
+static int _IldlMode_get_opnn_id(emm_data_t *emm_data, const plmn_t *plmn);
+static int _IdleMode_get_suitable_cell(nas_user_t *user, int index);
 
 /* Callback executed whenever a network indication is received */
 static IdleMode_callback_t _emm_indication_notify;
@@ -152,21 +105,23 @@ static IdleMode_callback_t _emm_indication_notify;
  **      Others:    _emm_plmn_list, _emm_indication_notify     **
  **                                                                        **
  ***************************************************************************/
-void IdleMode_initialize(IdleMode_callback_t cb)
+void IdleMode_initialize(nas_user_t *user, IdleMode_callback_t cb)
 {
+  emm_plmn_list_t *emm_plmn_list = calloc_or_fail( sizeof(emm_plmn_list_t));
+  user->emm_plmn_list = emm_plmn_list;
   /* Initialize the list of available PLMNs */
-  _emm_plmn_list.n_plmns = 0;
-  _emm_plmn_list.index = 0;
-  _emm_plmn_list.hplmn = -1;
-  _emm_plmn_list.fplmn = -1;
-  _emm_plmn_list.splmn = -1;
-  _emm_plmn_list.rplmn = -1;
+  emm_plmn_list->n_plmns = 0;
+  emm_plmn_list->index = 0;
+  emm_plmn_list->hplmn = -1;
+  emm_plmn_list->fplmn = -1;
+  emm_plmn_list->splmn = -1;
+  emm_plmn_list->rplmn = -1;
 
   /* Initialize the network notification handler */
   _emm_indication_notify = *cb;
 
   /* Initialize EMM Service Access Point */
-  emm_sap_initialize();
+  emm_sap_initialize(user);
 }
 
 /*
@@ -190,9 +145,9 @@ void IdleMode_initialize(IdleMode_callback_t cb)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_nb_plmns(void)
+int IdleMode_get_nb_plmns(emm_plmn_list_t *emm_plmn_list)
 {
-  return _emm_plmn_list.n_plmns;
+  return emm_plmn_list->n_plmns;
 }
 
 /****************************************************************************
@@ -211,9 +166,9 @@ int IdleMode_get_nb_plmns(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_hplmn_index(void)
+int IdleMode_get_hplmn_index(emm_plmn_list_t *emm_plmn_list)
 {
-  return _emm_plmn_list.hplmn;
+  return emm_plmn_list->hplmn;
 }
 
 /****************************************************************************
@@ -232,9 +187,9 @@ int IdleMode_get_hplmn_index(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_rplmn_index(void)
+int IdleMode_get_rplmn_index(emm_plmn_list_t *emm_plmn_list)
 {
-  return _emm_plmn_list.rplmn;
+  return emm_plmn_list->rplmn;
 }
 
 /****************************************************************************
@@ -245,16 +200,15 @@ int IdleMode_get_rplmn_index(void)
  **      available PLMNs.                                          **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_plmn_list                             **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The index of the selected PLMN in the list **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_splmn_index(void)
+int IdleMode_get_splmn_index(emm_plmn_list_t *emm_plmn_list)
 {
-  return _emm_plmn_list.splmn;
+  return emm_plmn_list->splmn;
 }
 
 /****************************************************************************
@@ -265,38 +219,36 @@ int IdleMode_get_splmn_index(void)
  **      tors present in the network                               **
  **                                                                        **
  ** Inputs:  i:     Index of the first operator to update      **
- **          Others:    _emm_plmn_list                             **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The size of the list in bytes              **
- **          Others:    _emm_data.plist                            **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_update_plmn_list(int i)
+int IdleMode_update_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, int i)
 {
   int offset = 0;
   int n = 1;
 
-  while ( (i < _emm_plmn_list.n_plmns) && (offset < EMM_DATA_BUFFER_SIZE) ) {
-    struct plmn_param_t *plmn = &(_emm_plmn_list.param[i++]);
+  while ( (i < emm_plmn_list->n_plmns) && (offset < EMM_DATA_BUFFER_SIZE) ) {
+    struct plmn_param_t *plmn = &(emm_plmn_list->param[i++]);
 
     if (n++ > 1) {
-      offset += snprintf(_emm_data.plist.buffer + offset,
+      offset += snprintf(emm_data->plist.buffer + offset,
                          EMM_DATA_BUFFER_SIZE - offset, ",");
     }
 
-    offset += snprintf(_emm_data.plist.buffer + offset,
+    offset += snprintf(emm_data->plist.buffer + offset,
                        EMM_DATA_BUFFER_SIZE - offset, "(%d,%s,%s,%s",
                        plmn->stat, plmn->fullname,
                        plmn->shortname, plmn->num);
 
     if (plmn->rat != NET_ACCESS_UNAVAILABLE) {
-      offset += snprintf(_emm_data.plist.buffer + offset,
+      offset += snprintf(emm_data->plist.buffer + offset,
                          EMM_DATA_BUFFER_SIZE - offset, ",%d",
                          plmn->rat);
     }
 
-    offset += snprintf(_emm_data.plist.buffer + offset,
+    offset += snprintf(emm_data->plist.buffer + offset,
                        EMM_DATA_BUFFER_SIZE - offset, ")");
   }
 
@@ -320,13 +272,13 @@ int IdleMode_update_plmn_list(int i)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const char *IdleMode_get_plmn_fullname(const plmn_t *plmn, int index,
+const char *IdleMode_get_plmn_fullname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                        size_t *len)
 {
-  if (index < _emm_plmn_list.n_plmns) {
-    assert( PLMNS_ARE_EQUAL(*plmn, *_emm_plmn_list.plmn[index]) );
-    *len = strlen(_emm_plmn_list.param[index].fullname);
-    return _emm_plmn_list.param[index].fullname;
+  if (index < emm_plmn_list->n_plmns) {
+    assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) );
+    *len = strlen(emm_plmn_list->param[index].fullname);
+    return emm_plmn_list->param[index].fullname;
   }
 
   return NULL;
@@ -349,13 +301,13 @@ const char *IdleMode_get_plmn_fullname(const plmn_t *plmn, int index,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const char *IdleMode_get_plmn_shortname(const plmn_t *plmn, int index,
+const char *IdleMode_get_plmn_shortname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                         size_t *len)
 {
-  if (index < _emm_plmn_list.n_plmns) {
-    assert( PLMNS_ARE_EQUAL(*plmn, *_emm_plmn_list.plmn[index]) );
-    *len = strlen(_emm_plmn_list.param[index].shortname);
-    return _emm_plmn_list.param[index].shortname;
+  if (index < emm_plmn_list->n_plmns) {
+    assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) );
+    *len = strlen(emm_plmn_list->param[index].shortname);
+    return emm_plmn_list->param[index].shortname;
   }
 
   return NULL;
@@ -379,12 +331,12 @@ const char *IdleMode_get_plmn_shortname(const plmn_t *plmn, int index,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const char *IdleMode_get_plmn_id(const plmn_t *plmn, int index, size_t *len)
+const char *IdleMode_get_plmn_id(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index, size_t *len)
 {
-  if (index < _emm_plmn_list.n_plmns) {
-    assert( PLMNS_ARE_EQUAL(*plmn, *_emm_plmn_list.plmn[index]) );
-    *len = strlen(_emm_plmn_list.param[index].num);
-    return _emm_plmn_list.param[index].num;
+  if (index < emm_plmn_list->n_plmns) {
+    assert( PLMNS_ARE_EQUAL(*plmn, *emm_plmn_list->plmn[index]) );
+    *len = strlen(emm_plmn_list->param[index].num);
+    return emm_plmn_list->param[index].num;
   }
 
   return NULL;
@@ -406,13 +358,13 @@ const char *IdleMode_get_plmn_id(const plmn_t *plmn, int index, size_t *len)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_plmn_fullname_index(const char *plmn)
+int IdleMode_get_plmn_fullname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn)
 {
   int index;
 
   /* Get the index of the PLMN identifier with specified full name */
-  for (index = 0; index < _emm_plmn_list.n_plmns; index++) {
-    if ( strncmp(plmn, _emm_plmn_list.param[index].fullname,
+  for (index = 0; index < emm_plmn_list->n_plmns; index++) {
+    if ( strncmp(plmn, emm_plmn_list->param[index].fullname,
                  NET_FORMAT_LONG_SIZE) != 0 ) {
       continue;
     }
@@ -439,13 +391,13 @@ int IdleMode_get_plmn_fullname_index(const char *plmn)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_plmn_shortname_index(const char *plmn)
+int IdleMode_get_plmn_shortname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn)
 {
   int index;
 
   /* Get the index of the PLMN identifier with specified short name */
-  for (index = 0; index < _emm_plmn_list.n_plmns; index++) {
-    if ( !strncmp(plmn, _emm_plmn_list.param[index].shortname,
+  for (index = 0; index < emm_plmn_list->n_plmns; index++) {
+    if ( !strncmp(plmn, emm_plmn_list->param[index].shortname,
                   NET_FORMAT_SHORT_SIZE) ) {
       continue;
     }
@@ -472,13 +424,13 @@ int IdleMode_get_plmn_shortname_index(const char *plmn)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int IdleMode_get_plmn_id_index(const char *plmn)
+int IdleMode_get_plmn_id_index(emm_plmn_list_t *emm_plmn_list, const char *plmn)
 {
   int index;
 
   /* Get the index of the PLMN identifier with specified numeric identifier */
-  for (index = 0; index < _emm_plmn_list.n_plmns; index++) {
-    if ( !strncmp(plmn, _emm_plmn_list.param[index].num,
+  for (index = 0; index < emm_plmn_list->n_plmns; index++) {
+    if ( !strncmp(plmn, emm_plmn_list->param[index].num,
                   NET_FORMAT_LONG_SIZE) ) {
       continue;
     }
@@ -503,66 +455,67 @@ int IdleMode_get_plmn_id_index(const char *plmn)
  **      to PLMN selection procedure.                              **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    _emm_plmn_list                             **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_initialize(void)
+int emm_proc_initialize(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc;
   int i;
+  emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
 
-  if (!_emm_data.usim_is_valid) {
+  if (!user->emm_data->usim_is_valid) {
     /* The USIM application is not present or not valid */
     LOG_TRACE(WARNING, "EMM-IDLE  - USIM is not valid");
     emm_sap.primitive = EMMREG_NO_IMSI;
   } else {
     /* The highest priority is given to either the "equivalent PLMNs"
      * if available, or the last registered PLMN */
-    if (_emm_data.nvdata.eplmn.n_plmns > 0) {
-      for (i=0; i < _emm_data.nvdata.eplmn.n_plmns; i++) {
-        _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] =
-          &_emm_data.nvdata.eplmn.plmn[i];
+    if (user->emm_data->nvdata.eplmn.n_plmns > 0) {
+      for (i=0; i < user->emm_data->nvdata.eplmn.n_plmns; i++) {
+        emm_plmn_list->plmn[emm_plmn_list->n_plmns++] =
+          &user->emm_data->nvdata.eplmn.plmn[i];
       }
-    } else if ( PLMN_IS_VALID(_emm_data.nvdata.rplmn) ) {
-      _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] =
-        &_emm_data.nvdata.rplmn;
+    } else if ( PLMN_IS_VALID(user->emm_data->nvdata.rplmn) ) {
+      emm_plmn_list->plmn[emm_plmn_list->n_plmns++] =
+        &user->emm_data->nvdata.rplmn;
     }
 
     /* Update the index of the HPLMN or EHPLM of highest priority.
      * When switched on, the UE will try to automatically register
      * to each previous PLMN within the ordered list of available
      * PLMNs regardless of the network selection mode of operation */
-    _emm_plmn_list.hplmn = _emm_plmn_list.n_plmns - 1;
-    // LG_emm_plmn_list.hplmn = _emm_plmn_list.n_plmns;
+    emm_plmn_list->hplmn = emm_plmn_list->n_plmns - 1;
+    // LGemm_plmn_list->hplmn = emm_plmn_list->n_plmns;
 
     /* Add the highest priority PLMN in the list of "equivalent HPLMNs"
        if present and not empty, or the HPLMN derived from the IMSI */
-    if (_emm_data.ehplmn.n_plmns > 0) {
-      _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] =
-        &_emm_data.ehplmn.plmn[0];
+    if (user->emm_data->ehplmn.n_plmns > 0) {
+      emm_plmn_list->plmn[emm_plmn_list->n_plmns++] =
+        &user->emm_data->ehplmn.plmn[0];
     } else {
-      _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] = &_emm_data.hplmn;
+      emm_plmn_list->plmn[emm_plmn_list->n_plmns++] = &user->emm_data->hplmn;
     }
 
     /* Each PLMN/access technology combination in the "User
      * Controlled PLMN Selector with Access Technology" */
-    for (i=0; i < _emm_data.plmn.n_plmns; i++) {
-      _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] =
-        &_emm_data.plmn.plmn[i];
+    for (i=0; i < user->emm_data->plmn.n_plmns; i++) {
+      emm_plmn_list->plmn[emm_plmn_list->n_plmns++] =
+        &user->emm_data->plmn.plmn[i];
     }
 
     /* Each PLMN/access technology combination in the "Operator
      * Controlled PLMN Selector with Access Technology" */
-    for (i=0; i < _emm_data.oplmn.n_plmns; i++) {
-      _emm_plmn_list.plmn[_emm_plmn_list.n_plmns++] =
-        &_emm_data.oplmn.plmn[i];
+    for (i=0; i < user->emm_data->oplmn.n_plmns; i++) {
+      emm_plmn_list->plmn[emm_plmn_list->n_plmns++] =
+        &user->emm_data->oplmn.plmn[i];
     }
 
     /* Other PLMN/access technology combinations with received
@@ -574,21 +527,21 @@ int emm_proc_initialize(void)
     /* TODO: Schedule periodic network selection attemps (hpplmn timer) */
 
     /* Initialize the PLMNs' parameters */
-    for (i=0; i < _emm_plmn_list.n_plmns; i++) {
-      struct plmn_param_t *plmn = &(_emm_plmn_list.param[i]);
-      int id = _IldlMode_get_opnn_id(_emm_plmn_list.plmn[i]);
+    for (i=0; i < emm_plmn_list->n_plmns; i++) {
+      struct plmn_param_t *plmn = &(emm_plmn_list->param[i]);
+      int id = _IldlMode_get_opnn_id(user->emm_data, emm_plmn_list->plmn[i]);
 
       if (id < 0) {
         plmn->fullname[0] = '\0';
         plmn->shortname[0] = '\0';
       } else {
-        strncpy(plmn->fullname, _emm_data.opnn[id].fullname,
+        strncpy(plmn->fullname, user->emm_data->opnn[id].fullname,
                 NET_FORMAT_LONG_SIZE);
-        strncpy(plmn->shortname, _emm_data.opnn[id].shortname,
+        strncpy(plmn->shortname, user->emm_data->opnn[id].shortname,
                 NET_FORMAT_SHORT_SIZE);
       }
 
-      (void)_IdleMode_plmn_str(plmn->num, _emm_plmn_list.plmn[i]);
+      (void)_IdleMode_plmn_str(plmn->num, emm_plmn_list->plmn[i]);
       plmn->stat = NET_OPER_UNKNOWN;
       plmn->tac = 0;
       plmn->ci = 0;
@@ -596,14 +549,14 @@ int emm_proc_initialize(void)
     }
 
     LOG_TRACE(INFO, "EMM-IDLE  - %d PLMNs available for network selection",
-              _emm_plmn_list.n_plmns);
+              emm_plmn_list->n_plmns);
 
     /* Notify EMM that PLMN selection procedure has to be executed */
     emm_sap.primitive = EMMREG_REGISTER_REQ;
     emm_sap.u.emm_reg.u.regist.index = 0;
   }
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN(rc);
 
@@ -627,35 +580,38 @@ int emm_proc_initialize(void)
  **      mode.                                                     **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_plmn_list, _emm_data                  **
+ **      Others:    _emm_plmn_list, user->emm_data->                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _emm_plmn_list.index                       **
+ **      Others:    emm_plmn_list->index                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_plmn_selection(int index)
+int emm_proc_plmn_selection(nas_user_t *user, int index)
 {
   LOG_FUNC_IN;
+  emm_data_t *emm_data = user->emm_data;
+  user_api_id_t *user_api_id = user->user_api_id;
+  emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
 
   int rc = RETURNok;
 
-  if (_emm_data.plmn_mode != EMM_DATA_PLMN_AUTO) {
+  if (emm_data->plmn_mode != EMM_DATA_PLMN_AUTO) {
     /*
      * Manual or manual/automatic mode of operation
      * --------------------------------------------
      */
-    if (index >= _emm_plmn_list.hplmn) {
+    if (index >= emm_plmn_list->hplmn) {
       /*
        * Selection of the last registered or equivalent PLMNs failed
        */
-      if (_emm_data.plmn_index < 0) {
+      if (emm_data->plmn_index < 0) {
         /*
          * The user did not select any PLMN yet; display the ordered
          * list of available PLMNs to the user
          */
         index = -1;
-        rc = emm_proc_network_notify(_emm_plmn_list.hplmn);
+        rc = emm_proc_network_notify(emm_plmn_list, user_api_id, emm_data, emm_plmn_list->hplmn);
 
         if (rc != RETURNok) {
           LOG_TRACE(WARNING, "EMM-IDLE  - Failed to notify "
@@ -665,7 +621,7 @@ int emm_proc_plmn_selection(int index)
         /*
          * Try to register to the PLMN manually selected by the user
          */
-        index = _emm_data.plmn_index;
+        index = emm_data->plmn_index;
       }
     }
   }
@@ -678,8 +634,8 @@ int emm_proc_plmn_selection(int index)
      * or any other PLMN in the ordered list of available PLMNs in
      * automatic mode.
      */
-    _emm_plmn_list.index = index;
-    rc = _IdleMode_get_suitable_cell(index);
+    emm_plmn_list->index = index;
+    rc = _IdleMode_get_suitable_cell(user, index);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -714,47 +670,48 @@ int emm_proc_plmn_selection(int index)
  **      ci:        The identifier of the cell                 **
  **      rat:       The radio access technology supported by   **
  **             the cell                                   **
- **      Others:    _emm_plmn_list, _emm_data                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _emm_plmn_list, _emm_data                  **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
+int emm_proc_plmn_selection_end(nas_user_t *user, int found, tac_t tac, ci_t ci, AcT_t rat)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc = RETURNerror;
-  int index = _emm_plmn_list.index;
+  emm_data_t *emm_data = user->emm_data;
+  emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
+  user_api_id_t *user_api_id = user->user_api_id;
+  int index = emm_plmn_list->index;
   int select_next_plmn = FALSE;
 
   LOG_TRACE(INFO, "EMM-IDLE  - %s cell found for PLMN %d in %s mode",
             (found)? "One" : "No", index,
-            (_emm_data.plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" :
-            (_emm_data.plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" :
+            (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" :
+            (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" :
             "Automatic/manual");
 
   if (found) {
     int is_forbidden = FALSE;
 
     /* Select the PLMN of which a suitable cell has been found */
-    _emm_data.splmn = *_emm_plmn_list.plmn[index];
+    emm_data->splmn = *emm_plmn_list->plmn[index];
 
     /* Update the selected PLMN's parameters */
-    _emm_plmn_list.param[index].tac = tac;
-    _emm_plmn_list.param[index].ci = ci;
-    _emm_plmn_list.param[index].rat = rat;
+    emm_plmn_list->param[index].tac = tac;
+    emm_plmn_list->param[index].ci = ci;
+    emm_plmn_list->param[index].rat = rat;
 
     /* Update the location data and notify EMM that data have changed */
-    rc = emm_proc_location_notify(tac, ci , rat);
+    rc = emm_proc_location_notify(user_api_id, emm_data, tac, ci , rat);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "EMM-IDLE  - Failed to notify location update");
     }
 
-    if (_emm_data.plmn_mode == EMM_DATA_PLMN_AUTO) {
+    if (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO) {
       /*
        * Automatic mode of operation
        * ---------------------------
@@ -762,17 +719,17 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
       int i;
 
       /* Check if the selected PLMN is in the forbidden list */
-      for (i = 0; i < _emm_data.fplmn.n_plmns; i++) {
-        if (PLMNS_ARE_EQUAL(_emm_data.splmn, _emm_data.fplmn.plmn[i])) {
+      for (i = 0; i < emm_data->fplmn.n_plmns; i++) {
+        if (PLMNS_ARE_EQUAL(emm_data->splmn, emm_data->fplmn.plmn[i])) {
           is_forbidden = TRUE;
           break;
         }
       }
 
       if (!is_forbidden) {
-        for (i = 0; i < _emm_data.fplmn_gprs.n_plmns; i++) {
-          if (PLMNS_ARE_EQUAL(_emm_data.splmn,
-                              _emm_data.fplmn_gprs.plmn[i])) {
+        for (i = 0; i < emm_data->fplmn_gprs.n_plmns; i++) {
+          if (PLMNS_ARE_EQUAL(emm_data->splmn,
+                              emm_data->fplmn_gprs.plmn[i])) {
             is_forbidden = TRUE;
             break;
           }
@@ -782,12 +739,12 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
       /* Check if the selected PLMN belongs to a forbidden
        * tracking area */
       tai_t tai;
-      tai.plmn = _emm_data.splmn;
+      tai.plmn = emm_data->splmn;
       tai.tac = tac;
 
       if (!is_forbidden) {
-        for (i = 0; i < _emm_data.ftai.n_tais; i++) {
-          if (TAIS_ARE_EQUAL(tai, _emm_data.ftai.tai[i])) {
+        for (i = 0; i < emm_data->ftai.n_tais; i++) {
+          if (TAIS_ARE_EQUAL(tai, emm_data->ftai.tai[i])) {
             is_forbidden = TRUE;
             break;
           }
@@ -795,8 +752,8 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
       }
 
       if (!is_forbidden) {
-        for (i = 0; i < _emm_data.ftai_roaming.n_tais; i++) {
-          if (TAIS_ARE_EQUAL(tai, _emm_data.ftai_roaming.tai[i])) {
+        for (i = 0; i < emm_data->ftai_roaming.n_tais; i++) {
+          if (TAIS_ARE_EQUAL(tai, emm_data->ftai_roaming.tai[i])) {
             is_forbidden = TRUE;
             break;
           }
@@ -810,25 +767,25 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
       LOG_TRACE(INFO, "EMM-IDLE  - UE may camp on this acceptable cell for limited services");
 
       /* Save the index of the first forbidden PLMN */
-      if (_emm_plmn_list.fplmn < 0) {
-        _emm_plmn_list.fplmn = index;
+      if (emm_plmn_list->fplmn < 0) {
+        emm_plmn_list->fplmn = index;
       }
 
-      _emm_plmn_list.param[index].stat = NET_OPER_FORBIDDEN;
+      emm_plmn_list->param[index].stat = NET_OPER_FORBIDDEN;
     } else {
       /* A suitable cell has been found and the PLMN or tracking area
        * is not in the forbidden list */
       LOG_TRACE(INFO, "EMM-IDLE  - UE may camp on this suitable cell for normal services");
-      _emm_plmn_list.fplmn = -1;
-      _emm_plmn_list.param[index].stat = NET_OPER_CURRENT;
+      emm_plmn_list->fplmn = -1;
+      emm_plmn_list->param[index].stat = NET_OPER_CURRENT;
       emm_sap.primitive = EMMREG_REGISTER_CNF;
     }
 
     /* Duplicate the new selected PLMN at the end of the ordered list */
-    _emm_plmn_list.plmn[_emm_plmn_list.n_plmns] = &_emm_data.splmn;
+    emm_plmn_list->plmn[emm_plmn_list->n_plmns] = &emm_data->splmn;
   }
 
-  else if (_emm_data.plmn_mode == EMM_DATA_PLMN_AUTO) {
+  else if (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO) {
     /*
      * Automatic mode of operation
      * ---------------------------
@@ -839,12 +796,12 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
     select_next_plmn = TRUE;
 
     /* Bypass the previously selected PLMN */
-    if (index == _emm_plmn_list.splmn) {
+    if (index == emm_plmn_list->splmn) {
       index += 1;
     }
   }
 
-  else if (_emm_data.plmn_index < 0) {
+  else if (emm_data->plmn_index < 0) {
     /*
      * Manual or manual/automatic mode of operation
      * --------------------------------------------
@@ -855,7 +812,7 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
     select_next_plmn = TRUE;
   }
 
-  else if (_emm_data.plmn_mode == EMM_DATA_PLMN_MANUAL) {
+  else if (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL) {
     /*
      * Manual mode of operation
      * ------------------------
@@ -871,8 +828,8 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
      * Attempt to find a suitable cell of the PLMN selected by the user
      * failed; Try to automatically select another PLMN
      */
-    _emm_data.plmn_mode = EMM_DATA_PLMN_AUTO;
-    index = _emm_plmn_list.hplmn;
+    emm_data->plmn_mode = EMM_DATA_PLMN_AUTO;
+    index = emm_plmn_list->hplmn;
     select_next_plmn = TRUE;
   }
 
@@ -880,17 +837,17 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
    * Force an attempt to register to the next PLMN
    */
   if (select_next_plmn) {
-    int last_plmn_index = _emm_plmn_list.n_plmns;
+    int last_plmn_index = emm_plmn_list->n_plmns;
 
-    if (_emm_plmn_list.splmn != -1) {
+    if (emm_plmn_list->splmn != -1) {
       /* The last attempt was to register the previously selected PLMN */
       last_plmn_index += 1;
     }
 
     if (index < last_plmn_index) {
       /* Try to select the next PLMN in the list of available PLMNs */
-      _emm_plmn_list.index = index;
-      rc = emm_proc_plmn_selection(index);
+      emm_plmn_list->index = index;
+      rc = emm_proc_plmn_selection(user, index);
     } else {
       /* No suitable cell of any PLMN within the ordered list
        * of available PLMNs has been found */
@@ -903,46 +860,43 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
    * Or terminate the PLMN selection procedure
    */
   if (!select_next_plmn) {
-    /* TODO: be sure of this fix */
-    LOG_TRACE(WARNING, "%s:%d:%s: be sure!!\n", __FILE__, __LINE__, __FUNCTION__);
-    //if (!(_emm_plmn_list.fplmn) < 0) { // FIXME this comparison makes no sense (bool < 0)
-    if (!(_emm_plmn_list.fplmn < 0)) { // FIXME this comparison makes no sense (bool < 0)
+    if (emm_plmn_list->fplmn >= 0) {
       /* There were one or more PLMNs which were available and allowable,
        * but an LR failure made registration on those PLMNs unsuccessful
        * or an entry in any of the forbidden area lists prevented a
        * registration attempt; select the first such PLMN and enters a
        * limited service state. */
-      index = _emm_plmn_list.fplmn;
-      _emm_plmn_list.fplmn = -1;
+      index = emm_plmn_list->fplmn;
+      emm_plmn_list->fplmn = -1;
       emm_sap.primitive = EMMREG_REGISTER_REJ;
     }
 
     /* Update the availability indicator of the previously selected PLMN */
-    if (_emm_plmn_list.splmn != -1) {
-      _emm_plmn_list.param[_emm_plmn_list.splmn].stat = NET_OPER_UNKNOWN;
+    if (emm_plmn_list->splmn != -1) {
+      emm_plmn_list->param[emm_plmn_list->splmn].stat = NET_OPER_UNKNOWN;
     }
 
     /* Update the index of the new selected PLMN */
     if (emm_sap.primitive != EMMREG_NO_CELL) {
-      _emm_plmn_list.splmn = index;
+      emm_plmn_list->splmn = index;
     } else {
-      _emm_plmn_list.splmn = -1;
+      emm_plmn_list->splmn = -1;
     }
 
     /*
      * Notify EMM that PLMN selection procedure has completed
      */
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
-    if (_emm_plmn_list.splmn != -1) {
-      if (_emm_plmn_list.splmn == _emm_plmn_list.rplmn) {
+    if (emm_plmn_list->splmn != -1) {
+      if (emm_plmn_list->splmn == emm_plmn_list->rplmn) {
         /* The selected PLMN is the registered PLMN */
         LOG_TRACE(INFO, "EMM-IDLE  - The selected PLMN is the registered PLMN");
-        _emm_data.is_rplmn = TRUE;
-      } else if (_emm_plmn_list.splmn < _emm_plmn_list.hplmn) {
+        emm_data->is_rplmn = TRUE;
+      } else if (emm_plmn_list->splmn < emm_plmn_list->hplmn) {
         /* The selected PLMN is in the list of equivalent PLMNs */
         LOG_TRACE(INFO, "EMM-IDLE  - The selected PLMN is in the list of equivalent PLMNs");
-        _emm_data.is_eplmn = TRUE;
+        emm_data->is_eplmn = TRUE;
       }
 
       /*
@@ -950,7 +904,7 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
        * to register the presence of the UE to the selected PLMN
        */
       emm_sap.primitive = EMMREG_ATTACH_INIT;
-      rc = emm_sap_send(&emm_sap);
+      rc = emm_sap_send(user, &emm_sap);
     }
   }
 
@@ -976,20 +930,20 @@ int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **          Others:    _emm_data                                  **
+ **          Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_registration_notify(Stat_t status)
+int emm_proc_registration_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, Stat_t status)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
   /* Update the network registration status */
-  if (_emm_data.stat != status) {
-    _emm_data.stat = status;
+  if (emm_data->stat != status) {
+    emm_data->stat = status;
     /* Notify EMM that data has changed */
-    rc = (*_emm_indication_notify)(1);
+    rc = (*_emm_indication_notify)(user_api_id, emm_data, 1);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -1010,24 +964,24 @@ int emm_proc_registration_notify(Stat_t status)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **          Others:    _emm_data                                  **
+ **          Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_location_notify(tac_t tac, ci_t ci, AcT_t rat)
+int emm_proc_location_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, tac_t tac, ci_t ci, AcT_t rat)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
   /* Update the location information */
-  if ( (_emm_data.tac != tac) ||
-       (_emm_data.ci  != ci)  ||
-       (_emm_data.rat != rat) ) {
-    _emm_data.tac = tac;
-    _emm_data.ci = ci;
-    _emm_data.rat = rat;
+  if ( (emm_data->tac != tac) ||
+       (emm_data->ci  != ci)  ||
+       (emm_data->rat != rat) ) {
+    emm_data->tac = tac;
+    emm_data->ci = ci;
+    emm_data->rat = rat;
     /* Notify EMM that data has changed */
-    rc = (*_emm_indication_notify)(0);
+    rc = (*_emm_indication_notify)(user_api_id, emm_data, 0);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -1047,17 +1001,17 @@ int emm_proc_location_notify(tac_t tac, ci_t ci, AcT_t rat)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **          Others:    _emm_data                                  **
+ **          Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_network_notify(int index)
+int emm_proc_network_notify(emm_plmn_list_t *emm_plmn_list, user_api_id_t *user_api_id, emm_data_t *emm_data, int index)
 {
   LOG_FUNC_IN;
 
   /* Update the list of operators present in the network */
-  int size = IdleMode_update_plmn_list(index);
+  int size = IdleMode_update_plmn_list(emm_plmn_list, emm_data, index);
   /* Notify EMM that data has changed */
-  int rc = (*_emm_indication_notify)(size);
+  int rc = (*_emm_indication_notify)(user_api_id, emm_data, size);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1126,7 +1080,6 @@ static int _IdleMode_plmn_str(char *plmn_str, const plmn_t *plmn)
  **      tor network name records                                  **
  **                                                                        **
  ** Inputs:  plmn:      The PLMN identifier                        **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The index of the PLMN if found in the list **
@@ -1135,32 +1088,32 @@ static int _IdleMode_plmn_str(char *plmn_str, const plmn_t *plmn)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _IldlMode_get_opnn_id(const plmn_t *plmn)
+static int _IldlMode_get_opnn_id(emm_data_t *emm_data, const plmn_t *plmn)
 {
   int i;
 
-  for (i = 0; i < _emm_data.n_opnns; i++) {
-    if (plmn->MCCdigit1 != _emm_data.opnn[i].plmn->MCCdigit1) {
+  for (i = 0; i < emm_data->n_opnns; i++) {
+    if (plmn->MCCdigit1 != emm_data->opnn[i].plmn->MCCdigit1) {
       continue;
     }
 
-    if (plmn->MCCdigit2 != _emm_data.opnn[i].plmn->MCCdigit2) {
+    if (plmn->MCCdigit2 != emm_data->opnn[i].plmn->MCCdigit2) {
       continue;
     }
 
-    if (plmn->MCCdigit3 != _emm_data.opnn[i].plmn->MCCdigit3) {
+    if (plmn->MCCdigit3 != emm_data->opnn[i].plmn->MCCdigit3) {
       continue;
     }
 
-    if (plmn->MNCdigit1 != _emm_data.opnn[i].plmn->MNCdigit1) {
+    if (plmn->MNCdigit1 != emm_data->opnn[i].plmn->MNCdigit1) {
       continue;
     }
 
-    if (plmn->MNCdigit2 != _emm_data.opnn[i].plmn->MNCdigit2) {
+    if (plmn->MNCdigit2 != emm_data->opnn[i].plmn->MNCdigit2) {
       continue;
     }
 
-    if (plmn->MNCdigit3 != _emm_data.opnn[i].plmn->MNCdigit3) {
+    if (plmn->MNCdigit3 != emm_data->opnn[i].plmn->MNCdigit3) {
       continue;
     }
 
@@ -1181,22 +1134,23 @@ static int _IldlMode_get_opnn_id(const plmn_t *plmn)
  **                                                                        **
  ** Inputs:  index:     Index of the selected PLMN in the ordered  **
  **             list of available PLMNs                    **
- **      Others:    _emm_plmn_list.plmn                        **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _IdleMode_get_suitable_cell(int index)
+static int _IdleMode_get_suitable_cell(nas_user_t *user, int index)
 {
   emm_sap_t emm_sap;
-  const plmn_t *plmn = _emm_plmn_list.plmn[index];
+  emm_data_t *emm_data = user->emm_data;
+  emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
+  const plmn_t *plmn = emm_plmn_list->plmn[index];
 
   LOG_TRACE(INFO, "EMM-IDLE  - Trying to search a suitable cell "
             "of PLMN %d in %s mode", index,
-            (_emm_data.plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" :
-            (_emm_data.plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" :
+            (emm_data->plmn_mode == EMM_DATA_PLMN_AUTO)? "Automatic" :
+            (emm_data->plmn_mode == EMM_DATA_PLMN_MANUAL)? "Manual" :
             "Automatic/manual");
   /*
    * Notify EMM-AS SAP that cell information related to the given
@@ -1206,12 +1160,12 @@ static int _IdleMode_get_suitable_cell(int index)
   emm_sap.u.emm_as.u.cell_info.plmnIDs.n_plmns = 1;
   emm_sap.u.emm_as.u.cell_info.plmnIDs.plmn[0] = *plmn;
 
-  if (_emm_data.plmn_rat != NET_ACCESS_UNAVAILABLE) {
-    emm_sap.u.emm_as.u.cell_info.rat = (1 << _emm_data.plmn_rat);
+  if (emm_data->plmn_rat != NET_ACCESS_UNAVAILABLE) {
+    emm_sap.u.emm_as.u.cell_info.rat = (1 << emm_data->plmn_rat);
   } else {
     emm_sap.u.emm_as.u.cell_info.rat = NET_ACCESS_UNAVAILABLE;
   }
 
-  return emm_sap_send(&emm_sap);
+  return emm_sap_send(user, &emm_sap);
 }
 
diff --git a/openair3/NAS/UE/EMM/IdleMode.h b/openair3/NAS/UE/EMM/IdleMode.h
index f75d149e911c049cab88a17bd7b991c216a2d4f3..e80b4f91344931edd7117917a33ed5d38150f068 100644
--- a/openair3/NAS/UE/EMM/IdleMode.h
+++ b/openair3/NAS/UE/EMM/IdleMode.h
@@ -41,6 +41,7 @@ Description Defines the functions used to get information from the list
 #define __IDLEMODE_H__
 
 #include "commonDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -50,7 +51,7 @@ Description Defines the functions used to get information from the list
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
-typedef int (*IdleMode_callback_t) (int);
+typedef int (*IdleMode_callback_t) (user_api_id_t *user_api_id, emm_data_t *emm_data, int);
 
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
@@ -60,23 +61,23 @@ typedef int (*IdleMode_callback_t) (int);
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void IdleMode_initialize(IdleMode_callback_t cb);
+void IdleMode_initialize(nas_user_t *user, IdleMode_callback_t cb);
 
-int IdleMode_get_nb_plmns(void);
-int IdleMode_get_hplmn_index(void);
-int IdleMode_get_rplmn_index(void);
-int IdleMode_get_splmn_index(void);
+int IdleMode_get_nb_plmns(emm_plmn_list_t *emm_plmn_list);
+int IdleMode_get_hplmn_index(emm_plmn_list_t *emm_plmn_list);
+int IdleMode_get_rplmn_index(emm_plmn_list_t *emm_plmn_list);
+int IdleMode_get_splmn_index(emm_plmn_list_t *emm_plmn_list);
 
-int IdleMode_update_plmn_list(int index);
+int IdleMode_update_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, int i);
 
-const char *IdleMode_get_plmn_fullname(const plmn_t *plmn, int index,
+const char *IdleMode_get_plmn_fullname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                        size_t *len);
-const char *IdleMode_get_plmn_shortname(const plmn_t *plmn, int index,
+const char *IdleMode_get_plmn_shortname(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                         size_t *len);
-const char *IdleMode_get_plmn_id(const plmn_t *plmn, int index, size_t *len);
+const char *IdleMode_get_plmn_id(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index, size_t *len);
 
-int IdleMode_get_plmn_fullname_index(const char *plmn);
-int IdleMode_get_plmn_shortname_index(const char *plmn);
-int IdleMode_get_plmn_id_index(const char *plmn);
+int IdleMode_get_plmn_fullname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn);
+int IdleMode_get_plmn_shortname_index(emm_plmn_list_t *emm_plmn_list, const char *plmn);
+int IdleMode_get_plmn_id_index(emm_plmn_list_t *emm_plmn_list, const char *plmn);
 
 #endif /* __IDLEMODE_H__*/
diff --git a/openair3/NAS/UE/EMM/IdleMode_defs.h b/openair3/NAS/UE/EMM/IdleMode_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ef6c8e86d407ec9813537f8c88745438a51fa94
--- /dev/null
+++ b/openair3/NAS/UE/EMM/IdleMode_defs.h
@@ -0,0 +1,52 @@
+#ifndef _IDLEMODE_DEFS_H
+#define _IDLEMODE_DEFS_H
+
+/*
+ * A list of PLMN identities in priority order is maintained locally
+ * to perform the PLMN selection procedure.
+ *
+ * In automatic mode of operation, this list is used for PLMN selection when
+ * the UE is switched on, or upon recovery from lack of coverage, or when the
+ * user requests the UE to initiate PLMN reselection, and registration.
+ * In manual mode of operation, this list is displayed to the user that may
+ * select an available PLMN and initiate registration.
+ *
+ * The list may contain PLMN identifiers in the following order:
+ * - The last registered PLMN or each equivalent PLMN present in the list of
+ *   "equivalent PLMNs" (EPLMN_MAX), when UE is switched on or following
+ *   recovery from lack of coverage;
+ * - The highest priority PLMN in the list of "equivalent HPLMNs" or the
+ *   HPLMN derived from the IMSI (1)
+ * - Each PLMN/access technology combination in the "User Controlled PLMN
+ *   Selector with Access Technology" (PLMN_MAX)
+ * - Each PLMN/access technology combination in the "Operator Controlled PLMN
+ *   Selector with Access Technology" (OPLMN_MAX)
+ * - Other PLMN/access technology combinations with received high quality
+ *   signal in random order (TODO)
+ * - Other PLMN/access technology combinations in order of decreasing signal
+ *   quality (TODO)
+ * - The last selected PLMN again (1)
+ */
+typedef struct {
+  int n_plmns;
+#define EMM_PLMN_LIST_SIZE (EMM_DATA_EPLMN_MAX + EMM_DATA_PLMN_MAX +    \
+                            EMM_DATA_OPLMN_MAX + 2)
+  plmn_t *plmn[EMM_PLMN_LIST_SIZE];
+  int index;    /* Index of the PLMN for which selection is ongoing        */
+  int hplmn;    /* Index of the home PLMN or the highest priority
+           * equivalent home PLMN                    */
+  int fplmn;    /* Index of the first forbidden PLMN               */
+  int splmn;    /* Index of the currently selected PLMN            */
+  int rplmn;    /* Index of the currently registered PLMN          */
+  struct plmn_param_t {
+    char fullname[NET_FORMAT_LONG_SIZE+1];   /* PLMN full identifier     */
+    char shortname[NET_FORMAT_SHORT_SIZE+1]; /* PLMN short identifier    */
+    char num[NET_FORMAT_NUM_SIZE+1];     /* PLMN numeric identifier  */
+    int stat; /* Indication of the PLMN availability             */
+    int tac;  /* Location/Tracking Area Code                 */
+    int ci;   /* Serving cell identifier                     */
+    int rat;  /* Radio Access Technology supported by the serving cell   */
+  } param[EMM_PLMN_LIST_SIZE];
+} emm_plmn_list_t;
+
+#endif
diff --git a/openair3/NAS/UE/EMM/LowerLayer.c b/openair3/NAS/UE/EMM/LowerLayer.c
index f0f7a3c5b6a42d84b1eb6d6d8257d01628e2fb6a..b5555b2c2fa13b68a0180b3ede87474666662869 100644
--- a/openair3/NAS/UE/EMM/LowerLayer.c
+++ b/openair3/NAS/UE/EMM/LowerLayer.c
@@ -60,17 +60,6 @@ Description Defines EMM procedures executed by the Non-Access Stratum
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
 
-/*
- * Data structure used to handle EMM procedures executed by the UE upon
- * receiving lower layer notifications
- */
-static struct {
-  lowerlayer_success_callback_t success; /* Successful data delivery  */
-  lowerlayer_failure_callback_t failure; /* Lower layer failure   */
-  lowerlayer_release_callback_t release; /* NAS signalling release    */
-  void *args;         /* EMM procedure argument parameters    */
-} _lowerlayer_data;
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
@@ -88,7 +77,6 @@ static struct {
  ** Description: Notify the EPS Mobility Management entity that data have  **
  **      been successfully delivered to the network                **
  **                                                                        **
- ** Inputs:  ueid:      UE lower layer identifier                  **
  **      Others:    None                                       **
  **                                                                        **
  ** Outputs:     None                                                      **
@@ -96,7 +84,7 @@ static struct {
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_success(unsigned int ueid)
+int lowerlayer_success(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -104,8 +92,8 @@ int lowerlayer_success(unsigned int ueid)
   int rc;
 
   emm_sap.primitive = EMMREG_LOWERLAYER_SUCCESS;
-  emm_sap.u.emm_reg.ueid = ueid;
-  rc = emm_sap_send(&emm_sap);
+  emm_sap.u.emm_reg.ueid = user->ueid;
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -117,7 +105,6 @@ int lowerlayer_success(unsigned int ueid)
  ** Description: Notify the EPS Mobility Management entity that lower la-  **
  **      yers failed to deliver data to the network                **
  **                                                                        **
- ** Inputs:  ueid:      UE lower layer identifier                  **
  **      Others:    None                                       **
  **                                                                        **
  ** Outputs:     None                                                      **
@@ -125,7 +112,7 @@ int lowerlayer_success(unsigned int ueid)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_failure(unsigned int ueid)
+int lowerlayer_failure(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -133,8 +120,8 @@ int lowerlayer_failure(unsigned int ueid)
   int rc;
 
   emm_sap.primitive = EMMREG_LOWERLAYER_FAILURE;
-  emm_sap.u.emm_reg.ueid = ueid;
-  rc = emm_sap_send(&emm_sap);
+  emm_sap.u.emm_reg.ueid = user->ueid;
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -155,12 +142,12 @@ int lowerlayer_failure(unsigned int ueid)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_establish(void)
+int lowerlayer_establish(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   /* Update the EPS Connection Management status */
-  _emm_data.ecm_status = ECM_CONNECTED;
+  user->emm_data->ecm_status = ECM_CONNECTED;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -180,7 +167,7 @@ int lowerlayer_establish(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_release(int cause)
+int lowerlayer_release(nas_user_t *user, int cause)
 {
   LOG_FUNC_IN;
 
@@ -188,11 +175,11 @@ int lowerlayer_release(int cause)
   int rc;
 
   /* Update the EPS Connection Management status */
-  _emm_data.ecm_status = ECM_IDLE;
+  user->emm_data->ecm_status = ECM_IDLE;
 
   emm_sap.primitive = EMMREG_LOWERLAYER_RELEASE;
-  emm_sap.u.emm_reg.ueid = 0;
-  rc = emm_sap_send(&emm_sap);
+  emm_sap.u.emm_reg.ueid = user->ueid;
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -204,7 +191,6 @@ int lowerlayer_release(int cause)
  ** Description: Notify the EPS Session Management entity that data have   **
  **      been received from lower layers                           **
  **                                                                        **
- ** Inputs:  ueid:      UE lower layer identifier                  **
  **      data:      Data transfered from lower layers          **
  **      Others:    None                                       **
  **                                                                        **
@@ -213,7 +199,7 @@ int lowerlayer_release(int cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_data_ind(unsigned int ueid, const OctetString *data)
+int lowerlayer_data_ind(nas_user_t *user, const OctetString *data)
 {
   esm_sap_t esm_sap;
   int rc;
@@ -223,10 +209,10 @@ int lowerlayer_data_ind(unsigned int ueid, const OctetString *data)
 
   esm_sap.primitive = ESM_UNITDATA_IND;
   esm_sap.is_standalone = TRUE;
-  esm_sap.ueid = ueid;
+  esm_sap.ueid = user->ueid;
 
   esm_sap.recv = data;
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -238,7 +224,6 @@ int lowerlayer_data_ind(unsigned int ueid, const OctetString *data)
  ** Description: Notify the EPS Mobility Management entity that data have  **
  **      to be transfered to lower layers                          **
  **                                                                        **
- ** Inputs:  ueid:      UE lower layer identifier                  **
  **          data:      Data to be transfered to lower layers      **
  **      Others:    None                                       **
  **                                                                        **
@@ -247,7 +232,7 @@ int lowerlayer_data_ind(unsigned int ueid, const OctetString *data)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int lowerlayer_data_req(unsigned int ueid, const OctetString *data)
+int lowerlayer_data_req(nas_user_t *user, const OctetString *data)
 {
   LOG_FUNC_IN;
 
@@ -257,16 +242,16 @@ int lowerlayer_data_req(unsigned int ueid, const OctetString *data)
   //struct emm_data_context_s *ctx  = NULL;
 
   emm_sap.primitive = EMMAS_DATA_REQ;
-  emm_sap.u.emm_as.u.data.guti = _emm_data.guti;
-  emm_sap.u.emm_as.u.data.ueid = 0;
-  sctx = _emm_data.security;
+  emm_sap.u.emm_as.u.data.guti = user->emm_data->guti;
+  emm_sap.u.emm_as.u.data.ueid = user->ueid;
+  sctx = user->emm_data->security;
 
   emm_sap.u.emm_as.u.data.NASinfo = 0;
   emm_sap.u.emm_as.u.data.NASmsg.length = data->length;
   emm_sap.u.emm_as.u.data.NASmsg.value = data->value;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.data.sctx, sctx, FALSE, TRUE);
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -297,17 +282,17 @@ int lowerlayer_data_req(unsigned int ueid, const OctetString *data)
  **      Others:    _lowerlayer_data                           **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_lowerlayer_initialize(lowerlayer_success_callback_t success,
+int emm_proc_lowerlayer_initialize(lowerlayer_data_t *lowerlayer_data, lowerlayer_success_callback_t success,
                                    lowerlayer_failure_callback_t failure,
                                    lowerlayer_release_callback_t release,
                                    void *args)
 {
   LOG_FUNC_IN;
 
-  _lowerlayer_data.success = success;
-  _lowerlayer_data.failure = failure;
-  _lowerlayer_data.release = release;
-  _lowerlayer_data.args = args;
+  lowerlayer_data->success = success;
+  lowerlayer_data->failure = failure;
+  lowerlayer_data->release = release;
+  lowerlayer_data->args = args;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -328,17 +313,17 @@ int emm_proc_lowerlayer_initialize(lowerlayer_success_callback_t success,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_lowerlayer_success(void)
+int emm_proc_lowerlayer_success(lowerlayer_data_t *lowerlayer_data)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
-  lowerlayer_success_callback_t emm_callback = _lowerlayer_data.success;
+  lowerlayer_success_callback_t emm_callback = lowerlayer_data->success;
 
   if (emm_callback) {
-    rc = (*emm_callback)(_lowerlayer_data.args);
-    _lowerlayer_data.success = NULL;
+    rc = (*emm_callback)(lowerlayer_data->args);
+    lowerlayer_data->success = NULL;
   }
 
   LOG_FUNC_RETURN (rc);
@@ -360,17 +345,17 @@ int emm_proc_lowerlayer_success(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_lowerlayer_failure(int is_initial)
+int emm_proc_lowerlayer_failure(lowerlayer_data_t *lowerlayer_data, int is_initial)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
-  lowerlayer_failure_callback_t emm_callback = _lowerlayer_data.failure;
+  lowerlayer_failure_callback_t emm_callback = lowerlayer_data->failure;
 
   if (emm_callback) {
-    rc = (*emm_callback)(is_initial, _lowerlayer_data.args);
-    _lowerlayer_data.failure = NULL;
+    rc = (*emm_callback)(is_initial, lowerlayer_data->args);
+    lowerlayer_data->failure = NULL;
   }
 
   LOG_FUNC_RETURN (rc);
@@ -391,17 +376,17 @@ int emm_proc_lowerlayer_failure(int is_initial)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_lowerlayer_release(void)
+int emm_proc_lowerlayer_release(lowerlayer_data_t *lowerlayer_data)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
-  lowerlayer_release_callback_t emm_callback = _lowerlayer_data.release;
+  lowerlayer_release_callback_t emm_callback = lowerlayer_data->release;
 
   if (emm_callback) {
-    rc = (*emm_callback)(_lowerlayer_data.args);
-    _lowerlayer_data.release = NULL;
+    rc = (*emm_callback)(lowerlayer_data->args);
+    lowerlayer_data->release = NULL;
   }
 
   LOG_FUNC_RETURN (rc);
diff --git a/openair3/NAS/UE/EMM/LowerLayer.h b/openair3/NAS/UE/EMM/LowerLayer.h
index 396de203d3da340565542069053262d16595d088..00e271feaa7e9c0dd3e3426401b6dd75c589f5e7 100644
--- a/openair3/NAS/UE/EMM/LowerLayer.h
+++ b/openair3/NAS/UE/EMM/LowerLayer.h
@@ -43,33 +43,17 @@ Description Defines EMM procedures executed by the Non-Access Stratum
 #define __LOWERLAYER_H__
 
 #include "OctetString.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
 
-/*
- * Type of EMM procedure callback function executed whenever data are
- * successfully delivered to the network
- */
-typedef int (*lowerlayer_success_callback_t)(void *);
-
-/*
- * Type of EMM procedure callback function executed when data are not
- * delivered to the network because a lower layer failure occurred
- */
-typedef int (*lowerlayer_failure_callback_t)(int, void *);
-
-/*
- * Type of EMM procedure callback function executed when NAS signalling
- * connection is released
- */
-typedef int (*lowerlayer_release_callback_t)(void *);
-
 /****************************************************************************/
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
+
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -78,12 +62,26 @@ typedef int (*lowerlayer_release_callback_t)(void *);
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-int lowerlayer_success(unsigned int ueid);
-int lowerlayer_failure(unsigned int ueid);
-int lowerlayer_establish(void);
-int lowerlayer_release(int cause);
-
-int lowerlayer_data_ind(unsigned int ueid, const OctetString *data);
-int lowerlayer_data_req(unsigned int ueid, const OctetString *data);
+/*
+ *---------------------------------------------------------------------------
+ *              Lower layer procedure
+ *---------------------------------------------------------------------------
+ */
+int emm_proc_lowerlayer_initialize(lowerlayer_data_t *lowerlayer_data, lowerlayer_success_callback_t success,
+                                   lowerlayer_failure_callback_t failure,
+                                   lowerlayer_release_callback_t release,
+                                   void *args);
+int emm_proc_lowerlayer_success(lowerlayer_data_t *lowerlayer_data);
+int emm_proc_lowerlayer_failure(lowerlayer_data_t *lowerlayer_data, int is_initial);
+int emm_proc_lowerlayer_release(lowerlayer_data_t *lowerlayer_data);
+
+
+int lowerlayer_success(nas_user_t *user);
+int lowerlayer_failure(nas_user_t *user);
+int lowerlayer_establish(nas_user_t *user);
+int lowerlayer_release(nas_user_t *user, int cause);
+
+int lowerlayer_data_ind(nas_user_t *user, const OctetString *data);
+int lowerlayer_data_req(nas_user_t *user, const OctetString *data);
 
 #endif /* __LOWERLAYER_H__*/
diff --git a/openair3/NAS/UE/EMM/LowerLayer_defs.h b/openair3/NAS/UE/EMM/LowerLayer_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..6035d4ed12db9111ef0bd75e7f81a550c4e58018
--- /dev/null
+++ b/openair3/NAS/UE/EMM/LowerLayer_defs.h
@@ -0,0 +1,33 @@
+#ifndef _LOWER_LAYER_DEFS_H
+#define _LOWER_LAYER_DEFS_H
+
+/*
+ * Type of EMM procedure callback function executed whenever data are
+ * successfully delivered to the network
+ */
+typedef int (*lowerlayer_success_callback_t)(void *);
+
+/*
+ * Type of EMM procedure callback function executed when data are not
+ * delivered to the network because a lower layer failure occurred
+ */
+typedef int (*lowerlayer_failure_callback_t)(int, void *);
+
+/*
+ * Type of EMM procedure callback function executed when NAS signalling
+ * connection is released
+ */
+typedef int (*lowerlayer_release_callback_t)(void *);
+
+/*
+ * Data structure used to handle EMM procedures executed by the UE upon
+ * receiving lower layer notifications
+ */
+typedef struct {
+  lowerlayer_success_callback_t success; /* Successful data delivery  */
+  lowerlayer_failure_callback_t failure; /* Lower layer failure   */
+  lowerlayer_release_callback_t release; /* NAS signalling release    */
+  void *args;         /* EMM procedure argument parameters    */
+} lowerlayer_data_t;
+
+#endif
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregistered.c b/openair3/NAS/UE/EMM/SAP/EmmDeregistered.c
index 801de96145d1ec4ec58271e0a5d13888086ba322..a2a93c4d780a68e36c6b97ee3a7c3eb0ee64d8b5 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregistered.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregistered.c
@@ -54,6 +54,7 @@ Description Implements the EPS Mobility Management procedures executed
 #include "nas_log.h"
 
 #include "emm_proc.h"
+#include "user_defs.h"
 
 #include <assert.h>
 
@@ -86,17 +87,17 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregistered(const emm_reg_t *evt)
+int EmmDeregistered(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED);
 
 
   /* Delete the authentication data RAND and RES */
-  rc = emm_proc_authentication_delete();
+  rc = emm_proc_authentication_delete(user);
 
   if (rc != RETURNok) {
     LOG_FUNC_RETURN (rc);
@@ -116,7 +117,7 @@ int EmmDeregistered(const emm_reg_t *evt)
     /*
      * The UE was powered on without a valid USIM application present
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_NO_IMSI);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NO_IMSI);
     break;
 
   case _EMMREG_REGISTER_REQ:
@@ -124,11 +125,11 @@ int EmmDeregistered(const emm_reg_t *evt)
      * The default EMM primary substate when the UE is switched on
      * with valid USIM application shall be PLMN-SEARCH
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_PLMN_SEARCH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_PLMN_SEARCH);
 
     if (rc != RETURNerror) {
       /* Process the network registration request */
-      rc = emm_fsm_process(evt);
+      rc = emm_fsm_process(user, evt);
     }
 
     break;
@@ -142,14 +143,14 @@ int EmmDeregistered(const emm_reg_t *evt)
 
     /* Move to the corresponding initial EMM state */
     if (evt->u.attach.is_emergency) {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_LIMITED_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_LIMITED_SERVICE);
     } else {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_NORMAL_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NORMAL_SERVICE);
     }
 
     if (rc != RETURNerror) {
       /* Restart the attach procedure */
-      rc = emm_proc_attach_restart();
+      rc = emm_proc_attach_restart(user);
     }
 
     break;
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttachNeeded.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttachNeeded.c
index 4f8cbc862bbc08ca30733660d13da0cfd77f7a0f..cbdc470a0b844d996fcef9c23626a5543734ec87 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttachNeeded.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttachNeeded.c
@@ -47,6 +47,7 @@ Description Implements the EPS Mobility Management procedures executed
 #include "emm_fsm.h"
 #include "commonDef.h"
 #include "nas_log.h"
+#include "user_defs.h"
 
 #include <assert.h>
 
@@ -77,11 +78,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredAttachNeeded(const emm_reg_t *evt)
+int EmmDeregisteredAttachNeeded(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_ATTACH_NEEDED);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_ATTACH_NEEDED);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttemptingToAttach.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttemptingToAttach.c
index ed1d079930a467611dd9420306d366944330d3fc..7ddfac03db225bfc34cb83c093dcff0f121f911d 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttemptingToAttach.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredAttemptingToAttach.c
@@ -79,13 +79,13 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredAttemptingToAttach(const emm_reg_t *evt)
+int EmmDeregisteredAttemptingToAttach(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
 
   switch (evt->primitive) {
   case _EMMREG_ATTACH_INIT:
@@ -97,14 +97,14 @@ int EmmDeregisteredAttemptingToAttach(const emm_reg_t *evt)
 
     /* Move to the corresponding initial EMM state */
     if (evt->u.attach.is_emergency) {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_LIMITED_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_LIMITED_SERVICE);
     } else {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_NORMAL_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NORMAL_SERVICE);
     }
 
     if (rc != RETURNerror) {
       /* Restart the attach procedure */
-      rc = emm_proc_attach_restart();
+      rc = emm_proc_attach_restart(user);
     }
 
     break;
@@ -113,14 +113,14 @@ int EmmDeregisteredAttemptingToAttach(const emm_reg_t *evt)
     /*
      * Data successfully delivered to the network
      */
-    rc = emm_proc_lowerlayer_success();
+    rc = emm_proc_lowerlayer_success(user->lowerlayer_data);
     break;
 
   case _EMMREG_LOWERLAYER_FAILURE:
     /*
      * Data failed to be delivered to the network
      */
-    rc = emm_proc_lowerlayer_failure(FALSE);
+    rc = emm_proc_lowerlayer_failure(user->lowerlayer_data, FALSE);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredInitiated.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredInitiated.c
index 220e666aa7d91a35c5417d42b2ac684d714426ab..d18b66a3162bc876043d649d78804973400aab6b 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredInitiated.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredInitiated.c
@@ -78,13 +78,13 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredInitiated(const emm_reg_t *evt)
+int EmmDeregisteredInitiated(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_INITIATED);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_INITIATED);
 
   switch (evt->primitive) {
 
@@ -94,7 +94,7 @@ int EmmDeregisteredInitiated(const emm_reg_t *evt)
      * bearer contexts have been deactivated as UE initiated
      * detach procedure successfully completed)
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED);
     break;
 
   case _EMMREG_DETACH_FAILED:
@@ -103,9 +103,9 @@ int EmmDeregisteredInitiated(const emm_reg_t *evt)
      * The detach procedure failed
      */
     if (evt->u.detach.type == EMM_DETACH_TYPE_IMSI) {
-      rc = emm_fsm_set_status(EMM_REGISTERED_NORMAL_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_REGISTERED_NORMAL_SERVICE);
     } else {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED);
     }
 
     break;
@@ -123,7 +123,8 @@ int EmmDeregisteredInitiated(const emm_reg_t *evt)
      * Lower layer failure or release of the NAS signalling connection
      * before the Detach Accept is received
      */
-    rc = emm_proc_lowerlayer_release();
+    // FIXME review
+    rc = emm_proc_lowerlayer_release(user->lowerlayer_data);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredLimitedService.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredLimitedService.c
index 78d418c69c57653b8727693e7e813b9649f182af..394325441597f64991cedf81764e4408caaae0c9 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredLimitedService.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredLimitedService.c
@@ -83,24 +83,24 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredLimitedService(const emm_reg_t *evt)
+int EmmDeregisteredLimitedService(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_LIMITED_SERVICE);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_LIMITED_SERVICE);
 
   switch (evt->primitive) {
   case _EMMREG_REGISTER_REQ:
     /*
      * The user manually re-selected a PLMN to register to
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_PLMN_SEARCH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_PLMN_SEARCH);
 
     if (rc != RETURNerror) {
       /* Process the network registration request */
-      rc = emm_fsm_process(evt);
+      rc = emm_fsm_process(user, evt);
     }
 
     break;
@@ -109,7 +109,7 @@ int EmmDeregisteredLimitedService(const emm_reg_t *evt)
     /*
      * Initiate attach procedure for emergency bearer services
      */
-    rc = emm_proc_attach(EMM_ATTACH_TYPE_EMERGENCY);
+    rc = emm_proc_attach(user, EMM_ATTACH_TYPE_EMERGENCY);
     break;
 
   case _EMMREG_ATTACH_REQ:
@@ -118,7 +118,7 @@ int EmmDeregisteredLimitedService(const emm_reg_t *evt)
      * (Attach Request message successfully delivered to the network);
      * enter state EMM-REGISTERED-INITIATED
      */
-    rc = emm_fsm_set_status(EMM_REGISTERED_INITIATED);
+    rc = emm_fsm_set_status(user, EMM_REGISTERED_INITIATED);
     break;
 
   case _EMMREG_LOWERLAYER_SUCCESS:
@@ -126,21 +126,21 @@ int EmmDeregisteredLimitedService(const emm_reg_t *evt)
      * Initial NAS message has been successfully delivered
      * to the network
      */
-    rc = emm_proc_lowerlayer_success();
+    rc = emm_proc_lowerlayer_success(user->lowerlayer_data);
     break;
 
   case _EMMREG_LOWERLAYER_FAILURE:
     /*
      * Initial NAS message failed to be delivered to the network
      */
-    rc = emm_proc_lowerlayer_failure(TRUE);
+    rc = emm_proc_lowerlayer_failure(user->lowerlayer_data, TRUE);
     break;
 
   case _EMMREG_LOWERLAYER_RELEASE:
     /*
      * NAS signalling connection has been released
      */
-    rc = emm_proc_lowerlayer_release();
+    rc = emm_proc_lowerlayer_release(user->lowerlayer_data);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoCellAvailable.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoCellAvailable.c
index 9f1f526e83f4dff12002c1c8eef0e8b2862c7494..af9ab5e5be773ccd0090a9986d506139bfe365be 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoCellAvailable.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoCellAvailable.c
@@ -84,13 +84,15 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredNoCellAvailable(const emm_reg_t *evt)
+int EmmDeregisteredNoCellAvailable(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
+  emm_data_t *emm_data = user->emm_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_NO_CELL_AVAILABLE);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_NO_CELL_AVAILABLE);
 
   switch (evt->primitive) {
     /* TODO: network re-selection is not allowed when in No Cell
@@ -101,14 +103,14 @@ int EmmDeregisteredNoCellAvailable(const emm_reg_t *evt)
     /*
      * The user manually re-selected a PLMN to register to
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_PLMN_SEARCH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_PLMN_SEARCH);
 
     if (rc != RETURNerror) {
       /*
        * Notify EMM that the MT is currently searching an operator
        * to register to
        */
-      rc = emm_proc_registration_notify(NET_REG_STATE_ON);
+      rc = emm_proc_registration_notify(user_api_id, emm_data, NET_REG_STATE_ON);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "EMM-FSM   - "
@@ -118,7 +120,7 @@ int EmmDeregisteredNoCellAvailable(const emm_reg_t *evt)
       /*
        * Perform network re-selection procedure
        */
-      rc = emm_proc_plmn_selection(evt->u.regist.index);
+      rc = emm_proc_plmn_selection(user, evt->u.regist.index);
     }
 
     break;
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoImsi.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoImsi.c
index 059c1f551ba6d955cce108f09285172821e9ea59..0f883c8109d3990939b755bdec22698dbe1013d5 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoImsi.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNoImsi.c
@@ -78,11 +78,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredNoImsi(const emm_reg_t *evt)
+int EmmDeregisteredNoImsi(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_NO_IMSI);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_NO_IMSI);
 
   LOG_TRACE(ERROR, "EMM-FSM   - USIM is not present or not valid");
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNormalService.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNormalService.c
index 85f98c4bd7b03e40e093b37d3c86827f1c21fe82..3ec7a96ba8ab416ff73ba3f2ac2bfa332a369a70 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNormalService.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredNormalService.c
@@ -84,24 +84,24 @@ extern uint8_t usim_test;
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredNormalService(const emm_reg_t *evt)
+int EmmDeregisteredNormalService(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_NORMAL_SERVICE);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_NORMAL_SERVICE);
 
   switch (evt->primitive) {
   case _EMMREG_REGISTER_REQ:
     /*
      * The user manually re-selected a PLMN to register to
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_PLMN_SEARCH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_PLMN_SEARCH);
 
     if (rc != RETURNerror) {
       /* Process the network registration request */
-      rc = emm_fsm_process(evt);
+      rc = emm_fsm_process(user, evt);
     }
 
     break;
@@ -112,11 +112,11 @@ int EmmDeregisteredNormalService(const emm_reg_t *evt)
      */
     if(usim_test == 0)
     {
-      rc = emm_proc_attach(EMM_ATTACH_TYPE_EPS);
+      rc = emm_proc_attach(user, EMM_ATTACH_TYPE_EPS);
     }
     else
     {
-      rc = emm_proc_attach(EMM_ATTACH_TYPE_IMSI); // CMW500 IMSI initial attach expected
+      rc = emm_proc_attach(user, EMM_ATTACH_TYPE_IMSI); // CMW500 IMSI initial attach expected
     }
     break;
 
@@ -126,7 +126,7 @@ int EmmDeregisteredNormalService(const emm_reg_t *evt)
      * message successfully delivered to the network);
      * enter state EMM-REGISTERED-INITIATED
      */
-    rc = emm_fsm_set_status(EMM_REGISTERED_INITIATED);
+    rc = emm_fsm_set_status(user, EMM_REGISTERED_INITIATED);
     break;
 
   case _EMMREG_LOWERLAYER_SUCCESS:
@@ -134,21 +134,21 @@ int EmmDeregisteredNormalService(const emm_reg_t *evt)
      * Initial NAS message has been successfully delivered
      * to the network
      */
-    rc = emm_proc_lowerlayer_success();
+    rc = emm_proc_lowerlayer_success(user->lowerlayer_data);
     break;
 
   case _EMMREG_LOWERLAYER_FAILURE:
     /*
      * Initial NAS message failed to be delivered to the network
      */
-    rc = emm_proc_lowerlayer_failure(TRUE);
+    rc = emm_proc_lowerlayer_failure(user->lowerlayer_data, TRUE);
     break;
 
   case _EMMREG_LOWERLAYER_RELEASE:
     /*
      * NAS signalling connection has been released
      */
-    rc = emm_proc_lowerlayer_release();
+    rc = emm_proc_lowerlayer_release(user->lowerlayer_data);
     break;
 
   case _EMMREG_ATTACH_CNF:
@@ -157,7 +157,7 @@ int EmmDeregisteredNormalService(const emm_reg_t *evt)
      * context activated;
      * enter state EMM-REGISTERED.
      */
-    rc = emm_fsm_set_status(EMM_REGISTERED);
+    rc = emm_fsm_set_status(user, EMM_REGISTERED);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredPlmnSearch.c b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredPlmnSearch.c
index d9865c9b720bf53334fa486b7eedf135c2966a0c..ed41362f2f900563e5776fe2847408cab369b93b 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmDeregisteredPlmnSearch.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmDeregisteredPlmnSearch.c
@@ -82,27 +82,29 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmDeregisteredPlmnSearch(const emm_reg_t *evt)
+int EmmDeregisteredPlmnSearch(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
+  emm_data_t *emm_data = user->emm_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_DEREGISTERED_PLMN_SEARCH);
+  assert(emm_fsm_get_status(user) == EMM_DEREGISTERED_PLMN_SEARCH);
 
   switch (evt->primitive) {
   case _EMMREG_NO_CELL:
     /*
      * No suitable cell of the selected PLMN has been found to camp on
      */
-    rc = emm_proc_registration_notify(NET_REG_STATE_DENIED);
+    rc = emm_proc_registration_notify(user_api_id, emm_data, NET_REG_STATE_DENIED);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "EMM-FSM   - "
                 "Failed to notify registration update");
     }
 
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_NO_CELL_AVAILABLE);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NO_CELL_AVAILABLE);
     break;
 
   case _EMMREG_REGISTER_REQ:
@@ -112,7 +114,7 @@ int EmmDeregisteredPlmnSearch(const emm_reg_t *evt)
      * may be selected either automatically or manually.
      * Or the user manually re-selected a PLMN to register to.
      */
-    rc = emm_proc_registration_notify(NET_REG_STATE_ON);
+    rc = emm_proc_registration_notify(user_api_id, emm_data, NET_REG_STATE_ON);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "EMM-FSM   - "
@@ -122,7 +124,7 @@ int EmmDeregisteredPlmnSearch(const emm_reg_t *evt)
     /*
      * Perform network selection procedure
      */
-    rc = emm_proc_plmn_selection(evt->u.regist.index);
+    rc = emm_proc_plmn_selection(user, evt->u.regist.index);
     break;
 
   case _EMMREG_REGISTER_REJ:
@@ -130,14 +132,14 @@ int EmmDeregisteredPlmnSearch(const emm_reg_t *evt)
      * The selected cell is known not to be able to provide normal
      * service
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_LIMITED_SERVICE);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_LIMITED_SERVICE);
     break;
 
   case _EMMREG_REGISTER_CNF:
     /*
      * A suitable cell of the selected PLMN has been found to camp on
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_NORMAL_SERVICE);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NORMAL_SERVICE);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmNull.c b/openair3/NAS/UE/EMM/SAP/EmmNull.c
index e482281a7184974a9604ec7a0f42d85912eb57dc..7b553f0a765474bc97e405d6c5d591874cdf0a2e 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmNull.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmNull.c
@@ -46,6 +46,7 @@ Description Implements the EPS Mobility Management procedures executed
 #include "nas_log.h"
 
 #include "emm_proc.h"
+#include "user_defs.h"
 
 #include <assert.h>
 
@@ -76,16 +77,16 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmNull(const emm_reg_t *evt)
+int EmmNull(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc;
 
-  assert(emm_fsm_get_status() == EMM_NULL);
+  assert(emm_fsm_get_status(user) == EMM_NULL);
 
   /* Delete the authentication data RAND and RES */
-  rc = emm_proc_authentication_delete();
+  rc = emm_proc_authentication_delete(user);
 
   if (rc != RETURNok) {
     LOG_FUNC_RETURN (rc);
@@ -97,14 +98,14 @@ int EmmNull(const emm_reg_t *evt)
      * The EPS capability has been enabled in the UE:
      * Move to the DEREGISTERED state;
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED);
 
     /*
      * And initialize the EMM procedure call manager in order to
      * establish an EMM context and make the UE reachable by an MME.
      */
     if (rc != RETURNerror) {
-      rc = emm_proc_initialize();
+      rc = emm_proc_initialize(user);
     }
 
     break;
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegistered.c b/openair3/NAS/UE/EMM/SAP/EmmRegistered.c
index dcbeee167eb8c3cabaec7fa3c15b369e7905df46..dbc51e51554382af949dee6bd1c6116e334d5186 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegistered.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegistered.c
@@ -81,13 +81,13 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegistered(const emm_reg_t *evt)
+int EmmRegistered(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED);
 
 
   switch (evt->primitive) {
@@ -96,7 +96,7 @@ int EmmRegistered(const emm_reg_t *evt)
     /*
      * Initiate detach procedure for EPS services
      */
-    rc = emm_proc_detach(EMM_DETACH_TYPE_EPS, evt->u.detach.switch_off);
+    rc = emm_proc_detach(user, EMM_DETACH_TYPE_EPS, evt->u.detach.switch_off);
     break;
 
   case _EMMREG_DETACH_REQ:
@@ -105,7 +105,7 @@ int EmmRegistered(const emm_reg_t *evt)
      * message successfully delivered to the network);
      * enter state EMM-DEREGISTERED-INITIATED
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_INITIATED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_INITIATED);
     break;
 
   case _EMMREG_DETACH_CNF:
@@ -113,7 +113,7 @@ int EmmRegistered(const emm_reg_t *evt)
      * The UE implicitly detached from the network (all EPS
      * bearer contexts may have been deactivated)
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED);
     break;
 
   case _EMMREG_TAU_REQ:
@@ -136,21 +136,21 @@ int EmmRegistered(const emm_reg_t *evt)
     /*
      * Data transfer message has been successfully delivered
      */
-    rc = emm_proc_lowerlayer_success();
+    rc = emm_proc_lowerlayer_success(user->lowerlayer_data);
     break;
 
   case _EMMREG_LOWERLAYER_FAILURE:
     /*
      * Data transfer message failed to be delivered
      */
-    rc = emm_proc_lowerlayer_failure(FALSE);
+    rc = emm_proc_lowerlayer_failure(user->lowerlayer_data, FALSE);
     break;
 
   case _EMMREG_LOWERLAYER_RELEASE:
     /*
      * NAS signalling connection has been released
      */
-    rc = emm_proc_lowerlayer_release();
+    rc = emm_proc_lowerlayer_release(user->lowerlayer_data);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredAttemptingToUpdate.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredAttemptingToUpdate.c
index 59d8f45df9a195a37f35115e5cfef6bac5fe05bd..21654123d1462e3deeba854f5790acc9398e68a4 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredAttemptingToUpdate.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredAttemptingToUpdate.c
@@ -78,11 +78,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredAttemptingToUpdate(const emm_reg_t *evt)
+int EmmRegisteredAttemptingToUpdate(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_ATTEMPTING_TO_UPDATE);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_ATTEMPTING_TO_UPDATE);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredImsiDetachInitiated.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredImsiDetachInitiated.c
index c522d5ec5cc0c84e31aba3b8c66bd74f2129e378..6855843a25bbe01ba70e58913ada26d0d12983d1 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredImsiDetachInitiated.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredImsiDetachInitiated.c
@@ -78,11 +78,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredImsiDetachInitiated(const emm_reg_t *evt)
+int EmmRegisteredImsiDetachInitiated(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_IMSI_DETACH_INITIATED);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_IMSI_DETACH_INITIATED);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredInitiated.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredInitiated.c
index 688b168b5318025aac0b049c7f825731c4bce5c0..d10f0d1053c0c939408600b85fc8c5a2424ce0dc 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredInitiated.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredInitiated.c
@@ -78,13 +78,15 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredInitiated(const emm_reg_t *evt)
+int EmmRegisteredInitiated(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
+  emm_data_t *emm_data = user->emm_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_INITIATED);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_INITIATED);
 
   switch (evt->primitive) {
   case _EMMREG_ATTACH_INIT:
@@ -96,14 +98,14 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
 
     /* Move to the corresponding initial EMM state */
     if (evt->u.attach.is_emergency) {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_LIMITED_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_LIMITED_SERVICE);
     } else {
-      rc = emm_fsm_set_status(EMM_DEREGISTERED_NORMAL_SERVICE);
+      rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NORMAL_SERVICE);
     }
 
     if (rc != RETURNerror) {
       /* Restart the attach procedure */
-      rc = emm_proc_attach_restart();
+      rc = emm_proc_attach_restart(user);
     }
 
     break;
@@ -114,7 +116,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * timer T3410 expired). The network attach procedure shall be
      * restarted when timer T3411 expires.
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
     break;
 
   case _EMMREG_ATTACH_EXCEEDED:
@@ -126,7 +128,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * SEARCH in order to perform a PLMN selection.
      */
     /* TODO: ATTEMPTING-TO-ATTACH or PLMN-SEARCH ??? */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH);
     break;
 
   case _EMMREG_ATTACH_CNF:
@@ -134,13 +136,13 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * EPS network attach accepted by the network;
      * enter state EMM-REGISTERED.
      */
-    rc = emm_fsm_set_status(EMM_REGISTERED);
+    rc = emm_fsm_set_status(user, EMM_REGISTERED);
 
     if (rc != RETURNerror) {
       /*
        * Notify EMM that the MT is registered
        */
-      rc = emm_proc_registration_notify(NET_REG_STATE_HN);
+      rc = emm_proc_registration_notify(user_api_id, emm_data, NET_REG_STATE_HN);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "EMM-FSM   - "
@@ -161,13 +163,13 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * EPS network attach rejected by the network;
      * enter state EMM-DEREGISTERED.
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED);
 
     if (rc != RETURNerror) {
       /*
        * Notify EMM that the MT's registration is denied
        */
-      rc = emm_proc_registration_notify(NET_REG_STATE_DENIED);
+      rc = emm_proc_registration_notify(user_api_id, emm_data, NET_REG_STATE_DENIED);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "EMM-FSM   - "
@@ -181,11 +183,11 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
     /*
      * The UE has to select a new PLMN to register to
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_PLMN_SEARCH);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_PLMN_SEARCH);
 
     if (rc != RETURNerror) {
       /* Process the network registration request */
-      rc = emm_fsm_process(evt);
+      rc = emm_fsm_process(user, evt);
     }
 
     break;
@@ -194,7 +196,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
     /*
      * The UE failed to register to the network for normal EPS service
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_LIMITED_SERVICE);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_LIMITED_SERVICE);
     break;
 
   case _EMMREG_NO_IMSI:
@@ -202,14 +204,14 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * The UE failed to register to the network for emergency
      * bearer services
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_NO_IMSI);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_NO_IMSI);
     break;
 
   case _EMMREG_DETACH_INIT:
     /*
      * Initiate detach procedure for EPS services
      */
-    rc = emm_proc_detach(EMM_DETACH_TYPE_EPS, evt->u.detach.switch_off);
+    rc = emm_proc_detach(user, EMM_DETACH_TYPE_EPS, evt->u.detach.switch_off);
     break;
 
   case _EMMREG_DETACH_REQ:
@@ -218,7 +220,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * message successfully delivered to the network);
      * enter state EMM-DEREGISTERED-INITIATED
      */
-    rc = emm_fsm_set_status(EMM_DEREGISTERED_INITIATED);
+    rc = emm_fsm_set_status(user, EMM_DEREGISTERED_INITIATED);
     break;
 
   case _EMMREG_LOWERLAYER_SUCCESS:
@@ -228,7 +230,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * any message transfered by EMM common procedures requested
      * by the network.
      */
-    rc = emm_proc_lowerlayer_success();
+    rc = emm_proc_lowerlayer_success(user->lowerlayer_data);
     break;
 
   case _EMMREG_LOWERLAYER_FAILURE:
@@ -238,7 +240,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * any message transfered by EMM common procedures requested
      * by the network.
      */
-    rc = emm_proc_lowerlayer_failure(FALSE);
+    rc = emm_proc_lowerlayer_failure(user->lowerlayer_data, FALSE);
     break;
 
   case _EMMREG_LOWERLAYER_RELEASE:
@@ -247,7 +249,7 @@ int EmmRegisteredInitiated(const emm_reg_t *evt)
      * Accept, Attach Reject, or any message transfered by EMM common
      * procedures requested by the network, is received.
      */
-    rc = emm_proc_lowerlayer_release();
+    rc = emm_proc_lowerlayer_release(user->lowerlayer_data);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredLimitedService.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredLimitedService.c
index 280252b8ed671eb1f827d04306dd40a16a08b0b9..b4ea68f689573330e45d844b076f180e2c92f0e8 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredLimitedService.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredLimitedService.c
@@ -74,11 +74,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredLimitedService(const emm_reg_t *evt)
+int EmmRegisteredLimitedService(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_LIMITED_SERVICE);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_LIMITED_SERVICE);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredNoCellAvailable.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredNoCellAvailable.c
index a22f3d76c6bbb4483f1254c74f8c7b112838d04c..0328ae81920f9eece5dddc709d377351222b09a9 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredNoCellAvailable.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredNoCellAvailable.c
@@ -75,11 +75,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredNoCellAvailable(const emm_reg_t *evt)
+int EmmRegisteredNoCellAvailable(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_NO_CELL_AVAILABLE);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_NO_CELL_AVAILABLE);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredNormalService.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredNormalService.c
index bc25a1e4be693ac17211e1c9e9c38d66437084d6..8b1c8721f2d6f091645c0db14a19d22611e5b0f9 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredNormalService.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredNormalService.c
@@ -74,11 +74,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredNormalService(const emm_reg_t *evt)
+int EmmRegisteredNormalService(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_NORMAL_SERVICE);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_NORMAL_SERVICE);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredPlmnSearch.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredPlmnSearch.c
index 453e39558efc67646f330f6fc017f21ed971a7af..8ee92c4454cce5d18301cb6dbced9b0d8a7aab32 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredPlmnSearch.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredPlmnSearch.c
@@ -74,46 +74,13 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredPlmnSearch(const emm_reg_t *evt)
+int EmmRegisteredPlmnSearch(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_PLMN_SEARCH);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_PLMN_SEARCH);
 
   /* TODO */
 
   LOG_FUNC_RETURN (RETURNok);
 }
-
-#if 0
-/****************************************************************************
- **                                                                        **
- ** Name:    EmmRegisteredPlmnSearch_xxx()                             **
- **                                                                        **
- ** Description: Procedure executed when xxx                               **
- **      while the EMM-SAP is in EMM-REGISTERED.PLMN-SEARCH state. **
- **                                                                        **
- ** Inputs:  evt:       The received EMM-SAP event                 **
- **      Others:    emm_fsm_status                             **
- **                                                                        **
- ** Outputs:     None                                                      **
- **      Return:    RETURNok, RETURNerror                      **
- **      Others:    emm_fsm_status                             **
- **                                                                        **
- ***************************************************************************/
-int EmmRegisteredPlmnSearch_xxx(const emm_reg_t *evt)
-{
-  LOG_FUNC_IN;
-
-  assert(emm_fsm_get_status() == EMM_REGISTERED_PLMN_SEARCH);
-
-  /* TODO */
-
-  LOG_FUNC_RETURN (RETURNok);
-}
-#endif
-
-/****************************************************************************/
-/*********************  L O C A L    F U N C T I O N S  *********************/
-/****************************************************************************/
-
diff --git a/openair3/NAS/UE/EMM/SAP/EmmRegisteredUpdateNeeded.c b/openair3/NAS/UE/EMM/SAP/EmmRegisteredUpdateNeeded.c
index 36f35fe351bdaa337e3152cd3d6baefca408bf4f..2a81b488dce0dd9ce5d71ff3609d392a0b2ae413 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmRegisteredUpdateNeeded.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmRegisteredUpdateNeeded.c
@@ -80,11 +80,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmRegisteredUpdateNeeded(const emm_reg_t *evt)
+int EmmRegisteredUpdateNeeded(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_REGISTERED_UPDATE_NEEDED);
+  assert(emm_fsm_get_status(user) == EMM_REGISTERED_UPDATE_NEEDED);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmServiceRequestInitiated.c b/openair3/NAS/UE/EMM/SAP/EmmServiceRequestInitiated.c
index a72b2d6003e35431e9df061d79f1cf1362bf9250..b556efb7f44fa02ef98c75eeb0b13b8620b6ada7 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmServiceRequestInitiated.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmServiceRequestInitiated.c
@@ -76,11 +76,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmServiceRequestInitiated(const emm_reg_t *evt)
+int EmmServiceRequestInitiated(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_SERVICE_REQUEST_INITIATED);
+  assert(emm_fsm_get_status(user) == EMM_SERVICE_REQUEST_INITIATED);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/EmmTrackingAreaUpdatingInitiated.c b/openair3/NAS/UE/EMM/SAP/EmmTrackingAreaUpdatingInitiated.c
index 69699cc117b5c86ef9b4df37ae08a2b931b3b18d..91bb660ee121f4b328a3d38a04d2590910e13244 100644
--- a/openair3/NAS/UE/EMM/SAP/EmmTrackingAreaUpdatingInitiated.c
+++ b/openair3/NAS/UE/EMM/SAP/EmmTrackingAreaUpdatingInitiated.c
@@ -76,11 +76,11 @@ Description Implements the EPS Mobility Management procedures executed
  **      Others:    emm_fsm_status                             **
  **                                                                        **
  ***************************************************************************/
-int EmmTrackingAreaUpdatingInitiated(const emm_reg_t *evt)
+int EmmTrackingAreaUpdatingInitiated(nas_user_t *user, const emm_reg_t *evt)
 {
   LOG_FUNC_IN;
 
-  assert(emm_fsm_get_status() == EMM_TRACKING_AREA_UPDATING_INITIATED);
+  assert(emm_fsm_get_status(user) == EMM_TRACKING_AREA_UPDATING_INITIATED);
 
   /* TODO */
 
diff --git a/openair3/NAS/UE/EMM/SAP/emm_as.c b/openair3/NAS/UE/EMM/SAP/emm_as.c
index 7650f97ee88d0e43911a3adefe67594da0606e5a..d833b5bba08178921e45151d3e2114c096d1bb06 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_as.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_as.c
@@ -53,6 +53,8 @@ Description Defines the EMMAS Service Access Point that provides
 #include "emm_cause.h"
 #include "LowerLayer.h"
 
+#include "emm_proc.h"
+
 #include <string.h> // memset
 #include <stdlib.h> // malloc, free
 
@@ -65,10 +67,6 @@ Description Defines the EMMAS Service Access Point that provides
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
-extern int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat);
-
-extern int emm_proc_status(unsigned int ueid, int emm_cause);
-
 /****************************************************************************/
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
@@ -99,19 +97,19 @@ static const char *_emm_as_primitive_str[] = {
  * Functions executed to process EMM procedures upon receiving
  * data from the network
  */
-static int _emm_as_recv(unsigned int ueid, const char *msg, int len,
+static int _emm_as_recv(nas_user_t *user, const char *msg, int len,
                         int *emm_cause);
 
-static int _emm_as_establish_cnf(const emm_as_establish_t *msg, int *emm_cause);
-static int _emm_as_establish_rej(void);
-static int _emm_as_release_ind(const emm_as_release_t *msg);
+static int _emm_as_establish_cnf(nas_user_t *user, const emm_as_establish_t *msg, int *emm_cause);
+static int _emm_as_establish_rej(nas_user_t *user);
+static int _emm_as_release_ind(nas_user_t *user, const emm_as_release_t *msg);
 static int _emm_as_page_ind(const emm_as_page_t *msg);
 
 
-static int _emm_as_cell_info_res(const emm_as_cell_info_t *msg);
+static int _emm_as_cell_info_res(nas_user_t *user, const emm_as_cell_info_t *msg);
 static int _emm_as_cell_info_ind(const emm_as_cell_info_t *msg);
 
-static int _emm_as_data_ind(const emm_as_data_t *msg, int *emm_cause);
+static int _emm_as_data_ind(nas_user_t *user, const emm_as_data_t *msg, int *emm_cause);
 
 /*
  * Functions executed to send data to the network when requested
@@ -133,18 +131,18 @@ static int _emm_as_encrypt(
   int length,
   emm_security_context_t *emm_security_context);
 
-static int _emm_as_send(const emm_as_t *msg);
+static int _emm_as_send(const nas_user_t *user, const emm_as_t *msg);
 
-static int _emm_as_security_res(const emm_as_security_t *,
+static int _emm_as_security_res(const emm_data_t *emm_data, const emm_as_security_t *,
                                 ul_info_transfer_req_t *);
-static int _emm_as_establish_req(const emm_as_establish_t *,
+static int _emm_as_establish_req(const emm_data_t *emm_data, const emm_as_establish_t *,
                                  nas_establish_req_t *);
 
 
 static int _emm_as_cell_info_req(const emm_as_cell_info_t *, cell_info_req_t *);
 
-static int _emm_as_data_req(const emm_as_data_t *, ul_info_transfer_req_t *);
-static int _emm_as_status_ind(const emm_as_status_t *, ul_info_transfer_req_t *);
+static int _emm_as_data_req(const emm_data_t *emm_data, const emm_as_data_t *msg, ul_info_transfer_req_t *);
+static int _emm_as_status_ind(const emm_data_t *emm_data, const emm_as_status_t *, ul_info_transfer_req_t *);
 static int _emm_as_release_req(const emm_as_release_t *, nas_release_req_t *);
 
 /****************************************************************************/
@@ -165,7 +163,7 @@ static int _emm_as_release_req(const emm_as_release_t *, nas_release_req_t *);
  **      Others:    NONE                                       **
  **                                                                        **
  ***************************************************************************/
-void emm_as_initialize(void)
+void emm_as_initialize(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -188,7 +186,7 @@ void emm_as_initialize(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_as_send(const emm_as_t *msg)
+int emm_as_send(nas_user_t *user, const emm_as_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -196,28 +194,25 @@ int emm_as_send(const emm_as_t *msg)
   int emm_cause = EMM_CAUSE_SUCCESS;
   emm_as_primitive_t primitive = msg->primitive;
 
-  uint32_t ueid = 0;
-
   LOG_TRACE(INFO, "EMMAS-SAP - Received primitive %s (%d)",
             _emm_as_primitive_str[primitive - _EMMAS_START - 1], primitive);
 
   switch (primitive) {
   case _EMMAS_DATA_IND:
-    rc = _emm_as_data_ind(&msg->u.data, &emm_cause);
-    ueid = msg->u.data.ueid;
+    rc = _emm_as_data_ind(user, &msg->u.data, &emm_cause);
     break;
 
 
   case _EMMAS_ESTABLISH_CNF:
-    rc = _emm_as_establish_cnf(&msg->u.establish, &emm_cause);
+    rc = _emm_as_establish_cnf(user, &msg->u.establish, &emm_cause);
     break;
 
   case _EMMAS_ESTABLISH_REJ:
-    rc = _emm_as_establish_rej();
+    rc = _emm_as_establish_rej(user);
     break;
 
   case _EMMAS_RELEASE_IND:
-    rc = _emm_as_release_ind(&msg->u.release);
+    rc = _emm_as_release_ind(user, &msg->u.release);
     break;
 
   case _EMMAS_PAGE_IND:
@@ -225,7 +220,7 @@ int emm_as_send(const emm_as_t *msg)
     break;
 
   case _EMMAS_CELL_INFO_RES:
-    rc = _emm_as_cell_info_res(&msg->u.cell_info);
+    rc = _emm_as_cell_info_res(user, &msg->u.cell_info);
     break;
 
   case _EMMAS_CELL_INFO_IND:
@@ -234,7 +229,7 @@ int emm_as_send(const emm_as_t *msg)
 
   default:
     /* Other primitives are forwarded to the Access Stratum */
-    rc = _emm_as_send(msg);
+    rc = _emm_as_send(user, msg);
 
     if (rc != RETURNok) {
       LOG_TRACE(ERROR, "EMMAS-SAP - "
@@ -266,7 +261,7 @@ int emm_as_send(const emm_as_t *msg)
     LOG_TRACE(WARNING, "EMMAS-SAP - Received EMM message is not valid "
               "(cause=%d)", emm_cause);
     /* Return an EMM status message */
-    rc = emm_proc_status(ueid, emm_cause);
+    rc = emm_proc_status(user, emm_cause);
   }
 
   if (rc != RETURNok) {
@@ -306,7 +301,7 @@ int emm_as_send(const emm_as_t *msg)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_recv(unsigned int ueid, const char *msg, int len,
+static int _emm_as_recv(nas_user_t *user, const char *msg, int len,
                         int *emm_cause)
 {
   LOG_FUNC_IN;
@@ -321,7 +316,7 @@ static int _emm_as_recv(unsigned int ueid, const char *msg, int len,
 
   emm_security_context_t       *security = NULL;    /* Current EPS NAS security context     */
 
-  security = _emm_data.security;
+  security = user->emm_data->security;
 
   /* Decode the received message */
   decoder_rc = nas_message_decode(msg, &nas_msg, len, security);
@@ -338,34 +333,34 @@ static int _emm_as_recv(unsigned int ueid, const char *msg, int len,
 
   switch (emm_msg->header.message_type) {
   case EMM_STATUS:
-    rc = emm_recv_status(ueid, &emm_msg->emm_status, emm_cause);
+    rc = emm_recv_status(user->ueid, &emm_msg->emm_status, emm_cause);
     break;
 
   case IDENTITY_REQUEST:
-    rc = emm_recv_identity_request(&emm_msg->identity_request,
+    rc = emm_recv_identity_request(user, &emm_msg->identity_request,
                                    emm_cause);
     break;
 
   case AUTHENTICATION_REQUEST:
-    rc = emm_recv_authentication_request(
+    rc = emm_recv_authentication_request(user,
            &emm_msg->authentication_request,
            emm_cause);
     break;
 
   case AUTHENTICATION_REJECT:
-    rc = emm_recv_authentication_reject(
+    rc = emm_recv_authentication_reject(user,
            &emm_msg->authentication_reject,
            emm_cause);
     break;
 
   case SECURITY_MODE_COMMAND:
-    rc = emm_recv_security_mode_command(
+    rc = emm_recv_security_mode_command(user,
            &emm_msg->security_mode_command,
            emm_cause);
     break;
 
   case DETACH_ACCEPT:
-    rc = emm_recv_detach_accept(&emm_msg->detach_accept, emm_cause);
+    rc = emm_recv_detach_accept(user, &emm_msg->detach_accept, emm_cause);
     break;
 
 
@@ -406,7 +401,7 @@ static int _emm_as_recv(unsigned int ueid, const char *msg, int len,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_data_ind(const emm_as_data_t *msg, int *emm_cause)
+static int _emm_as_data_ind(nas_user_t *user, const emm_as_data_t *msg, int *emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -428,7 +423,7 @@ static int _emm_as_data_ind(const emm_as_data_t *msg, int *emm_cause)
         memset(&header, 0, sizeof(header));
         /* Decrypt the received security protected message */
 
-        security = _emm_data.security;
+        security = user->emm_data->security;
         int bytes = nas_message_decrypt((char *)(msg->NASmsg.value),
                                         plain_msg,
                                         &header,
@@ -443,23 +438,23 @@ static int _emm_as_data_ind(const emm_as_data_t *msg, int *emm_cause)
         } else if (header.protocol_discriminator ==
                    EPS_MOBILITY_MANAGEMENT_MESSAGE) {
           /* Process EMM data */
-          rc = _emm_as_recv(msg->ueid, plain_msg, bytes, emm_cause);
+          rc = _emm_as_recv(user, plain_msg, bytes, emm_cause);
         } else if (header.protocol_discriminator ==
                    EPS_SESSION_MANAGEMENT_MESSAGE) {
           const OctetString data = {bytes, (uint8_t *)plain_msg};
           /* Foward ESM data to EPS session management */
-          rc = lowerlayer_data_ind(msg->ueid, &data);
+          rc = lowerlayer_data_ind(user, &data);
         }
 
         free(plain_msg);
       }
     } else {
       /* Process successfull lower layer transfer indication */
-      rc = lowerlayer_success(msg->ueid);
+      rc = lowerlayer_success(user);
     }
   } else {
     /* Process lower layer transmission failure of NAS message */
-    rc = lowerlayer_failure(msg->ueid);
+    rc = lowerlayer_failure(user);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -482,7 +477,7 @@ static int _emm_as_data_ind(const emm_as_data_t *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_establish_cnf(const emm_as_establish_t *msg,
+static int _emm_as_establish_cnf(nas_user_t *user, const emm_as_establish_t *msg,
                                  int *emm_cause)
 {
   LOG_FUNC_IN;
@@ -494,11 +489,11 @@ static int _emm_as_establish_cnf(const emm_as_establish_t *msg,
 
   if (msg->NASmsg.length > 0) {
     /* The NAS signalling connection is established */
-    (void) lowerlayer_establish();
+    (void) lowerlayer_establish(user);
   } else {
     /* The initial NAS message has been successfully delivered to
      * lower layers */
-    rc = lowerlayer_success(0);
+    rc = lowerlayer_success(user);
     LOG_FUNC_RETURN (rc);
   }
 
@@ -509,7 +504,7 @@ static int _emm_as_establish_cnf(const emm_as_establish_t *msg,
   decoder_rc = nas_message_decode((char *)(msg->NASmsg.value),
                                   &nas_msg,
                                   msg->NASmsg.length,
-                                  _emm_data.security);
+                                  user->emm_data->security);
 
   if (decoder_rc < 0) {
     LOG_TRACE(WARNING, "EMMAS-SAP - Failed to decode initial NAS message"
@@ -523,11 +518,11 @@ static int _emm_as_establish_cnf(const emm_as_establish_t *msg,
 
   switch (emm_msg->header.message_type) {
   case ATTACH_ACCEPT:
-    rc = emm_recv_attach_accept(&emm_msg->attach_accept, emm_cause);
+    rc = emm_recv_attach_accept(user, &emm_msg->attach_accept, emm_cause);
     break;
 
   case ATTACH_REJECT:
-    rc = emm_recv_attach_reject(&emm_msg->attach_reject, emm_cause);
+    rc = emm_recv_attach_reject(user, &emm_msg->attach_reject, emm_cause);
     break;
 
   case DETACH_ACCEPT:
@@ -563,7 +558,7 @@ static int _emm_as_establish_cnf(const emm_as_establish_t *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_establish_rej(void)
+static int _emm_as_establish_rej(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -573,7 +568,7 @@ static int _emm_as_establish_rej(void)
             "failure");
 
   /* Process lower layer transmission failure of initial NAS message */
-  rc = lowerlayer_failure(0);
+  rc = lowerlayer_failure(user);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -595,7 +590,7 @@ static int _emm_as_establish_rej(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_release_ind(const emm_as_release_t *msg)
+static int _emm_as_release_ind(nas_user_t *user, const emm_as_release_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -605,7 +600,7 @@ static int _emm_as_release_ind(const emm_as_release_t *msg)
             "(cause=%d)", msg->cause);
 
   /* Process NAS signalling connection release indication */
-  rc = lowerlayer_release(msg->cause);
+  rc = lowerlayer_release(user, msg->cause);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -663,7 +658,7 @@ static int _emm_as_page_ind(const emm_as_page_t *msg)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_cell_info_res(const emm_as_cell_info_t *msg)
+static int _emm_as_cell_info_res(nas_user_t *user, const emm_as_cell_info_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -685,7 +680,7 @@ static int _emm_as_cell_info_res(const emm_as_cell_info_t *msg)
   }
 
   /* Notify EMM that a cell has been found */
-  rc = emm_proc_plmn_selection_end(msg->found, msg->tac, msg->cellID, AcT);
+  rc = emm_proc_plmn_selection_end(user, msg->found, msg->tac, msg->cellID, AcT);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -928,7 +923,7 @@ _emm_as_encrypt(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_send(const emm_as_t *msg)
+static int _emm_as_send(const nas_user_t *user, const emm_as_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -937,13 +932,13 @@ static int _emm_as_send(const emm_as_t *msg)
 
   switch (msg->primitive) {
   case _EMMAS_DATA_REQ:
-    as_msg.msgID = _emm_as_data_req(
+    as_msg.msgID = _emm_as_data_req(user->emm_data,
                      &msg->u.data,
                      &as_msg.msg.ul_info_transfer_req);
     break;
 
   case _EMMAS_STATUS_IND:
-    as_msg.msgID = _emm_as_status_ind(
+    as_msg.msgID = _emm_as_status_ind(user->emm_data,
                      &msg->u.status,
                      &as_msg.msg.ul_info_transfer_req);
     break;
@@ -956,13 +951,13 @@ static int _emm_as_send(const emm_as_t *msg)
 
 
   case _EMMAS_SECURITY_RES:
-    as_msg.msgID = _emm_as_security_res(
+    as_msg.msgID = _emm_as_security_res(user->emm_data,
                      &msg->u.security,
                      &as_msg.msg.ul_info_transfer_req);
     break;
 
   case _EMMAS_ESTABLISH_REQ:
-    as_msg.msgID = _emm_as_establish_req(
+    as_msg.msgID = _emm_as_establish_req(user->emm_data,
                      &msg->u.establish,
                      &as_msg.msg.nas_establish_req);
     break;
@@ -996,7 +991,8 @@ static int _emm_as_send(const emm_as_t *msg)
     case AS_CELL_INFO_REQ: {
       nas_itti_cell_info_req(
         as_msg.msg.cell_info_req.plmnID,
-        as_msg.msg.cell_info_req.rat);
+        as_msg.msg.cell_info_req.rat,
+        user->ueid);
       LOG_FUNC_RETURN (RETURNok);
     }
     break;
@@ -1008,7 +1004,8 @@ static int _emm_as_send(const emm_as_t *msg)
         as_msg.msg.nas_establish_req.s_tmsi,
         as_msg.msg.nas_establish_req.plmnID,
         as_msg.msg.nas_establish_req.initialNasMsg.data,
-        as_msg.msg.nas_establish_req.initialNasMsg.length);
+        as_msg.msg.nas_establish_req.initialNasMsg.length,
+        user->ueid);
       LOG_FUNC_RETURN (RETURNok);
     }
     break;
@@ -1017,7 +1014,8 @@ static int _emm_as_send(const emm_as_t *msg)
       nas_itti_ul_data_req(
         as_msg.msg.ul_info_transfer_req.UEid,
         as_msg.msg.ul_info_transfer_req.nasMsg.data,
-        as_msg.msg.ul_info_transfer_req.nasMsg.length);
+        as_msg.msg.ul_info_transfer_req.nasMsg.length,
+        user->ueid);
       LOG_FUNC_RETURN (RETURNok);
     }
     break;
@@ -1026,7 +1024,8 @@ static int _emm_as_send(const emm_as_t *msg)
       nas_itti_rab_establish_rsp(
         as_msg.msg.rab_establish_rsp.s_tmsi,
         as_msg.msg.rab_establish_rsp.rabID,
-        as_msg.msg.rab_establish_rsp.errCode);
+        as_msg.msg.rab_establish_rsp.errCode,
+        user->ueid);
       LOG_FUNC_RETURN (RETURNok);
     }
     break;
@@ -1065,7 +1064,7 @@ static int _emm_as_send(const emm_as_t *msg)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_data_req(const emm_as_data_t *msg,
+static int _emm_as_data_req(const emm_data_t *emm_data, const emm_as_data_t *msg,
                             ul_info_transfer_req_t *as_msg)
 {
   LOG_FUNC_IN;
@@ -1112,7 +1111,7 @@ static int _emm_as_data_req(const emm_as_data_t *msg,
     int bytes;
     emm_security_context_t    *emm_security_context   = NULL;
 
-    emm_security_context = _emm_data.security;
+    emm_security_context = emm_data->security;
 
     if (emm_security_context) {
 
@@ -1161,7 +1160,7 @@ static int _emm_as_data_req(const emm_as_data_t *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_status_ind(const emm_as_status_t *msg,
+static int _emm_as_status_ind(const emm_data_t *emm_data, const emm_as_status_t *msg,
                               ul_info_transfer_req_t *as_msg)
 {
   LOG_FUNC_IN;
@@ -1193,7 +1192,7 @@ static int _emm_as_status_ind(const emm_as_status_t *msg,
   if (size > 0) {
     emm_security_context_t    *emm_security_context   = NULL;
 
-    emm_security_context = _emm_data.security;
+    emm_security_context = emm_data->security;
 
     if (emm_security_context) {
 
@@ -1275,7 +1274,7 @@ static int _emm_as_release_req(const emm_as_release_t *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_security_res(const emm_as_security_t *msg,
+static int _emm_as_security_res(const emm_data_t *emm_data, const emm_as_security_t *msg,
                                 ul_info_transfer_req_t *as_msg)
 {
   LOG_FUNC_IN;
@@ -1340,7 +1339,7 @@ static int _emm_as_security_res(const emm_as_security_t *msg,
     int bytes = _emm_as_encode(&as_msg->nasMsg,
                                &nas_msg,
                                size,
-                               _emm_data.security);
+                               emm_data->security);
 
     if (bytes > 0) {
       LOG_FUNC_RETURN (AS_UL_INFO_TRANSFER_REQ);
@@ -1369,7 +1368,7 @@ static int _emm_as_security_res(const emm_as_security_t *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_as_establish_req(const emm_as_establish_t *msg,
+static int _emm_as_establish_req(const emm_data_t *emm_data, const emm_as_establish_t *msg,
                                  nas_establish_req_t *as_msg)
 {
   LOG_FUNC_IN;
@@ -1433,7 +1432,7 @@ static int _emm_as_establish_req(const emm_as_establish_t *msg,
                   &as_msg->initialNasMsg,
                   &nas_msg,
                   size,
-                  _emm_data.security);
+                  emm_data->security);
 
     if (bytes > 0) {
       LOG_FUNC_RETURN (AS_NAS_ESTABLISH_REQ);
diff --git a/openair3/NAS/UE/EMM/SAP/emm_as.h b/openair3/NAS/UE/EMM/SAP/emm_as.h
index 04a14cf3cb26f84cd681315bd76e4d3370e9fcc7..726a363d75c859d4ae8381a44e00c29b951f23e3 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_as.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_as.h
@@ -41,6 +41,7 @@ Description Defines the EMMAS Service Access Point that provides
 #define __EMM_AS_H__
 
 #include "emm_asDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -58,8 +59,8 @@ Description Defines the EMMAS Service Access Point that provides
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void emm_as_initialize(void);
+void emm_as_initialize(nas_user_t *user);
 
-int emm_as_send(const emm_as_t *msg);
+int emm_as_send(nas_user_t *user, const emm_as_t *msg);
 
 #endif /* __EMM_AS_H__*/
diff --git a/openair3/NAS/UE/EMM/SAP/emm_esm.c b/openair3/NAS/UE/EMM/SAP/emm_esm.c
index a12d71d79beca3d6458aaadf49998dcbe213de83..798e682b112c09f7d54ccebcb4b7dc6fec0492a9 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_esm.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_esm.c
@@ -108,7 +108,7 @@ void emm_esm_initialize(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_esm_send(const emm_esm_t *msg)
+int emm_esm_send(nas_user_t *user, const emm_esm_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -123,7 +123,7 @@ int emm_esm_send(const emm_esm_t *msg)
   case _EMMESM_ESTABLISH_REQ:
     /* ESM requests EMM to initiate an attach procedure before
      * requesting subsequent connectivity to additional PDNs */
-    rc = emm_proc_attach_restart();
+    rc = emm_proc_attach_restart(user);
     break;
 
   case _EMMESM_ESTABLISH_CNF:
@@ -134,11 +134,11 @@ int emm_esm_send(const emm_esm_t *msg)
       if (msg->u.establish.is_emergency) {
         /* Consider the UE attached for emergency bearer services
          * only */
-        rc = emm_proc_attach_set_emergency();
+        rc = emm_proc_attach_set_emergency(user->emm_data);
       }
     } else {
       /* Consider the UE locally detached from the network */
-      rc = emm_proc_attach_set_detach();
+      rc = emm_proc_attach_set_detach(user);
     }
 
     break;
@@ -149,7 +149,7 @@ int emm_esm_send(const emm_esm_t *msg)
 
   case _EMMESM_UNITDATA_REQ:
     /* ESM requests EMM to transfer ESM data unit to lower layer */
-    rc = lowerlayer_data_req(msg->ueid, &msg->u.data.msg);
+    rc = lowerlayer_data_req(user, &msg->u.data.msg);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/SAP/emm_esm.h b/openair3/NAS/UE/EMM/SAP/emm_esm.h
index 7a763d4759a3788c5f5808b130e89012fdd6ca70..11aa42213f8f7681e9399104db26980741339149 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_esm.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_esm.h
@@ -42,6 +42,7 @@ Description Defines the EMMESM Service Access Point that provides
 #define __EMM_ESM_H__
 
 #include "emm_esmDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -61,6 +62,6 @@ Description Defines the EMMESM Service Access Point that provides
 
 void emm_esm_initialize(void);
 
-int emm_esm_send(const emm_esm_t *msg);
+int emm_esm_send(nas_user_t *user, const emm_esm_t *msg);
 
 #endif /* __EMM_ESM_H__*/
diff --git a/openair3/NAS/UE/EMM/SAP/emm_fsm.c b/openair3/NAS/UE/EMM/SAP/emm_fsm.c
index a76b101119b11e0551235fa99f115f1420a87fcb..be1517a7ccd21d4c051394ef0be29adac95658fa 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_fsm.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_fsm.c
@@ -43,6 +43,7 @@ Description Defines the EPS Mobility Management procedures executed at
 #include "nas_log.h"
 
 #include "emmData.h"
+#include "user_defs.h"
 
 
 
@@ -127,30 +128,30 @@ static const char *_emm_fsm_status_str[EMM_STATE_MAX] = {
  */
 
 /* Type of the EPS Mobility Management state machine handler */
-typedef int(*emm_fsm_handler_t)(const emm_reg_t *);
-
-int EmmNull(const emm_reg_t *);
-int EmmDeregistered(const emm_reg_t *);
-int EmmRegistered(const emm_reg_t *);
-int EmmDeregisteredInitiated(const emm_reg_t *);
-int EmmDeregisteredNormalService(const emm_reg_t *);
-int EmmDeregisteredLimitedService(const emm_reg_t *);
-int EmmDeregisteredAttemptingToAttach(const emm_reg_t *);
-int EmmDeregisteredPlmnSearch(const emm_reg_t *);
-int EmmDeregisteredNoImsi(const emm_reg_t *);
-int EmmDeregisteredAttachNeeded(const emm_reg_t *);
-int EmmDeregisteredNoCellAvailable(const emm_reg_t *);
-int EmmRegisteredInitiated(const emm_reg_t *);
-int EmmRegisteredNormalService(const emm_reg_t *);
-int EmmRegisteredAttemptingToUpdate(const emm_reg_t *);
-int EmmRegisteredLimitedService(const emm_reg_t *);
-int EmmRegisteredPlmnSearch(const emm_reg_t *);
-int EmmRegisteredUpdateNeeded(const emm_reg_t *);
-int EmmRegisteredNoCellAvailable(const emm_reg_t *);
-int EmmRegisteredAttemptingToUpdate(const emm_reg_t *);
-int EmmRegisteredImsiDetachInitiated(const emm_reg_t *);
-int EmmTrackingAreaUpdatingInitiated(const emm_reg_t *);
-int EmmServiceRequestInitiated(const emm_reg_t *);
+typedef int(*emm_fsm_handler_t)(nas_user_t *user, const emm_reg_t *);
+
+int EmmNull(nas_user_t *user, const emm_reg_t *);
+int EmmDeregistered(nas_user_t *user, const emm_reg_t *);
+int EmmRegistered(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredInitiated(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredNormalService(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredLimitedService(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredAttemptingToAttach(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredPlmnSearch(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredNoImsi(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredAttachNeeded(nas_user_t *user, const emm_reg_t *);
+int EmmDeregisteredNoCellAvailable(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredInitiated(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredNormalService(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredAttemptingToUpdate(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredLimitedService(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredPlmnSearch(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredUpdateNeeded(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredNoCellAvailable(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredAttemptingToUpdate(nas_user_t *user, const emm_reg_t *);
+int EmmRegisteredImsiDetachInitiated(nas_user_t *user, const emm_reg_t *);
+int EmmTrackingAreaUpdatingInitiated(nas_user_t *user, const emm_reg_t *);
+int EmmServiceRequestInitiated(nas_user_t *user, const emm_reg_t *);
 
 
 /* EMM state machine handlers */
@@ -180,14 +181,6 @@ static const emm_fsm_handler_t _emm_fsm_handlers[EMM_STATE_MAX] = {
   EmmServiceRequestInitiated,
 };
 
-/*
- * -----------------------------------------------------------------------------
- *          Current EPS Mobility Management status
- * -----------------------------------------------------------------------------
- */
-
-emm_fsm_state_t _emm_fsm_status[EMM_FSM_NB_UE_MAX];
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
@@ -206,14 +199,12 @@ emm_fsm_state_t _emm_fsm_status[EMM_FSM_NB_UE_MAX];
  **      Others:    _emm_fsm_status                            **
  **                                                                        **
  ***************************************************************************/
-void emm_fsm_initialize(void)
+emm_fsm_state_t emm_fsm_initialize()
 {
-  //int ueid;
   LOG_FUNC_IN;
 
-  _emm_fsm_status[0] = EMM_NULL;
-
   LOG_FUNC_OUT;
+  return EMM_NULL;
 }
 
 /****************************************************************************
@@ -231,22 +222,18 @@ void emm_fsm_initialize(void)
  **      Others:    _emm_fsm_status                            **
  **                                                                        **
  ***************************************************************************/
-int emm_fsm_set_status(
+int emm_fsm_set_status(nas_user_t *user,
   emm_fsm_state_t  status)
 {
   LOG_FUNC_IN;
 
-  unsigned int ueid = 0;
-
-
-
-  if ( (status < EMM_STATE_MAX) && (ueid < EMM_FSM_NB_UE_MAX) ) {
+  if ( status < EMM_STATE_MAX ) {
     LOG_TRACE(INFO, "EMM-FSM   - Status changed: %s ===> %s",
-              _emm_fsm_status_str[_emm_fsm_status[ueid]],
+              _emm_fsm_status_str[user->emm_fsm_status],
               _emm_fsm_status_str[status]);
 
-    if (status != _emm_fsm_status[ueid]) {
-      _emm_fsm_status[ueid] = status;
+    if (status != user->emm_fsm_status) {
+      user->emm_fsm_status = status;
     }
 
     LOG_FUNC_RETURN (RETURNok);
@@ -270,9 +257,9 @@ int emm_fsm_set_status(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-emm_fsm_state_t emm_fsm_get_status(void)
+emm_fsm_state_t emm_fsm_get_status(nas_user_t *user)
 {
-  return (_emm_fsm_status[0]);
+  return user->emm_fsm_status;
 }
 
 /****************************************************************************
@@ -289,7 +276,7 @@ emm_fsm_state_t emm_fsm_get_status(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_fsm_process(const emm_reg_t *evt)
+int emm_fsm_process(nas_user_t *user, const emm_reg_t *evt)
 {
   int rc;
   emm_fsm_state_t status;
@@ -299,7 +286,7 @@ int emm_fsm_process(const emm_reg_t *evt)
 
   primitive = evt->primitive;
 
-  status = _emm_fsm_status[0];
+  status = user->emm_fsm_status;
 
   LOG_TRACE(INFO, "EMM-FSM   - Received event %s (%d) in state %s",
             _emm_fsm_event_str[primitive - _EMMREG_START - 1], primitive,
@@ -307,7 +294,7 @@ int emm_fsm_process(const emm_reg_t *evt)
 
 
   /* Execute the EMM state machine */
-  rc = (_emm_fsm_handlers[status])(evt);
+  rc = (_emm_fsm_handlers[status])(user, evt);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/SAP/emm_fsm.h b/openair3/NAS/UE/EMM/SAP/emm_fsm.h
index ea7d4ed62228cf6875aad3e31ede55437a1c64b2..aba1ef327ca2e8f5f010ece712a501c2600dc625 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_fsm.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_fsm.h
@@ -41,48 +41,13 @@ Description Defines the EPS Mobility Management procedures executed at
 #define __EMM_FSM_H__
 
 #include "emm_regDef.h"
+#include "emm_fsm_defs.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
 
-/****************************************************************************/
-/************************  G L O B A L    T Y P E S  ************************/
-/****************************************************************************/
-
-/*
- * States of the EPS Mobility Management sublayer
- * ----------------------------------------------
- * The EMM protocol of the UE and the network is described by means of two
- * different state machines.
- */
-typedef enum {
-  EMM_INVALID,
-  EMM_NULL,
-  EMM_DEREGISTERED,
-  EMM_REGISTERED,
-  EMM_DEREGISTERED_INITIATED,
-  EMM_DEREGISTERED_NORMAL_SERVICE,
-  EMM_DEREGISTERED_LIMITED_SERVICE,
-  EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH,
-  EMM_DEREGISTERED_PLMN_SEARCH,
-  EMM_DEREGISTERED_NO_IMSI,
-  EMM_DEREGISTERED_ATTACH_NEEDED,
-  EMM_DEREGISTERED_NO_CELL_AVAILABLE,
-  EMM_REGISTERED_INITIATED,
-  EMM_REGISTERED_NORMAL_SERVICE,
-  EMM_REGISTERED_ATTEMPTING_TO_UPDATE,
-  EMM_REGISTERED_LIMITED_SERVICE,
-  EMM_REGISTERED_PLMN_SEARCH,
-  EMM_REGISTERED_UPDATE_NEEDED,
-  EMM_REGISTERED_NO_CELL_AVAILABLE,
-  EMM_REGISTERED_ATTEMPTING_TO_UPDATE_MM,
-  EMM_REGISTERED_IMSI_DETACH_INITIATED,
-  EMM_TRACKING_AREA_UPDATING_INITIATED,
-  EMM_SERVICE_REQUEST_INITIATED,
-  EMM_STATE_MAX
-} emm_fsm_state_t;
-
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -91,12 +56,12 @@ typedef enum {
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void emm_fsm_initialize(void);
+emm_fsm_state_t emm_fsm_initialize(void);
 
-int emm_fsm_set_status(emm_fsm_state_t status);
-emm_fsm_state_t emm_fsm_get_status(void);
+int emm_fsm_set_status(nas_user_t *user, emm_fsm_state_t status);
+emm_fsm_state_t emm_fsm_get_status(nas_user_t *user);
 
 
-int emm_fsm_process(const emm_reg_t *evt);
+int emm_fsm_process(nas_user_t *user, const emm_reg_t *evt);
 
 #endif /* __EMM_FSM_H__*/
diff --git a/openair3/NAS/UE/EMM/SAP/emm_recv.c b/openair3/NAS/UE/EMM/SAP/emm_recv.c
index 0ad41b09d878862b677cc35da535f103baf1cadf..5b7c6d7dda34fe0c057da04071522ceeefffa69f 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_recv.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_recv.c
@@ -125,7 +125,7 @@ int emm_recv_status(unsigned int ueid, emm_status_msg *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_attach_accept(attach_accept_msg *msg, int *emm_cause)
+int emm_recv_attach_accept(nas_user_t *user, attach_accept_msg *msg, int *emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -224,7 +224,7 @@ int emm_recv_attach_accept(attach_accept_msg *msg, int *emm_cause)
   }
 
   /* Execute attach procedure accepted by the network */
-  rc = emm_proc_attach_accept(T3412, T3402, T3423, n_tais, tai, pguti,
+  rc = emm_proc_attach_accept(user, T3412, T3402, T3423, n_tais, tai, pguti,
                               n_eplmns, &eplmn,
                               &msg->esmmessagecontainer.esmmessagecontainercontents);
 
@@ -245,7 +245,7 @@ int emm_recv_attach_accept(attach_accept_msg *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_attach_reject(attach_reject_msg *msg, int *emm_cause)
+int emm_recv_attach_reject(nas_user_t *user, attach_reject_msg *msg, int *emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -275,11 +275,11 @@ int emm_recv_attach_reject(attach_reject_msg *msg, int *emm_cause)
    */
   if (msg->presencemask & ATTACH_REJECT_ESM_MESSAGE_CONTAINER_PRESENT) {
     /* Execute attach procedure rejected by the network */
-    rc = emm_proc_attach_reject(msg->emmcause,
+    rc = emm_proc_attach_reject(user, msg->emmcause,
                                 &msg->esmmessagecontainer.esmmessagecontainercontents);
   } else {
     /* Execute attach procedure rejected by the network */
-    rc = emm_proc_attach_reject(msg->emmcause, NULL);
+    rc = emm_proc_attach_reject(user, msg->emmcause, NULL);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -299,7 +299,7 @@ int emm_recv_attach_reject(attach_reject_msg *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_detach_accept(detach_accept_msg *msg, int *emm_cause)
+int emm_recv_detach_accept(nas_user_t *user, detach_accept_msg *msg, int *emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -311,7 +311,7 @@ int emm_recv_detach_accept(detach_accept_msg *msg, int *emm_cause)
    * Message processing
    */
   /* Execute the UE initiated detach procedure completion by the UE */
-  rc = emm_proc_detach_accept();
+  rc = emm_proc_detach_accept(user);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -330,7 +330,7 @@ int emm_recv_detach_accept(detach_accept_msg *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_identity_request(identity_request_msg *msg, int *emm_cause)
+int emm_recv_identity_request(nas_user_t *user, identity_request_msg *msg, int *emm_cause)
 {
   LOG_FUNC_IN;
 
@@ -358,7 +358,7 @@ int emm_recv_identity_request(identity_request_msg *msg, int *emm_cause)
   }
 
   /* Execute the identification procedure initiated by the network */
-  rc = emm_proc_identification_request(type);
+  rc = emm_proc_identification_request(user, type);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -377,7 +377,7 @@ int emm_recv_identity_request(identity_request_msg *msg, int *emm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_authentication_request(authentication_request_msg *msg,
+int emm_recv_authentication_request(nas_user_t *user, authentication_request_msg *msg,
                                     int *emm_cause)
 {
   LOG_FUNC_IN;
@@ -404,7 +404,7 @@ int emm_recv_authentication_request(authentication_request_msg *msg,
    * Message processing
    */
   /* Execute the authentication procedure initiated by the network */
-  rc = emm_proc_authentication_request(
+  rc = emm_proc_authentication_request(user,
          msg->naskeysetidentifierasme.tsc != NAS_KEY_SET_IDENTIFIER_MAPPED,
          msg->naskeysetidentifierasme.naskeysetidentifier,
          &msg->authenticationparameterrand.rand,
@@ -427,7 +427,7 @@ int emm_recv_authentication_request(authentication_request_msg *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_authentication_reject(authentication_reject_msg *msg,
+int emm_recv_authentication_reject(nas_user_t *user, authentication_reject_msg *msg,
                                    int *emm_cause)
 {
   LOG_FUNC_IN;
@@ -440,7 +440,7 @@ int emm_recv_authentication_reject(authentication_reject_msg *msg,
    * Message processing
    */
   /* Execute the authentication procedure not accepted by the network */
-  rc = emm_proc_authentication_reject();
+  rc = emm_proc_authentication_reject(user);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -459,7 +459,7 @@ int emm_recv_authentication_reject(authentication_reject_msg *msg,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_recv_security_mode_command(security_mode_command_msg *msg,
+int emm_recv_security_mode_command(nas_user_t *user, security_mode_command_msg *msg,
                                    int *emm_cause)
 {
   LOG_FUNC_IN;
@@ -473,7 +473,7 @@ int emm_recv_security_mode_command(security_mode_command_msg *msg,
    */
   /* Execute the security mode control procedure initiated by the network */
   LOG_TRACE(INFO,"Execute the security mode control procedure initiated by the network:  imeisvrequest %d\n",msg->imeisvrequest);
-  rc = emm_proc_security_mode_command(
+  rc = emm_proc_security_mode_command(user,
          msg->naskeysetidentifier.tsc != NAS_KEY_SET_IDENTIFIER_MAPPED,
          msg->naskeysetidentifier.naskeysetidentifier,
          msg->selectednassecurityalgorithms.typeofcipheringalgorithm,
diff --git a/openair3/NAS/UE/EMM/SAP/emm_recv.h b/openair3/NAS/UE/EMM/SAP/emm_recv.h
index cec52a8c1f4025535403eff57719e0d5a096c834..ead59e3851bcfb44cf5224ac8942f218da24fc82 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_recv.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_recv.h
@@ -59,7 +59,7 @@ Description Defines functions executed at the EMMAS Service Access
 #include "EmmInformation.h"
 #include "DownlinkNasTransport.h"
 #include "CsServiceNotification.h"
-
+#include "user_defs.h"
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
@@ -88,17 +88,17 @@ int emm_recv_status(unsigned int ueid, emm_status_msg *msg, int *emm_cause);
  * Functions executed by the UE upon receiving EMM message from the network
  * --------------------------------------------------------------------------
  */
-int emm_recv_attach_accept(attach_accept_msg *msg, int *emm_cause);
-int emm_recv_attach_reject(attach_reject_msg *msg, int *emm_cause);
+int emm_recv_attach_accept(nas_user_t *user, attach_accept_msg *msg, int *emm_cause);
+int emm_recv_attach_reject(nas_user_t *user, attach_reject_msg *msg, int *emm_cause);
 
-int emm_recv_detach_accept(detach_accept_msg *msg, int *emm_cause);
+int emm_recv_detach_accept(nas_user_t *user, detach_accept_msg *msg, int *emm_cause);
 
-int emm_recv_identity_request(identity_request_msg *msg, int *emm_cause);
-int emm_recv_authentication_request(authentication_request_msg *msg,
+int emm_recv_identity_request(nas_user_t *user, identity_request_msg *msg, int *emm_cause);
+int emm_recv_authentication_request(nas_user_t *user, authentication_request_msg *msg,
                                     int *emm_cause);
-int emm_recv_authentication_reject(authentication_reject_msg *msg,
+int emm_recv_authentication_reject(nas_user_t *user, authentication_reject_msg *msg,
                                    int *emm_cause);
-int emm_recv_security_mode_command(security_mode_command_msg *msg,
+int emm_recv_security_mode_command(nas_user_t *user, security_mode_command_msg *msg,
                                    int *emm_cause);
 
 #endif /* __EMM_RECV_H__*/
diff --git a/openair3/NAS/UE/EMM/SAP/emm_reg.c b/openair3/NAS/UE/EMM/SAP/emm_reg.c
index 065b91bd3ba0eda105e9ab0c08be1e827a77a9c2..6cda066b584d89098421139fcf9f6f60d4dd7256 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_reg.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_reg.c
@@ -73,12 +73,12 @@ Description Defines the EMMREG Service Access Point that provides
  **      Others:    NONE                                       **
  **                                                                        **
  ***************************************************************************/
-void emm_reg_initialize(void)
+void emm_reg_initialize(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   /* Initialize the EMM state machine */
-  emm_fsm_initialize();
+  user->emm_fsm_status = emm_fsm_initialize();
 
   LOG_FUNC_OUT;
 }
@@ -97,7 +97,7 @@ void emm_reg_initialize(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_reg_send(const emm_reg_t *msg)
+int emm_reg_send(nas_user_t *user, const emm_reg_t *msg)
 {
   LOG_FUNC_IN;
 
@@ -111,7 +111,7 @@ int emm_reg_send(const emm_reg_t *msg)
   (void)primitive;
 
   /* Execute the EMM procedure */
-  rc = emm_fsm_process(msg);
+  rc = emm_fsm_process(user, msg);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/SAP/emm_reg.h b/openair3/NAS/UE/EMM/SAP/emm_reg.h
index 08d28b67db83757d885f4ec97fdea6e48a4cb770..9cd1132c7a333f148625db8049d6847289b8da97 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_reg.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_reg.h
@@ -42,6 +42,7 @@ Description Defines the EMMREG Service Access Point that provides
 #define __EMM_REG_H__
 
 #include "emm_regDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -59,8 +60,8 @@ Description Defines the EMMREG Service Access Point that provides
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void emm_reg_initialize(void);
+void emm_reg_initialize(nas_user_t *user);
 
-int emm_reg_send(const emm_reg_t *msg);
+int emm_reg_send(nas_user_t *user, const emm_reg_t *msg);
 
 #endif /* __EMM_REG_H__*/
diff --git a/openair3/NAS/UE/EMM/SAP/emm_sap.c b/openair3/NAS/UE/EMM/SAP/emm_sap.c
index d8312ee74cb859e95c2905cb50a973ec23dcb839..429518d475c2c47e20ee86f215d38985de6fd6de 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_sap.c
+++ b/openair3/NAS/UE/EMM/SAP/emm_sap.c
@@ -46,6 +46,7 @@ Description Defines the EMM Service Access Points at which the EPS
 #include "emm_reg.h"
 #include "emm_esm.h"
 #include "emm_as.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
@@ -73,13 +74,13 @@ Description Defines the EMM Service Access Points at which the EPS
  **      Others:    NONE                                       **
  **                                                                        **
  ***************************************************************************/
-void emm_sap_initialize(void)
+void emm_sap_initialize(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
-  emm_reg_initialize();
+  emm_reg_initialize(user);
   emm_esm_initialize();
-  emm_as_initialize();
+  emm_as_initialize(user);
 
   LOG_FUNC_OUT;
 }
@@ -98,7 +99,7 @@ void emm_sap_initialize(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_sap_send(emm_sap_t *msg)
+int emm_sap_send(nas_user_t *user, emm_sap_t *msg)
 {
   int rc = RETURNerror;
 
@@ -111,17 +112,17 @@ int emm_sap_send(emm_sap_t *msg)
        (primitive < (emm_primitive_t)EMMREG_PRIMITIVE_MAX) ) {
     /* Forward to the EMMREG-SAP */
     msg->u.emm_reg.primitive = primitive;
-    rc = emm_reg_send(&msg->u.emm_reg);
+    rc = emm_reg_send(user, &msg->u.emm_reg);
   } else if ( (primitive > (emm_primitive_t)EMMESM_PRIMITIVE_MIN) &&
               (primitive < (emm_primitive_t)EMMESM_PRIMITIVE_MAX) ) {
     /* Forward to the EMMESM-SAP */
     msg->u.emm_esm.primitive = primitive;
-    rc = emm_esm_send(&msg->u.emm_esm);
+    rc = emm_esm_send(user, &msg->u.emm_esm);
   } else if ( (primitive > (emm_primitive_t)EMMAS_PRIMITIVE_MIN) &&
               (primitive < (emm_primitive_t)EMMAS_PRIMITIVE_MAX) ) {
     /* Forward to the EMMAS-SAP */
     msg->u.emm_as.primitive = primitive;
-    rc = emm_as_send(&msg->u.emm_as);
+    rc = emm_as_send(user, &msg->u.emm_as);
   }
   else {
     LOG_TRACE(WARNING, "EMM-SAP -   Out of range primitive (%d)", primitive);
diff --git a/openair3/NAS/UE/EMM/SAP/emm_sap.h b/openair3/NAS/UE/EMM/SAP/emm_sap.h
index f27e8d4e5832fb797fedb3f992460568d1e0a8b8..b84fd7808b4bf8ae217107041522dd22b2eabf95 100644
--- a/openair3/NAS/UE/EMM/SAP/emm_sap.h
+++ b/openair3/NAS/UE/EMM/SAP/emm_sap.h
@@ -46,6 +46,7 @@ Description Defines the EMM Service Access Points at which the EPS
 #include "emm_regDef.h"
 #include "emm_esmDef.h"
 #include "emm_asDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -156,8 +157,8 @@ typedef struct emm_sap_s {
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void emm_sap_initialize(void);
+void emm_sap_initialize(nas_user_t *user);
 
-int emm_sap_send(emm_sap_t *msg);
+int emm_sap_send(nas_user_t *user, emm_sap_t *msg);
 
 #endif /* __EMM_SAP_H__*/
diff --git a/openair3/NAS/UE/EMM/SecurityModeControl.c b/openair3/NAS/UE/EMM/SecurityModeControl.c
index f27eb7b4afa87a25a76e08beb95f4a2aa6607b9c..0991fb22bb4ff6a8a68aa7aa25e28141c3448229 100644
--- a/openair3/NAS/UE/EMM/SecurityModeControl.c
+++ b/openair3/NAS/UE/EMM/SecurityModeControl.c
@@ -64,6 +64,7 @@ Description Defines the security mode control EMM procedure executed by the
 # include "assertions.h"
 #include "secu_defs.h"
 #include "msc.h"
+#include "SecurityModeControl.h"
 
 #if  defined(NAS_BUILT_IN_UE)
 #include "nas_itti_messaging.h"
@@ -92,13 +93,6 @@ static int _security_knas_int(const OctetString *kasme, OctetString *knas_int,
 static int _security_kenb(const OctetString *kasme, OctetString *kenb,
                           uint32_t count);
 
-/*
- * Internal data used for security mode control procedure
- */
-static struct {
-  OctetString kenb;           /* eNodeB security key      */
-} _security_data;
-
 static void _security_release(emm_security_context_t *ctx);
 
 /*
@@ -145,7 +139,7 @@ static void _security_release(emm_security_context_t *ctx);
  **      Others:    None                                                   **
  **                                                                        **
  ***************************************************************************/
-int emm_proc_security_mode_command(int native_ksi, int ksi,
+int emm_proc_security_mode_command(nas_user_t *user, int native_ksi, int ksi,
                                    int seea, int seia, int reea, int reia, int imeisv_request)
 {
   LOG_FUNC_IN;
@@ -153,18 +147,19 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
   int rc = RETURNerror;
   int emm_cause = EMM_CAUSE_SUCCESS;
   int security_context_is_new = FALSE;
+  security_data_t *security_data = user->security_data;
 
   LOG_TRACE(INFO, "EMM-PROC  - Security mode control requested (ksi=%d)",
             ksi);
 
   /* Delete any previously stored RAND and RES and stop timer T3416 */
-  (void) emm_proc_authentication_delete();
+  (void) emm_proc_authentication_delete(user);
 
   /*
    * Check the replayed UE security capabilities
    */
-  uint8_t eea = (0x80 >> _emm_data.security->capability.eps_encryption);
-  uint8_t eia = (0x80 >> _emm_data.security->capability.eps_integrity);
+  uint8_t eea = (0x80 >> user->emm_data->security->capability.eps_encryption);
+  uint8_t eia = (0x80 >> user->emm_data->security->capability.eps_integrity);
 
   if ( (reea != eea) || (reia != eia) ) {
     LOG_TRACE(WARNING, "EMM-PROC  - Replayed UE security capabilities "
@@ -180,7 +175,7 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
   /*
    * Check the non-current EPS security context
    */
-  else if (_emm_data.non_current == NULL) {
+  else if (user->emm_data->non_current == NULL) {
     LOG_TRACE(WARNING, "EMM-PROC  - Non-current EPS security context "
               "is not valid");
     emm_cause = EMM_CAUSE_SECURITY_MODE_REJECTED;
@@ -191,53 +186,53 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
   else {
     LOG_TRACE(INFO, "EMM-PROC  - Update the non-current EPS security context seea=%u seia=%u", seea, seia);
     /* Update selected cyphering and integrity algorithms */
-    //LG COMENTED _emm_data.non_current->capability.encryption = seea;
-    //LG COMENTED _emm_data.non_current->capability.integrity  = seia;
+    //LG COMENTED user->emm_data->non_current->capability.encryption = seea;
+    //LG COMENTED user->emm_data->non_current->capability.integrity  = seia;
 
-    _emm_data.non_current->selected_algorithms.encryption = seea;
-    _emm_data.non_current->selected_algorithms.integrity = seia;
+    user->emm_data->non_current->selected_algorithms.encryption = seea;
+    user->emm_data->non_current->selected_algorithms.integrity = seia;
 
     /* Derive the NAS cyphering key */
-    if (_emm_data.non_current->knas_enc.value == NULL) {
-      _emm_data.non_current->knas_enc.value =
+    if (user->emm_data->non_current->knas_enc.value == NULL) {
+      user->emm_data->non_current->knas_enc.value =
         (uint8_t *)calloc(1,AUTH_KNAS_ENC_SIZE);
-      _emm_data.non_current->knas_enc.length = AUTH_KNAS_ENC_SIZE;
+      user->emm_data->non_current->knas_enc.length = AUTH_KNAS_ENC_SIZE;
     }
 
-    if (_emm_data.non_current->knas_enc.value != NULL) {
+    if (user->emm_data->non_current->knas_enc.value != NULL) {
       LOG_TRACE(INFO, "EMM-PROC  - Update the non-current EPS security context knas_enc");
-      rc = _security_knas_enc(&_emm_data.non_current->kasme,
-                              &_emm_data.non_current->knas_enc, seea);
+      rc = _security_knas_enc(&user->emm_data->non_current->kasme,
+                              &user->emm_data->non_current->knas_enc, seea);
     }
 
     /* Derive the NAS integrity key */
-    if (_emm_data.non_current->knas_int.value == NULL) {
-      _emm_data.non_current->knas_int.value =
+    if (user->emm_data->non_current->knas_int.value == NULL) {
+      user->emm_data->non_current->knas_int.value =
         (uint8_t *)calloc(1,AUTH_KNAS_INT_SIZE);
-      _emm_data.non_current->knas_int.length = AUTH_KNAS_INT_SIZE;
+      user->emm_data->non_current->knas_int.length = AUTH_KNAS_INT_SIZE;
     }
 
-    if (_emm_data.non_current->knas_int.value != NULL) {
+    if (user->emm_data->non_current->knas_int.value != NULL) {
       if (rc != RETURNerror) {
         LOG_TRACE(INFO, "EMM-PROC  - Update the non-current EPS security context knas_int");
-        rc = _security_knas_int(&_emm_data.non_current->kasme,
-                                &_emm_data.non_current->knas_int, seia);
+        rc = _security_knas_int(&user->emm_data->non_current->kasme,
+                                &user->emm_data->non_current->knas_int, seia);
       }
     }
 
     /* Derive the eNodeB key */
-    if (_security_data.kenb.value == NULL) {
-      _security_data.kenb.value = (uint8_t *)calloc(1,AUTH_KENB_SIZE);
-      _security_data.kenb.length = AUTH_KENB_SIZE;
+    if (security_data->kenb.value == NULL) {
+      security_data->kenb.value = (uint8_t *)calloc(1,AUTH_KENB_SIZE);
+      security_data->kenb.length = AUTH_KENB_SIZE;
     }
 
-    if (_security_data.kenb.value != NULL) {
+    if (security_data->kenb.value != NULL) {
       if (rc != RETURNerror) {
         LOG_TRACE(INFO, "EMM-PROC  - Update the non-current EPS security context kenb");
-        // LG COMMENT rc = _security_kenb(&_emm_data.security->kasme,
-        rc = _security_kenb(&_emm_data.non_current->kasme,
-                            &_security_data.kenb,
-                            *(uint32_t *)(&_emm_data.non_current->ul_count));
+        // LG COMMENT rc = _security_kenb(&user->emm_data->security->kasme,
+        rc = _security_kenb(&user->emm_data->non_current->kasme,
+                            &security_data->kenb,
+                            *(uint32_t *)(&user->emm_data->non_current->ul_count));
       }
     }
 
@@ -248,13 +243,13 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
       LOG_TRACE(INFO, "EMM-PROC  - NAS security mode command accepted by the UE");
 
       /* Update the current EPS security context */
-      if ( native_ksi && (_emm_data.security->type != EMM_KSI_NATIVE) ) {
+      if ( native_ksi && (user->emm_data->security->type != EMM_KSI_NATIVE) ) {
         /* The type of security context flag included in the SECURITY
          * MODE COMMAND message is set to "native security context" and
          * the UE has a mapped EPS security context as the current EPS
          * security context */
-        if ( (_emm_data.non_current->type == EMM_KSI_NATIVE) &&
-             (_emm_data.non_current->eksi == ksi) ) {
+        if ( (user->emm_data->non_current->type == EMM_KSI_NATIVE) &&
+             (user->emm_data->non_current->eksi == ksi) ) {
           /* The KSI matches the non-current native EPS security
            * context; the UE shall take the non-current native EPS
            * security context into use which then becomes the
@@ -263,36 +258,37 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
           LOG_TRACE(INFO,
                     "EMM-PROC  - Update Current security context");
           /* Release non-current security context */
-          _security_release(_emm_data.security);
-          _emm_data.security = _emm_data.non_current;
+          _security_release(user->emm_data->security);
+          user->emm_data->security = user->emm_data->non_current;
           /* Reset the uplink NAS COUNT counter */
-          _emm_data.security->ul_count.overflow = 0;
-          _emm_data.security->ul_count.seq_num = 0;
+          user->emm_data->security->ul_count.overflow = 0;
+          user->emm_data->security->ul_count.seq_num = 0;
           /* Set new security context indicator */
           security_context_is_new = TRUE;
         }
       }
 
-      if ( !native_ksi && (_emm_data.security->type != EMM_KSI_NATIVE) ) {
+      if ( !native_ksi && (user->emm_data->security->type != EMM_KSI_NATIVE) ) {
         /* The type of security context flag included in the SECURITY
          * MODE COMMAND message is set to "mapped security context" and
          * the UE has a mapped EPS security context as the current EPS
          * security context */
-        if (ksi != _emm_data.security->eksi) {
+        if (ksi != user->emm_data->security->eksi) {
           /* The KSI does not match the current EPS security context;
            * the UE shall reset the uplink NAS COUNT counter */
           LOG_TRACE(INFO,
                     "EMM-PROC  - Reset uplink NAS COUNT counter");
-          _emm_data.security->ul_count.overflow = 0;
-          _emm_data.security->ul_count.seq_num = 0;
+          user->emm_data->security->ul_count.overflow = 0;
+          user->emm_data->security->ul_count.seq_num = 0;
         }
       }
 
-      _emm_data.security->selected_algorithms.encryption = seea;
-      _emm_data.security->selected_algorithms.integrity  = seia;
+      user->emm_data->security->selected_algorithms.encryption = seea;
+      user->emm_data->security->selected_algorithms.integrity  = seia;
 #if  defined(NAS_BUILT_IN_UE)
-      nas_itti_kenb_refresh_req(_security_data.kenb.value);
+      nas_itti_kenb_refresh_req(security_data->kenb.value);
 #endif
+
     }
     /*
      * NAS security mode command not accepted by the UE
@@ -302,17 +298,17 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
       emm_cause = EMM_CAUSE_SECURITY_MODE_REJECTED;
 
       /* Release security mode control internal data */
-      if (_security_data.kenb.value) {
-        free(_security_data.kenb.value);
-        _security_data.kenb.value = NULL;
-        _security_data.kenb.length = 0;
+      if (security_data->kenb.value) {
+        free(security_data->kenb.value);
+        security_data->kenb.value = NULL;
+        security_data->kenb.length = 0;
       }
     }
   }
 
   /* Setup EMM procedure handler to be executed upon receiving
    * lower layer notification */
-  rc = emm_proc_lowerlayer_initialize(NULL, NULL, NULL, NULL);
+  rc = emm_proc_lowerlayer_initialize(user->lowerlayer_data, NULL, NULL, NULL, NULL);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING,
@@ -326,15 +322,15 @@ int emm_proc_security_mode_command(int native_ksi, int ksi,
    */
   emm_sap_t emm_sap;
   emm_sap.primitive = EMMAS_SECURITY_RES;
-  emm_sap.u.emm_as.u.security.guti = _emm_data.guti;
-  emm_sap.u.emm_as.u.security.ueid = 0;
+  emm_sap.u.emm_as.u.security.guti = user->emm_data->guti;
+  emm_sap.u.emm_as.u.security.ueid = user->ueid;
   emm_sap.u.emm_as.u.security.msgType = EMM_AS_MSG_TYPE_SMC;
   emm_sap.u.emm_as.u.security.imeisv_request = imeisv_request;
   emm_sap.u.emm_as.u.security.emm_cause = emm_cause;
   /* Setup EPS NAS security data */
   emm_as_set_security_data(&emm_sap.u.emm_as.u.security.sctx,
-                           _emm_data.security, security_context_is_new, TRUE);
-  rc = emm_sap_send(&emm_sap);
+                           user->emm_data->security, security_context_is_new, TRUE);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/EMM/SecurityModeControl.h b/openair3/NAS/UE/EMM/SecurityModeControl.h
new file mode 100644
index 0000000000000000000000000000000000000000..314c168544dc24564371988bf16f1065ef3f54f6
--- /dev/null
+++ b/openair3/NAS/UE/EMM/SecurityModeControl.h
@@ -0,0 +1,11 @@
+#ifndef _SECURITYMODECONTROL_H
+#define _SECURITYMODECONTROL_H
+
+/*
+ * Internal data used for security mode control procedure
+ */
+typedef struct {
+  OctetString kenb;           /* eNodeB security key      */
+} security_data_t;
+
+#endif
diff --git a/openair3/NAS/UE/EMM/ServiceRequestHdl.c b/openair3/NAS/UE/EMM/ServiceRequestHdl.c
index 349fce7cad19c9cdf358e480156994a10bcb0b91..93e66efdbdb2ae7ad387403fa58f60f08d745204 100644
--- a/openair3/NAS/UE/EMM/ServiceRequestHdl.c
+++ b/openair3/NAS/UE/EMM/ServiceRequestHdl.c
@@ -67,11 +67,6 @@ Description Defines the service request EMM procedure executed by the
  *  Internal data handled by the service request procedure in the UE
  * --------------------------------------------------------------------------
  */
-/*
- * Timer handlers
- */
-void *_emm_service_t3417_handler(void *);
-
 /*
  * --------------------------------------------------------------------------
  *  Internal data handled by the service request procedure in the MME
@@ -109,14 +104,16 @@ void *_emm_service_t3417_handler(void *);
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void *_emm_service_t3417_handler(void *args)
+void *emm_service_t3417_handler(void *args)
 {
   LOG_FUNC_IN;
+  nas_user_t *user = args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3417 timer expired");
 
   /* Stop timer T3417 */
-  T3417.id = nas_timer_stop(T3417.id);
+  emm_timers->T3417.id = nas_timer_stop(emm_timers->T3417.id);
 
   LOG_FUNC_RETURN(NULL);
 }
diff --git a/openair3/NAS/UE/EMM/TrackingAreaUpdate.c b/openair3/NAS/UE/EMM/TrackingAreaUpdate.c
index 50a440951b3bb1c8cd69d23d0c92bd2e2ac67bc9..bafddaf31739edf8f07cab807fb92215f91e73e6 100644
--- a/openair3/NAS/UE/EMM/TrackingAreaUpdate.c
+++ b/openair3/NAS/UE/EMM/TrackingAreaUpdate.c
@@ -67,10 +67,6 @@ Description Defines the tracking area update EMM procedure executed by the
  *   Internal data handled by the tracking area update procedure in the UE
  * --------------------------------------------------------------------------
  */
-/*
- * Timer handlers
- */
-void *_emm_tau_t3430_handler(void *);
 
 /*
  * --------------------------------------------------------------------------
@@ -110,14 +106,16 @@ void *_emm_tau_t3430_handler(void *);
  **              Others:        None                                       **
  **                                                                        **
  ***************************************************************************/
-void *_emm_tau_t3430_handler(void *args)
+void *emm_tau_t3430_handler(void *args)
 {
   LOG_FUNC_IN;
+  nas_user_t *user = args;
+  emm_timers_t *emm_timers = user->emm_data->emm_timers;
 
   LOG_TRACE(WARNING, "EMM-PROC  - T3430 timer expired");
 
   /* Stop timer T3430 */
-  T3430.id = nas_timer_stop(T3430.id);
+  emm_timers->T3430.id = nas_timer_stop(emm_timers->T3430.id);
 
   LOG_FUNC_RETURN(NULL);
 }
diff --git a/openair3/NAS/UE/EMM/emmData.h b/openair3/NAS/UE/EMM/emmData.h
index df89fb0c8e04937c4d0243b49dca97a2de55d9ed..84e58bb963023f393b3d6333eb743d4753b04455 100644
--- a/openair3/NAS/UE/EMM/emmData.h
+++ b/openair3/NAS/UE/EMM/emmData.h
@@ -47,6 +47,7 @@ Description Defines internal private data handled by EPS Mobility
 #include "nas_timer.h"
 
 #include "esmData.h"
+#include "emm_proc_defs.h"
 
 
 
@@ -93,6 +94,29 @@ Description Defines internal private data handled by EPS Mobility
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
+/*
+ * Internal data used for attach procedure
+ */
+
+#define EMM_ATTACH_COUNTER_MAX  5
+
+typedef struct {
+  unsigned int attempt_count; /* Counter used to limit the number of
+                 * subsequently rejected attach attempts */
+} emm_attach_data_t;
+
+/*
+ * Internal data used for detach procedure
+ */
+typedef struct {
+#define EMM_DETACH_COUNTER_MAX  5
+  unsigned int count;      /* Counter used to limit the number of
+                  * subsequently detach attempts    */
+  int switch_off;      /* UE switch-off indicator     */
+  emm_proc_detach_type_t type; /* Type of the detach procedure
+                  * currently in progress       */
+} emm_detach_data_t;
+
 /*
  * --------------------------------------------------------------------------
  * EPS NAS security context handled by EPS Mobility Management sublayer in
@@ -197,6 +221,20 @@ typedef struct emm_nvdata_s {
   PLMN_LIST_T(EMM_DATA_EPLMN_MAX) eplmn;
 } emm_nvdata_t;
 
+typedef struct {
+    struct nas_timer_t T3402;   /* attach failure timer         */
+    struct nas_timer_t T3410;   /* attach timer             */
+    struct nas_timer_t T3411;   /* attach restart timer         */
+    struct nas_timer_t T3412;   /* periodic tracking area update timer  */
+    struct nas_timer_t T3416;   /* EPS authentication challenge timer   */
+    struct nas_timer_t T3417;   /* Service request timer        */
+    struct nas_timer_t T3418;   /* MAC authentication failure timer */
+    struct nas_timer_t T3420;   /* Synch authentication failure timer   */
+    struct nas_timer_t T3421;   /* Detach timer             */
+    struct nas_timer_t T3430;   /* tracking area update timer       */
+    struct nas_timer_t T3423;   /* E-UTRAN deactivate ISR timer     */
+} emm_timers_t;
+
 /*
  * Structure of the EMM data
  * -------------------------
@@ -324,7 +362,13 @@ typedef struct emm_data_s {
    */
   emm_security_context_t *security;    /* current security context     */
   emm_security_context_t *non_current; /* non-current security context */
-
+  /*
+   * EPS mobility management timers – UE side
+   * ----------------------------------------
+   */
+  emm_timers_t *emm_timers;
+  emm_detach_data_t *emm_detach_data;
+  emm_attach_data_t *emm_attach_data;
 } emm_data_t;
 
 
@@ -333,18 +377,6 @@ typedef struct emm_data_s {
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
 
-/*
- * --------------------------------------------------------------------------
- *      EPS mobility management data (used within EMM only)
- * --------------------------------------------------------------------------
- */
-emm_data_t _emm_data;
-
-/*
- * --------------------------------------------------------------------------
- *      EPS mobility management timers – UE side
- * --------------------------------------------------------------------------
- */
 #define T3402_DEFAULT_VALUE 720 /* 12 minutes   */
 #define T3410_DEFAULT_VALUE 15  /* 15 seconds   */
 #define T3411_DEFAULT_VALUE 10  /* 10 seconds   */
@@ -359,19 +391,6 @@ emm_data_t _emm_data;
 #define T3430_DEFAULT_VALUE 15  /* 15 seconds   */
 #define T3440_DEFAULT_VALUE 10  /* 10 seconds   */
 
-struct nas_timer_t T3402;   /* attach failure timer         */
-struct nas_timer_t T3410;   /* attach timer             */
-struct nas_timer_t T3411;   /* attach restart timer         */
-struct nas_timer_t T3412;   /* periodic tracking area update timer  */
-struct nas_timer_t T3416;   /* EPS authentication challenge timer   */
-struct nas_timer_t T3417;   /* Service request timer        */
-struct nas_timer_t T3418;   /* MAC authentication failure timer */
-struct nas_timer_t T3420;   /* Synch authentication failure timer   */
-struct nas_timer_t T3421;   /* Detach timer             */
-struct nas_timer_t T3430;   /* tracking area update timer       */
-
-struct nas_timer_t T3423;   /* E-UTRAN deactivate ISR timer     */
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
diff --git a/openair3/NAS/UE/EMM/emm_fsm_defs.h b/openair3/NAS/UE/EMM/emm_fsm_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..4e5b4a8ee67b82030ad0ccdecde2f25ee88ad7de
--- /dev/null
+++ b/openair3/NAS/UE/EMM/emm_fsm_defs.h
@@ -0,0 +1,42 @@
+#ifndef _EMM_FSM_DEFS_H
+#define _EMM_FSM_DEFS_H
+
+/****************************************************************************/
+/************************  G L O B A L    T Y P E S  ************************/
+/****************************************************************************/
+
+/*
+ * States of the EPS Mobility Management sublayer
+ * ----------------------------------------------
+ * The EMM protocol of the UE and the network is described by means of two
+ * different state machines.
+ */
+typedef enum {
+  EMM_INVALID,
+  EMM_NULL,
+  EMM_DEREGISTERED,
+  EMM_REGISTERED,
+  EMM_DEREGISTERED_INITIATED,
+  EMM_DEREGISTERED_NORMAL_SERVICE,
+  EMM_DEREGISTERED_LIMITED_SERVICE,
+  EMM_DEREGISTERED_ATTEMPTING_TO_ATTACH,
+  EMM_DEREGISTERED_PLMN_SEARCH,
+  EMM_DEREGISTERED_NO_IMSI,
+  EMM_DEREGISTERED_ATTACH_NEEDED,
+  EMM_DEREGISTERED_NO_CELL_AVAILABLE,
+  EMM_REGISTERED_INITIATED,
+  EMM_REGISTERED_NORMAL_SERVICE,
+  EMM_REGISTERED_ATTEMPTING_TO_UPDATE,
+  EMM_REGISTERED_LIMITED_SERVICE,
+  EMM_REGISTERED_PLMN_SEARCH,
+  EMM_REGISTERED_UPDATE_NEEDED,
+  EMM_REGISTERED_NO_CELL_AVAILABLE,
+  EMM_REGISTERED_ATTEMPTING_TO_UPDATE_MM,
+  EMM_REGISTERED_IMSI_DETACH_INITIATED,
+  EMM_TRACKING_AREA_UPDATING_INITIATED,
+  EMM_SERVICE_REQUEST_INITIATED,
+  EMM_STATE_MAX
+} emm_fsm_state_t;
+
+
+#endif
diff --git a/openair3/NAS/UE/EMM/emm_main.c b/openair3/NAS/UE/EMM/emm_main.c
index 2eb4d8fa24d6b18c1d7e8f88d01bbfc00961c2ca..c64fd6df2614381083d16a66f8555daf64f0b679 100644
--- a/openair3/NAS/UE/EMM/emm_main.c
+++ b/openair3/NAS/UE/EMM/emm_main.c
@@ -39,8 +39,10 @@ Description Defines the EPS Mobility Management procedure call manager,
 
 #include "emm_main.h"
 #include "nas_log.h"
+#include "utils.h"
 #include "emmData.h"
 #include "MobileIdentity.h"
+#include "emm_proc_defs.h"
 
 #include "memory.h"
 #include "usim_api.h"
@@ -63,27 +65,58 @@ static int _emm_main_get_imei(imei_t *imei, const char *imei_str);
 
 static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2);
 
-static const char *_emm_main_get_plmn(const plmn_t *plmn, int index,
+static const char *_emm_main_get_plmn(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                       int format, size_t *size);
 
-static int _emm_main_get_plmn_index(const char *plmn, int format);
-
-/*
- * USIM application data
- */
-static usim_data_t _usim_data;
+static int _emm_main_get_plmn_index(emm_plmn_list_t *emm_plmn_list, const char *plmn, int format);
 
 /*
  * Callback executed whenever a change in the network has to be notified
  * to the user application
  */
 static emm_indication_callback_t _emm_main_user_callback;
-static int _emm_main_callback(int);
+static int _emm_main_callback(user_api_id_t *user_api_id, emm_data_t *emm_data, int size);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
+  /*
+   * Initialize EMM timers
+   */
+void _emm_timers_initialize(emm_timers_t *emm_timers) {
+  emm_timers->T3410.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3410.sec = T3410_DEFAULT_VALUE;
+  emm_timers->T3411.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3411.sec = T3411_DEFAULT_VALUE;
+  emm_timers->T3402.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3402.sec = T3402_DEFAULT_VALUE;
+  emm_timers->T3416.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3416.sec = T3416_DEFAULT_VALUE;
+  emm_timers->T3417.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3417.sec = T3417_DEFAULT_VALUE;
+  emm_timers->T3418.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3418.sec = T3418_DEFAULT_VALUE;
+  emm_timers->T3420.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3420.sec = T3420_DEFAULT_VALUE;
+  emm_timers->T3421.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3421.sec = T3421_DEFAULT_VALUE;
+  emm_timers->T3423.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3423.sec = T3423_DEFAULT_VALUE;
+  emm_timers->T3430.id = NAS_TIMER_INACTIVE_ID;
+  emm_timers->T3430.sec = T3430_DEFAULT_VALUE;
+}
+
+void _emm_attach_initialize(emm_attach_data_t *emm_attach_data) {
+  emm_attach_data->attempt_count = 0;
+}
+
+void _emm_detach_initialize(emm_detach_data_t *emm_detach) {
+  emm_detach->count = 0;
+  emm_detach->switch_off = FALSE;
+  emm_detach->type = EMM_DETACH_TYPE_RESERVED;
+}
+
 /****************************************************************************
  **                                                                        **
  ** Name:    emm_main_initialize()                                     **
@@ -93,76 +126,74 @@ static int _emm_main_callback(int);
  ** Inputs:  cb:        The user notification callback             **
  **      imei:      The IMEI read from the UE's non-volatile   **
  **             memory                                     **
- **      Others:    _usim_data                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-void emm_main_initialize(emm_indication_callback_t cb, const char *imei)
+void emm_main_initialize(nas_user_t *user, emm_indication_callback_t cb, const char *imei)
 {
   LOG_FUNC_IN;
-
+  user->emm_data = calloc_or_fail(sizeof(emm_data_t));
   /* USIM validity indicator */
-  _emm_data.usim_is_valid = FALSE;
+  user->emm_data->usim_is_valid = FALSE;
   /* The IMEI read from the UE's non-volatile memory  */
-  _emm_data.imei = (imei_t *)malloc(sizeof(imei_t));
-  _emm_data.imei->length = _emm_main_get_imei(_emm_data.imei, imei);
+  user->emm_data->imei = (imei_t *)malloc(sizeof(imei_t));
+  user->emm_data->imei->length = _emm_main_get_imei(user->emm_data->imei, imei);
   /* The IMSI, valid only if USIM is present */
-  _emm_data.imsi = NULL;
+  user->emm_data->imsi = NULL;
   /* EPS location information */
-  _emm_data.guti = NULL;
-  _emm_data.tai = NULL;
-  _emm_data.ltai.n_tais = 0;
+  user->emm_data->guti = NULL;
+  user->emm_data->tai = NULL;
+  user->emm_data->ltai.n_tais = 0;
   /* EPS Connection Management status */
-  _emm_data.ecm_status = ECM_IDLE;
+  user->emm_data->ecm_status = ECM_IDLE;
   /* Network selection mode of operation */
-  _emm_data.plmn_mode = EMM_DATA_PLMN_AUTO;
+  user->emm_data->plmn_mode = EMM_DATA_PLMN_AUTO;
   /* Index of the PLMN manually selected by the user */
-  _emm_data.plmn_index = -1;
+  user->emm_data->plmn_index = -1;
   /* Selected Radio Access Technology */
-  _emm_data.plmn_rat = NET_ACCESS_UNAVAILABLE;
+  user->emm_data->plmn_rat = NET_ACCESS_UNAVAILABLE;
   /* Selected PLMN */
-  memset(&_emm_data.splmn, 0xFF, sizeof(plmn_t));
-  _emm_data.is_rplmn = FALSE;
-  _emm_data.is_eplmn = FALSE;
+  memset(&user->emm_data->splmn, 0xFF, sizeof(plmn_t));
+  user->emm_data->is_rplmn = FALSE;
+  user->emm_data->is_eplmn = FALSE;
   /* Radio Access Technology of the serving cell */
-  _emm_data.rat = NET_ACCESS_UNAVAILABLE;
+  user->emm_data->rat = NET_ACCESS_UNAVAILABLE;
   /* Network registration status */
-  _emm_data.stat = NET_REG_STATE_OFF;
-  _emm_data.is_attached = FALSE;
-  _emm_data.is_emergency = FALSE;
+  user->emm_data->stat = NET_REG_STATE_OFF;
+  user->emm_data->is_attached = FALSE;
+  user->emm_data->is_emergency = FALSE;
   /* Location/Tracking area code */
-  _emm_data.tac = 0;  // two byte in hexadecimal format
+  user->emm_data->tac = 0;  // two byte in hexadecimal format
   /* Identifier of the serving cell */
-  _emm_data.ci = 0;   // four byte in hexadecimal format
+  user->emm_data->ci = 0;   // four byte in hexadecimal format
   /* List of operators present in the network */
-  memset(_emm_data.plist.buffer, 0, EMM_DATA_BUFFER_SIZE + 1);
+  memset(user->emm_data->plist.buffer, 0, EMM_DATA_BUFFER_SIZE + 1);
   /* Home PLMN */
-  memset(&_emm_data.hplmn, 0xFF, sizeof(plmn_t));
+  memset(&user->emm_data->hplmn, 0xFF, sizeof(plmn_t));
   /* List of Forbidden PLMNs */
-  _emm_data.fplmn.n_plmns = 0;
+  user->emm_data->fplmn.n_plmns = 0;
   /* List of Forbidden PLMNs for GPRS service */
-  _emm_data.fplmn_gprs.n_plmns = 0;
+  user->emm_data->fplmn_gprs.n_plmns = 0;
   /* List of Equivalent HPLMNs */
-  _emm_data.ehplmn.n_plmns = 0;
+  user->emm_data->ehplmn.n_plmns = 0;
   /* List of user controlled PLMNs */
-  _emm_data.plmn.n_plmns = 0;
+  user->emm_data->plmn.n_plmns = 0;
   /* List of operator controlled PLMNs */
-  _emm_data.oplmn.n_plmns = 0;
+  user->emm_data->oplmn.n_plmns = 0;
   /* List of operator network name records */
-  _emm_data.n_opnns = 0;
+  user->emm_data->n_opnns = 0;
   /* List of Forbidden Tracking Areas */
-  _emm_data.ftai.n_tais = 0;
+  user->emm_data->ftai.n_tais = 0;
   /* List of Forbidden Tracking Areas for roaming */
-  _emm_data.ftai_roaming.n_tais = 0;
+  user->emm_data->ftai_roaming.n_tais = 0;
 
   /*
    * Get USIM application data
    */
-  _usim_data.usimtestmode = usim_test;
-  if ( usim_api_read(&_usim_data) != RETURNok ) {
+  if ( usim_api_read(user->usim_data_store, &user->usim_data) != RETURNok ) {
     /* The USIM application may not be present or not valid */
     LOG_TRACE(WARNING, "EMM-MAIN  - Failed to read USIM application data");
   } else {
@@ -170,138 +201,138 @@ void emm_main_initialize(emm_indication_callback_t cb, const char *imei)
 
     /* The USIM application is present and valid */
     LOG_TRACE(INFO, "EMM-MAIN  - USIM application data successfully read");
-    _emm_data.usim_is_valid = TRUE;
+    user->emm_data->usim_is_valid = TRUE;
 
     /* Get the Home PLMN derived from the IMSI */
-    _emm_data.hplmn.MCCdigit1 = _usim_data.imsi.u.num.digit1;
-    _emm_data.hplmn.MCCdigit2 = _usim_data.imsi.u.num.digit2;
-    _emm_data.hplmn.MCCdigit3 = _usim_data.imsi.u.num.digit3;
-    _emm_data.hplmn.MNCdigit1 = _usim_data.imsi.u.num.digit4;
-    _emm_data.hplmn.MNCdigit2 = _usim_data.imsi.u.num.digit5;
-    _emm_data.hplmn.MNCdigit3 = _usim_data.imsi.u.num.digit6;
+    user->emm_data->hplmn.MCCdigit1 = user->usim_data.imsi.u.num.digit1;
+    user->emm_data->hplmn.MCCdigit2 = user->usim_data.imsi.u.num.digit2;
+    user->emm_data->hplmn.MCCdigit3 = user->usim_data.imsi.u.num.digit3;
+    user->emm_data->hplmn.MNCdigit1 = user->usim_data.imsi.u.num.digit4;
+    user->emm_data->hplmn.MNCdigit2 = user->usim_data.imsi.u.num.digit5;
+    user->emm_data->hplmn.MNCdigit3 = user->usim_data.imsi.u.num.digit6;
 
     /* Get the list of forbidden PLMNs */
     for (i=0; (i < EMM_DATA_FPLMN_MAX) && (i < USIM_FPLMN_MAX); i++) {
-      if ( PLMN_IS_VALID(_usim_data.fplmn[i]) ) {
-        _emm_data.fplmn.plmn[i] = _usim_data.fplmn[i];
-        _emm_data.fplmn.n_plmns += 1;
+      if ( PLMN_IS_VALID(user->usim_data.fplmn[i]) ) {
+        user->emm_data->fplmn.plmn[i] = user->usim_data.fplmn[i];
+        user->emm_data->fplmn.n_plmns += 1;
       }
     }
 
     /* Get the list of Equivalent HPLMNs */
     for (i=0; (i < EMM_DATA_EHPLMN_MAX) && (i < USIM_EHPLMN_MAX); i++) {
-      if ( PLMN_IS_VALID(_usim_data.ehplmn[i]) ) {
-        _emm_data.ehplmn.plmn[i] = _usim_data.ehplmn[i];
-        _emm_data.ehplmn.n_plmns += 1;
+      if ( PLMN_IS_VALID(user->usim_data.ehplmn[i]) ) {
+        user->emm_data->ehplmn.plmn[i] = user->usim_data.ehplmn[i];
+        user->emm_data->ehplmn.n_plmns += 1;
       }
     }
 
     /* Get the list of User controlled PLMN Selector */
     for (i=0; (i < EMM_DATA_PLMN_MAX) && (i < USIM_PLMN_MAX); i++) {
-      if ( PLMN_IS_VALID(_usim_data.plmn[i].plmn) ) {
-        _emm_data.plmn.plmn[i] = _usim_data.plmn[i].plmn;
-        _emm_data.userAcT[i] = _usim_data.plmn[i].AcT;
-        _emm_data.plmn.n_plmns += 1;
+      if ( PLMN_IS_VALID(user->usim_data.plmn[i].plmn) ) {
+        user->emm_data->plmn.plmn[i] = user->usim_data.plmn[i].plmn;
+        user->emm_data->userAcT[i] = user->usim_data.plmn[i].AcT;
+        user->emm_data->plmn.n_plmns += 1;
       }
     }
 
     /* Get the list of Operator controlled PLMN Selector */
     for (i=0; (i < EMM_DATA_OPLMN_MAX) && (i < USIM_OPLMN_MAX); i++) {
-      if ( PLMN_IS_VALID(_usim_data.oplmn[i].plmn) ) {
-        _emm_data.oplmn.plmn[i] = _usim_data.oplmn[i].plmn;
-        _emm_data.operAcT[i] = _usim_data.oplmn[i].AcT;
-        _emm_data.oplmn.n_plmns += 1;
+      if ( PLMN_IS_VALID(user->usim_data.oplmn[i].plmn) ) {
+        user->emm_data->oplmn.plmn[i] = user->usim_data.oplmn[i].plmn;
+        user->emm_data->operAcT[i] = user->usim_data.oplmn[i].AcT;
+        user->emm_data->oplmn.n_plmns += 1;
       }
     }
 
     /* Get the list of Operator network name records */
     for (i=0; (i < EMM_DATA_OPNN_MAX) && (i < USIM_OPL_MAX); i++) {
-      if ( PLMN_IS_VALID(_usim_data.opl[i].plmn) ) {
-        int pnn_id = _usim_data.opl[i].record_id;
-        _emm_data.opnn[i].plmn = &_usim_data.opl[i].plmn;
-        _emm_data.opnn[i].fullname = (char *)_usim_data.pnn[pnn_id].fullname.value;
-        _emm_data.opnn[i].shortname = (char *)_usim_data.pnn[pnn_id].shortname.value;
-        _emm_data.n_opnns += 1;
+      if ( PLMN_IS_VALID(user->usim_data.opl[i].plmn) ) {
+        int pnn_id = user->usim_data.opl[i].record_id;
+        user->emm_data->opnn[i].plmn = &user->usim_data.opl[i].plmn;
+        user->emm_data->opnn[i].fullname = (char *)user->usim_data.pnn[pnn_id].fullname.value;
+        user->emm_data->opnn[i].shortname = (char *)user->usim_data.pnn[pnn_id].shortname.value;
+        user->emm_data->n_opnns += 1;
       }
     }
 
     /* TODO: Get the Higher Priority PLMN search period parameter */
 
     /* Get the EPS location information */
-    if (PLMN_IS_VALID(_usim_data.epsloci.guti.gummei.plmn)) {
-      _emm_data.guti = &_usim_data.epsloci.guti;
+    if (PLMN_IS_VALID(user->usim_data.epsloci.guti.gummei.plmn)) {
+      user->emm_data->guti = &user->usim_data.epsloci.guti;
     }
 
-    if (TAI_IS_VALID(_usim_data.epsloci.tai)) {
-      _emm_data.tai = &_usim_data.epsloci.tai;
+    if (TAI_IS_VALID(user->usim_data.epsloci.tai)) {
+      user->emm_data->tai = &user->usim_data.epsloci.tai;
     }
 
-    _emm_data.status = _usim_data.epsloci.status;
+    user->emm_data->status = user->usim_data.epsloci.status;
 
     /* Get NAS configuration parameters */
-    _emm_data.NAS_SignallingPriority =
-      _usim_data.nasconfig.NAS_SignallingPriority.value[0];
-    _emm_data.NMO_I_Behaviour = _usim_data.nasconfig.NMO_I_Behaviour.value[0];
-    _emm_data.AttachWithImsi = _usim_data.nasconfig.AttachWithImsi.value[0];
-    _emm_data.MinimumPeriodicSearchTimer =
-      _usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0];
-    _emm_data.ExtendedAccessBarring =
-      _usim_data.nasconfig.ExtendedAccessBarring.value[0];
-    _emm_data.Timer_T3245_Behaviour =
-      _usim_data.nasconfig.Timer_T3245_Behaviour.value[0];
+    user->emm_data->NAS_SignallingPriority =
+      user->usim_data.nasconfig.NAS_SignallingPriority.value[0];
+    user->emm_data->NMO_I_Behaviour = user->usim_data.nasconfig.NMO_I_Behaviour.value[0];
+    user->emm_data->AttachWithImsi = user->usim_data.nasconfig.AttachWithImsi.value[0];
+    user->emm_data->MinimumPeriodicSearchTimer =
+      user->usim_data.nasconfig.MinimumPeriodicSearchTimer.value[0];
+    user->emm_data->ExtendedAccessBarring =
+      user->usim_data.nasconfig.ExtendedAccessBarring.value[0];
+    user->emm_data->Timer_T3245_Behaviour =
+      user->usim_data.nasconfig.Timer_T3245_Behaviour.value[0];
 
     /*
      * Get EPS NAS security context
      */
     /* Create NAS security context */
-    _emm_data.security =
+    user->emm_data->security =
       (emm_security_context_t *)malloc(sizeof(emm_security_context_t));
 
-    if (_emm_data.security != NULL) {
-      memset(_emm_data.security, 0, sizeof(emm_security_context_t));
+    if (user->emm_data->security != NULL) {
+      memset(user->emm_data->security, 0, sizeof(emm_security_context_t));
 
       /* Type of security context */
-      if (_usim_data.securityctx.KSIasme.value[0] !=
+      if (user->usim_data.securityctx.KSIasme.value[0] !=
           USIM_KSI_NOT_AVAILABLE) {
-        _emm_data.security->type = EMM_KSI_NATIVE;
+        user->emm_data->security->type = EMM_KSI_NATIVE;
       } else {
-        _emm_data.security->type = EMM_KSI_NOT_AVAILABLE;
+        user->emm_data->security->type = EMM_KSI_NOT_AVAILABLE;
       }
 
       /* EPS key set identifier */
-      _emm_data.security->eksi = _usim_data.securityctx.KSIasme.value[0];
+      user->emm_data->security->eksi = user->usim_data.securityctx.KSIasme.value[0];
       /* ASME security key */
-      _emm_data.security->kasme.length =
-        _usim_data.securityctx.Kasme.length;
-      _emm_data.security->kasme.value =
-        (uint8_t *)malloc(_emm_data.security->kasme.length);
-
-      if (_emm_data.security->kasme.value) {
-        memcpy(_emm_data.security->kasme.value,
-               _usim_data.securityctx.Kasme.value,
-               _emm_data.security->kasme.length);
+      user->emm_data->security->kasme.length =
+        user->usim_data.securityctx.Kasme.length;
+      user->emm_data->security->kasme.value =
+        (uint8_t *)malloc(user->emm_data->security->kasme.length);
+
+      if (user->emm_data->security->kasme.value) {
+        memcpy(user->emm_data->security->kasme.value,
+               user->usim_data.securityctx.Kasme.value,
+               user->emm_data->security->kasme.length);
       }
 
       /* Downlink count parameter */
-      if (_usim_data.securityctx.dlNAScount.length <= sizeof(uint32_t)) {
-        memcpy(&_emm_data.security->dl_count,
-               _usim_data.securityctx.dlNAScount.value,
-               _usim_data.securityctx.dlNAScount.length);
+      if (user->usim_data.securityctx.dlNAScount.length <= sizeof(uint32_t)) {
+        memcpy(&user->emm_data->security->dl_count,
+               user->usim_data.securityctx.dlNAScount.value,
+               user->usim_data.securityctx.dlNAScount.length);
       }
 
       /* Uplink count parameter */
-      if (_usim_data.securityctx.ulNAScount.length <= sizeof(uint32_t)) {
-        memcpy(&_emm_data.security->ul_count,
-               _usim_data.securityctx.ulNAScount.value,
-               _usim_data.securityctx.ulNAScount.length);
+      if (user->usim_data.securityctx.ulNAScount.length <= sizeof(uint32_t)) {
+        memcpy(&user->emm_data->security->ul_count,
+               user->usim_data.securityctx.ulNAScount.value,
+               user->usim_data.securityctx.ulNAScount.length);
       }
 
       /* Ciphering algorithm */
-      _emm_data.security->capability.eps_encryption =
-        ((_usim_data.securityctx.algorithmID.value[0] >> 4) & 0xf);
+      user->emm_data->security->capability.eps_encryption =
+        ((user->usim_data.securityctx.algorithmID.value[0] >> 4) & 0xf);
       /* Identity protection algorithm */
-      _emm_data.security->capability.eps_integrity =
-        (_usim_data.securityctx.algorithmID.value[0] & 0xf);
+      user->emm_data->security->capability.eps_integrity =
+        (user->usim_data.securityctx.algorithmID.value[0] & 0xf);
       /* NAS integrity and cyphering keys are not available */
     } else {
       LOG_TRACE(WARNING,
@@ -311,78 +342,65 @@ void emm_main_initialize(emm_indication_callback_t cb, const char *imei)
     /*
      * Get EMM data from the UE's non-volatile memory
      */
-    memset(&_emm_data.nvdata.rplmn, 0xFF, sizeof(plmn_t));
-    _emm_data.nvdata.eplmn.n_plmns = 0;
-    /* Get EMM data pathname */
-    char *path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME);
+    memset(&user->emm_data->nvdata.rplmn, 0xFF, sizeof(plmn_t));
+    user->emm_data->nvdata.eplmn.n_plmns = 0;
 
-    if (path == NULL) {
-      LOG_TRACE(ERROR, "EMM-MAIN  - Failed to get EMM data pathname");
-    } else {
-      /* Get EMM data stored in the non-volatile memory device */
-      int rc = memory_read(path, &_emm_data.nvdata, sizeof(emm_nvdata_t));
+    /* Get EMM data stored in the non-volatile memory device */
+    int rc = memory_read(user->emm_nvdata_store, &user->emm_data->nvdata, sizeof(emm_nvdata_t));
 
-      if (rc != RETURNok) {
-        LOG_TRACE(ERROR, "EMM-MAIN  - Failed to read %s", path);
-      } else {
-        /* Check the IMSI */
-        LOG_TRACE(INFO, "EMM-MAIN  - EMM data successfully read");
-        _emm_data.imsi = &_usim_data.imsi;
-        int imsi_ok = _emm_main_imsi_cmp(&_emm_data.nvdata.imsi,
-                                         &_usim_data.imsi);
-
-        if (!imsi_ok) {
-          LOG_TRACE(WARNING, "EMM-MAIN  - IMSI checking failed nvram: "
-                    "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x, "
-                    "usim: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
-                    _emm_data.nvdata.imsi.u.value[0],
-                    _emm_data.nvdata.imsi.u.value[1],
-                    _emm_data.nvdata.imsi.u.value[2],
-                    _emm_data.nvdata.imsi.u.value[3],
-                    _emm_data.nvdata.imsi.u.value[4],
-                    _emm_data.nvdata.imsi.u.value[5],
-                    _emm_data.nvdata.imsi.u.value[6],
-                    _emm_data.nvdata.imsi.u.value[7],
-                    _usim_data.imsi.u.value[0],
-                    _usim_data.imsi.u.value[1],
-                    _usim_data.imsi.u.value[2],
-                    _usim_data.imsi.u.value[3],
-                    _usim_data.imsi.u.value[4],
-                    _usim_data.imsi.u.value[5],
-                    _usim_data.imsi.u.value[6],
-                    _usim_data.imsi.u.value[7]);
-          memset(&_emm_data.nvdata.rplmn, 0xFF, sizeof(plmn_t));
-          _emm_data.nvdata.eplmn.n_plmns = 0;
-        }
-      }
+    if (rc != RETURNok) {
+      LOG_TRACE(ERROR, "EMM-MAIN  - Failed to read %s", user->emm_nvdata_store);
+      exit(EXIT_FAILURE);
+    }
 
-      free(path);
+    /* Check the IMSI */
+    LOG_TRACE(INFO, "EMM-MAIN  - EMM data successfully read");
+    user->emm_data->imsi = &user->usim_data.imsi;
+    int imsi_ok = _emm_main_imsi_cmp(&user->emm_data->nvdata.imsi,
+                                     &user->usim_data.imsi);
+
+    if (!imsi_ok) {
+      LOG_TRACE(WARNING, "EMM-MAIN  - IMSI checking failed nvram: "
+                "%02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x, "
+                "usim: %02x.%02x.%02x.%02x.%02x.%02x.%02x.%02x",
+                user->emm_data->nvdata.imsi.u.value[0],
+                user->emm_data->nvdata.imsi.u.value[1],
+                user->emm_data->nvdata.imsi.u.value[2],
+                user->emm_data->nvdata.imsi.u.value[3],
+                user->emm_data->nvdata.imsi.u.value[4],
+                user->emm_data->nvdata.imsi.u.value[5],
+                user->emm_data->nvdata.imsi.u.value[6],
+                user->emm_data->nvdata.imsi.u.value[7],
+                user->usim_data.imsi.u.value[0],
+                user->usim_data.imsi.u.value[1],
+                user->usim_data.imsi.u.value[2],
+                user->usim_data.imsi.u.value[3],
+                user->usim_data.imsi.u.value[4],
+                user->usim_data.imsi.u.value[5],
+                user->usim_data.imsi.u.value[6],
+                user->usim_data.imsi.u.value[7]);
+      memset(&user->emm_data->nvdata.rplmn, 0xFF, sizeof(plmn_t));
+      user->emm_data->nvdata.eplmn.n_plmns = 0;
     }
   }
 
   /*
    * Initialize EMM timers
    */
-  T3410.id = NAS_TIMER_INACTIVE_ID;
-  T3410.sec = T3410_DEFAULT_VALUE;
-  T3411.id = NAS_TIMER_INACTIVE_ID;
-  T3411.sec = T3411_DEFAULT_VALUE;
-  T3402.id = NAS_TIMER_INACTIVE_ID;
-  T3402.sec = T3402_DEFAULT_VALUE;
-  T3416.id = NAS_TIMER_INACTIVE_ID;
-  T3416.sec = T3416_DEFAULT_VALUE;
-  T3417.id = NAS_TIMER_INACTIVE_ID;
-  T3417.sec = T3417_DEFAULT_VALUE;
-  T3418.id = NAS_TIMER_INACTIVE_ID;
-  T3418.sec = T3418_DEFAULT_VALUE;
-  T3420.id = NAS_TIMER_INACTIVE_ID;
-  T3420.sec = T3420_DEFAULT_VALUE;
-  T3421.id = NAS_TIMER_INACTIVE_ID;
-  T3421.sec = T3421_DEFAULT_VALUE;
-  T3423.id = NAS_TIMER_INACTIVE_ID;
-  T3423.sec = T3423_DEFAULT_VALUE;
-  T3430.id = NAS_TIMER_INACTIVE_ID;
-  T3430.sec = T3430_DEFAULT_VALUE;
+  user->emm_data->emm_timers = calloc_or_fail(sizeof(emm_timers_t));
+  _emm_timers_initialize(user->emm_data->emm_timers);
+
+  /*
+   * Initialize Internal data used for detach procedure
+   */
+  user->emm_data->emm_detach_data = calloc_or_fail(sizeof(emm_detach_data_t));
+  _emm_detach_initialize(user->emm_data->emm_detach_data);
+
+  /*
+   * Initialize Internal data used for attach procedure
+   */
+  user->emm_data->emm_attach_data = calloc_or_fail(sizeof(emm_attach_data_t));
+  _emm_attach_initialize(user->emm_data->emm_attach_data);
 
   /*
    * Initialize the user notification callback
@@ -392,7 +410,7 @@ void emm_main_initialize(emm_indication_callback_t cb, const char *imei)
   /*
    * Initialize EMM internal data used for UE in idle mode
    */
-  IdleMode_initialize(&_emm_main_callback);
+  IdleMode_initialize(user, &_emm_main_callback);
 
   LOG_FUNC_OUT;
 }
@@ -412,56 +430,16 @@ void emm_main_initialize(emm_indication_callback_t cb, const char *imei)
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void emm_main_cleanup(void)
+void emm_main_cleanup(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
-  if (_emm_data.usim_is_valid) {
-    /*
-     * TODO: Update USIM application data
-     */
-#if 0
-    int i;
-
-    /* Update the list of Forbidden PLMNs */
-    for (i=0; (i < _emm_data.fplmn.n_plmns) && (i < USIM_FPLMN_MAX); i++) {
-      _usim_data.fplmn[i] = _emm_data.fplmn.plmn[i];
-    }
-
-    /* Update the list of Equivalent HPLMNs */
-    for (i=0; (i < _emm_data.ehplmn.n_plmns) && (i < USIM_EHPLMN_MAX); i++) {
-      _usim_data.ehplmn[i] = _emm_data.ehplmn.plmn[i];
-    }
-
-    /* Update the GUTI */
-    if (_emm_data.guti) {
-      _usim_data.epsloci.guti = *(_emm_data.guti);
-    }
-
-    /* Update the last visited registered TAI */
-    if (_emm_data.tai) {
-      _usim_data.epsloci.tai = *(_emm_data.tai);
-    }
-
-    /* Update the EPS location information */
-    _usim_data.epsloci.status = _emm_data.status;
-
-    if (_emm_data.security && (_emm_data.security->type == EMM_KSI_NATIVE)) {
-      /* TODO: Update the EPS security context parameters from the full
-       * native EPS security context */
-    }
+  emm_data_t *emm_data = user->emm_data;
 
+  if (emm_data->usim_is_valid) {
     /*
-     * Store USIM application data
-     * - List of forbidden PLMNs
+     * TODO: Update USIM application data
      */
-    if ( usim_api_write(&_usim_data) != RETURNok ) {
-      /* The USIM application may not be present or not valid */
-      LOG_TRACE(WARNING, "EMM-MAIN  - "
-                "Failed to write USIM application data");
-    }
-
-#endif
   }
 
   /*
@@ -469,26 +447,20 @@ void emm_main_cleanup(void)
    * - Registered PLMN
    * - List of equivalent PLMNs
    */
-  char *path = memory_get_path(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    LOG_TRACE(ERROR, "EMM-MAIN  - Failed to get EMM data pathname");
-  } else {
-    int rc = memory_write(path, &_emm_data.nvdata, sizeof(emm_nvdata_t));
+  int rc = memory_write(user->emm_nvdata_store, &emm_data->nvdata, sizeof(emm_nvdata_t));
 
-    if (rc != RETURNok) {
-      LOG_TRACE(ERROR, "EMM-MAIN  - Failed to write %s", path);
-    }
+  if (rc != RETURNok) {
+    LOG_TRACE(ERROR, "EMM-MAIN  - Failed to write %s", user->emm_nvdata_store);
   }
 
   /* Release dynamically allocated memory */
-  if (_emm_data.imei) {
-    free(_emm_data.imei);
-    _emm_data.imei = NULL;
+  if (emm_data->imei) {
+    free(emm_data->imei);
+    emm_data->imei = NULL;
   }
 
-  if (_emm_data.security) {
-    emm_security_context_t *security = _emm_data.security;
+  if (emm_data->security) {
+    emm_security_context_t *security = emm_data->security;
 
     if (security->kasme.value) {
       free(security->kasme.value);
@@ -508,8 +480,8 @@ void emm_main_cleanup(void)
       security->knas_int.length = 0;
     }
 
-    free(_emm_data.security);
-    _emm_data.security = NULL;
+    free(emm_data->security);
+    emm_data->security = NULL;
   }
   LOG_FUNC_OUT;
 }
@@ -521,17 +493,17 @@ void emm_main_cleanup(void)
  ** Description: Get the International Mobile Subscriber Identity number   **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    Pointer to the IMSI                        **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const imsi_t *emm_main_get_imsi(void)
+const imsi_t *emm_main_get_imsi(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (&_emm_data.nvdata.imsi);
+  LOG_FUNC_RETURN (&emm_data->nvdata.imsi);
 }
 
 /****************************************************************************
@@ -541,17 +513,16 @@ const imsi_t *emm_main_get_imsi(void)
  ** Description: Get the Mobile Subscriber Dialing Number from the USIM    **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _usim_data                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    Pointer to the subscriber dialing number   **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const msisdn_t *emm_main_get_msisdn(void)
+const msisdn_t *emm_main_get_msisdn(nas_user_t *user)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (&_usim_data.msisdn.number);
+  LOG_FUNC_RETURN (&user->usim_data.msisdn.number);
 }
 
 /****************************************************************************
@@ -572,32 +543,34 @@ const msisdn_t *emm_main_get_msisdn(void)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ***************************************************************************/
-int emm_main_set_plmn_selection_mode(int mode, int format,
+int emm_main_set_plmn_selection_mode(nas_user_t *user, int mode, int format,
                                      const network_plmn_t *plmn, int rat)
 {
   LOG_FUNC_IN;
 
   int index;
+  emm_data_t *emm_data = user->emm_data;
+  emm_plmn_list_t *emm_plmn_list = user->emm_plmn_list;
 
   LOG_TRACE(INFO, "EMM-MAIN  - PLMN selection: mode=%d, format=%d, plmn=%s, "
             "rat=%d", mode, format, (const char *)&plmn->id, rat);
 
-  _emm_data.plmn_mode = mode;
+  emm_data->plmn_mode = mode;
 
   if (mode != EMM_DATA_PLMN_AUTO) {
     /* Get the index of the PLMN in the list of available PLMNs */
-    index = _emm_main_get_plmn_index((const char *)&plmn->id, format);
+    index = _emm_main_get_plmn_index(emm_plmn_list, (const char *)&plmn->id, format);
 
     if (index < 0) {
       LOG_TRACE(WARNING, "EMM-MAIN  - PLMN %s not available",
                 (const char *)&plmn->id);
     } else {
       /* Update the manually selected network selection data */
-      _emm_data.plmn_index = index;
-      _emm_data.plmn_rat = rat;
+      emm_data->plmn_index = index;
+      emm_data->plmn_rat = rat;
     }
   } else {
     /*
@@ -605,7 +578,7 @@ int emm_main_set_plmn_selection_mode(int mode, int format,
      * register to when switched on; the equivalent PLMNs list shall not be
      * applied to the user reselection in Automatic Network Selection Mode.
      */
-    index = IdleMode_get_hplmn_index();
+    index = IdleMode_get_hplmn_index(emm_plmn_list);
   }
 
   LOG_FUNC_RETURN (index);
@@ -619,17 +592,17 @@ int emm_main_set_plmn_selection_mode(int mode, int format,
  **      operation                                                 **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The value of the network selection mode    **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_main_get_plmn_selection_mode(void)
+int emm_main_get_plmn_selection_mode(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.plmn_mode);
+  LOG_FUNC_RETURN (emm_data->plmn_mode);
 }
 
 /****************************************************************************
@@ -639,19 +612,19 @@ int emm_main_get_plmn_selection_mode(void)
  ** Description: Get the list of available PLMNs                           **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     plist:     Pointer to the list of available PLMNs     **
  **      Return:    The size of the list in bytes              **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_main_get_plmn_list(const char **plist)
+int emm_main_get_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, const char **plist)
 {
   LOG_FUNC_IN;
 
-  int size = IdleMode_update_plmn_list(0);
-  *plist = _emm_data.plist.buffer;
+  int size = IdleMode_update_plmn_list(emm_plmn_list, emm_data, 0);
+  *plist = emm_data->plist.buffer;
 
   LOG_FUNC_RETURN (size);
 }
@@ -664,7 +637,6 @@ int emm_main_get_plmn_list(const char **plist)
  **                                                                        **
  ** Inputs:  format:    The requested format of the string repre-  **
  **             sentation of the PLMN identifier           **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     plmn:      The selected PLMN identifier coded in the  **
  **             requested format                           **
@@ -673,7 +645,7 @@ int emm_main_get_plmn_list(const char **plist)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format)
+const char *emm_main_get_selected_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format)
 {
   LOG_FUNC_IN;
 
@@ -681,10 +653,10 @@ const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format)
   /*
    * Get the identifier of the selected PLMN in the list of available PLMNs
    */
-  int index = IdleMode_get_splmn_index();
+  int index = IdleMode_get_splmn_index(emm_plmn_list);
 
   if ( !(index < 0) ) {
-    const char *name = _emm_main_get_plmn(&_emm_data.splmn, index,
+    const char *name = _emm_main_get_plmn(emm_plmn_list, &emm_data->splmn, index,
                                           format, &size);
 
     if (size > 0) {
@@ -703,7 +675,6 @@ const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format)
  **                                                                        **
  ** Inputs:  format:    The requested format of the string repre-  **
  **             sentation of the PLMN identifier           **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     plmn:      The registered PLMN identifier coded in    **
  **             the requested format                       **
@@ -712,7 +683,7 @@ const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const char *emm_main_get_registered_plmn(network_plmn_t *plmn, int format)
+const char *emm_main_get_registered_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format)
 {
   LOG_FUNC_IN;
 
@@ -721,10 +692,10 @@ const char *emm_main_get_registered_plmn(network_plmn_t *plmn, int format)
   /*
    * Get the identifier of the registered PLMN in the list of available PLMNs
    */
-  int index = IdleMode_get_rplmn_index();
+  int index = IdleMode_get_rplmn_index(emm_plmn_list);
 
   if ( !(index < 0) ) {
-    const char *name = _emm_main_get_plmn(&_emm_data.nvdata.rplmn,
+    const char *name = _emm_main_get_plmn(emm_plmn_list, &emm_data->nvdata.rplmn,
                                           index, format, &size);
 
     if (size > 0) {
@@ -744,17 +715,16 @@ const char *emm_main_get_registered_plmn(network_plmn_t *plmn, int format)
  **      registration of the UE                                    **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The current network registration status    **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-Stat_t emm_main_get_plmn_status(void)
+Stat_t emm_main_get_plmn_status(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.stat);
+  LOG_FUNC_RETURN (emm_data->stat);
 }
 
 /****************************************************************************
@@ -765,17 +735,16 @@ Stat_t emm_main_get_plmn_status(void)
  **      belongs to                                                **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The Location/Tracking area code            **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-tac_t emm_main_get_plmn_tac(void)
+tac_t emm_main_get_plmn_tac(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.tac);
+  LOG_FUNC_RETURN (emm_data->tac);
 }
 
 /****************************************************************************
@@ -785,17 +754,16 @@ tac_t emm_main_get_plmn_tac(void)
  ** Description: Get the identifier of the serving cell                    **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The serving cell identifier                **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-ci_t emm_main_get_plmn_ci(void)
+ci_t emm_main_get_plmn_ci(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.ci);
+  LOG_FUNC_RETURN (emm_data->ci);
 }
 
 /****************************************************************************
@@ -806,7 +774,6 @@ ci_t emm_main_get_plmn_ci(void)
  **      ving cell                                                 **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The value of the Radio Access Technology   **
@@ -814,10 +781,10 @@ ci_t emm_main_get_plmn_ci(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-AcT_t emm_main_get_plmn_rat(void)
+AcT_t emm_main_get_plmn_rat(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.rat);
+  LOG_FUNC_RETURN (emm_data->rat);
 }
 
 /****************************************************************************
@@ -828,7 +795,7 @@ AcT_t emm_main_get_plmn_rat(void)
  **      network for EPS services or emergency service only        **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    TRUE if the UE is currently attached to    **
@@ -836,10 +803,10 @@ AcT_t emm_main_get_plmn_rat(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_main_is_attached(void)
+int emm_main_is_attached(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.is_attached);
+  LOG_FUNC_RETURN (emm_data->is_attached);
 }
 
 /****************************************************************************
@@ -850,7 +817,7 @@ int emm_main_is_attached(void)
  **      network for emergency bearer services                     **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _emm_data                                  **
+ **      Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    TRUE if the UE is currently attached or is **
@@ -859,10 +826,10 @@ int emm_main_is_attached(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int emm_main_is_emergency(void)
+int emm_main_is_emergency(emm_data_t *emm_data)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN (_emm_data.is_attached && _emm_data.is_emergency);
+  LOG_FUNC_RETURN (emm_data->is_attached && emm_data->is_emergency);
 }
 
 /****************************************************************************/
@@ -881,21 +848,21 @@ int emm_main_is_emergency(void)
  **             present in the network. The list has to be **
  **             displayed to the user application when     **
  **             size > 0.                                  **
- **          Others:    _emm_data                                  **
+ **          Others:    user->emm_data->                                 **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_main_callback(int size)
+static int _emm_main_callback(user_api_id_t *user_api_id, emm_data_t *emm_data, int size)
 {
   LOG_FUNC_IN;
 
   /* Forward the notification to the user API */
-  int rc = (*_emm_main_user_callback)(_emm_data.stat, _emm_data.tac,
-                                      _emm_data.ci, _emm_data.rat,
-                                      _emm_data.plist.buffer, size);
+  int rc = (*_emm_main_user_callback)(user_api_id, emm_data->stat, emm_data->tac,
+                                      emm_data->ci, emm_data->rat,
+                                      emm_data->plist.buffer, size);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -995,24 +962,24 @@ static int _emm_main_imsi_cmp(imsi_t *imsi1, imsi_t *imsi2)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static const char *_emm_main_get_plmn(const plmn_t *plmn, int index,
+static const char *_emm_main_get_plmn(emm_plmn_list_t *emm_plmn_list, const plmn_t *plmn, int index,
                                       int format, size_t *size)
 {
   if ( PLMN_IS_VALID(*plmn) ) {
     switch (format) {
     case NET_FORMAT_LONG:
       /* Get the long alpha-numeric representation of the PLMN */
-      return IdleMode_get_plmn_fullname(plmn, index, size);
+      return IdleMode_get_plmn_fullname(emm_plmn_list, plmn, index, size);
       break;
 
     case NET_FORMAT_SHORT:
       /* Get the short alpha-numeric representation of the PLMN */
-      return IdleMode_get_plmn_shortname(plmn, index, size);
+      return IdleMode_get_plmn_shortname(emm_plmn_list, plmn, index, size);
       break;
 
     case NET_FORMAT_NUM:
       /* Get the numeric representation of the PLMN */
-      return IdleMode_get_plmn_id(plmn, index, size);
+      return IdleMode_get_plmn_id(emm_plmn_list, plmn, index, size);
       break;
 
     default:
@@ -1045,24 +1012,24 @@ static const char *_emm_main_get_plmn(const plmn_t *plmn, int index,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _emm_main_get_plmn_index(const char *plmn, int format)
+static int _emm_main_get_plmn_index(emm_plmn_list_t *emm_plmn_list, const char *plmn, int format)
 {
   int index = -1;
 
   switch (format) {
   case NET_FORMAT_LONG:
     /* Get the index of the long alpha-numeric PLMN identifier */
-    index = IdleMode_get_plmn_fullname_index(plmn);
+    index = IdleMode_get_plmn_fullname_index(emm_plmn_list, plmn);
     break;
 
   case NET_FORMAT_SHORT:
     /* Get the index of the short alpha-numeric PLMN identifier */
-    index = IdleMode_get_plmn_shortname_index(plmn);
+    index = IdleMode_get_plmn_shortname_index(emm_plmn_list, plmn);
     break;
 
   case NET_FORMAT_NUM:
     /* Get the index of the numeric PLMN identifier */
-    index = IdleMode_get_plmn_id_index(plmn);
+    index = IdleMode_get_plmn_id_index(emm_plmn_list, plmn);
     break;
 
   default:
diff --git a/openair3/NAS/UE/EMM/emm_main.h b/openair3/NAS/UE/EMM/emm_main.h
index 1ce584210e5c4183e50653b2ac9b527556f16bbd..aa98965b041245bb818efa4a1a5a3be93f972fd4 100644
--- a/openair3/NAS/UE/EMM/emm_main.h
+++ b/openair3/NAS/UE/EMM/emm_main.h
@@ -41,6 +41,7 @@ Description Defines the EPS Mobility Management procedure call manager,
 
 #include "commonDef.h"
 #include "networkDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -50,6 +51,15 @@ Description Defines the EPS Mobility Management procedure call manager,
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
+/*
+ * User notification callback, executed whenever a change of data with
+ * respect of network information (e.g. network registration and/or
+ * location change, new PLMN becomes available) is notified by the
+ * EPS Mobility Management sublayer
+ */
+typedef int (*emm_indication_callback_t) (user_api_id_t *user_api_id, Stat_t, tac_t, ci_t, AcT_t,
+    const char*, size_t);
+
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -58,34 +68,34 @@ Description Defines the EPS Mobility Management procedure call manager,
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void emm_main_initialize(emm_indication_callback_t cb, const char *imei);
+void emm_main_initialize(nas_user_t *user, emm_indication_callback_t cb, const char *imei);
 
-void emm_main_cleanup(void);
+void emm_main_cleanup(nas_user_t *user);
 
 
 /* User's getter of UE's identity */
-const imsi_t *emm_main_get_imsi(void);
+const imsi_t *emm_main_get_imsi(emm_data_t *emm_data);
 
 /* User's getter of the subscriber dialing number */
-const msisdn_t *emm_main_get_msisdn(void);
+const msisdn_t *emm_main_get_msisdn(nas_user_t *user);
 
 /* User's getter/setter for network selection */
-int emm_main_set_plmn_selection_mode(int mode, int format,
+int emm_main_set_plmn_selection_mode(nas_user_t *user, int mode, int format,
                                      const network_plmn_t *plmn, int rat);
-int emm_main_get_plmn_selection_mode(void);
-int emm_main_get_plmn_list(const char **plist);
-const char *emm_main_get_selected_plmn(network_plmn_t *plmn, int format);
+int emm_main_get_plmn_selection_mode(emm_data_t *emm_data);
+int emm_main_get_plmn_list(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, const char **plist);
+const char *emm_main_get_selected_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format);
 
 /* User's getter for network registration */
-Stat_t emm_main_get_plmn_status(void);
-tac_t emm_main_get_plmn_tac(void);
-ci_t emm_main_get_plmn_ci(void);
-AcT_t emm_main_get_plmn_rat(void);
-const char *emm_main_get_registered_plmn(network_plmn_t *plmn, int format);
+Stat_t emm_main_get_plmn_status(emm_data_t *emm_data);
+tac_t emm_main_get_plmn_tac(emm_data_t *emm_data);
+ci_t emm_main_get_plmn_ci(emm_data_t *emm_data);
+AcT_t emm_main_get_plmn_rat(emm_data_t *emm_data);
+const char *emm_main_get_registered_plmn(emm_plmn_list_t *emm_plmn_list, emm_data_t *emm_data, network_plmn_t *plmn, int format);
 
 /* User's getter for network attachment */
-int emm_main_is_attached(void);
-int emm_main_is_emergency(void);
+int emm_main_is_attached(emm_data_t *emm_data);
+int emm_main_is_emergency(emm_data_t *emm_data);
 
 
 #endif /* __EMM_MAIN_H__*/
diff --git a/openair3/NAS/UE/EMM/emm_proc.h b/openair3/NAS/UE/EMM/emm_proc.h
index 60f71f11c200ebd67ef02b90b5088cfa92dbaead..45052573352c3402fb8f2dcb2e0e9a162f75fa6b 100644
--- a/openair3/NAS/UE/EMM/emm_proc.h
+++ b/openair3/NAS/UE/EMM/emm_proc.h
@@ -42,38 +42,12 @@ Description Defines the EPS Mobility Management procedures executed at
 #include "commonDef.h"
 #include "OctetString.h"
 #include "LowerLayer.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
 
-/* Type of network attachment */
-typedef enum {
-  EMM_ATTACH_TYPE_EPS = 0,
-  EMM_ATTACH_TYPE_IMSI,
-  EMM_ATTACH_TYPE_EMERGENCY,
-  EMM_ATTACH_TYPE_RESERVED,
-} emm_proc_attach_type_t;
-
-/* Type of network detach */
-typedef enum {
-  EMM_DETACH_TYPE_EPS = 0,
-  EMM_DETACH_TYPE_IMSI,
-  EMM_DETACH_TYPE_EPS_IMSI,
-  EMM_DETACH_TYPE_REATTACH,
-  EMM_DETACH_TYPE_NOT_REATTACH,
-  EMM_DETACH_TYPE_RESERVED,
-} emm_proc_detach_type_t;
-
-/* Type of requested identity */
-typedef enum {
-  EMM_IDENT_TYPE_NOT_AVAILABLE = 0,
-  EMM_IDENT_TYPE_IMSI,
-  EMM_IDENT_TYPE_IMEI,
-  EMM_IDENT_TYPE_IMEISV,
-  EMM_IDENT_TYPE_TMSI
-} emm_proc_identity_type_t;
-
 /****************************************************************************/
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
@@ -92,48 +66,35 @@ typedef enum {
  *---------------------------------------------------------------------------
  */
 int emm_proc_status_ind(unsigned int ueid, int emm_cause);
-int emm_proc_status(unsigned int ueid, int emm_cause);
-
-/*
- *---------------------------------------------------------------------------
- *              Lower layer procedure
- *---------------------------------------------------------------------------
- */
-int emm_proc_lowerlayer_initialize(lowerlayer_success_callback_t success,
-                                   lowerlayer_failure_callback_t failure,
-                                   lowerlayer_release_callback_t release,
-                                   void *args);
-int emm_proc_lowerlayer_success(void);
-int emm_proc_lowerlayer_failure(int is_initial);
-int emm_proc_lowerlayer_release(void);
+int emm_proc_status(nas_user_t *user, int emm_cause);
 
 /*
  *---------------------------------------------------------------------------
  *              UE's Idle mode procedure
  *---------------------------------------------------------------------------
  */
-int emm_proc_initialize(void);
-int emm_proc_plmn_selection(int index);
-int emm_proc_plmn_selection_end(int found, tac_t tac, ci_t ci, AcT_t rat);
+int emm_proc_initialize(nas_user_t *user);
+int emm_proc_plmn_selection(nas_user_t *user, int index);
+int emm_proc_plmn_selection_end(nas_user_t *user, int found, tac_t tac, ci_t ci, AcT_t rat);
 
 /*
  * --------------------------------------------------------------------------
  *              Attach procedure
  * --------------------------------------------------------------------------
  */
-int emm_proc_attach(emm_proc_attach_type_t type);
+int emm_proc_attach(nas_user_t *user, emm_proc_attach_type_t type);
 int emm_proc_attach_request(void *args);
-int emm_proc_attach_accept(long T3412, long T3402, long T3423, int n_tais,
+int emm_proc_attach_accept(nas_user_t *user, long T3412, long T3402, long T3423, int n_tais,
                            tai_t *tai, GUTI_t *guti, int n_eplmns, plmn_t *eplmn,
                            const OctetString *esm_msg);
-int emm_proc_attach_reject(int emm_cause, const OctetString *esm_msg);
+int emm_proc_attach_reject(nas_user_t *user, int emm_cause, const OctetString *esm_msg);
 int emm_proc_attach_complete(void *args);
 int emm_proc_attach_failure(int is_initial, void *args);
 int emm_proc_attach_release(void *args);
-int emm_proc_attach_restart(void);
+int emm_proc_attach_restart(nas_user_t *user);
 
-int emm_proc_attach_set_emergency(void);
-int emm_proc_attach_set_detach(void);
+int emm_proc_attach_set_emergency(emm_data_t *emm_data);
+int emm_proc_attach_set_detach(void *user);
 
 
 
@@ -142,9 +103,9 @@ int emm_proc_attach_set_detach(void);
  *              Detach procedure
  * --------------------------------------------------------------------------
  */
-int emm_proc_detach(emm_proc_detach_type_t type, int switch_off);
+int emm_proc_detach(nas_user_t *user, emm_proc_detach_type_t type, int switch_off);
 int emm_proc_detach_request(void *args);
-int emm_proc_detach_accept(void);
+int emm_proc_detach_accept(void *args);
 int emm_proc_detach_failure(int is_initial, void *args);
 int emm_proc_detach_release(void *args);
 
@@ -154,7 +115,7 @@ int emm_proc_detach_release(void *args);
  *              Identification procedure
  * --------------------------------------------------------------------------
  */
-int emm_proc_identification_request(emm_proc_identity_type_t type);
+int emm_proc_identification_request(nas_user_t *user, emm_proc_identity_type_t type);
 
 
 /*
@@ -162,10 +123,10 @@ int emm_proc_identification_request(emm_proc_identity_type_t type);
  *              Authentication procedure
  * --------------------------------------------------------------------------
  */
-int emm_proc_authentication_request(int native_ksi, int ksi,
+int emm_proc_authentication_request(nas_user_t *user, int native_ksi, int ksi,
                                     const OctetString *rand, const OctetString *autn);
-int emm_proc_authentication_reject(void);
-int emm_proc_authentication_delete(void);
+int emm_proc_authentication_reject(nas_user_t *user);
+int emm_proc_authentication_delete(nas_user_t *user);
 
 
 /*
@@ -173,16 +134,16 @@ int emm_proc_authentication_delete(void);
  *          Security mode control procedure
  * --------------------------------------------------------------------------
  */
-int emm_proc_security_mode_command(int native_ksi, int ksi, int seea, int seia,
-                                   int reea, int reia,int imeisv_request);
+int emm_proc_security_mode_command(nas_user_t *user, int native_ksi, int ksi, int seea, int seia,
+                                   int reea, int reia, int imeisv_request);
 
 /*
  *---------------------------------------------------------------------------
  *             Network indication handlers
  *---------------------------------------------------------------------------
  */
-int emm_proc_registration_notify(Stat_t status);
-int emm_proc_location_notify(tac_t tac, ci_t ci, AcT_t rat);
-int emm_proc_network_notify(int index);
+int emm_proc_registration_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, Stat_t status);
+int emm_proc_location_notify(user_api_id_t *user_api_id, emm_data_t *emm_data, tac_t tac, ci_t ci, AcT_t rat);
+int emm_proc_network_notify(emm_plmn_list_t *emm_plmn_list, user_api_id_t *user_api_id, emm_data_t *emm_data, int index);
 
 #endif /* __EMM_PROC_H__*/
diff --git a/openair3/NAS/UE/EMM/emm_proc_defs.h b/openair3/NAS/UE/EMM/emm_proc_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..3779626ac0c384bc3c6fa997d6d12aaaf1be0e60
--- /dev/null
+++ b/openair3/NAS/UE/EMM/emm_proc_defs.h
@@ -0,0 +1,31 @@
+#ifndef _EMM_PROC_DEFS_H
+#define _EMM_PROC_DEFS_H
+
+/* Type of network attachment */
+typedef enum {
+  EMM_ATTACH_TYPE_EPS = 0,
+  EMM_ATTACH_TYPE_IMSI,
+  EMM_ATTACH_TYPE_EMERGENCY,
+  EMM_ATTACH_TYPE_RESERVED,
+} emm_proc_attach_type_t;
+
+/* Type of network detach */
+typedef enum {
+  EMM_DETACH_TYPE_EPS = 0,
+  EMM_DETACH_TYPE_IMSI,
+  EMM_DETACH_TYPE_EPS_IMSI,
+  EMM_DETACH_TYPE_REATTACH,
+  EMM_DETACH_TYPE_NOT_REATTACH,
+  EMM_DETACH_TYPE_RESERVED,
+} emm_proc_detach_type_t;
+
+/* Type of requested identity */
+typedef enum {
+  EMM_IDENT_TYPE_NOT_AVAILABLE = 0,
+  EMM_IDENT_TYPE_IMSI,
+  EMM_IDENT_TYPE_IMEI,
+  EMM_IDENT_TYPE_IMEISV,
+  EMM_IDENT_TYPE_TMSI
+} emm_proc_identity_type_t;
+
+#endif
diff --git a/openair3/NAS/UE/EMM/emm_timers.h b/openair3/NAS/UE/EMM/emm_timers.h
new file mode 100644
index 0000000000000000000000000000000000000000..abf7c9ff87411aa362e1e4141af4adeab4d0fbd8
--- /dev/null
+++ b/openair3/NAS/UE/EMM/emm_timers.h
@@ -0,0 +1,14 @@
+#ifndef EMM_TIMERS_H
+#define EMM_TIMERS_H
+
+/*
+ * Retransmission timer handlers
+ */
+
+void *emm_attach_t3410_handler(void *);
+void *emm_service_t3417_handler(void *);
+void *emm_detach_t3421_handler(void *);
+void *emm_tau_t3430_handler(void *);
+
+
+#endif
diff --git a/openair3/NAS/UE/ESM/DedicatedEpsBearerContextActivation.c b/openair3/NAS/UE/ESM/DedicatedEpsBearerContextActivation.c
index 21e54fd2d033371dfe99d76bea4b8825a85ffd7a..e2133563324f60c61df629acfeb0304d7211e6f1 100644
--- a/openair3/NAS/UE/ESM/DedicatedEpsBearerContextActivation.c
+++ b/openair3/NAS/UE/ESM/DedicatedEpsBearerContextActivation.c
@@ -104,20 +104,20 @@ Description Defines the dedicated EPS bearer context activation ESM
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
+int esm_proc_dedicated_eps_bearer_context_request(nas_user_t *user, int ebi, int default_ebi,
     const esm_proc_qos_t *qos,
     const esm_proc_tft_t *tft,
     int *esm_cause)
 {
   LOG_FUNC_IN;
-
+  esm_data_t *esm_data = user->esm_data;
   int rc = RETURNerror;
 
   LOG_TRACE(INFO, "ESM-PROC  - Dedicated EPS bearer context activation "
             "requested by the network (ebi=%d)", ebi);
 
   /* Get the PDN connection the dedicated EPS bearer is linked to */
-  int pid = esm_ebr_context_get_pid(default_ebi);
+  int pid = esm_ebr_context_get_pid(esm_data, default_ebi);
 
   if (pid < 0) {
     /* 3GPP TS 24.301, section 6.4.2.5, abnormal case c
@@ -131,7 +131,7 @@ int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
   }
 
   /* Assign dedicated EPS bearer context */
-  int new_ebi = esm_ebr_assign(ebi, pid+1, FALSE);
+  int new_ebi = esm_ebr_assign(user->esm_ebr_data, ebi, pid+1, FALSE);
 
   if (new_ebi == ESM_EBI_UNASSIGNED) {
     /* 3GPP TS 24.301, section 6.4.2.5, abnormal cases a and b
@@ -141,7 +141,7 @@ int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
     int old_pid, old_bid;
     /* Locally deactivate the existing EPS bearer context and proceed
      * with the requested dedicated EPS bearer context activation */
-    rc = esm_proc_eps_bearer_context_deactivate(TRUE, ebi,
+    rc = esm_proc_eps_bearer_context_deactivate(user, TRUE, ebi,
          &old_pid, &old_bid);
 
     if (rc != RETURNok) {
@@ -149,13 +149,13 @@ int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
       *esm_cause = ESM_CAUSE_PROTOCOL_ERROR;
     } else {
       /* Assign new dedicated EPS bearer context */
-      ebi = esm_ebr_assign(ebi, pid+1, FALSE);
+      ebi = esm_ebr_assign(user->esm_ebr_data, ebi, pid+1, FALSE);
     }
   }
 
   if (ebi != ESM_EBI_UNASSIGNED) {
     /* Check syntactical errors in packet filters */
-    rc = esm_ebr_context_check_tft(pid, ebi, tft,
+    rc = esm_ebr_context_check_tft(esm_data, pid, ebi, tft,
                                    ESM_EBR_CONTEXT_TFT_CREATE);
 
     if (rc != RETURNok) {
@@ -165,7 +165,7 @@ int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
       *esm_cause = ESM_CAUSE_SYNTACTICAL_ERROR_IN_PACKET_FILTER;
     } else {
       /* Create new dedicated EPS bearer context */
-      default_ebi = esm_ebr_context_create(pid, ebi, FALSE, qos, tft);
+      default_ebi = esm_ebr_context_create(esm_data, user->ueid, pid, ebi, FALSE, qos, tft);
 
       if (default_ebi != ESM_EBI_UNASSIGNED) {
         /* Dedicated EPS bearer contextx successfully created */
@@ -206,12 +206,14 @@ int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_dedicated_eps_bearer_context_accept(int is_standalone, int ebi,
+int esm_proc_dedicated_eps_bearer_context_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered)
 {
   LOG_FUNC_IN;
 
   int rc;
+  esm_ebr_data_t *esm_ebr_data = user->esm_ebr_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   LOG_TRACE(INFO,"ESM-PROC  - Dedicated EPS bearer context activation "
             "accepted by the UE (ebi=%d)", ebi);
@@ -222,14 +224,14 @@ int esm_proc_dedicated_eps_bearer_context_accept(int is_standalone, int ebi,
    * Notity EMM that ESM PDU has to be forwarded to lower layers
    */
   emm_sap.primitive = EMMESM_UNITDATA_REQ;
-  emm_sap.u.emm_esm.ueid = 0;
+  emm_sap.u.emm_esm.ueid = user->ueid;
   emm_esm->msg.length = msg->length;
   emm_esm->msg.value = msg->value;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   if (rc != RETURNerror) {
     /* Set the EPS bearer context state to ACTIVE */
-    rc = esm_ebr_set_status(ebi, ESM_EBR_ACTIVE, ue_triggered);
+    rc = esm_ebr_set_status(user_api_id, esm_ebr_data, ebi, ESM_EBR_ACTIVE, ue_triggered);
 
     if (rc != RETURNok) {
       /* The EPS bearer context was already in ACTIVE state */
@@ -266,7 +268,7 @@ int esm_proc_dedicated_eps_bearer_context_accept(int is_standalone, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_dedicated_eps_bearer_context_reject(int is_standalone, int ebi,
+int esm_proc_dedicated_eps_bearer_context_reject(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered)
 {
   LOG_FUNC_IN;
@@ -276,9 +278,9 @@ int esm_proc_dedicated_eps_bearer_context_reject(int is_standalone, int ebi,
   LOG_TRACE(WARNING, "ESM-PROC  - Dedicated EPS bearer context activation "
             "not accepted by the UE (ebi=%d)", ebi);
 
-  if ( !esm_ebr_is_not_in_use(ebi) ) {
+  if ( !esm_ebr_is_not_in_use(user->esm_ebr_data, ebi) ) {
     /* Release EPS bearer data currently in use */
-    rc = esm_ebr_release(ebi);
+    rc = esm_ebr_release(user->esm_ebr_data, ebi);
   }
 
   if (rc != RETURNok) {
@@ -290,10 +292,10 @@ int esm_proc_dedicated_eps_bearer_context_reject(int is_standalone, int ebi,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = msg->length;
     emm_esm->msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN (rc);
diff --git a/openair3/NAS/UE/ESM/DefaultEpsBearerContextActivation.c b/openair3/NAS/UE/ESM/DefaultEpsBearerContextActivation.c
index 82c4e51be8ea82b88d99ac61b536901b0976d12f..a438d65ddb28cac1c76f1240a236f5e163b36e46 100644
--- a/openair3/NAS/UE/ESM/DefaultEpsBearerContextActivation.c
+++ b/openair3/NAS/UE/ESM/DefaultEpsBearerContextActivation.c
@@ -63,19 +63,6 @@ Description Defines the default EPS bearer context activation ESM
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
 
-/*
- * --------------------------------------------------------------------------
- * Internal data handled by the default EPS bearer context activation
- * procedure in the UE
- * --------------------------------------------------------------------------
- */
-static struct {
-  int ebi;    /* EPS bearer identity of the default EPS bearer associated
-         * to the PDN connection to be activated */
-} _default_eps_bearer_context_data = {ESM_EBI_UNASSIGNED};
-
-
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
@@ -101,22 +88,22 @@ static struct {
  ** Outputs:     esm_cause: Cause code returned upon ESM procedure     **
  **             failure                                    **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _default_eps_bearer_context_data           **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_default_eps_bearer_context_request(int pid, int ebi,
+int esm_proc_default_eps_bearer_context_request(nas_user_t *user, int pid, int ebi,
     const esm_proc_qos_t *qos,
     int *esm_cause)
 {
   LOG_FUNC_IN;
-
+  esm_data_t *esm_data = user->esm_data;
+  default_eps_bearer_context_data_t *default_eps_bearer_context_data = user->default_eps_bearer_context_data;
   int rc = RETURNerror;
 
   LOG_TRACE(INFO, "ESM-PROC  - Default EPS bearer context activation "
             "requested by the network (ebi=%d)", ebi);
 
   /* Assign default EPS bearer context */
-  int new_ebi = esm_ebr_assign(ebi, pid+1, TRUE);
+  int new_ebi = esm_ebr_assign(user->esm_ebr_data, ebi, pid+1, TRUE);
 
   if (new_ebi == ESM_EBI_UNASSIGNED) {
     /* 3GPP TS 24.301, section 6.4.1.5, abnormal cases a and b
@@ -126,7 +113,7 @@ int esm_proc_default_eps_bearer_context_request(int pid, int ebi,
     int old_pid, old_bid;
     /* Locally deactivate the existing EPS bearer context and proceed
      * with the requested default EPS bearer context activation */
-    rc = esm_proc_eps_bearer_context_deactivate(TRUE, ebi,
+    rc = esm_proc_eps_bearer_context_deactivate(user, TRUE, ebi,
          &old_pid, &old_bid);
 
     if (rc != RETURNok) {
@@ -134,17 +121,17 @@ int esm_proc_default_eps_bearer_context_request(int pid, int ebi,
       *esm_cause = ESM_CAUSE_PROTOCOL_ERROR;
     } else {
       /* Assign new default EPS bearer context */
-      ebi = esm_ebr_assign(ebi, pid+1, TRUE);
+      ebi = esm_ebr_assign(user->esm_ebr_data, ebi, pid+1, TRUE);
     }
   }
 
   if (ebi != ESM_EBI_UNASSIGNED) {
     /* Create new default EPS bearer context */
-    ebi = esm_ebr_context_create(pid, ebi, TRUE, qos, NULL);
+    ebi = esm_ebr_context_create(esm_data, user->ueid, pid, ebi, TRUE, qos, NULL);
 
     if (ebi != ESM_EBI_UNASSIGNED) {
       /* Default EPS bearer contextx successfully created */
-      _default_eps_bearer_context_data.ebi = ebi;
+      default_eps_bearer_context_data->ebi = ebi;
       rc = RETURNok;
     } else {
       /* No resource available */
@@ -188,12 +175,14 @@ int esm_proc_default_eps_bearer_context_request(int pid, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_default_eps_bearer_context_accept(int is_standalone, int ebi,
+int esm_proc_default_eps_bearer_context_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
+  esm_ebr_data_t *esm_ebr_data = user->esm_ebr_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   LOG_TRACE(INFO,"ESM-PROC  - Default EPS bearer context activation "
             "accepted by the UE (ebi=%d)", ebi);
@@ -205,15 +194,15 @@ int esm_proc_default_eps_bearer_context_accept(int is_standalone, int ebi,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = msg->length;
     emm_esm->msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   if (rc != RETURNerror) {
     /* Set the EPS bearer context state to ACTIVE */
-    rc = esm_ebr_set_status(ebi, ESM_EBR_ACTIVE, ue_triggered);
+    rc = esm_ebr_set_status(user_api_id, esm_ebr_data, ebi, ESM_EBR_ACTIVE, ue_triggered);
 
     if (rc != RETURNok) {
       /* The EPS bearer context was already in ACTIVE state */
@@ -256,7 +245,7 @@ int esm_proc_default_eps_bearer_context_accept(int is_standalone, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
+int esm_proc_default_eps_bearer_context_reject(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered)
 {
   LOG_FUNC_IN;
@@ -266,9 +255,9 @@ int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
   LOG_TRACE(WARNING, "ESM-PROC  - Default EPS bearer context activation "
             "not accepted by the UE (ebi=%d)", ebi);
 
-  if ( !esm_ebr_is_not_in_use(ebi) ) {
+  if ( !esm_ebr_is_not_in_use(user->esm_ebr_data, ebi) ) {
     /* Release EPS bearer data currently in use */
-    rc = esm_ebr_release(ebi);
+    rc = esm_ebr_release(user->esm_ebr_data, ebi);
   }
 
   if (rc != RETURNok) {
@@ -280,10 +269,10 @@ int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = msg->length;
     emm_esm->msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   } else {
     /* An error is returned to notify EMM that the default EPS bearer
      * activation procedure initiated as part of the initial attach
@@ -310,10 +299,9 @@ int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _default_eps_bearer_context_data           **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_default_eps_bearer_context_complete(void)
+int esm_proc_default_eps_bearer_context_complete(default_eps_bearer_context_data_t *default_eps_bearer_context_data)
 {
   LOG_FUNC_IN;
 
@@ -321,7 +309,7 @@ int esm_proc_default_eps_bearer_context_complete(void)
             "ESM-PROC  - Default EPS bearer context activation complete");
 
   /* Reset default EPS bearer context internal data */
-  _default_eps_bearer_context_data.ebi = ESM_EBI_UNASSIGNED;
+  default_eps_bearer_context_data->ebi = ESM_EBI_UNASSIGNED;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -339,40 +327,29 @@ int esm_proc_default_eps_bearer_context_complete(void)
  **      ACCEPT message was sent.                                  **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _default_eps_bearer_context_data           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _default_eps_bearer_context_data           **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_default_eps_bearer_context_failure(void)
+int esm_proc_default_eps_bearer_context_failure(nas_user_t *user)
 {
   LOG_FUNC_IN;
+  default_eps_bearer_context_data_t *default_eps_bearer_context_data = user->default_eps_bearer_context_data;
 
-  int ebi = _default_eps_bearer_context_data.ebi;
+  int ebi = default_eps_bearer_context_data->ebi;
   int pid, bid;
 
   LOG_TRACE(WARNING,
             "ESM-PROC  - Default EPS bearer context activation failure");
 
   /* Release the default EPS bearer context and enter state INACTIVE */
-  int rc = esm_proc_eps_bearer_context_deactivate(TRUE, ebi, &pid, &bid);
+  int rc = esm_proc_eps_bearer_context_deactivate(user, TRUE, ebi, &pid, &bid);
 
   if (rc != RETURNerror) {
     /* Reset default EPS bearer context internal data */
-    _default_eps_bearer_context_data.ebi = ESM_EBI_UNASSIGNED;
+    default_eps_bearer_context_data->ebi = ESM_EBI_UNASSIGNED;
   }
 
   LOG_FUNC_RETURN (rc);
 }
-
-/****************************************************************************/
-/*********************  L O C A L    F U N C T I O N S  *********************/
-/****************************************************************************/
-
-/*
- * --------------------------------------------------------------------------
- *              Timer handlers
- * --------------------------------------------------------------------------
- */
diff --git a/openair3/NAS/UE/ESM/EpsBearerContextDeactivation.c b/openair3/NAS/UE/ESM/EpsBearerContextDeactivation.c
index 774c80e97fe453b018e17166801f000e33491083..6ba5e6e1cc2233f16faba458817d6d7ef477ac05 100644
--- a/openair3/NAS/UE/ESM/EpsBearerContextDeactivation.c
+++ b/openair3/NAS/UE/ESM/EpsBearerContextDeactivation.c
@@ -74,7 +74,7 @@ Description Defines the EPS bearer context deactivation ESM procedure
  * in the UE
  * --------------------------------------------------------------------------
  */
-static int _eps_bearer_release(int ebi, int *pid, int *bid);
+static int _eps_bearer_release(nas_user_t *user, int ebi, int *pid, int *bid);
 
 
 /****************************************************************************/
@@ -102,7 +102,6 @@ static int _eps_bearer_release(int ebi, int *pid, int *bid);
  **             gnalling between the UE and the MME        **
  **      ebi:       EPS bearer identity of the EPS bearer con- **
  **             text to be deactivated                     **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     pid:       Identifier of the PDN connection the EPS   **
  **             bearer belongs to                          **
@@ -112,25 +111,25 @@ static int _eps_bearer_release(int ebi, int *pid, int *bid);
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_eps_bearer_context_deactivate(int is_local, int ebi,
+int esm_proc_eps_bearer_context_deactivate(nas_user_t *user, int is_local, int ebi,
     int *pid, int *bid)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
   int i;
-
+  esm_data_t *esm_data = user->esm_data;
   if (is_local) {
     if (ebi != ESM_SAP_ALL_EBI) {
       /* Locally release the EPS bearer context */
-      rc = _eps_bearer_release(ebi, pid, bid);
+      rc = _eps_bearer_release(user, ebi, pid, bid);
     } else {
       /* Locally release all the EPS bearer contexts */
       *bid = 0;
 
       for (*pid = 0; *pid < ESM_DATA_PDN_MAX; (*pid)++) {
-        if (_esm_data.pdn[*pid].data) {
-          rc = _eps_bearer_release(ESM_EBI_UNASSIGNED, pid, bid);
+        if (esm_data->pdn[*pid].data) {
+          rc = _eps_bearer_release(user, ESM_EBI_UNASSIGNED, pid, bid);
 
           if (rc != RETURNok) {
             break;
@@ -146,17 +145,17 @@ int esm_proc_eps_bearer_context_deactivate(int is_local, int ebi,
             ebi);
 
   if (*pid < ESM_DATA_PDN_MAX) {
-    if (_esm_data.pdn[*pid].pid != *pid) {
+    if (esm_data->pdn[*pid].pid != *pid) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection identifier %d "
                 "is not valid", *pid);
-    } else if (_esm_data.pdn[*pid].data == NULL) {
+    } else if (esm_data->pdn[*pid].data == NULL) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection %d has not been "
                 "allocated", *pid);
-    } else if (!_esm_data.pdn[*pid].is_active) {
+    } else if (!esm_data->pdn[*pid].is_active) {
       LOG_TRACE(WARNING, "ESM-PROC  - PDN connection %d is not active",
                 *pid);
     } else {
-      esm_pdn_t *pdn = _esm_data.pdn[*pid].data;
+      esm_pdn_t *pdn = esm_data->pdn[*pid].data;
 
       for (i = 0; i < pdn->n_bearers; i++) {
         if (pdn->bearer[i]->ebi != ebi) {
@@ -189,18 +188,19 @@ int esm_proc_eps_bearer_context_deactivate(int is_local, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_eps_bearer_context_deactivate_request(int ebi, int *esm_cause)
+int esm_proc_eps_bearer_context_deactivate_request(nas_user_t *user, int ebi, int *esm_cause)
 {
   LOG_FUNC_IN;
 
   int pid, bid;
   int rc = RETURNok;
+  esm_data_t *esm_data = user->esm_data;
 
   LOG_TRACE(INFO, "ESM-PROC  - EPS bearer context deactivation "
             "requested by the network (ebi=%d)", ebi);
 
   /* Release the EPS bearer context entry */
-  if (esm_ebr_context_release(ebi, &pid, &bid) == ESM_EBI_UNASSIGNED) {
+  if (esm_ebr_context_release(user, ebi, &pid, &bid) == ESM_EBI_UNASSIGNED) {
     LOG_TRACE(WARNING, "ESM-PROC  - Failed to release EPS bearer context");
     *esm_cause = ESM_CAUSE_PROTOCOL_ERROR;
     LOG_FUNC_RETURN (RETURNerror);
@@ -222,7 +222,7 @@ int esm_proc_eps_bearer_context_deactivate_request(int ebi, int *esm_cause)
                 "connection reactivation");
 
       /* Get PDN context parameters */
-      rc = esm_main_get_pdn(pid + 1, &esm_sap.data.pdn_connect.pdn_type,
+      rc = esm_main_get_pdn(esm_data, pid + 1, &esm_sap.data.pdn_connect.pdn_type,
                             &esm_sap.data.pdn_connect.apn,
                             &esm_sap.data.pdn_connect.is_emergency,
                             &active);
@@ -243,7 +243,7 @@ int esm_proc_eps_bearer_context_deactivate_request(int ebi, int *esm_cause)
         esm_sap.is_standalone = TRUE;
         esm_sap.data.pdn_connect.is_defined = TRUE;
         esm_sap.data.pdn_connect.cid = pid + 1;
-        rc = esm_sap_send(&esm_sap);
+        rc = esm_sap_send(user, &esm_sap);
       }
     }
   }
@@ -275,12 +275,14 @@ int esm_proc_eps_bearer_context_deactivate_request(int ebi, int *esm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_eps_bearer_context_deactivate_accept(int is_standalone, int ebi,
+int esm_proc_eps_bearer_context_deactivate_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
+  esm_ebr_data_t *esm_ebr_data = user->esm_ebr_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   LOG_TRACE(INFO,"ESM-PROC  - EPS bearer context deactivation accepted");
 
@@ -290,15 +292,15 @@ int esm_proc_eps_bearer_context_deactivate_accept(int is_standalone, int ebi,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_sap.u.emm_esm.u.data.msg.length = msg->length;
     emm_sap.u.emm_esm.u.data.msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   if (rc != RETURNerror) {
     /* Set the EPS bearer context state to INACTIVE */
-    rc = esm_ebr_set_status(ebi, ESM_EBR_INACTIVE, ue_triggered);
+    rc = esm_ebr_set_status(user_api_id, esm_ebr_data, ebi, ESM_EBR_INACTIVE, ue_triggered);
 
     if (rc != RETURNok) {
       /* The EPS bearer context was already in INACTIVE state */
@@ -309,7 +311,7 @@ int esm_proc_eps_bearer_context_deactivate_accept(int is_standalone, int ebi,
     }
 
     /* Release EPS bearer data */
-    rc = esm_ebr_release(ebi);
+    rc = esm_ebr_release(esm_ebr_data, ebi);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -352,27 +354,29 @@ int esm_proc_eps_bearer_context_deactivate_accept(int is_standalone, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _eps_bearer_release(int ebi, int *pid, int *bid)
+static int _eps_bearer_release(nas_user_t *user, int ebi, int *pid, int *bid)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNerror;
+  esm_ebr_data_t *esm_ebr_data = user->esm_ebr_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   /* Release the EPS bearer context entry */
-  ebi = esm_ebr_context_release(ebi, pid, bid);
+  ebi = esm_ebr_context_release(user, ebi, pid, bid);
 
   if (ebi == ESM_EBI_UNASSIGNED) {
     LOG_TRACE(WARNING, "ESM-PROC  - Failed to release EPS bearer context");
   } else {
     /* Set the EPS bearer context state to INACTIVE */
-    rc = esm_ebr_set_status(ebi, ESM_EBR_INACTIVE, FALSE);
+    rc = esm_ebr_set_status(user_api_id, esm_ebr_data, ebi, ESM_EBR_INACTIVE, FALSE);
 
     if (rc != RETURNok) {
       /* The EPS bearer context was already in INACTIVE state */
       LOG_TRACE(WARNING, "ESM-PROC  - EBI %d was already INACTIVE", ebi);
     } else {
       /* Release EPS bearer data */
-      rc = esm_ebr_release(ebi);
+      rc = esm_ebr_release(esm_ebr_data, ebi);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING,
diff --git a/openair3/NAS/UE/ESM/EsmStatusHdl.c b/openair3/NAS/UE/ESM/EsmStatusHdl.c
index 48e86f4a6062c2ecb12972190f7899a710ab032f..4c8e0aafc1386f089e1a9a0c0f02ebc0df5401aa 100644
--- a/openair3/NAS/UE/ESM/EsmStatusHdl.c
+++ b/openair3/NAS/UE/ESM/EsmStatusHdl.c
@@ -161,7 +161,7 @@ int esm_proc_status_ind(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_status(int is_standalone,
+int esm_proc_status(nas_user_t *user, int is_standalone,
                     int ebi, OctetString *msg,
                     int ue_triggered)
 {
@@ -176,10 +176,10 @@ int esm_proc_status(int is_standalone,
    * Notity EMM that ESM PDU has to be forwarded to lower layers
    */
   emm_sap.primitive = EMMESM_UNITDATA_REQ;
-  emm_sap.u.emm_esm.ueid = 0;
+  emm_sap.u.emm_esm.ueid = user->ueid;
   emm_sap.u.emm_esm.u.data.msg.length = msg->length;
   emm_sap.u.emm_esm.u.data.msg.value = msg->value;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
diff --git a/openair3/NAS/UE/ESM/PdnConnectivity.c b/openair3/NAS/UE/ESM/PdnConnectivity.c
index 2d8718b0a30c6dc0d00c46c453e20307102b3b25..c51d08cdbc289032cddc18d597d95f67da1cd373 100644
--- a/openair3/NAS/UE/ESM/PdnConnectivity.c
+++ b/openair3/NAS/UE/ESM/PdnConnectivity.c
@@ -81,15 +81,15 @@ Description Defines the PDN connectivity ESM procedure executed by the
 /*
  * PDN connection handlers
  */
-static int _pdn_connectivity_create(int pid, const OctetString *apn,
+static int _pdn_connectivity_create(esm_data_t *esm_data, int pid, const OctetString *apn,
                                     esm_proc_pdn_type_t pdn_type, int is_emergency);
-static int _pdn_connectivity_update(int pid, const OctetString *apn,
+static int _pdn_connectivity_update(esm_data_t *esm_data, int pid, const OctetString *apn,
                                     esm_proc_pdn_type_t pdn_type, const OctetString *pdn_addr, int esm_cause);
-static int _pdn_connectivity_delete(int pid);
+static int _pdn_connectivity_delete(esm_data_t *esm_data, int pid);
 
-static int _pdn_connectivity_set_pti(int pid, int pti);
-static int _pdn_connectivity_find_apn(const OctetString *apn);
-static int _pdn_connectivity_find_pdn(const OctetString *apn,
+static int _pdn_connectivity_set_pti(esm_data_t *esm_data, int pid, int pti);
+static int _pdn_connectivity_find_apn(esm_data_t *esm_data, const OctetString *apn);
+static int _pdn_connectivity_find_pdn(esm_data_t * esm_data, const OctetString *apn,
                                       esm_proc_pdn_type_t pdn_type);
 
 /*
@@ -131,10 +131,9 @@ static void *_pdn_connectivity_t3482_handler(void *);
  **             the new PDN connection or the released PDN **
  **             connection                                 **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity(int cid, int is_to_define,
+int esm_proc_pdn_connectivity(nas_user_t *user, int cid, int is_to_define,
                               esm_proc_pdn_type_t pdn_type,
                               const OctetString *apn, int is_emergency,
                               unsigned int *pti)
@@ -143,15 +142,17 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
 
   int rc = RETURNerror;
   int pid = cid - 1;
+  esm_data_t *esm_data = user-> esm_data;
+  esm_pt_data_t *esm_pt_data = user-> esm_pt_data;
 
   if (!is_to_define) {
     LOG_TRACE(INFO, "ESM-PROC  - Undefine PDN connection (cid=%d)", cid);
     /* Delete the PDN connection entry */
-    int pti = _pdn_connectivity_delete(pid);
+    int pti = _pdn_connectivity_delete(esm_data, pid);
 
     if (pti != ESM_PT_UNASSIGNED) {
       /* Release the procedure transaction data */
-      rc = esm_pt_release(pti);
+      rc = esm_pt_release(esm_pt_data, pti);
     }
 
     LOG_FUNC_RETURN(rc);
@@ -159,7 +160,7 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
     LOG_TRACE(INFO, "ESM-PROC  - Assign new procedure transaction identity "
               "(cid=%d)", cid);
     /* Assign new procedure transaction identity */
-    *pti = esm_pt_assign();
+    *pti = esm_pt_assign(esm_pt_data);
 
     if (*pti == ESM_PT_UNASSIGNED) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to assign new procedure "
@@ -168,7 +169,7 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
     }
 
     /* Update the PDN connection data */
-    rc = _pdn_connectivity_set_pti(pid, *pti);
+    rc = _pdn_connectivity_set_pti(esm_data, pid, *pti);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to update PDN connection");
@@ -182,14 +183,14 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
             (pdn_type == ESM_PDN_TYPE_IPV6)? "IPv6" : "IPv4v6",
             apn->value, cid);
 
-  if (is_emergency && _esm_data.emergency) {
+  if (is_emergency && esm_data->emergency) {
     /* The UE shall not request additional PDN connection for
      * emergency bearer services */
     LOG_TRACE(WARNING, "ESM-PROC  - PDN connection for emergency bearer "
               "services is already active");
     LOG_FUNC_RETURN (RETURNerror);
   } else if (pid < ESM_DATA_PDN_MAX) {
-    if ((pid == _esm_data.pdn[pid].pid) && (_esm_data.pdn[pid].is_active)) {
+    if ((pid == esm_data->pdn[pid].pid) && (esm_data->pdn[pid].is_active)) {
       /* PDN connection with the specified identifier is active */
       LOG_TRACE(WARNING, "ESM-PROC  - PDN connection is active");
       LOG_FUNC_RETURN (RETURNerror);
@@ -201,15 +202,15 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
 
   if (apn && apn->length > 0) {
     /* The UE requested subsequent connectivity to additionnal PDNs */
-    int pid = _pdn_connectivity_find_apn(apn);
+    int pid = _pdn_connectivity_find_apn(esm_data, apn);
 
-    if ( (pid >= 0) && _esm_data.pdn[pid].is_active ) {
+    if ( (pid >= 0) && esm_data->pdn[pid].is_active ) {
       /* An active PDN connection to this APN already exists */
-      if ( (_esm_data.pdn[pid].data->type != ESM_PDN_TYPE_IPV4V6) &&
-           (_esm_data.pdn[pid].data->type != pdn_type) ) {
+      if ( (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4V6) &&
+           (esm_data->pdn[pid].data->type != pdn_type) ) {
         /* The UE is requesting PDN connection for other IP version
          * than the one already activated */
-        if (!_esm_data.pdn[pid].data->addr_realloc) {
+        if (!esm_data->pdn[pid].data->addr_realloc) {
           /* The network does not allow PDN connectivity using
            * IPv4 and IPv6 address versions to the same APN */
           if (pdn_type != ESM_PDN_TYPE_IPV4V6) {
@@ -220,7 +221,7 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
           } else {
             LOG_TRACE(WARNING, "ESM-PROC  - %s PDN connection to %s "
                       "already exists",
-                      (_esm_data.pdn[pid].data->type !=
+                      (esm_data->pdn[pid].data->type !=
                        ESM_PDN_TYPE_IPV4)? "IPv6" : "IPv4",
                       apn->value);
           }
@@ -232,8 +233,8 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
          * same IP version than the one already activated */
         LOG_TRACE(WARNING, "ESM-PROC  - %s PDN connection to %s "
                   "already exists",
-                  (_esm_data.pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
-                  (_esm_data.pdn[pid].data->type != ESM_PDN_TYPE_IPV6)?
+                  (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
+                  (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV6)?
           "IPv4v6" : "IPv6" : "IPv4", apn->value);
         LOG_FUNC_RETURN (RETURNerror);
       }
@@ -248,7 +249,7 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
    * not already established, or may have been allowed to request PDN
    * connectivity for other IP version than the one already activated
    */
-  rc = _pdn_connectivity_create(pid, apn, pdn_type, is_emergency);
+  rc = _pdn_connectivity_create(esm_data, pid, apn, pdn_type, is_emergency);
   LOG_FUNC_RETURN(rc);
 }
 
@@ -279,11 +280,11 @@ int esm_proc_pdn_connectivity(int cid, int is_to_define,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity_request(int is_standalone, int pti,
+int esm_proc_pdn_connectivity_request(nas_user_t *user, int is_standalone, int pti,
                                       OctetString *msg, int sent_by_ue)
 {
   LOG_FUNC_IN;
-
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   int rc = RETURNok;
 
   LOG_TRACE(INFO, "ESM-PROC  - Initiate PDN connectivity (pti=%d)", pti);
@@ -295,21 +296,21 @@ int esm_proc_pdn_connectivity_request(int is_standalone, int pti,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = msg->length;
     emm_esm->msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
     if (rc != RETURNerror) {
       /* Start T3482 retransmission timer */
-      rc = esm_pt_start_timer(pti, msg, T3482_DEFAULT_VALUE,
+      rc = esm_pt_start_timer(user, pti, msg, T3482_DEFAULT_VALUE,
                               _pdn_connectivity_t3482_handler);
     }
   }
 
   if (rc != RETURNerror) {
     /* Set the procedure transaction state to PENDING */
-    rc = esm_pt_set_status(pti, ESM_PT_PENDING);
+    rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_PENDING);
 
     if (rc != RETURNok) {
       /* The procedure transaction was already in PENDING state */
@@ -346,12 +347,13 @@ int esm_proc_pdn_connectivity_request(int is_standalone, int pti,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
+int esm_proc_pdn_connectivity_accept(nas_user_t *user, int pti, esm_proc_pdn_type_t pdn_type,
                                      const OctetString *pdn_addr,
                                      const OctetString *apn, int *esm_cause)
 {
   LOG_FUNC_IN;
-
+  esm_data_t *esm_data  = user->esm_data;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   int     rc;
   int     pid = RETURNerror;
   char    apn_first_char[4];
@@ -369,9 +371,9 @@ int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
             esm_data_get_ipv4v6_addr(pdn_addr));
 
   /* Stop T3482 timer if running */
-  (void) esm_pt_stop_timer(pti);
+  esm_pt_stop_timer(esm_pt_data, pti);
   /* Set the procedure transaction state to INACTIVE */
-  rc = esm_pt_set_status(pti, ESM_PT_INACTIVE);
+  rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
 
   if (rc != RETURNok) {
     /* The procedure transaction was already in INACTIVE state
@@ -391,7 +393,7 @@ int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
      */
 
     /* Check whether a PDN connection exists to this APN */
-    pid = _pdn_connectivity_find_pdn(apn, pdn_type);
+    pid = _pdn_connectivity_find_pdn(esm_data, apn, pdn_type);
 
     if (pid < 0) {
       /* No any PDN connection has been defined to establish connectivity
@@ -403,7 +405,7 @@ int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
     }
 
     /* Update the PDN connection */
-    rc = _pdn_connectivity_update(pid, apn, pdn_type, pdn_addr, *esm_cause);
+    rc = _pdn_connectivity_update(esm_data, pid, apn, pdn_type, pdn_addr, *esm_cause);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to update PDN connection "
@@ -438,19 +440,19 @@ int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity_reject(int pti, int *esm_cause)
+int esm_proc_pdn_connectivity_reject(nas_user_t *user, int pti, int *esm_cause)
 {
   LOG_FUNC_IN;
-
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   int rc;
 
   LOG_TRACE(WARNING, "ESM-PROC  - PDN connectivity rejected by "
             "the network (pti=%d), ESM cause = %d", pti, *esm_cause);
 
   /* Stop T3482 timer if running */
-  (void) esm_pt_stop_timer(pti);
+  (void) esm_pt_stop_timer(esm_pt_data, pti);
   /* Set the procedure transaction state to INACTIVE */
-  rc = esm_pt_set_status(pti, ESM_PT_INACTIVE);
+  rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
 
   if (rc != RETURNok) {
     /* The procedure transaction was already in INACTIVE state */
@@ -458,7 +460,7 @@ int esm_proc_pdn_connectivity_reject(int pti, int *esm_cause)
     *esm_cause = ESM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE;
   } else {
     /* Release the procedure transaction identity */
-    rc = esm_pt_release(pti);
+    rc = esm_pt_release(user->esm_pt_data, pti);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
@@ -489,21 +491,21 @@ int esm_proc_pdn_connectivity_reject(int pti, int *esm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity_complete(void)
+int esm_proc_pdn_connectivity_complete(nas_user_t *user)
 {
   LOG_FUNC_IN;
-
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   int rc = RETURNerror;
 
   LOG_TRACE(INFO, "ESM-PROC  - PDN connectivity complete");
 
   /* Get the procedure transaction identity assigned to the PDN connection
    * entry which is still pending in the inactive state */
-  int pti = esm_pt_get_pending_pti(ESM_PT_INACTIVE);
+  int pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_INACTIVE);
 
   if (pti != ESM_PT_UNASSIGNED) {
     /* Release the procedure transaction identity */
-    rc = esm_pt_release(pti);
+    rc = esm_pt_release(esm_pt_data, pti);
   }
 
   LOG_FUNC_RETURN(rc);
@@ -530,10 +532,10 @@ int esm_proc_pdn_connectivity_complete(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_connectivity_failure(int is_pending)
+int esm_proc_pdn_connectivity_failure(nas_user_t *user, int is_pending)
 {
   LOG_FUNC_IN;
-
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   int rc;
   int pti;
 
@@ -543,7 +545,7 @@ int esm_proc_pdn_connectivity_failure(int is_pending)
   if (is_pending) {
     /* Get the procedure transaction identity assigned to the pending PDN
      * connection entry */
-    pti = esm_pt_get_pending_pti(ESM_PT_PENDING);
+    pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_PENDING);
 
     if (pti == ESM_PT_UNASSIGNED) {
       LOG_TRACE(ERROR, "ESM-PROC  - No procedure transaction is PENDING");
@@ -551,15 +553,15 @@ int esm_proc_pdn_connectivity_failure(int is_pending)
     }
 
     /* Set the procedure transaction state to INACTIVE */
-    (void) esm_pt_set_status(pti, ESM_PT_INACTIVE);
+    (void) esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
   } else {
     /* Get the procedure transaction identity assigned to the PDN
      * connection entry which is still pending in the inactive state */
-    pti = esm_pt_get_pending_pti(ESM_PT_INACTIVE);
+    pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_INACTIVE);
   }
 
   /* Release the procedure transaction identity */
-  rc = esm_pt_release(pti);
+  rc = esm_pt_release(esm_pt_data, pti);
 
   if (rc != RETURNok) {
     LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
@@ -603,6 +605,7 @@ int esm_proc_pdn_connectivity_failure(int is_pending)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
+// FIXME 
 static void *_pdn_connectivity_t3482_handler(void *args)
 {
   LOG_FUNC_IN;
@@ -610,7 +613,9 @@ static void *_pdn_connectivity_t3482_handler(void *args)
   int rc;
 
   /* Get retransmission timer parameters data */
-  esm_pt_timer_data_t *data = (esm_pt_timer_data_t *)(args);
+  esm_pt_timer_data_t *data = args;
+  nas_user_t *user = data->user;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
 
   /* Increment the retransmission counter */
   data->count += 1;
@@ -626,19 +631,19 @@ static void *_pdn_connectivity_t3482_handler(void *args)
      * has to be sent again
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = data->msg.length;
     emm_esm->msg.value = data->msg.value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
     if (rc != RETURNerror) {
       /* Restart the timer T3482 */
-      rc = esm_pt_start_timer(data->pti, &data->msg, T3482_DEFAULT_VALUE,
+      rc = esm_pt_start_timer(user, data->pti, &data->msg, T3482_DEFAULT_VALUE,
                               _pdn_connectivity_t3482_handler);
     }
   } else {
     /* Set the procedure transaction state to INACTIVE */
-    rc = esm_pt_set_status(data->pti, ESM_PT_INACTIVE);
+    rc = esm_pt_set_status(esm_pt_data, data->pti, ESM_PT_INACTIVE);
 
     if (rc != RETURNok) {
       /* The procedure transaction was already in INACTIVE state */
@@ -646,7 +651,7 @@ static void *_pdn_connectivity_t3482_handler(void *args)
                 data->pti);
     } else {
       /* Release the transaction identity assigned to this procedure */
-      rc = esm_pt_release(data->pti);
+      rc = esm_pt_release(esm_pt_data, data->pti);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d",
@@ -676,14 +681,12 @@ static void *_pdn_connectivity_t3482_handler(void *args)
  **      pdn_type:  PDN type (IPv4, IPv6, IPv4v6)              **
  **      is_emergency:  TRUE if the PDN connection has to be esta- **
  **             blished for emergency bearer services      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_create(int pid, const OctetString *apn,
+static int _pdn_connectivity_create(esm_data_t *esm_data, int pid, const OctetString *apn,
                                     esm_proc_pdn_type_t pdn_type,
                                     int is_emergency)
 {
@@ -693,14 +696,14 @@ static int _pdn_connectivity_create(int pid, const OctetString *apn,
 
   if (pid >= ESM_DATA_PDN_MAX) {
     return (RETURNerror);
-  } else if (_esm_data.pdn[pid].is_active) {
+  } else if (esm_data->pdn[pid].is_active) {
     LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
     return (RETURNerror);
   }
 
-  if (_esm_data.pdn[pid].data != NULL) {
+  if (esm_data->pdn[pid].data != NULL) {
     /* Update existing non-active PDN connection */
-    pdn = _esm_data.pdn[pid].data;
+    pdn = esm_data->pdn[pid].data;
   } else {
     /* Create new PDN connection */
     pdn = (esm_pdn_t *)malloc(sizeof(esm_pdn_t));
@@ -713,13 +716,13 @@ static int _pdn_connectivity_create(int pid, const OctetString *apn,
 
     memset(pdn, 0, sizeof(esm_pdn_t));
     /* Increment the number of PDN connections */
-    _esm_data.n_pdns += 1;
+    esm_data->n_pdns += 1;
     /* Set the PDN connection identifier */
-    _esm_data.pdn[pid].pid = pid;
+    esm_data->pdn[pid].pid = pid;
     /* Reset the PDN connection active indicator */
-    _esm_data.pdn[pid].is_active = FALSE;
+    esm_data->pdn[pid].is_active = FALSE;
     /* Setup the PDN connection data */
-    _esm_data.pdn[pid].data = pdn;
+    esm_data->pdn[pid].data = pdn;
   }
 
   /* Update the PDN connection data */
@@ -760,10 +763,9 @@ static int _pdn_connectivity_create(int pid, const OctetString *apn,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_update(int pid, const OctetString *apn,
+static int _pdn_connectivity_update(esm_data_t *esm_data, int pid, const OctetString *apn,
                                     esm_proc_pdn_type_t pdn_type,
                                     const OctetString *pdn_addr,
                                     int esm_cause)
@@ -772,21 +774,21 @@ static int _pdn_connectivity_update(int pid, const OctetString *apn,
 
   if (pid >= ESM_DATA_PDN_MAX) {
     return (RETURNerror);
-  } else if (pid != _esm_data.pdn[pid].pid) {
+  } else if (pid != esm_data->pdn[pid].pid) {
     LOG_TRACE(ERROR, "ESM-PROC  - PDN connection identifier is not valid");
     return (RETURNerror);
-  } else if (_esm_data.pdn[pid].data == NULL) {
+  } else if (esm_data->pdn[pid].data == NULL) {
     LOG_TRACE(ERROR, "ESM-PROC  - PDN connection has not been allocated");
     return (RETURNerror);
-  } else if (_esm_data.pdn[pid].is_active) {
+  } else if (esm_data->pdn[pid].is_active) {
     LOG_TRACE(WARNING, "ESM-PROC  - Active %s PDN connection to %s already "
-              "exists", (_esm_data.pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
-              "IPv6" : "IPv4", _esm_data.pdn[pid].data->apn.value);
+              "exists", (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
+              "IPv6" : "IPv4", esm_data->pdn[pid].data->apn.value);
     return (RETURNerror);
   }
 
   /* Get the PDN connection */
-  esm_pdn_t *pdn = _esm_data.pdn[pid].data;
+  esm_pdn_t *pdn = esm_data->pdn[pid].data;
 
   /* Setup the Access Point Name value */
   if ( apn && (apn->length > 0) ) {
@@ -847,49 +849,47 @@ static int _pdn_connectivity_update(int pid, const OctetString *apn,
  **                                                                        **
  ** Inputs:  pid:       Identifier of the PDN connection to be     **
  **             released                                   **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The identity of the procedure transaction  **
  **             assigned to the PDN connection when suc-   **
  **             cessfully released;                        **
  **             UNASSIGNED value otherwise.                **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_delete(int pid)
+static int _pdn_connectivity_delete(esm_data_t *esm_data, int pid)
 {
   int pti = ESM_PT_UNASSIGNED;
 
   if (pid < ESM_DATA_PDN_MAX) {
-    if (pid != _esm_data.pdn[pid].pid) {
+    if (pid != esm_data->pdn[pid].pid) {
       LOG_TRACE(ERROR,
                 "ESM-PROC  - PDN connection identifier is not valid");
-    } else if (_esm_data.pdn[pid].data == NULL) {
+    } else if (esm_data->pdn[pid].data == NULL) {
       LOG_TRACE(ERROR,
                 "ESM-PROC  - PDN connection has not been allocated");
-    } else if (_esm_data.pdn[pid].is_active) {
+    } else if (esm_data->pdn[pid].is_active) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
     } else {
       /* Get the identity of the procedure transaction that created
        * the PDN connection */
-      pti = _esm_data.pdn[pid].data->pti;
+      pti = esm_data->pdn[pid].data->pti;
     }
   }
 
   if (pti != ESM_PT_UNASSIGNED) {
     /* Decrement the number of PDN connections */
-    _esm_data.n_pdns -= 1;
+    esm_data->n_pdns -= 1;
     /* Set the PDN connection as available */
-    _esm_data.pdn[pid].pid = -1;
+    esm_data->pdn[pid].pid = -1;
 
     /* Release allocated PDN connection data */
-    if (_esm_data.pdn[pid].data->apn.length > 0) {
-      free(_esm_data.pdn[pid].data->apn.value);
+    if (esm_data->pdn[pid].data->apn.length > 0) {
+      free(esm_data->pdn[pid].data->apn.value);
     }
 
-    free(_esm_data.pdn[pid].data);
-    _esm_data.pdn[pid].data = NULL;
+    free(esm_data->pdn[pid].data);
+    esm_data->pdn[pid].data = NULL;
     LOG_TRACE(WARNING, "ESM-PROC  - PDN connection %d released", pid);
   }
 
@@ -910,24 +910,23 @@ static int _pdn_connectivity_delete(int pid)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_set_pti(int pid, int pti)
+static int _pdn_connectivity_set_pti(esm_data_t *esm_data, int pid, int pti)
 {
   if (pid < ESM_DATA_PDN_MAX) {
-    if (pid != _esm_data.pdn[pid].pid) {
+    if (pid != esm_data->pdn[pid].pid) {
       LOG_TRACE(ERROR,
                 "ESM-PROC  - PDN connection identifier is not valid");
-    } else if (_esm_data.pdn[pid].data == NULL) {
+    } else if (esm_data->pdn[pid].data == NULL) {
       LOG_TRACE(ERROR,
                 "ESM-PROC  - PDN connection has not been allocated");
-    } else if (_esm_data.pdn[pid].is_active) {
+    } else if (esm_data->pdn[pid].is_active) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
     } else {
       /* Update the identity of the procedure transaction assigned to
        * the PDN connection */
-      _esm_data.pdn[pid].data->pti = pti;
+      esm_data->pdn[pid].data->pti = pti;
       return (RETURNok);
     }
   }
@@ -943,7 +942,6 @@ static int _pdn_connectivity_set_pti(int pid, int pti)
  **      for the specified APN                                     **
  **                                                                        **
  ** Inputs:  apn:       Access Point Name of the PDN connection    **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The identifier of the PDN connection if    **
@@ -951,17 +949,17 @@ static int _pdn_connectivity_set_pti(int pid, int pti)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_find_apn(const OctetString *apn)
+static int _pdn_connectivity_find_apn(esm_data_t *esm_data, const OctetString *apn)
 {
   int i;
 
   for (i = 0; i < ESM_DATA_PDN_MAX; i++) {
-    if ( (_esm_data.pdn[i].pid != -1) && _esm_data.pdn[i].data ) {
-      if (_esm_data.pdn[i].data->apn.length != apn->length) {
+    if ( (esm_data->pdn[i].pid != -1) && esm_data->pdn[i].data ) {
+      if (esm_data->pdn[i].data->apn.length != apn->length) {
         continue;
       }
 
-      if (memcmp(_esm_data.pdn[i].data->apn.value,
+      if (memcmp(esm_data->pdn[i].data->apn.value,
                  apn->value, apn->length) != 0) {
         continue;
       }
@@ -972,7 +970,7 @@ static int _pdn_connectivity_find_apn(const OctetString *apn)
   }
 
   /* Return the identifier of the PDN connection */
-  return (_esm_data.pdn[i].pid);
+  return (esm_data->pdn[i].pid);
 }
 
 /****************************************************************************
@@ -984,7 +982,6 @@ static int _pdn_connectivity_find_apn(const OctetString *apn)
  **                                                                        **
  ** Inputs:  apn:       Access Point Name of the PDN connection    **
  **      pdn_type:  PDN address type                           **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The identifier of the PDN connection if    **
@@ -992,38 +989,38 @@ static int _pdn_connectivity_find_apn(const OctetString *apn)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_connectivity_find_pdn(const OctetString *apn,
+static int _pdn_connectivity_find_pdn(esm_data_t *esm_data, const OctetString *apn,
                                       const esm_proc_pdn_type_t pdn_type)
 {
   int i;
 
   for (i = 0; i < ESM_DATA_PDN_MAX; i++) {
-    if ( (_esm_data.pdn[i].pid != -1) && _esm_data.pdn[i].data ) {
+    if ( (esm_data->pdn[i].pid != -1) && esm_data->pdn[i].data ) {
       /* PDN connection established during initial network attachment */
-      if (_esm_data.pdn[i].data->apn.length == 0) {
+      if (esm_data->pdn[i].data->apn.length == 0) {
         break;
       }
 
       /* Subsequent PDN connection established for the specified APN */
-      if (_esm_data.pdn[i].data->apn.length != apn->length) {
+      if (esm_data->pdn[i].data->apn.length != apn->length) {
         continue;
       }
 
-      if (memcmp(_esm_data.pdn[i].data->apn.value,
+      if (memcmp(esm_data->pdn[i].data->apn.value,
                  apn->value, apn->length) != 0) {
         continue;
       }
 
-      if (_esm_data.pdn[i].data->type == ESM_PDN_TYPE_IPV4V6) {
+      if (esm_data->pdn[i].data->type == ESM_PDN_TYPE_IPV4V6) {
         break;
       }
 
-      if (_esm_data.pdn[i].data->type == pdn_type) {
+      if (esm_data->pdn[i].data->type == pdn_type) {
         break;
       }
     }
   }
 
   /* Return the identifier of the PDN connection */
-  return (_esm_data.pdn[i].pid);
+  return (esm_data->pdn[i].pid);
 }
diff --git a/openair3/NAS/UE/ESM/PdnDisconnect.c b/openair3/NAS/UE/ESM/PdnDisconnect.c
index 3cb2f563858ad1306ea241ccf8357874dfb5b571..4b5c0f06e5b0bfe14d7cae3613c3393ea7350e5e 100644
--- a/openair3/NAS/UE/ESM/PdnDisconnect.c
+++ b/openair3/NAS/UE/ESM/PdnDisconnect.c
@@ -73,7 +73,7 @@ Description Defines the PDN disconnect ESM procedure executed by the
 /*
  * PDN disconnection handlers
  */
-static int _pdn_disconnect_get_default_ebi(int pti);
+static int _pdn_disconnect_get_default_ebi(esm_data_t *esm_data, int pti);
 
 /*
  * Timer handlers
@@ -104,7 +104,6 @@ static void *_pdn_disconnect_t3492_handler(void *);
  **      tifier                                                    **
  **                                                                        **
  ** Inputs:  cid:       PDN context identifier                     **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     pti:       Procedure transaction identity assigned to **
  **             the PDN connection to be released          **
@@ -114,7 +113,7 @@ static void *_pdn_disconnect_t3492_handler(void *);
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_disconnect(int cid, unsigned int *pti, unsigned int *ebi)
+int esm_proc_pdn_disconnect(esm_data_t *esm_data, int cid, unsigned int *pti, unsigned int *ebi)
 {
   LOG_FUNC_IN;
 
@@ -122,21 +121,21 @@ int esm_proc_pdn_disconnect(int cid, unsigned int *pti, unsigned int *ebi)
   int pid = cid - 1;
 
   if (pid < ESM_DATA_PDN_MAX) {
-    if (pid != _esm_data.pdn[pid].pid) {
+    if (pid != esm_data->pdn[pid].pid) {
       LOG_TRACE(WARNING, "ESM-PROC  - PDN connection identifier %d is "
                 "not valid", pid);
-    } else if (_esm_data.pdn[pid].data == NULL) {
+    } else if (esm_data->pdn[pid].data == NULL) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection %d has not been "
                 "allocated", pid);
-    } else if (!_esm_data.pdn[pid].is_active) {
+    } else if (!esm_data->pdn[pid].is_active) {
       LOG_TRACE(WARNING, "ESM-PROC  - PDN connection is not active");
     } else {
       /* Get the procedure transaction identity assigned to the PDN
        * connection to be released */
-      *pti = _esm_data.pdn[pid].data->pti;
+      *pti = esm_data->pdn[pid].data->pti;
       /* Get the EPS bearer identity of the default bearer associated
        * with the PDN to disconnect from */
-      *ebi = _esm_data.pdn[pid].data->bearer[0]->ebi;
+      *ebi = esm_data->pdn[pid].data->bearer[0]->ebi;
       rc = RETURNok;
     }
   }
@@ -168,13 +167,13 @@ int esm_proc_pdn_disconnect(int cid, unsigned int *pti, unsigned int *ebi)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_disconnect_request(int is_standalone, int pti,
+int esm_proc_pdn_disconnect_request(nas_user_t *user, int is_standalone, int pti,
                                     OctetString *msg, int sent_by_ue)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
-
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
   LOG_TRACE(INFO, "ESM-PROC  - Initiate PDN disconnection (pti=%d)", pti);
 
   if (is_standalone) {
@@ -184,21 +183,21 @@ int esm_proc_pdn_disconnect_request(int is_standalone, int pti,
      * Notity EMM that ESM PDU has to be forwarded to lower layers
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = msg->length;
     emm_esm->msg.value = msg->value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
     if (rc != RETURNerror) {
       /* Start T3482 retransmission timer */
-      rc = esm_pt_start_timer(pti, msg, T3492_DEFAULT_VALUE,
+      rc = esm_pt_start_timer(user, pti, msg, T3492_DEFAULT_VALUE,
                               _pdn_disconnect_t3492_handler);
     }
   }
 
   if (rc != RETURNerror) {
     /* Set the procedure transaction state to PENDING */
-    rc = esm_pt_set_status(pti, ESM_PT_PENDING);
+    rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_PENDING);
 
     if (rc != RETURNok) {
       /* The procedure transaction was already in PENDING state */
@@ -232,7 +231,7 @@ int esm_proc_pdn_disconnect_request(int is_standalone, int pti,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_disconnect_accept(int pti, int *esm_cause)
+int esm_proc_pdn_disconnect_accept(esm_pt_data_t *esm_pt_data, int pti, int *esm_cause)
 {
   LOG_FUNC_IN;
 
@@ -240,9 +239,9 @@ int esm_proc_pdn_disconnect_accept(int pti, int *esm_cause)
             "(pti=%d)", pti);
 
   /* Stop T3492 timer if running */
-  (void) esm_pt_stop_timer(pti);
+  (void) esm_pt_stop_timer(esm_pt_data, pti);
   /* Set the procedure transaction state to INACTIVE */
-  int rc = esm_pt_set_status(pti, ESM_PT_INACTIVE);
+  int rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
 
   if (rc != RETURNok) {
     /* The procedure transaction was already in INACTIVE state */
@@ -251,7 +250,7 @@ int esm_proc_pdn_disconnect_accept(int pti, int *esm_cause)
   } else {
     /* Immediately release the transaction identity assigned to this
      * procedure */
-    rc = esm_pt_release(pti);
+    rc = esm_pt_release(esm_pt_data, pti);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
@@ -284,19 +283,19 @@ int esm_proc_pdn_disconnect_accept(int pti, int *esm_cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause)
+int esm_proc_pdn_disconnect_reject(nas_user_t *user, int pti, int *esm_cause)
 {
   LOG_FUNC_IN;
-
+  esm_data_t *esm_data = user->esm_data;
   int rc;
 
   LOG_TRACE(WARNING, "ESM-PROC  - PDN disconnection rejected by the network "
             "(pti=%d), ESM cause = %d", pti, *esm_cause);
 
   /* Stop T3492 timer if running */
-  (void) esm_pt_stop_timer(pti);
+  (void) esm_pt_stop_timer(user->esm_pt_data, pti);
   /* Set the procedure transaction state to INACTIVE */
-  rc = esm_pt_set_status(pti, ESM_PT_INACTIVE);
+  rc = esm_pt_set_status(user->esm_pt_data, pti, ESM_PT_INACTIVE);
 
   if (rc != RETURNok) {
     /* The procedure transaction was already in INACTIVE state
@@ -305,7 +304,7 @@ int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause)
     *esm_cause = ESM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE;
   } else {
     /* Release the transaction identity assigned to this procedure */
-    rc = esm_pt_release(pti);
+    rc = esm_pt_release(user->esm_pt_data, pti);
 
     if (rc != RETURNok) {
       LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
@@ -313,7 +312,7 @@ int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause)
     } else if (*esm_cause != ESM_CAUSE_LAST_PDN_DISCONNECTION_NOT_ALLOWED) {
       /* Get the identity of the default EPS bearer context allocated to
        * the PDN connection entry assigned to this procedure transaction */
-      int ebi = _pdn_disconnect_get_default_ebi(pti);
+      int ebi = _pdn_disconnect_get_default_ebi(esm_data, pti);
 
       if (ebi < 0) {
         LOG_TRACE(ERROR, "ESM-PROC  - No default EPS bearer found");
@@ -331,7 +330,7 @@ int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause)
       esm_sap.recv = NULL;
       esm_sap.send.length = 0;
       esm_sap.data.eps_bearer_context_deactivate.ebi = ebi;
-      rc = esm_sap_send(&esm_sap);
+      rc = esm_sap_send(user, &esm_sap);
 
       if (rc != RETURNok) {
         *esm_cause = ESM_CAUSE_PROTOCOL_ERROR;
@@ -382,7 +381,8 @@ int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause)
 static void *_pdn_disconnect_t3492_handler(void *args)
 {
   LOG_FUNC_IN;
-
+  nas_user_t *user = args;
+  esm_data_t *esm_data = user->esm_data;;
   int rc;
 
   /* Get retransmission timer parameters data */
@@ -402,19 +402,19 @@ static void *_pdn_disconnect_t3492_handler(void *args)
      * has to be sent again
      */
     emm_sap.primitive = EMMESM_UNITDATA_REQ;
-    emm_sap.u.emm_esm.ueid = 0;
+    emm_sap.u.emm_esm.ueid = user->ueid;
     emm_esm->msg.length = data->msg.length;
     emm_esm->msg.value = data->msg.value;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
 
     if (rc != RETURNerror) {
       /* Restart the timer T3492 */
-      rc = esm_pt_start_timer(data->pti, &data->msg, T3492_DEFAULT_VALUE,
+      rc = esm_pt_start_timer(user, data->pti, &data->msg, T3492_DEFAULT_VALUE,
                               _pdn_disconnect_t3492_handler);
     }
   } else {
     /* Set the procedure transaction state to INACTIVE */
-    rc = esm_pt_set_status(data->pti, ESM_PT_INACTIVE);
+    rc = esm_pt_set_status(user->esm_pt_data, data->pti, ESM_PT_INACTIVE);
 
     if (rc != RETURNok) {
       /* The procedure transaction was already in INACTIVE state */
@@ -422,7 +422,7 @@ static void *_pdn_disconnect_t3492_handler(void *args)
                 data->pti);
     } else {
       /* Release the transaction identity assigned to this procedure */
-      rc = esm_pt_release(data->pti);
+      rc = esm_pt_release(user->esm_pt_data, data->pti);
 
       if (rc != RETURNok) {
         LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d",
@@ -431,7 +431,7 @@ static void *_pdn_disconnect_t3492_handler(void *args)
         /* Get the identity of the default EPS bearer context
          * allocated to the PDN connection entry assigned to
          * this procedure transaction */
-        int ebi = _pdn_disconnect_get_default_ebi(data->pti);
+        int ebi = _pdn_disconnect_get_default_ebi(esm_data, data->pti);
 
         if (ebi < 0) {
           LOG_TRACE(ERROR, "ESM-PROC  - No default EPS bearer found");
@@ -448,7 +448,7 @@ static void *_pdn_disconnect_t3492_handler(void *args)
         esm_sap.recv = NULL;
         esm_sap.send.length = 0;
         esm_sap.data.eps_bearer_context_deactivate.ebi = ebi;
-        rc = esm_sap_send(&esm_sap);
+        rc = esm_sap_send(user, &esm_sap);
       }
     }
   }
@@ -471,7 +471,6 @@ static void *_pdn_disconnect_t3492_handler(void *args)
  **      ven procedure transaction identity has been assigned      **
  **                                                                        **
  ** Inputs:  pti:       The procedure transaction identity         **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The EPS bearer identity of the default EPS **
@@ -479,22 +478,22 @@ static void *_pdn_disconnect_t3492_handler(void *args)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _pdn_disconnect_get_default_ebi(int pti)
+static int _pdn_disconnect_get_default_ebi(esm_data_t *esm_data, int pti)
 {
   int ebi = -1;
   int i;
 
   for (i = 0; i < ESM_DATA_PDN_MAX; i++) {
-    if ( (_esm_data.pdn[i].pid != -1) && _esm_data.pdn[i].data ) {
-      if (_esm_data.pdn[i].data->pti != pti) {
+    if ( (esm_data->pdn[i].pid != -1) && esm_data->pdn[i].data ) {
+      if (esm_data->pdn[i].data->pti != pti) {
         continue;
       }
 
       /* PDN entry found */
-      if (_esm_data.pdn[i].data->bearer[0] != NULL) {
+      if (esm_data->pdn[i].data->bearer[0] != NULL) {
         /* Get the EPS bearer identity of the default EPS bearer
          * context associated to the PDN connection */
-        ebi = _esm_data.pdn[i].data->bearer[0]->ebi;
+        ebi = esm_data->pdn[i].data->bearer[0]->ebi;
       }
 
       break;
diff --git a/openair3/NAS/UE/ESM/SAP/esm_recv.c b/openair3/NAS/UE/ESM/SAP/esm_recv.c
index 243d619896732ca14f4635ba00729d9a362186c5..03a421558113c48500f8d5893fcfb21e842970ec 100644
--- a/openair3/NAS/UE/ESM/SAP/esm_recv.c
+++ b/openair3/NAS/UE/ESM/SAP/esm_recv.c
@@ -74,7 +74,7 @@ Description Defines functions executed at the ESM Service Access
  **                                                                        **
  ** Description: Processes ESM status message                              **
  **                                                                        **
- ** Inputs:  ueid:      UE local identifier                        **
+ ** Inputs:  **
  **      pti:       Procedure transaction identity             **
  **      ebi:       EPS bearer identity                        **
  **      msg:       The received ESM message                   **
@@ -135,7 +135,7 @@ int esm_recv_status(int pti, int ebi, const esm_status_msg *msg)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_recv_pdn_connectivity_reject(int pti, int ebi,
+int esm_recv_pdn_connectivity_reject(nas_user_t *user, int pti, int ebi,
                                      const pdn_connectivity_reject_msg *msg)
 {
   LOG_FUNC_IN;
@@ -154,7 +154,7 @@ int esm_recv_pdn_connectivity_reject(int pti, int ebi,
      */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid PTI value (pti=%d)", pti);
     LOG_FUNC_RETURN (ESM_CAUSE_INVALID_PTI_VALUE);
-  } else if ( esm_pt_is_not_in_use(pti) ) {
+  } else if ( esm_pt_is_not_in_use(user->esm_pt_data, pti) ) {
     /* 3GPP TS 24.301, section 7.3.1, case a
      * Assigned value that does not match any PTI in use
      */
@@ -164,7 +164,7 @@ int esm_recv_pdn_connectivity_reject(int pti, int ebi,
   /*
    * EPS bearer identity checking
    */
-  else if ( (ebi != ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(ebi) ) {
+  else if ( (ebi != ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(user->esm_ebr_data, ebi) ) {
     /* 3GPP TS 24.301, section 7.3.2, case a
      * Assigned or reserved EPS bearer identity value */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid EPS bearer identity (ebi=%d)",
@@ -179,7 +179,7 @@ int esm_recv_pdn_connectivity_reject(int pti, int ebi,
   esm_cause = msg->esmcause;
 
   /* Execute the PDN connectivity procedure not accepted by the network */
-  int rc = esm_proc_pdn_connectivity_reject(pti, &esm_cause);
+  int rc = esm_proc_pdn_connectivity_reject(user, pti, &esm_cause);
 
   if (rc != RETURNerror) {
     esm_cause = ESM_CAUSE_SUCCESS;
@@ -206,7 +206,7 @@ int esm_recv_pdn_connectivity_reject(int pti, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_recv_pdn_disconnect_reject(int pti, int ebi,
+int esm_recv_pdn_disconnect_reject(nas_user_t *user, int pti, int ebi,
                                    const pdn_disconnect_reject_msg *msg)
 {
   LOG_FUNC_IN;
@@ -225,7 +225,7 @@ int esm_recv_pdn_disconnect_reject(int pti, int ebi,
      */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid PTI value (pti=%d)", pti);
     LOG_FUNC_RETURN (ESM_CAUSE_INVALID_PTI_VALUE);
-  } else if ( esm_pt_is_not_in_use(pti) ) {
+  } else if ( esm_pt_is_not_in_use(user->esm_pt_data, pti) ) {
     /* 3GPP TS 24.301, section 7.3.1, case b
      * Assigned value that does not match any PTI in use
      */
@@ -235,7 +235,7 @@ int esm_recv_pdn_disconnect_reject(int pti, int ebi,
   /*
    * EPS bearer identity checking
    */
-  else if ( (ebi != ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(ebi) ) {
+  else if ( (ebi != ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(user->esm_ebr_data, ebi) ) {
     /* 3GPP TS 24.301, section 7.3.2, case b
      * Assigned or reserved EPS bearer identity value */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid EPS bearer identity (ebi=%d)",
@@ -250,7 +250,7 @@ int esm_recv_pdn_disconnect_reject(int pti, int ebi,
   esm_cause = msg->esmcause;
 
   /* Execute the PDN disconnect procedure not accepted by the network */
-  int rc = esm_proc_pdn_disconnect_reject(pti, &esm_cause);
+  int rc = esm_proc_pdn_disconnect_reject(user, pti, &esm_cause);
 
   if (rc != RETURNerror) {
     esm_cause = ESM_CAUSE_SUCCESS;
@@ -278,12 +278,13 @@ int esm_recv_pdn_disconnect_reject(int pti, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_activate_default_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const activate_default_eps_bearer_context_request_msg *msg)
 {
   LOG_FUNC_IN;
 
   int esm_cause = ESM_CAUSE_SUCCESS;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
 
   LOG_TRACE(INFO, "ESM-SAP   - Received Activate Default EPS Bearer Context "
             "Request message (pti=%d, ebi=%d)", pti, ebi);
@@ -297,7 +298,7 @@ int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
      */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid PTI value (pti=%d)", pti);
     LOG_FUNC_RETURN (ESM_CAUSE_INVALID_PTI_VALUE);
-  } else if ( esm_pt_is_not_in_use(pti) ) {
+  } else if ( esm_pt_is_not_in_use(esm_pt_data, pti) ) {
     /* 3GPP TS 24.301, section 7.3.1, case g
      * Assigned value that does not match any PTI in use
      */
@@ -307,7 +308,7 @@ int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
   /*
    * EPS bearer identity checking
    */
-  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(ebi) ) {
+  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(user->esm_ebr_data, ebi) ) {
     /* 3GPP TS 24.301, section 7.3.2, case g
      * Reserved or unassigned EPS bearer identity value
      */
@@ -369,14 +370,14 @@ int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
   }
 
   /* Execute the PDN connectivity procedure accepted by the network */
-  int pid = esm_proc_pdn_connectivity_accept(pti, pdn_type,
+  int pid = esm_proc_pdn_connectivity_accept(user, pti, pdn_type,
             &msg->pdnaddress.pdnaddressinformation,
             &msg->accesspointname.accesspointnamevalue,
             &esm_cause);
 
   if (pid != RETURNerror) {
     /* Create local default EPS bearer context */
-    int rc = esm_proc_default_eps_bearer_context_request(pid, ebi, &qos,
+    int rc = esm_proc_default_eps_bearer_context_request(user, pid, ebi, &qos,
              &esm_cause);
 
     if (rc != RETURNerror) {
@@ -406,7 +407,7 @@ int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_activate_dedicated_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const activate_dedicated_eps_bearer_context_request_msg *msg)
 {
   LOG_FUNC_IN;
@@ -414,6 +415,7 @@ int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
   int esm_cause = ESM_CAUSE_SUCCESS;
   int i;
   int j;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
 
   LOG_TRACE(INFO, "ESM-SAP   - Received Activate Dedicated EPS Bearer "
             "Context Request message (pti=%d, ebi=%d)", pti, ebi);
@@ -427,7 +429,7 @@ int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
      */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid PTI value (pti=%d)", pti);
     LOG_FUNC_RETURN (ESM_CAUSE_INVALID_PTI_VALUE);
-  } else if ( (pti != ESM_PT_UNASSIGNED) && esm_pt_is_not_in_use(pti) ) {
+  } else if ( (pti != ESM_PT_UNASSIGNED) && esm_pt_is_not_in_use(esm_pt_data, pti) ) {
     /* 3GPP TS 24.301, section 7.3.1, case i
      * Assigned value that does not match any PTI in use
      */
@@ -437,7 +439,7 @@ int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
   /*
    * EPS bearer identity checking
    */
-  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(ebi) ) {
+  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(user->esm_ebr_data, ebi) ) {
     /* 3GPP TS 24.301, section 7.3.2, case h
      * Reserved or unassigned EPS bearer identity value
      */
@@ -580,7 +582,7 @@ int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
   }
 
   /* Execute the dedicated EPS bearer context activation procedure */
-  int rc = esm_proc_dedicated_eps_bearer_context_request(ebi,
+  int rc = esm_proc_dedicated_eps_bearer_context_request(user, ebi,
            msg->linkedepsbeareridentity,
            &qos, &tft, &esm_cause);
 
@@ -614,13 +616,14 @@ int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_recv_deactivate_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_deactivate_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const deactivate_eps_bearer_context_request_msg *msg)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
   int esm_cause;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
 
   LOG_TRACE(INFO, "ESM-SAP   - Received Deactivate EPS Bearer Context "
             "Request message (pti=%d, ebi=%d)", pti, ebi);
@@ -634,7 +637,7 @@ int esm_recv_deactivate_eps_bearer_context_request(int pti, int ebi,
      */
     LOG_TRACE(WARNING, "ESM-SAP   - Invalid PTI value (pti=%d)", pti);
     LOG_FUNC_RETURN (ESM_CAUSE_INVALID_PTI_VALUE);
-  } else if ( esm_pt_is_not_in_use(pti) ) {
+  } else if ( esm_pt_is_not_in_use(esm_pt_data, pti) ) {
     /* 3GPP TS 24.301, section 7.3.1, case m
      * Assigned value does not match any PTI in use
      */
@@ -644,8 +647,8 @@ int esm_recv_deactivate_eps_bearer_context_request(int pti, int ebi,
   /*
    * EPS bearer identity checking
    */
-  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(ebi) ||
-            esm_ebr_is_not_in_use(ebi) ) {
+  else if ( (ebi == ESM_EBI_UNASSIGNED) || esm_ebr_is_reserved(user->esm_ebr_data, ebi) ||
+            esm_ebr_is_not_in_use(user->esm_ebr_data, ebi) ) {
     /* 3GPP TS 24.301, section 7.3.2, case j
      * Reserved or unassigned EPS bearer identity value or,
      * assigned value that does not match an existing EPS bearer context
@@ -665,12 +668,12 @@ int esm_recv_deactivate_eps_bearer_context_request(int pti, int ebi,
 
   /* Execute the PDN disconnect procedure accepted by the network */
   if (pti != ESM_PT_UNASSIGNED) {
-    rc = esm_proc_pdn_disconnect_accept(pti, &esm_cause);
+    rc = esm_proc_pdn_disconnect_accept(esm_pt_data, pti, &esm_cause);
   }
 
   if (rc != RETURNerror) {
     /* Execute the EPS bearer context deactivation procedure */
-    rc = esm_proc_eps_bearer_context_deactivate_request(ebi, &esm_cause);
+    rc = esm_proc_eps_bearer_context_deactivate_request(user, ebi, &esm_cause);
 
     if (rc != RETURNerror) {
       esm_cause = ESM_CAUSE_SUCCESS;
diff --git a/openair3/NAS/UE/ESM/SAP/esm_recv.h b/openair3/NAS/UE/ESM/SAP/esm_recv.h
index db65230e1e3ec546efa5c0238488690b2c8b6b3e..ae36d650e3c37310e90b517934011e0f07e46dc7 100644
--- a/openair3/NAS/UE/ESM/SAP/esm_recv.h
+++ b/openair3/NAS/UE/ESM/SAP/esm_recv.h
@@ -42,6 +42,7 @@ Description Defines functions executed at the ESM Service Access
 
 #include "EsmStatus.h"
 #include "emmData.h"
+#include "user_defs.h"
 
 #include "PdnConnectivityReject.h"
 #include "PdnDisconnectReject.h"
@@ -88,23 +89,23 @@ int esm_recv_status(int pti, int ebi, const esm_status_msg *msg);
  * Transaction related messages
  * ----------------------------
  */
-int esm_recv_pdn_connectivity_reject(int pti, int ebi,
+int esm_recv_pdn_connectivity_reject(nas_user_t *user, int pti, int ebi,
                                      const pdn_connectivity_reject_msg *msg);
 
-int esm_recv_pdn_disconnect_reject(int pti, int ebi,
+int esm_recv_pdn_disconnect_reject(nas_user_t *user, int pti, int ebi,
                                    const pdn_disconnect_reject_msg *msg);
 
 /*
  * Messages related to EPS bearer contexts
  * ---------------------------------------
  */
-int esm_recv_activate_default_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_activate_default_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const activate_default_eps_bearer_context_request_msg *msg);
 
-int esm_recv_activate_dedicated_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_activate_dedicated_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const activate_dedicated_eps_bearer_context_request_msg *msg);
 
-int esm_recv_deactivate_eps_bearer_context_request(int pti, int ebi,
+int esm_recv_deactivate_eps_bearer_context_request(nas_user_t *user, int pti, int ebi,
     const deactivate_eps_bearer_context_request_msg *msg);
 
 
diff --git a/openair3/NAS/UE/ESM/SAP/esm_sap.c b/openair3/NAS/UE/ESM/SAP/esm_sap.c
index 5ead95d5b416ab5cf290e65dbcb2dc6b02713690..7ff1e57380bbb20b1dfb8b444593edd202809e87 100644
--- a/openair3/NAS/UE/ESM/SAP/esm_sap.c
+++ b/openair3/NAS/UE/ESM/SAP/esm_sap.c
@@ -63,9 +63,9 @@ Description Defines the ESM Service Access Points at which the EPS
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
 
-static int _esm_sap_recv(int msg_type, int is_standalone,
+static int _esm_sap_recv(nas_user_t *user, int msg_type, int is_standalone,
                          const OctetString *req, OctetString *rsp, esm_sap_error_t *err);
-static int _esm_sap_send(int msg_type, int is_standalone, int pti, int ebi,
+static int _esm_sap_send(nas_user_t *user, int msg_type, int is_standalone, int pti, int ebi,
                          const esm_sap_data_t *data, OctetString *rsp);
 
 
@@ -95,12 +95,6 @@ static const char *_esm_sap_primitive_str[] = {
   "ESM_UNITDATA_IND",
 };
 
-/*
- * Buffer used to encode ESM messages before being returned to the EPS
- * Mobility Management sublayer in order to be sent onto the network
- */
-#define ESM_SAP_BUFFER_SIZE 4096
-static char _esm_sap_buffer[ESM_SAP_BUFFER_SIZE];
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -144,9 +138,11 @@ void esm_sap_initialize(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_sap_send(esm_sap_t *msg)
+int esm_sap_send(nas_user_t *user, esm_sap_t *msg)
 {
   LOG_FUNC_IN;
+  esm_data_t *esm_data = user->esm_data;
+  msg->send.value = esm_data->send_buffer;
 
   int rc = RETURNerror;
   int pid;
@@ -172,7 +168,7 @@ int esm_sap_send(esm_sap_t *msg)
       }
 
       /* Define new PDN context */
-      rc = esm_proc_pdn_connectivity(pdn_connect->cid, TRUE,
+      rc = esm_proc_pdn_connectivity(user, pdn_connect->cid, TRUE,
                                      pdn_connect->pdn_type, &apn,
                                      pdn_connect->is_emergency, NULL);
 
@@ -184,13 +180,13 @@ int esm_sap_send(esm_sap_t *msg)
     if (pdn_connect->is_defined) {
       unsigned int pti;
       /* Assign new procedure transaction identity */
-      rc = esm_proc_pdn_connectivity(pdn_connect->cid, TRUE,
+      rc = esm_proc_pdn_connectivity(user, pdn_connect->cid, TRUE,
                                      pdn_connect->pdn_type, NULL,
                                      pdn_connect->is_emergency, &pti);
 
       if (rc != RETURNerror) {
         /* Send PDN connectivity request */
-        rc = _esm_sap_send(PDN_CONNECTIVITY_REQUEST,
+        rc = _esm_sap_send(user, PDN_CONNECTIVITY_REQUEST,
                            msg->is_standalone,
                            pti, EPS_BEARER_IDENTITY_UNASSIGNED,
                            &msg->data, &msg->send);
@@ -206,16 +202,16 @@ int esm_sap_send(esm_sap_t *msg)
 
       if ( msg->is_standalone && pdn_connect->is_defined ) {
         /* Undefine the specified PDN context */
-        rc = esm_proc_pdn_connectivity(pdn_connect->cid, FALSE,
+        rc = esm_proc_pdn_connectivity(user, pdn_connect->cid, FALSE,
                                        pdn_connect->pdn_type, NULL,
                                        pdn_connect->is_emergency, NULL);
       } else if (msg->recv != NULL) {
         /* The UE received a PDN connectivity reject message */
-        rc = _esm_sap_recv(PDN_CONNECTIVITY_REJECT, msg->is_standalone,
+        rc = _esm_sap_recv(user, PDN_CONNECTIVITY_REJECT, msg->is_standalone,
                            msg->recv, &msg->send, &msg->err);
       } else {
         /* The PDN connectivity procedure locally failed */
-        rc = esm_proc_pdn_connectivity_failure(TRUE);
+        rc = esm_proc_pdn_connectivity_failure(user, TRUE);
       }
     }
     break;
@@ -226,12 +222,12 @@ int esm_sap_send(esm_sap_t *msg)
     /* Get the procedure transaction identity and the EPS bearer
      * identity of the default bearer assigned to the PDN to
      * disconnect from */
-    rc = esm_proc_pdn_disconnect(msg->data.pdn_disconnect.cid,
+    rc = esm_proc_pdn_disconnect(esm_data, msg->data.pdn_disconnect.cid,
                                  &pti, &ebi);
 
     if (rc != RETURNerror) {
       /* Send PDN disconnect request */
-      rc = _esm_sap_send(PDN_DISCONNECT_REQUEST, TRUE, pti, ebi,
+      rc = _esm_sap_send(user, PDN_DISCONNECT_REQUEST, TRUE, pti, ebi,
                          &msg->data, &msg->send);
     }
   }
@@ -254,7 +250,7 @@ int esm_sap_send(esm_sap_t *msg)
 
   case ESM_DEFAULT_EPS_BEARER_CONTEXT_ACTIVATE_REQ:
     /* The UE received activate default ESP bearer context request */
-    rc = _esm_sap_recv(ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST,
+    rc = _esm_sap_recv(user, ACTIVATE_DEFAULT_EPS_BEARER_CONTEXT_REQUEST,
                        msg->is_standalone,
                        msg->recv, &msg->send, &msg->err);
     break;
@@ -264,10 +260,10 @@ int esm_sap_send(esm_sap_t *msg)
      * The activate default ESP bearer context accept message
      * has been successfully delivered to the other side
      */
-    rc = esm_proc_default_eps_bearer_context_complete();
+    rc = esm_proc_default_eps_bearer_context_complete(user->default_eps_bearer_context_data);
 
     if (rc != RETURNerror) {
-      rc = esm_proc_pdn_connectivity_complete();
+      rc = esm_proc_pdn_connectivity_complete(user);
     }
 
     break;
@@ -276,10 +272,10 @@ int esm_sap_send(esm_sap_t *msg)
     /*
      * Default ESP bearer context activation procedure locally failed
      */
-    rc = esm_proc_default_eps_bearer_context_failure();
+    rc = esm_proc_default_eps_bearer_context_failure(user);
 
     if (rc != RETURNerror) {
-      rc = esm_proc_pdn_connectivity_failure(FALSE);
+      rc = esm_proc_pdn_connectivity_failure(user, FALSE);
     }
 
     break;
@@ -307,7 +303,7 @@ int esm_sap_send(esm_sap_t *msg)
     /*
      * Locally deactivate EPS bearer context
      */
-    rc = esm_proc_eps_bearer_context_deactivate(TRUE,
+    rc = esm_proc_eps_bearer_context_deactivate(user, TRUE,
          msg->data.eps_bearer_context_deactivate.ebi, &pid, &bid);
   }
   break;
@@ -316,7 +312,7 @@ int esm_sap_send(esm_sap_t *msg)
     break;
 
   case ESM_UNITDATA_IND:
-    rc = _esm_sap_recv(-1, msg->is_standalone, msg->recv,
+    rc = _esm_sap_recv(user, -1, msg->is_standalone, msg->recv,
                        &msg->send, &msg->err);
     break;
 
@@ -358,10 +354,9 @@ int esm_sap_send(esm_sap_t *msg)
  **             turned upon ESM procedure completion       **
  **      err:       Error code of the ESM procedure            **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_sap_buffer                            **
  **                                                                        **
  ***************************************************************************/
-static int _esm_sap_recv(int msg_type, int is_standalone,
+static int _esm_sap_recv(nas_user_t *user, int msg_type, int is_standalone,
                          const OctetString *req, OctetString *rsp,
                          esm_sap_error_t *err)
 {
@@ -439,7 +434,7 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
        * received from the MME
        */
       esm_cause = esm_recv_activate_default_eps_bearer_context_request(
-                    pti, ebi,
+                    user, pti, ebi,
                     &esm_msg.activate_default_eps_bearer_context_request);
 
       if ( (esm_cause == ESM_CAUSE_SUCCESS) ||
@@ -477,7 +472,7 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
        * received from the MME
        */
       esm_cause = esm_recv_activate_dedicated_eps_bearer_context_request(
-                    pti, ebi,
+                    user, pti, ebi,
                     &esm_msg.activate_dedicated_eps_bearer_context_request);
 
       if ( (esm_cause == ESM_CAUSE_SUCCESS) ||
@@ -517,7 +512,7 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
        * Process deactivate EPS bearer context request message
        * received from the MME
        */
-      esm_cause = esm_recv_deactivate_eps_bearer_context_request(pti, ebi,
+      esm_cause = esm_recv_deactivate_eps_bearer_context_request(user, pti, ebi,
                   &esm_msg.deactivate_eps_bearer_context_request);
 
       if ( (esm_cause == ESM_CAUSE_INVALID_PTI_VALUE) ||
@@ -553,7 +548,7 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
       /*
        * Process PDN connectivity reject message received from the MME
        */
-      esm_cause = esm_recv_pdn_connectivity_reject(pti, ebi,
+      esm_cause = esm_recv_pdn_connectivity_reject(user, pti, ebi,
                   &esm_msg.pdn_connectivity_reject);
 
       if ( (esm_cause == ESM_CAUSE_INVALID_PTI_VALUE) ||
@@ -576,7 +571,7 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
       /*
        * Process PDN disconnect reject message received from the MME
        */
-      esm_cause = esm_recv_pdn_disconnect_reject(pti, ebi,
+      esm_cause = esm_recv_pdn_disconnect_reject(user, pti, ebi,
                   &esm_msg.pdn_disconnect_reject);
 
       if ( (esm_cause == ESM_CAUSE_INVALID_PTI_VALUE) ||
@@ -641,16 +636,13 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
 
   if ( (rc != RETURNerror) && (esm_procedure != NULL) ) {
     /* Encode the returned ESM response message */
-    int size = esm_msg_encode(&esm_msg, (uint8_t *)_esm_sap_buffer,
+    int size = esm_msg_encode(&esm_msg, rsp->value,
                               ESM_SAP_BUFFER_SIZE);
 
-    if (size > 0) {
-      rsp->length = size;
-      rsp->value = (uint8_t *)(_esm_sap_buffer);
-    }
+    rsp->length = size;
 
     /* Complete the relevant ESM procedure */
-    rc = (*esm_procedure)(is_standalone, ebi, rsp, triggered_by_ue);
+    rc = (*esm_procedure)(user, is_standalone, ebi, rsp, triggered_by_ue);
 
     if (is_discarded) {
       /* Return indication that received message has been discarded */
@@ -690,10 +682,9 @@ static int _esm_sap_recv(int msg_type, int is_standalone,
  ** Outputs:     rsp:       The encoded ESM response message to be re- **
  **             turned upon ESM procedure completion       **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_sap_buffer                            **
  **                                                                        **
  ***************************************************************************/
-static int _esm_sap_send(int msg_type, int is_standalone,
+static int _esm_sap_send(nas_user_t *user, int msg_type, int is_standalone,
                          int pti, int ebi, const esm_sap_data_t *data,
                          OctetString *rsp)
 {
@@ -771,18 +762,15 @@ static int _esm_sap_send(int msg_type, int is_standalone,
 
   if (rc != RETURNerror) {
     /* Encode the returned ESM response message */
-    int size = esm_msg_encode(&esm_msg, (uint8_t *)_esm_sap_buffer,
+    int size = esm_msg_encode(&esm_msg, rsp->value,
                               ESM_SAP_BUFFER_SIZE);
 
-    if (size > 0) {
-      rsp->length = size;
-      rsp->value = (uint8_t *)(_esm_sap_buffer);
-    }
-
+    rsp->length = size;
     /* Execute the relevant ESM procedure */
     if (esm_procedure) {
-      rc = (*esm_procedure)(is_standalone, pti, rsp, sent_by_ue);
+      rc = (*esm_procedure)(user, is_standalone, pti, rsp, sent_by_ue);
     }
+
   }
 
   LOG_FUNC_RETURN(rc);
diff --git a/openair3/NAS/UE/ESM/SAP/esm_sap.h b/openair3/NAS/UE/ESM/SAP/esm_sap.h
index 7364a4b6610961099706a2e49141efed1de5f629..b464e18900a330bdaf05bb6b6998da2d69e164b9 100644
--- a/openair3/NAS/UE/ESM/SAP/esm_sap.h
+++ b/openair3/NAS/UE/ESM/SAP/esm_sap.h
@@ -41,6 +41,7 @@ Description Defines the ESM Service Access Points at which the EPS
 #define __ESM_SAP_H__
 
 #include "esm_sapDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -60,6 +61,6 @@ Description Defines the ESM Service Access Points at which the EPS
 
 void esm_sap_initialize(void);
 
-int esm_sap_send(esm_sap_t *msg);
+int esm_sap_send(nas_user_t *user, esm_sap_t *msg);
 
 #endif /* __ESM_SAP_H__*/
diff --git a/openair3/NAS/UE/ESM/esmData.h b/openair3/NAS/UE/ESM/esmData.h
index 4a498b5d2e979ddcdd79fdd50f44f4b3b12396fd..02bc0dd93073e80601dae5dd028c86b9017efab6 100644
--- a/openair3/NAS/UE/ESM/esmData.h
+++ b/openair3/NAS/UE/ESM/esmData.h
@@ -52,11 +52,23 @@ Description Defines internal private data handled by EPS Session
 
 /* Total number of active EPS bearers */
 #define ESM_DATA_EPS_BEARER_TOTAL   11
+#define ESM_SAP_BUFFER_SIZE 4096
 
 /****************************************************************************/
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
+/*
+ * --------------------------------------------------------------------------
+ * Internal data handled by the default EPS bearer context activation
+ * procedure in the UE
+ * --------------------------------------------------------------------------
+ */
+typedef struct {
+  int ebi;    /* EPS bearer identity of the default EPS bearer associated
+         * to the PDN connection to be activated */
+} default_eps_bearer_context_data_t;
+
 /*
  * Minimal and maximal value of an EPS bearer identity:
  * The EPS Bearer Identity (EBI) identifies a message flow
@@ -72,8 +84,6 @@ typedef enum {
   ESM_EBR_STATE_MAX
 } esm_ebr_state;
 
-
-
 /*
  * -----------------------
  * EPS bearer context data
@@ -172,6 +182,7 @@ typedef struct esm_data_context_s {
   } pdn[ESM_DATA_PDN_MAX+1];
 
   esm_ebr_data_t ebr;
+  uint8_t send_buffer[ESM_SAP_BUFFER_SIZE];
 } esm_data_context_t;
 
 /*
@@ -185,21 +196,15 @@ typedef struct esm_data_context_s {
  */
 typedef esm_data_context_t esm_data_t;
 
-
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
 
-/*
- * ESM internal data (used within ESM only)
- * ----------------------------------------
- */
-esm_data_t _esm_data;
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
+// FIXME prototype and buffer allocation
 extern char ip_addr_str[100];
 
 extern char *esm_data_get_ipv4_addr(const OctetString *ip_addr);
diff --git a/openair3/NAS/UE/ESM/esm_ebr.c b/openair3/NAS/UE/ESM/esm_ebr.c
index 878f06c18439ce46f63aed921f434a7410195cff..330da2fb23660d2182b70a4faf7a103b1c2fe961 100644
--- a/openair3/NAS/UE/ESM/esm_ebr.c
+++ b/openair3/NAS/UE/ESM/esm_ebr.c
@@ -44,15 +44,13 @@ Description Defines functions used to handle state of EPS bearer contexts
 #include "esm_ebr.h"
 #include "commonDef.h"
 #include "nas_log.h"
+#include "utils.h"
 
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
-#define ESM_EBR_NB_UE_MAX   1
-
-
 /****************************************************************************/
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
@@ -63,16 +61,6 @@ static const char *_esm_ebr_state_str[ESM_EBR_STATE_MAX] = {
   "BEARER CONTEXT ACTIVE",
 };
 
-/*
- * ----------------------------------
- * List of EPS bearer contexts per UE
- * ----------------------------------
- */
-
-#if !defined(NAS_BUILT_IN_EPC)
-static esm_ebr_data_t _esm_ebr_data[ESM_EBR_NB_UE_MAX];
-#endif
-
 /*
  * ----------------------
  * User notification data
@@ -102,7 +90,7 @@ static const network_pdn_state_t _esm_ebr_pdn_state[2][2][2] = {
 /* Returns the index of the next available entry in the list of EPS bearer
  * context data */
 
-static int _esm_ebr_get_available_entry(unsigned int ueid);
+static int _esm_ebr_get_available_entry(esm_ebr_data_t *esm_ebr_data);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -119,41 +107,46 @@ static int _esm_ebr_get_available_entry(unsigned int ueid);
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ***************************************************************************/
-void esm_ebr_initialize(
-  esm_indication_callback_t cb
-)
+
+esm_ebr_data_t *esm_ebr_initialize(void)
 {
-#if !defined(NAS_BUILT_IN_EPC)
-  int ueid, i;
 
   LOG_FUNC_IN;
 
-  for (ueid = 0; ueid < ESM_EBR_NB_UE_MAX; ueid++) {
-    _esm_ebr_data[ueid].index = 0;
+  int i;
+  esm_ebr_data_t *esm_ebr_data = calloc_or_fail(sizeof(esm_ebr_data_t));
 
-    /* Initialize EPS bearer context data */
-    for (i = 0; i < ESM_EBR_DATA_SIZE + 1; i++) {
-      _esm_ebr_data[ueid].context[i] = NULL;
-    }
+  esm_ebr_data->index = 0;
+
+  /* Initialize EPS bearer context data */
+  for (i = 0; i < ESM_EBR_DATA_SIZE + 1; i++) {
+    esm_ebr_data->context[i] = NULL;
   }
 
+  LOG_FUNC_OUT;
+  return esm_ebr_data;
+}
+
+void esm_ebr_register_callback(esm_indication_callback_t cb)
+{
+
+  LOG_FUNC_IN;
+
   /* Initialize the user notification callback */
   _esm_ebr_callback = *cb;
 
   LOG_FUNC_OUT;
-#endif
 }
 
+
 /****************************************************************************
  **                                                                        **
  ** Name:    esm_ebr_assign()                                          **
  **                                                                        **
  ** Description: Assigns a new EPS bearer context                          **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
  **      ebi:       Identity of the new EPS bearer context     **
  **      cid:       Identifier of the PDN context the EPS bea- **
  **             rer context is associated to               **
@@ -165,18 +158,16 @@ void esm_ebr_initialize(
  **      Return:    The identity of the new EPS bearer context **
  **             if successfully assigned;                  **
  **             the not assigned EBI (0) otherwise.        **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_assign(int ebi, int cid, int default_ebr)
+int esm_ebr_assign(esm_ebr_data_t *esm_ebr_data, int ebi, int cid, int default_ebr)
 {
   esm_ebr_context_t *ebr_ctx = NULL;
-  unsigned int       ueid    = 0;
   int                i;
 
   LOG_FUNC_IN;
 
-  ebr_ctx = _esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN];
+  ebr_ctx = esm_ebr_data->context[ebi - ESM_EBI_MIN];
 
 
   if (ebi != ESM_EBI_UNASSIGNED) {
@@ -192,7 +183,7 @@ int esm_ebr_assign(int ebi, int cid, int default_ebr)
     i = ebi - ESM_EBI_MIN;
   } else {
     /* Search for an available EPS bearer identity */
-    i = _esm_ebr_get_available_entry(ueid);
+    i = _esm_ebr_get_available_entry(esm_ebr_data);
 
     if (i < 0) {
       LOG_FUNC_RETURN(ESM_EBI_UNASSIGNED);
@@ -211,10 +202,10 @@ int esm_ebr_assign(int ebi, int cid, int default_ebr)
   }
 
 
-  _esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN] = ebr_ctx;
+  esm_ebr_data->context[ebi - ESM_EBI_MIN] = ebr_ctx;
 
   /* Store the index of the next available EPS bearer identity */
-  _esm_ebr_data[ueid].index = i + 1;
+  esm_ebr_data->index = i + 1;
 
   /* Set the EPS bearer identity */
   ebr_ctx->ebi = ebi;
@@ -235,7 +226,7 @@ int esm_ebr_assign(int ebi, int cid, int default_ebr)
  **                                                                        **
  ** Description: Release the given EPS bearer identity                     **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
+ ** Inputs:   **
  **      ebi:       The identity of the EPS bearer context to  **
  **             be released                                **
  **      Others:    None                                       **
@@ -244,13 +235,11 @@ int esm_ebr_assign(int ebi, int cid, int default_ebr)
  **      Return:    RETURNok if the EPS bearer context has     **
  **             been successfully released;                **
  **             RETURNerror otherwise.                     **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_release(
+int esm_ebr_release(esm_ebr_data_t *esm_ebr_data,
   int ebi)
 {
-  unsigned int ueid = 0;
   esm_ebr_context_t *ebr_ctx;
 
   LOG_FUNC_IN;
@@ -260,7 +249,7 @@ int esm_ebr_release(
   }
 
   /* Get EPS bearer context data */
-  ebr_ctx = _esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN];
+  ebr_ctx = esm_ebr_data->context[ebi - ESM_EBI_MIN];
 
   if ( (ebr_ctx == NULL) || (ebr_ctx->ebi != ebi) ) {
     /* EPS bearer context not assigned */
@@ -290,7 +279,6 @@ int esm_ebr_release(
  ** Description: Set the status of the specified EPS bearer context to the **
  **      given state                                               **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
  **      ebi:       The identity of the EPS bearer             **
  **      status:    The new EPS bearer context status          **
  **      ue_requested:  TRUE/FALSE if the modification of the EPS  **
@@ -300,10 +288,9 @@ int esm_ebr_release(
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_set_status(
+int esm_ebr_set_status(user_api_id_t *user_api_id, esm_ebr_data_t *esm_ebr_data,
   int ebi, esm_ebr_state status, int ue_requested)
 {
   esm_ebr_context_t *ebr_ctx;
@@ -311,19 +298,13 @@ int esm_ebr_set_status(
 
   LOG_FUNC_IN;
 
-  unsigned int ueid = 0;
-
-  if (ueid >= ESM_EBR_NB_UE_MAX) {
-    LOG_FUNC_RETURN (RETURNerror);
-  }
-
 
   if ( (ebi < ESM_EBI_MIN) || (ebi > ESM_EBI_MAX) ) {
     LOG_FUNC_RETURN (RETURNerror);
   }
 
   /* Get EPS bearer context data */
-  ebr_ctx = _esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN];
+  ebr_ctx = esm_ebr_data->context[ebi - ESM_EBI_MIN];
 
   if ( (ebr_ctx == NULL) || (ebr_ctx->ebi != ebi) ) {
     /* EPS bearer context not assigned */
@@ -344,7 +325,7 @@ int esm_ebr_set_status(
       /*
        * Notify the user that the state of the EPS bearer has changed
        */
-      (*_esm_ebr_callback)(ebr_ctx->cid,
+      (*_esm_ebr_callback)(user_api_id, ebr_ctx->cid,
                            _esm_ebr_pdn_state[ue_requested][ebr_ctx->is_default_ebr][status]);
       LOG_FUNC_RETURN (RETURNok);
     }
@@ -360,9 +341,7 @@ int esm_ebr_set_status(
  ** Description: Get the current status value of the specified EPS bearer  **
  **      context                                                   **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
  **      ebi:       The identity of the EPS bearer             **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The current value of the EPS bearer con-   **
@@ -371,26 +350,25 @@ int esm_ebr_set_status(
  **                                                                        **
  ***************************************************************************/
 
-esm_ebr_state esm_ebr_get_status(
+esm_ebr_state esm_ebr_get_status(esm_ebr_data_t *esm_ebr_data,
   int ebi)
 {
-  unsigned int ueid = 0;
 
   if ( (ebi < ESM_EBI_MIN) || (ebi > ESM_EBI_MAX) ) {
     return (ESM_EBR_INACTIVE);
   }
 
-  if (_esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN] == NULL) {
+  if (esm_ebr_data->context[ebi - ESM_EBI_MIN] == NULL) {
     /* EPS bearer context not allocated */
     return (ESM_EBR_INACTIVE);
   }
 
-  if (_esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN]->ebi != ebi) {
+  if (esm_ebr_data->context[ebi - ESM_EBI_MIN]->ebi != ebi) {
     /* EPS bearer context not assigned */
     return (ESM_EBR_INACTIVE);
   }
 
-  return (_esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN]->status);
+  return (esm_ebr_data->context[ebi - ESM_EBI_MIN]->status);
 }
 
 /****************************************************************************
@@ -408,7 +386,7 @@ esm_ebr_state esm_ebr_get_status(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_is_reserved(int ebi)
+int esm_ebr_is_reserved(esm_ebr_data_t *esm_ebr_data, int ebi)
 {
   return ( (ebi != ESM_EBI_UNASSIGNED) && (ebi < ESM_EBI_MIN) );
 }
@@ -420,23 +398,20 @@ int esm_ebr_is_reserved(int ebi)
  ** Description: Check whether the given EPS bearer identity does not      **
  **      match an assigned EBI value currently in use              **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
  **      ebi:       The identity of the EPS bearer             **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    TRUE, FALSE                                **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_is_not_in_use(
+int esm_ebr_is_not_in_use(esm_ebr_data_t *esm_ebr_data,
   int ebi)
 {
-  unsigned int ueid = 0;
 
   return ( (ebi == ESM_EBI_UNASSIGNED) ||
-           (_esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN] == NULL) ||
-           (_esm_ebr_data[ueid].context[ebi - ESM_EBI_MIN]->ebi) != ebi);
+           (esm_ebr_data->context[ebi - ESM_EBI_MIN] == NULL) ||
+           (esm_ebr_data->context[ebi - ESM_EBI_MIN]->ebi) != ebi);
 }
 
 /****************************************************************************/
@@ -450,8 +425,6 @@ int esm_ebr_is_not_in_use(
  ** Description: Returns the index of the next available entry in the list **
  **      of EPS bearer context data                                **
  **                                                                        **
- ** Inputs:  ueid:      Lower layers UE identifier                 **
- **      Others:    _esm_ebr_data                              **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The index of the next available EPS bearer **
@@ -460,20 +433,20 @@ int esm_ebr_is_not_in_use(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _esm_ebr_get_available_entry(unsigned int ueid)
+static int _esm_ebr_get_available_entry(esm_ebr_data_t *esm_ebr_data)
 {
   int i;
 
-  for (i = _esm_ebr_data[ueid].index; i < ESM_EBR_DATA_SIZE; i++) {
-    if (_esm_ebr_data[ueid].context[i] != NULL) {
+  for (i = esm_ebr_data->index; i < ESM_EBR_DATA_SIZE; i++) {
+    if (esm_ebr_data->context[i] != NULL) {
       continue;
     }
 
     return i;
   }
 
-  for (i = 0; i < _esm_ebr_data[ueid].index; i++) {
-    if (_esm_ebr_data[ueid].context[i] != NULL) {
+  for (i = 0; i < esm_ebr_data->index; i++) {
+    if (esm_ebr_data->context[i] != NULL) {
       continue;
     }
 
diff --git a/openair3/NAS/UE/ESM/esm_ebr.h b/openair3/NAS/UE/ESM/esm_ebr.h
index dd586319564a15e986e708cb54fe5a942a22a39f..8e1eaee1a4291dd76fcf66c37d4201efd419e9e9 100644
--- a/openair3/NAS/UE/ESM/esm_ebr.h
+++ b/openair3/NAS/UE/ESM/esm_ebr.h
@@ -42,8 +42,10 @@ Description Defines functions used to handle state of EPS bearer contexts
 #include "OctetString.h"
 
 #include "networkDef.h"
+#include "esmData.h"
 
 #include "nas_timer.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -56,6 +58,13 @@ Description Defines functions used to handle state of EPS bearer contexts
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
+/*
+ * User notification callback, executed whenever a change of status with
+ * respect of PDN connection or EPS bearer context is notified by the EPS
+ * Session Management sublayer
+ */
+typedef int (*esm_indication_callback_t) (user_api_id_t *user_api_id, int, network_pdn_state_t);
+
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -64,15 +73,17 @@ Description Defines functions used to handle state of EPS bearer contexts
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-int esm_ebr_is_reserved(int ebi);
+void esm_ebr_register_callback(esm_indication_callback_t cb);
+
+int esm_ebr_is_reserved(esm_ebr_data_t *esm_ebr_data, int ebi);
 
-void esm_ebr_initialize(esm_indication_callback_t cb);
-int esm_ebr_assign(int ebi, int cid, int default_ebr);
-int esm_ebr_release(int ebi);
+esm_ebr_data_t *esm_ebr_initialize(void);
+int esm_ebr_assign(esm_ebr_data_t *esm_ebr_data, int ebi, int cid, int default_ebr);
+int esm_ebr_release(esm_ebr_data_t *esm_ebr_data, int ebi);
 
-int esm_ebr_set_status(int ebi, esm_ebr_state status, int ue_requested);
-esm_ebr_state esm_ebr_get_status(int ebi);
+int esm_ebr_set_status(user_api_id_t *user_api_id, esm_ebr_data_t *esm_ebr_data, int ebi, esm_ebr_state status, int ue_requested);
+esm_ebr_state esm_ebr_get_status(esm_ebr_data_t *esm_ebr_data, int ebi);
 
-int esm_ebr_is_not_in_use(int ebi);
+int esm_ebr_is_not_in_use(esm_ebr_data_t *esm_ebr_data, int ebi);
 
 #endif /* __ESM_EBR_H__*/
diff --git a/openair3/NAS/UE/ESM/esm_ebr_context.c b/openair3/NAS/UE/ESM/esm_ebr_context.c
index cca1e06913db2da5c436408e13681467c132bd00..bfa4a04cc82dd9d73946ac67702897fe2dce8a51 100644
--- a/openair3/NAS/UE/ESM/esm_ebr_context.c
+++ b/openair3/NAS/UE/ESM/esm_ebr_context.c
@@ -82,36 +82,33 @@ static int _esm_ebr_context_check_precedence(const network_tft_t *,
  ** Description: Creates a new EPS bearer context to the PDN with the spe- **
  **      cified PDN connection identifier                          **
  **                                                                        **
- ** Inputs:  ueid:      UE identifier                              **
+ ** Inputs: **
  **      pid:       PDN connection identifier                  **
  **      ebi:       EPS bearer identity                        **
  **      is_default:    TRUE if the new bearer is a default EPS    **
  **             bearer context                             **
  **      esm_qos:   EPS bearer level QoS parameters            **
  **      tft:       Traffic flow template parameters           **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The EPS bearer identity of the default EPS **
  **             bearer associated to the new EPS bearer    **
  **             context if successfully created;           **
  **             UNASSIGN EPS bearer value otherwise.       **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
 int esm_ebr_context_create(
-
+  esm_data_t *esm_data, int ueid,
   int pid, int ebi, int is_default,
   const network_qos_t *qos, const network_tft_t *tft)
 {
   int                 bid     = 0;
   esm_data_context_t *esm_ctx = NULL;
   esm_pdn_t          *pdn     = NULL;
-  //unsigned int        ueid    = 0;
 
   LOG_FUNC_IN;
 
-  esm_ctx = &_esm_data;
+  esm_ctx = esm_data;
 
   bid = ESM_DATA_EPS_BEARER_MAX;
 
@@ -209,7 +206,7 @@ int esm_ebr_context_create(
            char          *netmask      = NULL;
            char           broadcast[INET_ADDRSTRLEN];
            struct in_addr in_addr;
-           char           command_line[128];
+           char           command_line[500];
            int            res;
 
            switch (pdn->type) {
@@ -275,11 +272,17 @@ int esm_ebr_context_create(
              }
 
              res = sprintf(command_line,
-                           "ifconfig oip1 %s netmask %s broadcast %s",
-                           ipv4_addr, netmask, broadcast);
-             (void)res; /* avoid gcc warning "set but not used" */
-             //                            AssertFatal((res > 0) && (res < 128),
-             //                                    "error in system command line");
+                           "ifconfig oip%d %s netmask %s broadcast %s up && "
+                           "ip rule add from %s/32 table %d && "
+                           "ip rule add to %s/32 table %d && "
+                           "ip route add default dev oip%d table %d",
+                           ueid + 1, ipv4_addr, netmask, broadcast,
+                           ipv4_addr, ueid + 201,
+                           ipv4_addr, ueid + 201,
+                           ueid + 1, ueid + 201);
+             if ( res<0 ) {
+                LOG_TRACE(WARNING, "ESM-PROC  - Failed to system command string");
+             }
              LOG_TRACE(INFO, "ESM-PROC  - executing %s ",
                        command_line);
 
@@ -316,9 +319,8 @@ int esm_ebr_context_create(
  ** Description: Releases EPS bearer context entry previously allocated    **
  **      to the EPS bearer with the specified EPS bearer identity  **
  **                                                                        **
- ** Inputs:  ueid:      UE identifier                              **
+ ** Inputs:   **
  **      ebi:       EPS bearer identity                        **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     pid:       Identifier of the PDN connection entry the **
  **             EPS bearer context belongs to              **
@@ -327,22 +329,20 @@ int esm_ebr_context_create(
  **      Return:    The EPS bearer identity associated to the  **
  **             EPS bearer context if successfully relea-  **
  **             sed; UNASSIGN EPS bearer value otherwise.  **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_context_release(
-
+int esm_ebr_context_release(nas_user_t *user,
   int ebi, int *pid, int *bid)
 {
   int found = FALSE;
   esm_pdn_t *pdn = NULL;
   esm_data_context_t *esm_ctx;
-
-  //unsigned int ueid = 0;
+  esm_ebr_data_t *esm_ebr_data = user->esm_ebr_data;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   LOG_FUNC_IN;
 
-  esm_ctx = &_esm_data;
+  esm_ctx = user->esm_data;
 
   if (ebi != ESM_EBI_UNASSIGNED) {
     /*
@@ -456,11 +456,11 @@ int esm_ebr_context_release(
           }
 
           /* Set the EPS bearer context state to INACTIVE */
-          (void) esm_ebr_set_status(pdn->bearer[i]->ebi,
+          esm_ebr_set_status(user_api_id, esm_ebr_data, pdn->bearer[i]->ebi,
                                     ESM_EBR_INACTIVE, TRUE);
 
           /* Release EPS bearer data */
-          (void) esm_ebr_release(pdn->bearer[i]->ebi);
+          esm_ebr_release(esm_ebr_data, pdn->bearer[i]->ebi);
 
           // esm_ebr_release()
           /* Release dedicated EPS bearer data */
@@ -492,7 +492,7 @@ int esm_ebr_context_release(
       emm_sap_t emm_sap;
       emm_sap.primitive = EMMESM_ESTABLISH_CNF;
       emm_sap.u.emm_esm.u.establish.is_attached = FALSE;
-      (void) emm_sap_send(&emm_sap);
+      (void) emm_sap_send(user, &emm_sap);
     }
     /* 3GPP TS 24.301, section 6.4.4.3, 6.4.4.6
      * If due to the EPS bearer context deactivation only the PDN
@@ -505,7 +505,7 @@ int esm_ebr_context_release(
       emm_sap.primitive = EMMESM_ESTABLISH_CNF;
       emm_sap.u.emm_esm.u.establish.is_attached = TRUE;
       emm_sap.u.emm_esm.u.establish.is_emergency = TRUE;
-      (void) emm_sap_send(&emm_sap);
+      (void) emm_sap_send(user, &emm_sap);
     }
 
     LOG_FUNC_RETURN (ebi);
@@ -524,7 +524,6 @@ int esm_ebr_context_release(
  **                                                                        **
  ** Inputs:  ebi:       The EPS bearer identity of the default EPS **
  **             bearer context                             **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The identifier of the PDN connection entry **
@@ -533,22 +532,22 @@ int esm_ebr_context_release(
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_context_get_pid(int ebi)
+int esm_ebr_context_get_pid(esm_data_t *esm_data, int ebi)
 {
   LOG_FUNC_IN;
 
   int pid;
 
   for (pid = 0; pid < ESM_DATA_PDN_MAX; pid++) {
-    if (_esm_data.pdn[pid].data == NULL) {
+    if (esm_data->pdn[pid].data == NULL) {
       continue;
     }
 
-    if (_esm_data.pdn[pid].data->bearer[0] == NULL) {
+    if (esm_data->pdn[pid].data->bearer[0] == NULL) {
       continue;
     }
 
-    if (_esm_data.pdn[pid].data->bearer[0]->ebi == ebi) {
+    if (esm_data->pdn[pid].data->bearer[0]->ebi == ebi) {
       break;
     }
   }
@@ -576,14 +575,13 @@ int esm_ebr_context_get_pid(int ebi)
  **      tft:       The traffic flow template (set of packet   **
  **             filters) to be checked                     **
  **      operation: Traffic flow template operation            **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_ebr_context_check_tft(int pid, int ebi,
+int esm_ebr_context_check_tft(esm_data_t *esm_data, int pid, int ebi,
                               const network_tft_t *tft,
                               esm_ebr_context_tft_t operation)
 {
@@ -593,14 +591,14 @@ int esm_ebr_context_check_tft(int pid, int ebi,
   int i;
 
   if (pid < ESM_DATA_PDN_MAX) {
-    if (pid != _esm_data.pdn[pid].pid) {
+    if (pid != esm_data->pdn[pid].pid) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection identifier %d "
                 "is not valid", pid);
-    } else if (_esm_data.pdn[pid].data == NULL) {
+    } else if (esm_data->pdn[pid].data == NULL) {
       LOG_TRACE(ERROR, "ESM-PROC  - PDN connection %d has not been "
                 "allocated", pid);
     } else if (operation == ESM_EBR_CONTEXT_TFT_CREATE) {
-      esm_pdn_t *pdn = _esm_data.pdn[pid].data;
+      esm_pdn_t *pdn = esm_data->pdn[pid].data;
 
       /* For each EPS bearer context associated to the PDN connection */
       for (i = 0; i < pdn->n_bearers; i++) {
diff --git a/openair3/NAS/UE/ESM/esm_ebr_context.h b/openair3/NAS/UE/ESM/esm_ebr_context.h
index 95d7b081d884402da2b6ddff5ec5364e8a8d696c..a0016555a30233a9d5a3718dacae227b6e369692 100644
--- a/openair3/NAS/UE/ESM/esm_ebr_context.h
+++ b/openair3/NAS/UE/ESM/esm_ebr_context.h
@@ -39,6 +39,7 @@ Description Defines functions used to handle EPS bearer contexts.
 #define __ESM_EBR_CONTEXT_H__
 
 #include "networkDef.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -65,14 +66,14 @@ typedef enum {
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-int esm_ebr_context_create(int pid, int ebi, int is_default,
+int esm_ebr_context_create(esm_data_t *esm_data, int ueid, int pid, int ebi, int is_default,
                            const network_qos_t *qos, const network_tft_t *tft);
 
-int esm_ebr_context_release(int ebi, int *pid, int *bid);
+int esm_ebr_context_release(nas_user_t *user, int ebi, int *pid, int *bid);
 
-int esm_ebr_context_get_pid(int ebi);
+int esm_ebr_context_get_pid(esm_data_t *esm_data, int ebi);
 
-int esm_ebr_context_check_tft(int pid, int ebi, const network_tft_t *tft,
+int esm_ebr_context_check_tft(esm_data_t *esm_data, int pid, int ebi, const network_tft_t *tft,
                               esm_ebr_context_tft_t operation);
 
 
diff --git a/openair3/NAS/UE/ESM/esm_ip.c b/openair3/NAS/UE/ESM/esm_ip.c
index f21305a1a0ca9c1862b4cec94fb0ac879d58f392..2638221a8f9cb1a86826bb8201a3e0b40a967c42 100644
--- a/openair3/NAS/UE/ESM/esm_ip.c
+++ b/openair3/NAS/UE/ESM/esm_ip.c
@@ -22,8 +22,10 @@
 #include "emmData.h"
 #include "esmData.h"
 
+// FIXME don't work for reentrant calls
 char ip_addr_str[100];
 
+// FIXME can't be extern and inline at same time !
 inline char *esm_data_get_ipv4_addr(const OctetString *ip_addr)
 {
   if (ip_addr->length > 0) {
diff --git a/openair3/NAS/UE/ESM/esm_main.c b/openair3/NAS/UE/ESM/esm_main.c
index 74af9935aaaf0210088e900a514ae0daf691d61c..6c110abb52ea34f708e278198a6e37a5b577f118 100644
--- a/openair3/NAS/UE/ESM/esm_main.c
+++ b/openair3/NAS/UE/ESM/esm_main.c
@@ -40,11 +40,13 @@ Description Defines the EPS Session Management procedure call manager,
 #include "esm_main.h"
 #include "commonDef.h"
 #include "nas_log.h"
+#include "utils.h"
 
 #include "emmData.h"
 #include "esmData.h"
 #include "esm_pt.h"
 #include "esm_ebr.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
@@ -69,35 +71,46 @@ Description Defines the EPS Session Management procedure call manager,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ***************************************************************************/
-void esm_main_initialize(esm_indication_callback_t cb)
+void esm_main_initialize(nas_user_t *user, esm_indication_callback_t cb)
 {
   LOG_FUNC_IN;
 
   int i;
 
+  esm_data_t *esm_data = calloc_or_fail(sizeof(esm_data_t));
+  user->esm_data = esm_data;
+
+  default_eps_bearer_context_data_t *default_eps_bearer_context = calloc(1, sizeof(default_eps_bearer_context_data_t));
+  if ( default_eps_bearer_context == NULL ) {
+    LOG_TRACE(ERROR, "ESM-MAIN  - Can't malloc default_eps_bearer_context");
+    exit(EXIT_FAILURE);
+  }
+  default_eps_bearer_context->ebi = ESM_EBI_UNASSIGNED;
+  user->default_eps_bearer_context_data = default_eps_bearer_context;
   /* Total number of active EPS bearer contexts */
-  _esm_data.n_ebrs = 0;
+  esm_data->n_ebrs = 0;
   /* List of active PDN connections */
-  _esm_data.n_pdns = 0;
+  esm_data->n_pdns = 0;
 
   for (i = 0; i < ESM_DATA_PDN_MAX + 1; i++) {
-    _esm_data.pdn[i].pid = -1;
-    _esm_data.pdn[i].is_active = FALSE;
-    _esm_data.pdn[i].data = NULL;
+    esm_data->pdn[i].pid = -1;
+    esm_data->pdn[i].is_active = FALSE;
+    esm_data->pdn[i].data = NULL;
   }
 
   /* Emergency bearer services indicator */
-  _esm_data.emergency = FALSE;
+  esm_data->emergency = FALSE;
 
   /* Initialize the procedure transaction identity manager */
-  esm_pt_initialize();
 
-  /* Initialize the EPS bearer context manager */
-  esm_ebr_initialize(cb);
+  user->esm_pt_data = esm_pt_initialize();
 
+  /* Initialize the EPS bearer context manager */
+  user->esm_ebr_data = esm_ebr_initialize();
+  // FIXME only one callback for all user or many for many ?
+  esm_ebr_register_callback(cb);
   LOG_FUNC_OUT;
 }
 
@@ -116,7 +129,7 @@ void esm_main_initialize(esm_indication_callback_t cb)
  **                  Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void esm_main_cleanup(void)
+void esm_main_cleanup(esm_data_t *esm_data)
 {
   LOG_FUNC_IN;
 
@@ -127,8 +140,8 @@ void esm_main_cleanup(void)
 
     /* De-activate EPS bearers and clean up PDN connections */
     for (pid = 0; pid < ESM_DATA_PDN_MAX; pid++) {
-      if (_esm_data.pdn[pid].data) {
-        esm_pdn_t *pdn = _esm_data.pdn[pid].data;
+      if (esm_data->pdn[pid].data) {
+        esm_pdn_t *pdn = esm_data->pdn[pid].data;
 
         if (pdn->apn.length > 0) {
           free(pdn->apn.value);
@@ -152,7 +165,7 @@ void esm_main_cleanup(void)
         }
 
         /* Release the PDN connection */
-        free(_esm_data.pdn[pid].data);
+        free(esm_data->pdn[pid].data);
       }
     }
   }
@@ -168,7 +181,6 @@ void esm_main_cleanup(void)
  **      a defined state at the same time                          **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The maximum number of PDN connections that **
@@ -176,7 +188,7 @@ void esm_main_cleanup(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_get_nb_pdns_max(void)
+int esm_main_get_nb_pdns_max(esm_data_t *esm_data)
 {
   LOG_FUNC_IN;
 
@@ -190,18 +202,17 @@ int esm_main_get_nb_pdns_max(void)
  ** Description: Get the number of active PDN connections                  **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The number of active PDN connections       **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_get_nb_pdns(void)
+int esm_main_get_nb_pdns(esm_data_t *esm_data)
 {
   LOG_FUNC_IN;
 
-  LOG_FUNC_RETURN (_esm_data.n_pdns);
+  LOG_FUNC_RETURN (esm_data->n_pdns);
 }
 
 /****************************************************************************
@@ -212,7 +223,6 @@ int esm_main_get_nb_pdns(void)
  **      vices is established                                      **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    TRUE if a PDN connection for emergency     **
@@ -220,11 +230,11 @@ int esm_main_get_nb_pdns(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_has_emergency(void)
+int esm_main_has_emergency(esm_data_t *esm_data)
 {
   LOG_FUNC_IN;
 
-  LOG_FUNC_RETURN (_esm_data.emergency);
+  LOG_FUNC_RETURN (esm_data->emergency);
 }
 
 /****************************************************************************
@@ -234,7 +244,6 @@ int esm_main_has_emergency(void)
  ** Description: Get the status of the specified PDN connection            **
  **                                                                        **
  ** Inputs:  cid:       PDN connection identifier                  **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     state:     TRUE if the current state of the PDN con-  **
  **             nection is ACTIVE; FALSE otherwise.        **
@@ -245,28 +254,30 @@ int esm_main_has_emergency(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_get_pdn_status(int cid, int *state)
+int esm_main_get_pdn_status(nas_user_t *user, int cid, int *state)
 {
   LOG_FUNC_IN;
 
   unsigned int pid = cid - 1;
+  esm_data_t *esm_data = user->esm_data;
+  esm_ebr_data_t *esm_ebr_data = user-> esm_ebr_data;
 
   if (pid >= ESM_DATA_PDN_MAX) {
     return (FALSE);
-  } else if (pid != _esm_data.pdn[pid].pid) {
+  } else if (pid != esm_data->pdn[pid].pid) {
     LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
     return (FALSE);
-  } else if (_esm_data.pdn[pid].data == NULL) {
+  } else if (esm_data->pdn[pid].data == NULL) {
     LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
               cid);
     return (FALSE);
   }
 
-  if (_esm_data.pdn[pid].data->bearer[0] != NULL) {
+  if (esm_data->pdn[pid].data->bearer[0] != NULL) {
     /* The status of a PDN connection is the status of the default EPS bearer
      * that has been assigned to this PDN connection at activation time */
-    int ebi = _esm_data.pdn[pid].data->bearer[0]->ebi;
-    *state = (esm_ebr_get_status(ebi) == ESM_EBR_ACTIVE);
+    int ebi = esm_data->pdn[pid].data->bearer[0]->ebi;
+    *state = (esm_ebr_get_status(esm_ebr_data, ebi) == ESM_EBR_ACTIVE);
   }
 
   /* The PDN connection has not been activated yet */
@@ -280,7 +291,6 @@ int esm_main_get_pdn_status(int cid, int *state)
  ** Description: Get parameters defined for the specified PDN connection   **
  **                                                                        **
  ** Inputs:  cid:       PDN connection identifier                  **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     type:      PDN connection type (IPv4, IPv6, IPv4v6)   **
  **      apn:       Access Point logical Name in used          **
@@ -290,7 +300,7 @@ int esm_main_get_pdn_status(int cid, int *state)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_get_pdn(int cid, int *type, const char **apn,
+int esm_main_get_pdn(esm_data_t *esm_data, int cid, int *type, const char **apn,
                      int *is_emergency, int *is_active)
 {
   LOG_FUNC_IN;
@@ -299,29 +309,29 @@ int esm_main_get_pdn(int cid, int *type, const char **apn,
 
   if (pid >= ESM_DATA_PDN_MAX) {
     return (RETURNerror);
-  } else if (pid != _esm_data.pdn[pid].pid) {
+  } else if (pid != esm_data->pdn[pid].pid) {
     LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
     return (RETURNerror);
-  } else if (_esm_data.pdn[pid].data == NULL) {
+  } else if (esm_data->pdn[pid].data == NULL) {
     LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
               cid);
     return (RETURNerror);
   }
 
   /* Get the PDN type */
-  *type = _esm_data.pdn[pid].data->type;
+  *type = esm_data->pdn[pid].data->type;
 
   /* Get the Access Point Name */
-  if (_esm_data.pdn[pid].data->apn.length > 0) {
-    *apn = (char *)(_esm_data.pdn[pid].data->apn.value);
+  if (esm_data->pdn[pid].data->apn.length > 0) {
+    *apn = (char *)(esm_data->pdn[pid].data->apn.value);
   } else {
     *apn = NULL;
   }
 
   /* Get the emergency bearer services indicator */
-  *is_emergency = _esm_data.pdn[pid].data->is_emergency;
+  *is_emergency = esm_data->pdn[pid].data->is_emergency;
   /* Get the active PDN connection indicator */
-  *is_active = _esm_data.pdn[pid].is_active;
+  *is_active = esm_data->pdn[pid].is_active;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -334,7 +344,6 @@ int esm_main_get_pdn(int cid, int *type, const char **apn,
  **      tion                                                      **
  **                                                                        **
  ** Inputs:  cid:       PDN connection identifier                  **
- **      Others:    _esm_data                                  **
  **                                                                        **
  ** Outputs:     ipv4adddr: IPv4 address                               **
  **      ipv6adddr: IPv6 address                               **
@@ -342,7 +351,7 @@ int esm_main_get_pdn(int cid, int *type, const char **apn,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_main_get_pdn_addr(int cid, const char **ipv4addr, const char **ipv6addr)
+int esm_main_get_pdn_addr(esm_data_t *esm_data, int cid, const char **ipv4addr, const char **ipv6addr)
 {
   LOG_FUNC_IN;
 
@@ -350,35 +359,30 @@ int esm_main_get_pdn_addr(int cid, const char **ipv4addr, const char **ipv6addr)
 
   if (pid >= ESM_DATA_PDN_MAX) {
     return (RETURNerror);
-  } else if (pid != _esm_data.pdn[pid].pid) {
+  } else if (pid != esm_data->pdn[pid].pid) {
     LOG_TRACE(WARNING, "ESM-MAIN  - PDN connection %d is not defined", cid);
     return (RETURNerror);
-  } else if (_esm_data.pdn[pid].data == NULL) {
+  } else if (esm_data->pdn[pid].data == NULL) {
     LOG_TRACE(ERROR, "ESM-MAIN  - PDN connection %d has not been allocated",
               cid);
     return (RETURNerror);
-  } else if (!_esm_data.pdn[pid].is_active) {
+  } else if (!esm_data->pdn[pid].is_active) {
     /* No any IP address has been assigned to this PDN connection */
     return (RETURNok);
   }
 
-  if (_esm_data.pdn[pid].data->type == NET_PDN_TYPE_IPV4) {
+  if (esm_data->pdn[pid].data->type == NET_PDN_TYPE_IPV4) {
     /* Get IPv4 address */
-    *ipv4addr = _esm_data.pdn[pid].data->ip_addr;
-  } else if (_esm_data.pdn[pid].data->type == NET_PDN_TYPE_IPV6) {
+    *ipv4addr = esm_data->pdn[pid].data->ip_addr;
+  } else if (esm_data->pdn[pid].data->type == NET_PDN_TYPE_IPV6) {
     /* Get IPv6 address */
-    *ipv6addr = _esm_data.pdn[pid].data->ip_addr;
+    *ipv6addr = esm_data->pdn[pid].data->ip_addr;
   } else {
     /* IPv4v6 dual-stack terminal */
-    *ipv4addr = _esm_data.pdn[pid].data->ip_addr;
-    *ipv6addr = _esm_data.pdn[pid].data->ip_addr+ESM_DATA_IPV4_ADDRESS_SIZE;
+    *ipv4addr = esm_data->pdn[pid].data->ip_addr;
+    *ipv6addr = esm_data->pdn[pid].data->ip_addr+ESM_DATA_IPV4_ADDRESS_SIZE;
   }
 
   LOG_FUNC_RETURN (RETURNok);
 }
 
-
-/****************************************************************************/
-/*********************  L O C A L    F U N C T I O N S  *********************/
-/****************************************************************************/
-
diff --git a/openair3/NAS/UE/ESM/esm_main.h b/openair3/NAS/UE/ESM/esm_main.h
index d7346fdb0898c491595485e90e32137ecbf430b3..42381c8d5b15b89d97ab2d008289dd40f5768a9b 100644
--- a/openair3/NAS/UE/ESM/esm_main.h
+++ b/openair3/NAS/UE/ESM/esm_main.h
@@ -41,6 +41,9 @@ Description Defines the EPS Session Management procedure call manager,
 #define __ESM_MAIN_H__
 
 #include "networkDef.h"
+#include "esm_ebr.h"
+#include "esmData.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -58,19 +61,19 @@ Description Defines the EPS Session Management procedure call manager,
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void esm_main_initialize(esm_indication_callback_t cb);
+void esm_main_initialize(nas_user_t *user, esm_indication_callback_t cb);
 
-void esm_main_cleanup(void);
+void esm_main_cleanup(esm_data_t *esm_data);
 
 
 /* User's getter for PDN connections and EPS bearer contexts */
-int esm_main_get_nb_pdns_max(void);
-int esm_main_get_nb_pdns(void);
-int esm_main_has_emergency(void);
-int esm_main_get_pdn_status(int cid, int *state);
-int esm_main_get_pdn(int cid, int *type, const char **apn, int *is_emergency,
+int esm_main_get_nb_pdns_max(esm_data_t *esm_data);
+int esm_main_get_nb_pdns(esm_data_t *esm_data);
+int esm_main_has_emergency(esm_data_t *esm_data);
+int esm_main_get_pdn_status(nas_user_t *user, int cid, int *state);
+int esm_main_get_pdn(esm_data_t *esm_data, int cid, int *type, const char **apn, int *is_emergency,
                      int *is_active);
-int esm_main_get_pdn_addr(int cid, const char **ipv4addr, const char **ipv6addr);
+int esm_main_get_pdn_addr(esm_data_t *esm_data, int cid, const char **ipv4addr, const char **ipv6addr);
 
 
 #endif /* __ESM_MAIN_H__*/
diff --git a/openair3/NAS/UE/ESM/esm_proc.h b/openair3/NAS/UE/ESM/esm_proc.h
index edbd926309380549b597c63a8dc2241b9de27430..3d7260b83b7400c8a8d140dc648c7937fa9c6194 100644
--- a/openair3/NAS/UE/ESM/esm_proc.h
+++ b/openair3/NAS/UE/ESM/esm_proc.h
@@ -43,6 +43,7 @@ Description Defines the EPS Session Management procedures executed at
 #include "OctetString.h"
 #include "emmData.h"
 #include "ProtocolConfigurationOptions.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -78,7 +79,7 @@ typedef enum {
  * Type of the ESM procedure callback executed when requested by the UE
  * or initiated by the network
  */
-typedef int (*esm_proc_procedure_t) (int, int, OctetString *, int);
+typedef int (*esm_proc_procedure_t) (nas_user_t *user, int, int, OctetString *, int);
 
 /* EPS bearer level QoS parameters */
 typedef network_qos_t esm_proc_qos_t;
@@ -112,7 +113,7 @@ typedef struct {
  * --------------------------------------------------------------------------
  */
 int esm_proc_status_ind(int pti, int ebi, int *esm_cause);
-int esm_proc_status(int is_standalone, int pti, OctetString *msg,
+int esm_proc_status(nas_user_t *user, int is_standalone, int pti, OctetString *msg,
                     int sent_by_ue);
 
 
@@ -121,16 +122,16 @@ int esm_proc_status(int is_standalone, int pti, OctetString *msg,
  *          PDN connectivity procedure
  * --------------------------------------------------------------------------
  */
-int esm_proc_pdn_connectivity(int cid, int to_define,
+int esm_proc_pdn_connectivity(nas_user_t *user, int cid, int to_define,
                               esm_proc_pdn_type_t pdn_type, const OctetString *apn, int is_emergency,
                               unsigned int *pti);
-int esm_proc_pdn_connectivity_request(int is_standalone, int pti,
+int esm_proc_pdn_connectivity_request(nas_user_t *user, int is_standalone, int pti,
                                       OctetString *msg, int sent_by_ue);
-int esm_proc_pdn_connectivity_accept(int pti, esm_proc_pdn_type_t pdn_type,
+int esm_proc_pdn_connectivity_accept(nas_user_t *user, int pti, esm_proc_pdn_type_t pdn_type,
                                      const OctetString *pdn_address, const OctetString *apn, int *esm_cause);
-int esm_proc_pdn_connectivity_reject(int pti, int *esm_cause);
-int esm_proc_pdn_connectivity_complete(void);
-int esm_proc_pdn_connectivity_failure(int is_pending);
+int esm_proc_pdn_connectivity_reject(nas_user_t *user, int pti, int *esm_cause);
+int esm_proc_pdn_connectivity_complete(nas_user_t *user);
+int esm_proc_pdn_connectivity_failure(nas_user_t *user, int is_pending);
 
 
 /*
@@ -138,12 +139,12 @@ int esm_proc_pdn_connectivity_failure(int is_pending);
  *              PDN disconnect procedure
  * --------------------------------------------------------------------------
  */
-int esm_proc_pdn_disconnect(int cid, unsigned int *pti, unsigned int *ebi);
-int esm_proc_pdn_disconnect_request(int is_standalone, int pti,
+int esm_proc_pdn_disconnect(esm_data_t *esm_data, int cid, unsigned int *pti, unsigned int *ebi);
+int esm_proc_pdn_disconnect_request(nas_user_t *user, int is_standalone, int pti,
                                     OctetString *msg, int sent_by_ue);
 
-int esm_proc_pdn_disconnect_accept(int pti, int *esm_cause);
-int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause);
+int esm_proc_pdn_disconnect_accept(esm_pt_data_t *esm_pt_data, int pti, int *esm_cause);
+int esm_proc_pdn_disconnect_reject(nas_user_t *user, int pti, int *esm_cause);
 
 /*
  * --------------------------------------------------------------------------
@@ -151,14 +152,14 @@ int esm_proc_pdn_disconnect_reject(int pti, int *esm_cause);
  * --------------------------------------------------------------------------
  */
 
-int esm_proc_default_eps_bearer_context_request(int pid, int ebi,
+int esm_proc_default_eps_bearer_context_request(nas_user_t *user, int pid, int ebi,
     const esm_proc_qos_t *esm_qos, int *esm_cause);
-int esm_proc_default_eps_bearer_context_complete(void);
-int esm_proc_default_eps_bearer_context_failure(void);
+int esm_proc_default_eps_bearer_context_complete(default_eps_bearer_context_data_t *default_eps_bearer_context_data);
+int esm_proc_default_eps_bearer_context_failure(nas_user_t *user);
 
-int esm_proc_default_eps_bearer_context_accept(int is_standalone, int ebi,
+int esm_proc_default_eps_bearer_context_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered);
-int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
+int esm_proc_default_eps_bearer_context_reject(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered);
 
 /*
@@ -167,12 +168,12 @@ int esm_proc_default_eps_bearer_context_reject(int is_standalone, int ebi,
  * --------------------------------------------------------------------------
  */
 
-int esm_proc_dedicated_eps_bearer_context_request(int ebi, int default_ebi,
+int esm_proc_dedicated_eps_bearer_context_request(nas_user_t *user, int ebi, int default_ebi,
     const esm_proc_qos_t *qos, const esm_proc_tft_t *tft, int *esm_cause);
 
-int esm_proc_dedicated_eps_bearer_context_accept(int is_standalone, int ebi,
+int esm_proc_dedicated_eps_bearer_context_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered);
-int esm_proc_dedicated_eps_bearer_context_reject(int is_standalone, int ebi,
+int esm_proc_dedicated_eps_bearer_context_reject(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered);
 
 /*
@@ -181,11 +182,11 @@ int esm_proc_dedicated_eps_bearer_context_reject(int is_standalone, int ebi,
  * --------------------------------------------------------------------------
  */
 
-int esm_proc_eps_bearer_context_deactivate(int is_local, int ebi, int *pid,
+int esm_proc_eps_bearer_context_deactivate(nas_user_t *user, int is_local, int ebi, int *pid,
     int *bid);
-int esm_proc_eps_bearer_context_deactivate_request(int ebi, int *esm_cause);
+int esm_proc_eps_bearer_context_deactivate_request(nas_user_t *user, int ebi, int *esm_cause);
 
-int esm_proc_eps_bearer_context_deactivate_accept(int is_standalone, int ebi,
+int esm_proc_eps_bearer_context_deactivate_accept(nas_user_t *user, int is_standalone, int ebi,
     OctetString *msg, int ue_triggered);
 
 #endif /* __ESM_PROC_H__*/
diff --git a/openair3/NAS/UE/ESM/esm_pt.c b/openair3/NAS/UE/ESM/esm_pt.c
index 8e819fbd0bb6a956931a0a42785d86fcb8f784f0..9f8e948954ba7537e2abf6ada2303ef8e046ebc3 100644
--- a/openair3/NAS/UE/ESM/esm_pt.c
+++ b/openair3/NAS/UE/ESM/esm_pt.c
@@ -37,9 +37,12 @@ Description Defines functions used to handle ESM procedure transactions.
 *****************************************************************************/
 
 #include "esm_pt.h"
+#include "esm_pt_defs.h"
+#include "user_defs.h"
 
 #include "commonDef.h"
 #include "nas_log.h"
+#include "utils.h"
 
 #include <stdlib.h> // malloc, free
 #include <string.h> // memcpy
@@ -48,14 +51,6 @@ Description Defines functions used to handle ESM procedure transactions.
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
 /****************************************************************************/
 
-/*
- * Minimal and maximal value of a procedure transaction identity:
- * The Procedure Transaction Identity (PTI) identifies bi-directional
- * messages flows
- */
-#define ESM_PTI_MIN     (PROCEDURE_TRANSACTION_IDENTITY_FIRST)
-#define ESM_PTI_MAX     (PROCEDURE_TRANSACTION_IDENTITY_LAST)
-
 /****************************************************************************/
 /*******************  L O C A L    D E F I N I T I O N S  *******************/
 /****************************************************************************/
@@ -66,33 +61,9 @@ static const char *_esm_pt_state_str[ESM_PT_STATE_MAX] = {
   "PROCEDURE TRANSACTION PENDING"
 };
 
-/*
- * --------------------------
- * Procedure transaction data
- * --------------------------
- */
-typedef struct {
-  unsigned char pti;      /* Procedure transaction identity   */
-  esm_pt_state status;    /* Procedure transaction status     */
-  struct nas_timer_t timer;   /* Retransmission timer         */
-  esm_pt_timer_data_t *args;  /* Retransmission timer parameters data */
-} esm_pt_context_t;
-
-/*
- * ------------------------------
- * List of procedure transactions
- * ------------------------------
- */
-static struct {
-  unsigned char index;    /* Index of the next procedure transaction
-                 * identity to be used */
-#define ESM_PT_DATA_SIZE (ESM_PTI_MAX - ESM_PTI_MIN + 1)
-  esm_pt_context_t *context[ESM_PT_DATA_SIZE + 1];
-} _esm_pt_data;
-
 /* Return the index of the next available entry in the list of procedure
  * transaction data */
-static int _esm_pt_get_available_entry(void);
+static int _esm_pt_get_available_entry(esm_pt_data_t *esm_pt_data);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -109,22 +80,22 @@ static int _esm_pt_get_available_entry(void);
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-void esm_pt_initialize(void)
+esm_pt_data_t *esm_pt_initialize(void)
 {
   LOG_FUNC_IN;
-
+  esm_pt_data_t *esm_pt_data = calloc_or_fail(sizeof(esm_pt_data_t));
   int i;
 
-  _esm_pt_data.index = 0;
+  esm_pt_data->index = 0;
 
   for (i = 0; i < ESM_PT_DATA_SIZE + 1; i++) {
-    _esm_pt_data.context[i] = NULL;
+    esm_pt_data->context[i] = NULL;
   }
 
   LOG_FUNC_OUT;
+  return esm_pt_data;
 }
 
 /****************************************************************************
@@ -140,43 +111,42 @@ void esm_pt_initialize(void)
  **      Return:    The identity of the new procedure transac- **
  **             tion when successfully assigned;           **
  **             the unassigned PTI (0) otherwise.          **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_assign(void)
+int esm_pt_assign(esm_pt_data_t *esm_pt_data)
 {
   LOG_FUNC_IN;
 
   /* Search for an available procedure transaction identity */
-  int i = _esm_pt_get_available_entry();
+  int i = _esm_pt_get_available_entry(esm_pt_data);
 
   if (i < 0) {
     LOG_FUNC_RETURN (ESM_PT_UNASSIGNED);
   }
 
   /* Assign new procedure transaction */
-  _esm_pt_data.context[i] =
+  esm_pt_data->context[i] =
     (esm_pt_context_t *)malloc(sizeof(esm_pt_context_t));
 
-  if (_esm_pt_data.context[i] == NULL) {
+  if (esm_pt_data->context[i] == NULL) {
     LOG_FUNC_RETURN (ESM_PT_UNASSIGNED);
   }
 
   /* Store the index of the next available procedure transaction identity */
-  _esm_pt_data.index = i + 1;
+  esm_pt_data->index = i + 1;
 
   /* An available procedure transaction identity is found */
-  _esm_pt_data.context[i]->pti = i + ESM_PTI_MIN;
+  esm_pt_data->context[i]->pti = i + ESM_PTI_MIN;
   /* Set the procedure transaction status to INACTIVE */
-  _esm_pt_data.context[i]->status = ESM_PT_INACTIVE;
+  esm_pt_data->context[i]->status = ESM_PT_INACTIVE;
   /* Disable the retransmission timer */
-  _esm_pt_data.context[i]->timer.id = NAS_TIMER_INACTIVE_ID;
+  esm_pt_data->context[i]->timer.id = NAS_TIMER_INACTIVE_ID;
   /* Setup retransmission timer parameters */
-  _esm_pt_data.context[i]->args = NULL;
+  esm_pt_data->context[i]->args = NULL;
 
   LOG_TRACE(INFO, "ESM-FSM   - Procedure transaction identity %d assigned",
-            _esm_pt_data.context[i]->pti);
-  LOG_FUNC_RETURN (_esm_pt_data.context[i]->pti);
+            esm_pt_data->context[i]->pti);
+  LOG_FUNC_RETURN (esm_pt_data->context[i]->pti);
 }
 
 /****************************************************************************
@@ -193,10 +163,9 @@ int esm_pt_assign(void)
  **      Return:    RETURNok if the procedure transaction iden-**
  **             tity has been successfully released;       **
  **             RETURNerror otherwise.                     **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_release(int pti)
+int esm_pt_release(esm_pt_data_t *esm_pt_data, int pti)
 {
   LOG_FUNC_IN;
 
@@ -205,7 +174,7 @@ int esm_pt_release(int pti)
   }
 
   /* Get procedure transaction data */
-  esm_pt_context_t *ctx = _esm_pt_data.context[pti - ESM_PTI_MIN];
+  esm_pt_context_t *ctx = esm_pt_data->context[pti - ESM_PTI_MIN];
 
   if ( (ctx == NULL) || (ctx->pti != pti) ) {
     /* Procedure transaction not assigned */
@@ -236,8 +205,8 @@ int esm_pt_release(int pti)
   }
 
   /* Release transaction procedure data */
-  free(_esm_pt_data.context[pti - ESM_PTI_MIN]);
-  _esm_pt_data.context[pti - ESM_PTI_MIN] = NULL;
+  free(esm_pt_data->context[pti - ESM_PTI_MIN]);
+  esm_pt_data->context[pti - ESM_PTI_MIN] = NULL;
 
   LOG_TRACE(INFO, "ESM-FSM   - Procedure transaction %d released", pti);
 
@@ -261,20 +230,20 @@ int esm_pt_release(int pti)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_start_timer(int pti, const OctetString *msg,
+int esm_pt_start_timer(nas_user_t *user, int pti, const OctetString *msg,
                        long sec, nas_timer_callback_t cb)
 {
   LOG_FUNC_IN;
+  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
 
   if ( (pti < ESM_PTI_MIN) || (pti > ESM_PTI_MAX) ) {
     LOG_FUNC_RETURN (RETURNerror);
   }
 
   /* Get procedure transaction data */
-  esm_pt_context_t *ctx = _esm_pt_data.context[pti - ESM_PTI_MIN];
+  esm_pt_context_t *ctx = esm_pt_data->context[pti - ESM_PTI_MIN];
 
   if ( (ctx == NULL) || (ctx->pti != pti) ) {
     /* Procedure transaction not assigned */
@@ -291,6 +260,7 @@ int esm_pt_start_timer(int pti, const OctetString *msg,
     ctx->args = (esm_pt_timer_data_t *)malloc(sizeof(esm_pt_timer_data_t));
 
     if (ctx->args) {
+      ctx->args->user = user;
       /* Set the EPS bearer identity */
       ctx->args->pti = pti;
       /* Reset the retransmission counter */
@@ -332,10 +302,9 @@ int esm_pt_start_timer(int pti, const OctetString *msg,
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_stop_timer(int pti)
+int esm_pt_stop_timer(esm_pt_data_t *esm_pt_data, int pti)
 {
   LOG_FUNC_IN;
 
@@ -344,7 +313,7 @@ int esm_pt_stop_timer(int pti)
   }
 
   /* Get procedure transaction data */
-  esm_pt_context_t *ctx = _esm_pt_data.context[pti - ESM_PTI_MIN];
+  esm_pt_context_t *ctx = esm_pt_data->context[pti - ESM_PTI_MIN];
 
   if ( (ctx == NULL) || (ctx->pti != pti) ) {
     /* Procedure transaction not assigned */
@@ -384,10 +353,9 @@ int esm_pt_stop_timer(int pti)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok, RETURNerror                      **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_set_status(int pti, esm_pt_state status)
+int esm_pt_set_status(esm_pt_data_t *esm_pt_data, int pti, esm_pt_state status)
 {
   LOG_FUNC_IN;
 
@@ -398,7 +366,7 @@ int esm_pt_set_status(int pti, esm_pt_state status)
   }
 
   /* Get procedure transaction data */
-  esm_pt_context_t *ctx = _esm_pt_data.context[pti - ESM_PTI_MIN];
+  esm_pt_context_t *ctx = esm_pt_data->context[pti - ESM_PTI_MIN];
 
   if ( (ctx == NULL) || (ctx->pti != pti) ) {
     /* Procedure transaction not assigned */
@@ -431,7 +399,6 @@ int esm_pt_set_status(int pti, esm_pt_state status)
  **      transaction                                               **
  **                                                                        **
  ** Inputs:  pti:       The identity of the procedure transaction  **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The current value of the ESM procedure     **
@@ -439,23 +406,23 @@ int esm_pt_set_status(int pti, esm_pt_state status)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-esm_pt_state esm_pt_get_status(int pti)
+esm_pt_state esm_pt_get_status(esm_pt_data_t *esm_pt_data, int pti)
 {
   if ( (pti < ESM_PTI_MIN) || (pti > ESM_PTI_MAX) ) {
     return (ESM_PT_INACTIVE);
   }
 
-  if (_esm_pt_data.context[pti - ESM_PTI_MIN] == NULL) {
+  if (esm_pt_data->context[pti - ESM_PTI_MIN] == NULL) {
     /* Procedure transaction not allocated */
     return (ESM_PT_INACTIVE);
   }
 
-  if (_esm_pt_data.context[pti - ESM_PTI_MIN]->pti != pti) {
+  if (esm_pt_data->context[pti - ESM_PTI_MIN]->pti != pti) {
     /* Procedure transaction not assigned */
     return (ESM_PT_INACTIVE);
   }
 
-  return (_esm_pt_data.context[pti - ESM_PTI_MIN]->status);
+  return (esm_pt_data->context[pti - ESM_PTI_MIN]->status);
 }
 
 /****************************************************************************
@@ -467,7 +434,6 @@ esm_pt_state esm_pt_get_status(int pti)
  **      given state                                               **
  **                                                                        **
  ** Inputs:  status:    The PDN connection status                  **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The procedure transaction identity of the  **
@@ -476,18 +442,18 @@ esm_pt_state esm_pt_get_status(int pti)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_get_pending_pti(esm_pt_state status)
+int esm_pt_get_pending_pti(esm_pt_data_t *esm_pt_data, esm_pt_state status)
 {
   LOG_FUNC_IN;
 
   int i;
 
   for (i = 0; i < ESM_PT_DATA_SIZE; i++) {
-    if (_esm_pt_data.context[i] == NULL) {
+    if (esm_pt_data->context[i] == NULL) {
       continue;
     }
 
-    if (_esm_pt_data.context[i]->status != status) {
+    if (esm_pt_data->context[i]->status != status) {
       continue;
     }
 
@@ -496,7 +462,7 @@ int esm_pt_get_pending_pti(esm_pt_state status)
   }
 
   if (i < ESM_PT_DATA_SIZE) {
-    LOG_FUNC_RETURN (_esm_pt_data.context[i]->pti);
+    LOG_FUNC_RETURN (esm_pt_data->context[i]->pti);
   }
 
   /* PDN connection entry not found */
@@ -511,18 +477,17 @@ int esm_pt_get_pending_pti(esm_pt_state status)
  **      does not match an assigned PTI value currently in use     **
  **                                                                        **
  ** Inputs:  pti:       The identity of the procedure transaction  **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    TRUE, FALSE                                **
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int esm_pt_is_not_in_use(int pti)
+int esm_pt_is_not_in_use(esm_pt_data_t *esm_pt_data, int pti)
 {
   return ( (pti == ESM_PT_UNASSIGNED) ||
-           (_esm_pt_data.context[pti - ESM_PTI_MIN] == NULL) ||
-           (_esm_pt_data.context[pti - ESM_PTI_MIN]->pti) != pti);
+           (esm_pt_data->context[pti - ESM_PTI_MIN] == NULL) ||
+           (esm_pt_data->context[pti - ESM_PTI_MIN]->pti) != pti);
 }
 
 /****************************************************************************
@@ -557,7 +522,6 @@ int esm_pt_is_reserved(int pti)
  **      of procedure transaction data                             **
  **                                                                        **
  ** Inputs:  None                                                      **
- **      Others:    _esm_pt_data                               **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    The index of the next available procedure  **
@@ -566,20 +530,20 @@ int esm_pt_is_reserved(int pti)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _esm_pt_get_available_entry(void)
+static int _esm_pt_get_available_entry(esm_pt_data_t *esm_pt_data)
 {
   int i;
 
-  for (i = _esm_pt_data.index; i < ESM_PT_DATA_SIZE; i++) {
-    if (_esm_pt_data.context[i] != NULL) {
+  for (i = esm_pt_data->index; i < ESM_PT_DATA_SIZE; i++) {
+    if (esm_pt_data->context[i] != NULL) {
       continue;
     }
 
     return i;
   }
 
-  for (i = 0; i < _esm_pt_data.index; i++) {
-    if (_esm_pt_data.context[i] != NULL) {
+  for (i = 0; i < esm_pt_data->index; i++) {
+    if (esm_pt_data->context[i] != NULL) {
       continue;
     }
 
diff --git a/openair3/NAS/UE/ESM/esm_pt.h b/openair3/NAS/UE/ESM/esm_pt.h
index 7cc0c54780646403ae3c33fc3dbb23e898bd31b9..8b8693efdaf6bb6d78130e8b4c817a383af27640 100644
--- a/openair3/NAS/UE/ESM/esm_pt.h
+++ b/openair3/NAS/UE/ESM/esm_pt.h
@@ -40,6 +40,8 @@ Description Defines functions used to handle ESM procedure transactions.
 
 #include "OctetString.h"
 #include "nas_timer.h"
+#include "user_defs.h"
+#include "esm_pt_defs.h"
 
 #include "ProcedureTransactionIdentity.h"
 
@@ -54,21 +56,6 @@ Description Defines functions used to handle ESM procedure transactions.
 /************************  G L O B A L    T Y P E S  ************************/
 /****************************************************************************/
 
-/* Procedure transaction states */
-typedef enum {
-  ESM_PT_INACTIVE,    /* No procedure transaction exists      */
-  ESM_PT_PENDING, /* The UE has initiated a procedure transaction
-             * towards the network              */
-  ESM_PT_STATE_MAX
-} esm_pt_state;
-
-/* ESM message timer retransmission data */
-typedef struct {
-  unsigned char pti;      /* Procedure transaction identity   */
-  unsigned int count;     /* Retransmission counter       */
-  OctetString msg;        /* Encoded ESM message to re-transmit   */
-} esm_pt_timer_data_t;
-
 /****************************************************************************/
 /********************  G L O B A L    V A R I A B L E S  ********************/
 /****************************************************************************/
@@ -79,19 +66,19 @@ typedef struct {
 
 int esm_pt_is_reserved(int pti);
 
-void esm_pt_initialize(void);
+esm_pt_data_t *esm_pt_initialize(void);
 
-int esm_pt_assign(void);
-int esm_pt_release(int pti);
+int esm_pt_assign(esm_pt_data_t *esm_pt_data);
+int esm_pt_release(esm_pt_data_t *esm_pt_data, int pti);
 
-int esm_pt_start_timer(int pti, const OctetString *msg, long sec,
+int esm_pt_start_timer(nas_user_t *user, int pti, const OctetString *msg, long sec,
                        nas_timer_callback_t cb);
-int esm_pt_stop_timer(int pti);
+int esm_pt_stop_timer(esm_pt_data_t *esm_pt_data, int pti);
 
-int esm_pt_set_status(int pti, esm_pt_state status);
-esm_pt_state esm_pt_get_status(int pti);
-int esm_pt_get_pending_pti(esm_pt_state status);
+int esm_pt_set_status(esm_pt_data_t *esm_pt_data, int pti, esm_pt_state status);
+esm_pt_state esm_pt_get_status(esm_pt_data_t *esm_pt_data, int pti);
+int esm_pt_get_pending_pti(esm_pt_data_t *esm_pt_data, esm_pt_state status);
 
-int esm_pt_is_not_in_use(int pti);
+int esm_pt_is_not_in_use(esm_pt_data_t *esm_pt_data, int pti);
 
 #endif /* __ESM_PT_H__*/
diff --git a/openair3/NAS/UE/ESM/esm_pt_defs.h b/openair3/NAS/UE/ESM/esm_pt_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..36cc52dda92da2a52d15f1b7d4b3bf3b5b8af6a3
--- /dev/null
+++ b/openair3/NAS/UE/ESM/esm_pt_defs.h
@@ -0,0 +1,63 @@
+#ifndef _ESM_PT_DEFS_H
+#define _ESM_PT_DEFS_H
+
+#include "UTIL/nas_timer.h"
+#include "IES/ProcedureTransactionIdentity.h"
+
+/****************************************************************************/
+/*********************  G L O B A L    C O N S T A N T S  *******************/
+/****************************************************************************/
+
+/*
+ * Minimal and maximal value of a procedure transaction identity:
+ * The Procedure Transaction Identity (PTI) identifies bi-directional
+ * messages flows
+ */
+#define ESM_PTI_MIN     (PROCEDURE_TRANSACTION_IDENTITY_FIRST)
+#define ESM_PTI_MAX     (PROCEDURE_TRANSACTION_IDENTITY_LAST)
+
+/****************************************************************************/
+/************************  G L O B A L    T Y P E S  ************************/
+/****************************************************************************/
+
+/* Procedure transaction states */
+typedef enum {
+  ESM_PT_INACTIVE,    /* No procedure transaction exists      */
+  ESM_PT_PENDING, /* The UE has initiated a procedure transaction
+             * towards the network              */
+  ESM_PT_STATE_MAX
+} esm_pt_state;
+
+/* ESM message timer retransmission data */
+typedef struct {
+  unsigned char pti;      /* Procedure transaction identity   */
+  unsigned int count;     /* Retransmission counter       */
+  OctetString msg;        /* Encoded ESM message to re-transmit   */
+  void *user;             /* user reference - void to avoid cyclic dependency */
+} esm_pt_timer_data_t;
+
+/*
+ * --------------------------
+ * Procedure transaction data
+ * --------------------------
+ */
+typedef struct {
+  unsigned char pti;      /* Procedure transaction identity   */
+  esm_pt_state status;    /* Procedure transaction status     */
+  struct nas_timer_t timer;   /* Retransmission timer         */
+  esm_pt_timer_data_t *args;  /* Retransmission timer parameters data */
+} esm_pt_context_t;
+
+/*
+ * ------------------------------
+ * List of procedure transactions
+ * ------------------------------
+ */
+typedef struct {
+  unsigned char index;    /* Index of the next procedure transaction
+                 * identity to be used */
+#define ESM_PT_DATA_SIZE (ESM_PTI_MAX - ESM_PTI_MIN + 1)
+  esm_pt_context_t *context[ESM_PT_DATA_SIZE + 1];
+} esm_pt_data_t;
+
+#endif
diff --git a/openair3/NAS/UE/UEprocess.c b/openair3/NAS/UE/UEprocess.c
index 20af58dac67563576cb9c9f425a7bd63987033ec..72035da6c8c0fb687a4381a1ef332807463b05d5 100644
--- a/openair3/NAS/UE/UEprocess.c
+++ b/openair3/NAS/UE/UEprocess.c
@@ -46,6 +46,7 @@
 #include "nas_user.h"
 #include "nas_network.h"
 #include "nas_parser.h"
+#include "user_defs.h"
 
 #include <stdlib.h> // exit
 #include <poll.h>   // poll
@@ -69,9 +70,12 @@ static void *_nas_network_mngr(void *);
 static int _nas_set_signal_handler(int signal, void (handler)(int));
 static void _nas_signal_handler(int signal);
 
-static void _nas_clean(int usr_fd, int net_fd);
+static void _nas_clean(user_api_id_t *user_api_id, int net_fd);
 
 uint8_t usim_test = 0;
+// FIXME user must be set up with right itti message instance
+// FIXME allocate user and initialize its fields
+nas_user_t *user = NULL;
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -80,6 +84,8 @@ uint8_t usim_test = 0;
 /****************************************************************************/
 int main(int argc, const char *argv[])
 {
+  // FIXME allocate and put it in user
+  user_api_id_t *user_api_id = NULL;
   /*
    * Get the command line options
    */
@@ -108,19 +114,19 @@ int main(int argc, const char *argv[])
   /*
    * Initialize the User interface
    */
-  if (user_api_initialize (uhost, uport, devpath, devparams) != RETURNok) {
+  if (user_api_initialize (user_api_id, uhost, uport, devpath, devparams) != RETURNok) {
     LOG_TRACE (ERROR, "UE-MAIN   - user_api_initialize() failed");
     exit (EXIT_FAILURE);
   }
 
-  int user_fd = user_api_get_fd ();
+  int user_fd = user_api_get_fd (user_api_id);
 
   /*
    * Initialize the Network interface
    */
   if (network_api_initialize (nhost, nport) != RETURNok) {
     LOG_TRACE (ERROR, "UE-MAIN   - network_api_initialize() failed");
-    user_api_close (user_fd);
+    user_api_close (user_api_id);
     exit (EXIT_FAILURE);
   }
 
@@ -129,7 +135,7 @@ int main(int argc, const char *argv[])
   /*
    * Initialize the NAS contexts
    */
-  nas_user_initialize (&user_api_emm_callback, &user_api_esm_callback,
+  nas_user_initialize (user, &user_api_emm_callback, &user_api_esm_callback,
                        FIRMWARE_VERSION);
   nas_network_initialize ();
 
@@ -157,7 +163,7 @@ int main(int argc, const char *argv[])
   if (pthread_create (&user_mngr, &attr, _nas_user_mngr, &user_fd) != 0) {
     LOG_TRACE (ERROR, "UE-MAIN   - "
                "Failed to create the user management thread");
-    user_api_close (user_fd);
+    user_api_close (user_api_id);
     network_api_close (network_fd);
     exit (EXIT_FAILURE);
   }
@@ -171,7 +177,7 @@ int main(int argc, const char *argv[])
                       &network_fd) != 0) {
     LOG_TRACE (ERROR, "UE-MAIN   - "
                "Failed to create the network management thread");
-    user_api_close (user_fd);
+    user_api_close (user_api_id);
     network_api_close (network_fd);
     exit (EXIT_FAILURE);
   }
@@ -184,12 +190,12 @@ int main(int argc, const char *argv[])
    */
   while ((user_fd != -1) && (network_fd != -1)) {
     poll (NULL, 0, NAS_SLEEP_TIMEOUT);
-    user_fd = user_api_get_fd ();
+    user_fd = user_api_get_fd (user_api_id);
     network_fd = network_api_get_fd ();
   }
 
   /* Termination cleanup */
-  _nas_clean (user_fd, network_fd);
+  _nas_clean (user_api_id, network_fd);
 
   LOG_TRACE
   (WARNING, "UE-MAIN   - NAS main process exited");
@@ -226,11 +232,11 @@ static void *_nas_user_mngr(void *args)
 
   /* User receiving loop */
   while (!exit_loop) {
-    exit_loop = nas_user_receive_and_process(fd, NULL);
+    exit_loop = nas_user_receive_and_process(user, NULL);
   }
 
   /* Close the connection to the user application layer */
-  user_api_close (*fd);
+  user_api_close (user->user_api_id);
   LOG_TRACE (WARNING, "UE-MAIN   - "
              "The user connection endpoint manager exited");
 
@@ -291,7 +297,7 @@ static void *_nas_network_mngr(void *args)
     }
 
     /* Process the network data message */
-    ret_code = nas_network_process_data (network_message_id,
+    ret_code = nas_network_process_data (user, network_message_id,
                                          network_api_get_data ());
 
     if (ret_code != RETURNok) {
@@ -379,7 +385,8 @@ static void _nas_signal_handler(int signal)
   LOG_FUNC_IN;
 
   LOG_TRACE (WARNING, "UE-MAIN   - Signal %d received", signal);
-  _nas_clean (user_api_get_fd (), network_api_get_fd ());
+  // FIXME acces to global
+  _nas_clean (user->user_api_id, network_api_get_fd ());
   exit (EXIT_SUCCESS);
 
   LOG_FUNC_OUT
@@ -400,17 +407,19 @@ static void _nas_signal_handler(int signal)
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static void _nas_clean(int usr_fd, int net_fd)
+static void _nas_clean(user_api_id_t *user_api_id, int net_fd)
 {
   LOG_FUNC_IN;
 
   LOG_TRACE (INFO, "UE-MAIN   - Perform EMM and ESM cleanup");
-  nas_network_cleanup ();
+  // FIXME nas_network_cleanup depends on nas_user_t
+  // Why this program should interfere like that with oaisim ?
+  //nas_network_cleanup ();
 
   LOG_TRACE (INFO, "UE-MAIN   - "
              "Closing user connection %d and network connection %d",
-             usr_fd, net_fd);
-  user_api_close (usr_fd);
+             user_api_get_fd (user_api_id), net_fd);
+  user_api_close (user_api_id);
   network_api_close (net_fd);
 
   LOG_FUNC_OUT
diff --git a/openair3/NAS/UE/nas_itti_messaging.c b/openair3/NAS/UE/nas_itti_messaging.c
index 7667ad50a438b95892355a31c86b98b39c5f6e71..b6b2126dfb4800863a44473ac6dddb157416cd2b 100644
--- a/openair3/NAS/UE/nas_itti_messaging.c
+++ b/openair3/NAS/UE/nas_itti_messaging.c
@@ -213,7 +213,7 @@ int nas_itti_kenb_refresh_req(const Byte_t kenb[32])
   return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + 0 /* TODO to be virtualized */, message_p);
 }
 
-int nas_itti_cell_info_req(const plmn_t plmnID, const Byte_t rat)
+int nas_itti_cell_info_req(const plmn_t plmnID, const Byte_t rat, int user_id)
 {
   MessageDef *message_p;
 
@@ -230,10 +230,10 @@ int nas_itti_cell_info_req(const plmn_t plmnID, const Byte_t rat)
   	  plmnID.MCCdigit1, plmnID.MCCdigit2, plmnID.MCCdigit3,
   	  plmnID.MNCdigit1, plmnID.MNCdigit2, plmnID.MNCdigit3);
 
-  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + 0 /* TODO to be virtualized */, message_p);
+  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + user_id, message_p);
 }
 
-int nas_itti_nas_establish_req(as_cause_t cause, as_call_type_t type, as_stmsi_t s_tmsi, plmn_t plmnID, Byte_t *data, uint32_t length)
+int nas_itti_nas_establish_req(as_cause_t cause, as_call_type_t type, as_stmsi_t s_tmsi, plmn_t plmnID, Byte_t *data, uint32_t length, int user_id)
 {
   MessageDef *message_p;
 
@@ -255,10 +255,10 @@ int nas_itti_nas_establish_req(as_cause_t cause, as_call_type_t type, as_stmsi_t
   	     plmnID.MCCdigit1, plmnID.MCCdigit2, plmnID.MCCdigit3,
   	     plmnID.MNCdigit1, plmnID.MNCdigit2, plmnID.MNCdigit3);
 
-  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + 0 /* TODO to be virtualized */, message_p);
+  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + user_id, message_p);
 }
 
-int nas_itti_ul_data_req(const uint32_t ue_id, void *const data, const uint32_t length)
+int nas_itti_ul_data_req(const uint32_t ue_id, void *const data, const uint32_t length, int user_id)
 {
   MessageDef *message_p;
 
@@ -268,10 +268,10 @@ int nas_itti_ul_data_req(const uint32_t ue_id, void *const data, const uint32_t
   NAS_UPLINK_DATA_REQ(message_p).nasMsg.data   = data;
   NAS_UPLINK_DATA_REQ(message_p).nasMsg.length = length;
 
-  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + 0 /* TODO to be virtualized */, message_p);
+  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + user_id, message_p);
 }
 
-int nas_itti_rab_establish_rsp(const as_stmsi_t s_tmsi, const as_rab_id_t rabID, const nas_error_code_t errCode)
+int nas_itti_rab_establish_rsp(const as_stmsi_t s_tmsi, const as_rab_id_t rabID, const nas_error_code_t errCode, int user_id)
 {
   MessageDef *message_p;
 
@@ -287,5 +287,5 @@ int nas_itti_rab_establish_rsp(const as_stmsi_t s_tmsi, const as_rab_id_t rabID,
   	      NULL,0,
   	     "0 NAS_RAB_ESTABLI_RSP MME code %u m-TMSI %u rb id %u status %u",
   	     s_tmsi.MMEcode, s_tmsi.m_tmsi,rabID, errCode );
-  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + 0 /* TODO to be virtualized */, message_p);
+  return itti_send_msg_to_task(TASK_RRC_UE, NB_eNB_INST + user_id, message_p);
 }
diff --git a/openair3/NAS/UE/nas_itti_messaging.h b/openair3/NAS/UE/nas_itti_messaging.h
index 22c09f109b3be64977db004b3d12579f5ff2b688..f4f1f184143cc7e64f54293f04e9bb731da07b12 100644
--- a/openair3/NAS/UE/nas_itti_messaging.h
+++ b/openair3/NAS/UE/nas_itti_messaging.h
@@ -50,12 +50,12 @@ int nas_itti_protected_msg(
 
 int nas_itti_kenb_refresh_req(const Byte_t kenb[32]);
 
-int nas_itti_cell_info_req(const plmn_t plmnID, const Byte_t rat);
+int nas_itti_cell_info_req(const plmn_t plmnID, const Byte_t rat, int user_id);
 
-int nas_itti_nas_establish_req(as_cause_t cause, as_call_type_t type, as_stmsi_t s_tmsi, plmn_t plmnID, Byte_t *data_pP, uint32_t lengthP);
+int nas_itti_nas_establish_req(as_cause_t cause, as_call_type_t type, as_stmsi_t s_tmsi, plmn_t plmnID, Byte_t *data_pP, uint32_t lengthP, int user_id);
 
-int nas_itti_ul_data_req(const uint32_t ue_idP, void *const data_pP, const uint32_t lengthP);
+int nas_itti_ul_data_req(const uint32_t ue_idP, void *const data_pP, const uint32_t lengthP, int user_id);
 
-int nas_itti_rab_establish_rsp(const as_stmsi_t s_tmsi, const as_rab_id_t rabID, const nas_error_code_t errCode);
+int nas_itti_rab_establish_rsp(const as_stmsi_t s_tmsi, const as_rab_id_t rabID, const nas_error_code_t errCode, int user_id);
 # endif
 #endif /* NAS_ITTI_MESSAGING_H_ */
diff --git a/openair3/NAS/UE/nas_network.c b/openair3/NAS/UE/nas_network.c
index f3ee3d2955c5ed67dab2b9ba020cdb4873544edf..6ca4a34c7ffaa13b85d9cabe8dd4f160f84fa72c 100644
--- a/openair3/NAS/UE/nas_network.c
+++ b/openair3/NAS/UE/nas_network.c
@@ -43,6 +43,7 @@ Description NAS procedure functions triggered by the network
 
 #include "as_message.h"
 #include "nas_proc.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /****************  E X T E R N A L    D E F I N I T I O N S  ****************/
@@ -91,11 +92,11 @@ void nas_network_initialize(void)
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void nas_network_cleanup(void)
+void nas_network_cleanup(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
-  nas_proc_cleanup();
+  nas_proc_cleanup(user);
 
   LOG_FUNC_OUT;
 }
@@ -118,7 +119,7 @@ void nas_network_cleanup(void)
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_network_process_data(int msg_id, const void *data)
+int nas_network_process_data(nas_user_t *user, int msg_id, const void *data)
 {
   LOG_FUNC_IN;
 
@@ -142,7 +143,7 @@ int nas_network_process_data(int msg_id, const void *data)
     /* Received cell information confirm */
     const cell_info_cnf_t *info = &msg->msg.cell_info_cnf;
     int cell_found = (info->errCode == AS_SUCCESS);
-    rc = nas_proc_cell_info(cell_found, info->tac,
+    rc = nas_proc_cell_info(user, cell_found, info->tac,
                             info->cellID, info->rat,
                             info->rsrp, info->rsrq);
     break;
@@ -160,12 +161,12 @@ int nas_network_process_data(int msg_id, const void *data)
 
     if ( (confirm->errCode == AS_SUCCESS) ||
          (confirm->errCode == AS_TERMINATED_NAS) ) {
-      rc = nas_proc_establish_cnf(confirm->nasMsg.data,
+      rc = nas_proc_establish_cnf(user, confirm->nasMsg.data,
                                   confirm->nasMsg.length);
     } else {
       LOG_TRACE(WARNING, "NET-MAIN  - "
                 "Initial NAS message not delivered");
-      rc = nas_proc_establish_rej();
+      rc = nas_proc_establish_rej(user);
     }
 
     break;
@@ -173,7 +174,7 @@ int nas_network_process_data(int msg_id, const void *data)
 
   case AS_NAS_RELEASE_IND:
     /* Received NAS signalling connection releaase indication */
-    rc = nas_proc_release_ind(msg->msg.nas_release_ind.cause);
+    rc = nas_proc_release_ind(user, msg->msg.nas_release_ind.cause);
     break;
 
   case AS_UL_INFO_TRANSFER_CNF:
@@ -182,9 +183,9 @@ int nas_network_process_data(int msg_id, const void *data)
     if (msg->msg.ul_info_transfer_cnf.errCode != AS_SUCCESS) {
       LOG_TRACE(WARNING, "NET-MAIN  - "
                 "Uplink NAS message not delivered");
-      rc = nas_proc_ul_transfer_rej();
+      rc = nas_proc_ul_transfer_rej(user);
     } else {
-      rc = nas_proc_ul_transfer_cnf();
+      rc = nas_proc_ul_transfer_cnf(user);
     }
 
     break;
@@ -192,7 +193,7 @@ int nas_network_process_data(int msg_id, const void *data)
   case AS_DL_INFO_TRANSFER_IND: {
     const dl_info_transfer_ind_t *info = &msg->msg.dl_info_transfer_ind;
     /* Received downlink data transfer indication */
-    rc = nas_proc_dl_transfer_ind(info->nasMsg.data,
+    rc = nas_proc_dl_transfer_ind(user, info->nasMsg.data,
                                   info->nasMsg.length);
     break;
   }
diff --git a/openair3/NAS/UE/nas_network.h b/openair3/NAS/UE/nas_network.h
index 363f63c5ab16d2d4f3fe851b0b4dfe8d9c5f0206..fb05c8dd2a044b1c84a1ceb6c3891aa789960d60 100644
--- a/openair3/NAS/UE/nas_network.h
+++ b/openair3/NAS/UE/nas_network.h
@@ -40,6 +40,8 @@ Description NAS procedure functions triggered by the network
 #ifndef __NAS_NETWORK_H__
 #define __NAS_NETWORK_H__
 
+#include "user_defs.h"
+
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
 /****************************************************************************/
@@ -59,9 +61,9 @@ Description NAS procedure functions triggered by the network
 
 void nas_network_initialize(void);
 
-void nas_network_cleanup(void);
+void nas_network_cleanup(nas_user_t *user);
 
-int nas_network_process_data(int command_id, const void *data);
+int nas_network_process_data(nas_user_t *user, int command_id, const void *data);
 
 const void *nas_network_get_data(void);
 
diff --git a/openair3/NAS/UE/nas_proc.c b/openair3/NAS/UE/nas_proc.c
index 79b66269955616bb74db6659ac7bc5df75c33484..97ecea8e5a7b4ca09b8eeb9653e7797ce021d73f 100644
--- a/openair3/NAS/UE/nas_proc.c
+++ b/openair3/NAS/UE/nas_proc.c
@@ -38,6 +38,8 @@ Description NAS procedure call manager
 
 #include "nas_proc.h"
 #include "nas_log.h"
+#include "nas_user.h"
+#include "utils.h"
 
 #include "emm_main.h"
 #include "emm_sap.h"
@@ -62,20 +64,9 @@ Description NAS procedure call manager
 #define NAS_PROC_RSRQ_UNKNOWN   255
 #define NAS_PROC_RSRP_UNKNOWN   255
 
-/*
- * Local NAS data
- */
-static struct {
-  /* EPS capibility status */
-  int EPS_capability_status;
-  /* Reference signal received quality    */
-  int rsrq;
-  /* Reference signal received power      */
-  int rsrp;
-} _nas_proc_data;
-
-static int _nas_proc_activate(int cid, int apply_to_all);
-static int _nas_proc_deactivate(int cid, int apply_to_all);
+
+static int _nas_proc_activate(nas_user_t *user, int cid, int apply_to_all);
+static int _nas_proc_deactivate(nas_user_t *user, int cid, int apply_to_all);
 
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
@@ -97,21 +88,24 @@ static int _nas_proc_deactivate(int cid, int apply_to_all);
  **      Others:    _nas_proc_data                             **
  **                                                                        **
  ***************************************************************************/
-void nas_proc_initialize(emm_indication_callback_t emm_cb,
+void nas_proc_initialize(nas_user_t *user, emm_indication_callback_t emm_cb,
                          esm_indication_callback_t esm_cb, const char *imei)
 {
   LOG_FUNC_IN;
 
   /* Initialize local NAS data */
-  _nas_proc_data.EPS_capability_status = FALSE;
-  _nas_proc_data.rsrq = NAS_PROC_RSRQ_UNKNOWN;
-  _nas_proc_data.rsrp = NAS_PROC_RSRP_UNKNOWN;
+  user->proc.EPS_capability_status = FALSE;
+  user->proc.rsrq = NAS_PROC_RSRQ_UNKNOWN;
+  user->proc.rsrp = NAS_PROC_RSRP_UNKNOWN;
+
+  user->authentication_data = calloc_or_fail(sizeof(authentication_data_t));
+  user->security_data = calloc_or_fail( sizeof(security_data_t));
 
   /* Initialize the EMM procedure manager */
-  emm_main_initialize(emm_cb, imei);
+  emm_main_initialize(user, emm_cb, imei);
 
   /* Initialize the ESM procedure manager */
-  esm_main_initialize(esm_cb);
+  esm_main_initialize(user, esm_cb);
 
   LOG_FUNC_OUT;
 }
@@ -131,12 +125,12 @@ void nas_proc_initialize(emm_indication_callback_t emm_cb,
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-void nas_proc_cleanup(void)
+void nas_proc_cleanup(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   /* Detach the UE from the EPS network */
-  int rc = nas_proc_detach(TRUE);
+  int rc = nas_proc_detach(user, TRUE);
 
   if (rc != RETURNok) {
     LOG_TRACE(ERROR, "NAS-PROC  - Failed to detach from the network");
@@ -144,10 +138,10 @@ void nas_proc_cleanup(void)
 
 
   /* Perform the EPS Mobility Manager's clean up procedure */
-  emm_main_cleanup();
+  emm_main_cleanup(user);
 
   /* Perform the EPS Session Manager's clean up procedure */
-  esm_main_cleanup();
+  esm_main_cleanup(user->esm_data);
 
   LOG_FUNC_OUT;
 }
@@ -172,7 +166,7 @@ void nas_proc_cleanup(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_enable_s1_mode(void)
+int nas_proc_enable_s1_mode(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -183,9 +177,9 @@ int nas_proc_enable_s1_mode(void)
    * Notify the EMM procedure call manager that EPS capability
    * of the UE is enabled
    */
-  _nas_proc_data.EPS_capability_status = TRUE;
+  user->proc.EPS_capability_status = TRUE;
   emm_sap.primitive = EMMREG_S1_ENABLED;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -205,7 +199,7 @@ int nas_proc_enable_s1_mode(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_disable_s1_mode(void)
+int nas_proc_disable_s1_mode(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -216,9 +210,9 @@ int nas_proc_disable_s1_mode(void)
    * Notify the EMM procedure call manager that EPS capability
    * of the UE is disabled
    */
-  _nas_proc_data.EPS_capability_status = FALSE;
+  user->proc.EPS_capability_status = FALSE;
   emm_sap.primitive = EMMREG_S1_DISABLED;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -238,11 +232,11 @@ int nas_proc_disable_s1_mode(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_eps(int *stat)
+int nas_proc_get_eps(nas_user_t *user, int *stat)
 {
   LOG_FUNC_IN;
 
-  *stat = _nas_proc_data.EPS_capability_status;
+  *stat = user->proc.EPS_capability_status;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -261,11 +255,11 @@ int nas_proc_get_eps(int *stat)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_imsi(char *imsi_str)
+int nas_proc_get_imsi(emm_data_t *emm_data, char *imsi_str)
 {
   LOG_FUNC_IN;
 
-  const imsi_t *imsi = emm_main_get_imsi();
+  const imsi_t *imsi = emm_main_get_imsi(emm_data);
 
   if (imsi != NULL) {
     int offset = 0;
@@ -309,11 +303,11 @@ int nas_proc_get_imsi(char *imsi_str)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_msisdn(char *msisdn_str, int *ton_npi)
+int nas_proc_get_msisdn(nas_user_t *user, char *msisdn_str, int *ton_npi)
 {
   LOG_FUNC_IN;
 
-  const msisdn_t *msisdn = emm_main_get_msisdn();
+  const msisdn_t *msisdn = emm_main_get_msisdn(user);
 
   if (msisdn != NULL) {
     union {
@@ -358,12 +352,12 @@ int nas_proc_get_msisdn(char *msisdn_str, int *ton_npi)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_signal_quality(int *rsrq, int *rsrp)
+int nas_proc_get_signal_quality(nas_user_t *user, int *rsrq, int *rsrp)
 {
   LOG_FUNC_IN;
 
-  *rsrq = _nas_proc_data.rsrq;
-  *rsrp = _nas_proc_data.rsrp;
+  *rsrq = user->proc.rsrq;
+  *rsrp = user->proc.rsrp;
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -387,7 +381,7 @@ int nas_proc_get_signal_quality(int *rsrq, int *rsrp)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_register(int mode, int format, const network_plmn_t *oper, int AcT)
+int nas_proc_register(nas_user_t *user, int mode, int format, const network_plmn_t *oper, int AcT)
 {
   LOG_FUNC_IN;
 
@@ -396,7 +390,7 @@ int nas_proc_register(int mode, int format, const network_plmn_t *oper, int AcT)
   /*
    * Set the PLMN selection mode of operation
    */
-  int index = emm_main_set_plmn_selection_mode(mode, format, oper, AcT);
+  int index = emm_main_set_plmn_selection_mode(user, mode, format, oper, AcT);
 
   if ( !(index < 0) ) {
     /*
@@ -406,7 +400,7 @@ int nas_proc_register(int mode, int format, const network_plmn_t *oper, int AcT)
     emm_sap_t emm_sap;
     emm_sap.primitive = EMMREG_REGISTER_REQ;
     emm_sap.u.emm_reg.u.regist.index = index;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -426,7 +420,7 @@ int nas_proc_register(int mode, int format, const network_plmn_t *oper, int AcT)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_deregister(void)
+int nas_proc_deregister(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -456,22 +450,22 @@ int nas_proc_deregister(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_reg_data(int *mode, int *selected, int format,
+int nas_proc_get_reg_data(nas_user_t *user, int *mode, int *selected, int format,
                           network_plmn_t *oper, int *AcT)
 {
   LOG_FUNC_IN;
 
   /* Get the PLMN selection mode of operation */
-  *mode = emm_main_get_plmn_selection_mode();
+  *mode = emm_main_get_plmn_selection_mode(user->emm_data);
 
   /* Get the currently selected operator */
-  const char *oper_name = emm_main_get_selected_plmn(oper, format);
+  const char *oper_name = emm_main_get_selected_plmn(user->emm_plmn_list, user->emm_data, oper, format);
 
   if (oper_name != NULL) {
     /* An operator is currently selected */
     *selected = TRUE;
     /* Get the supported Radio Access Technology */
-    *AcT = emm_main_get_plmn_rat();
+    *AcT = emm_main_get_plmn_rat(user->emm_data);
   } else {
     /* No any operator is selected */
     *selected = FALSE;
@@ -495,11 +489,11 @@ int nas_proc_get_reg_data(int *mode, int *selected, int format,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_oper_list(const char **oper_list)
+int nas_proc_get_oper_list(nas_user_t *user, const char **oper_list)
 {
   LOG_FUNC_IN;
 
-  int size = emm_main_get_plmn_list(oper_list);
+  int size = emm_main_get_plmn_list(user->emm_plmn_list, user->emm_data, oper_list);
 
   LOG_FUNC_RETURN (size);
 }
@@ -520,11 +514,11 @@ int nas_proc_get_oper_list(const char **oper_list)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_reg_status(int *stat)
+int nas_proc_get_reg_status(nas_user_t *user, int *stat)
 {
   LOG_FUNC_IN;
 
-  *stat = emm_main_get_plmn_status();
+  *stat = emm_main_get_plmn_status(user->emm_data);
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -548,13 +542,13 @@ int nas_proc_get_reg_status(int *stat)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_loc_info(char *tac, char *ci, int *AcT)
+int nas_proc_get_loc_info(nas_user_t *user, char *tac, char *ci, int *AcT)
 {
   LOG_FUNC_IN;
 
-  sprintf(tac, "%.4x", emm_main_get_plmn_tac());  // two byte
-  sprintf(ci, "%.8x", emm_main_get_plmn_ci());    // four byte
-  *AcT = emm_main_get_plmn_rat();             // E-UTRAN
+  sprintf(tac, "%.4x", emm_main_get_plmn_tac(user->emm_data));  // two byte
+  sprintf(ci, "%.8x", emm_main_get_plmn_ci(user->emm_data));    // four byte
+  *AcT = emm_main_get_plmn_rat(user->emm_data);             // E-UTRAN
 
   LOG_FUNC_RETURN (RETURNok);
 }
@@ -573,18 +567,18 @@ int nas_proc_get_loc_info(char *tac, char *ci, int *AcT)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_detach(int switch_off)
+int nas_proc_detach(nas_user_t *user, int switch_off)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc = RETURNok;
 
-  if ( emm_main_is_attached() ) {
+  if ( emm_main_is_attached(user->emm_data) ) {
     /* Initiate an Detach procedure */
     emm_sap.primitive = EMMREG_DETACH_INIT;
     emm_sap.u.emm_reg.u.detach.switch_off = switch_off;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -604,18 +598,18 @@ int nas_proc_detach(int switch_off)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_attach(void)
+int nas_proc_attach(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
   emm_sap_t emm_sap;
   int rc = RETURNok;
 
-  if ( !emm_main_is_attached() ) {
+  if ( !emm_main_is_attached(user->emm_data) ) {
     /* Initiate an Attach procedure */
     emm_sap.primitive = EMMREG_ATTACH_INIT;
     emm_sap.u.emm_reg.u.attach.is_emergency = FALSE;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -636,11 +630,11 @@ int nas_proc_attach(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_attach_status(void)
+int nas_proc_get_attach_status(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
-  int is_attached = emm_main_is_attached();
+  int is_attached = emm_main_is_attached(user->emm_data);
 
   LOG_FUNC_RETURN (is_attached);
 }
@@ -659,11 +653,11 @@ int nas_proc_get_attach_status(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_pdn_range(void)
+int nas_proc_get_pdn_range(esm_data_t *esm_data)
 {
   LOG_FUNC_IN;
 
-  int max_pdn_id = esm_main_get_nb_pdns_max();
+  int max_pdn_id = esm_main_get_nb_pdns_max(esm_data);
 
   LOG_FUNC_RETURN (max_pdn_id);
 }
@@ -684,7 +678,7 @@ int nas_proc_get_pdn_range(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_pdn_status(int *cids, int *states, int n_pdn_max)
+int nas_proc_get_pdn_status(nas_user_t *user, int *cids, int *states, int n_pdn_max)
 {
   LOG_FUNC_IN;
 
@@ -692,13 +686,13 @@ int nas_proc_get_pdn_status(int *cids, int *states, int n_pdn_max)
   int n_defined_pdn = 0;
 
   /* Get the maximum number of supported PDN contexts */
-  int n_pdn = esm_main_get_nb_pdns_max();
+  int n_pdn = esm_main_get_nb_pdns_max(user->esm_data);
 
   /* For all PDN contexts */
   for (cid = 1; (cid < n_pdn+1) && (n_defined_pdn < n_pdn_max); cid++) {
     /* Get the status of this PDN */
     int state = FALSE;
-    int is_defined = esm_main_get_pdn_status(cid, &state);
+    int is_defined = esm_main_get_pdn_status(user, cid, &state);
 
     if (is_defined != FALSE) {
       /* This PDN has been defined */
@@ -728,7 +722,7 @@ int nas_proc_get_pdn_status(int *cids, int *states, int n_pdn_max)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_pdn_param(int *cids, int *types, const char **apns,
+int nas_proc_get_pdn_param(esm_data_t *esm_data, int *cids, int *types, const char **apns,
                            int n_pdn_max)
 {
   LOG_FUNC_IN;
@@ -737,13 +731,13 @@ int nas_proc_get_pdn_param(int *cids, int *types, const char **apns,
   int n_defined_pdn = 0;
 
   /* Get the maximum number of supported PDN contexts */
-  int n_pdn = esm_main_get_nb_pdns_max();
+  int n_pdn = esm_main_get_nb_pdns_max(esm_data);
 
   /* For all PDN contexts */
   for (cid = 1; (cid < n_pdn+1) && (n_defined_pdn < n_pdn_max); cid++) {
     int emergency, active;
     /* Get PDN connection parameters */
-    int rc = esm_main_get_pdn(cid, types, apns, &emergency, &active);
+    int rc = esm_main_get_pdn(esm_data, cid, types, apns, &emergency, &active);
 
     if (rc != RETURNerror) {
       /* This PDN has been defined */
@@ -780,7 +774,7 @@ int nas_proc_get_pdn_param(int *cids, int *types, const char **apns,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_get_pdn_addr(int cid, int *cids, const char **addr1,
+int nas_proc_get_pdn_addr(nas_user_t *user, int cid, int *cids, const char **addr1,
                           const char **addr2, int n_pdn_max)
 {
   LOG_FUNC_IN;
@@ -790,7 +784,7 @@ int nas_proc_get_pdn_addr(int cid, int *cids, const char **addr1,
 
   if (cid > 0) {
     /* Get addresses assigned to the specified PDN */
-    rc = esm_main_get_pdn_addr(cid, addr1, addr2);
+    rc = esm_main_get_pdn_addr(user->esm_data, cid, addr1, addr2);
 
     if (rc != RETURNerror) {
       *cids = cid;
@@ -798,12 +792,12 @@ int nas_proc_get_pdn_addr(int cid, int *cids, const char **addr1,
     }
   } else if (cid < 0) {
     /* Get the maximum number of supported PDN contexts */
-    int n_pdn = esm_main_get_nb_pdns_max();
+    int n_pdn = esm_main_get_nb_pdns_max(user->esm_data);
 
     /* For all PDN contexts */
     for (cid = 1; (cid < n_pdn+1) && (n_defined_pdn < n_pdn_max); cid++) {
       /* Get PDN connection addresses */
-      rc = esm_main_get_pdn_addr(cid, addr1, addr2);
+      rc = esm_main_get_pdn_addr(user->esm_data, cid, addr1, addr2);
 
       if (rc != RETURNerror) {
         /* This PDN has been defined */
@@ -843,7 +837,7 @@ int nas_proc_get_pdn_addr(int cid, int *cids, const char **addr1,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_set_pdn(int cid, int type, const char *apn, int ipv4_addr,
+int nas_proc_set_pdn(nas_user_t *user, int cid, int type, const char *apn, int ipv4_addr,
                      int emergency, int p_cscf, int im_cn_signal)
 {
   LOG_FUNC_IN;
@@ -862,7 +856,7 @@ int nas_proc_set_pdn(int cid, int type, const char *apn, int ipv4_addr,
    * Notify ESM that a new PDN context has to be defined for
    * the specified APN
    */
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -881,7 +875,7 @@ int nas_proc_set_pdn(int cid, int type, const char *apn, int ipv4_addr,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_reset_pdn(int cid)
+int nas_proc_reset_pdn(nas_user_t *user, int cid)
 {
   LOG_FUNC_IN;
 
@@ -895,7 +889,7 @@ int nas_proc_reset_pdn(int cid)
   /*
    * Notify ESM that the specified PDN context has to be undefined
    */
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
   LOG_FUNC_RETURN (rc);
 }
 
@@ -915,7 +909,7 @@ int nas_proc_reset_pdn(int cid)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_deactivate_pdn(int cid)
+int nas_proc_deactivate_pdn(nas_user_t *user, int cid)
 {
   LOG_FUNC_IN;
 
@@ -923,15 +917,15 @@ int nas_proc_deactivate_pdn(int cid)
 
   if (cid > 0) {
     /* Deactivate only the specified PDN context */
-    rc = _nas_proc_deactivate(cid, FALSE);
+    rc = _nas_proc_deactivate(user, cid, FALSE);
   } else {
     /* Do not deactivate the PDN connection established during initial
      * network attachment (identifier 1) */
     cid = 2;
 
     /* Deactivate all active PDN contexts */
-    while ((rc != RETURNerror) && (cid < esm_main_get_nb_pdns_max()+1)) {
-      rc = _nas_proc_deactivate(cid++, TRUE);
+    while ((rc != RETURNerror) && (cid < esm_main_get_nb_pdns_max(user->esm_data)+1)) {
+      rc = _nas_proc_deactivate(user, cid++, TRUE);
     }
   }
 
@@ -954,20 +948,20 @@ int nas_proc_deactivate_pdn(int cid)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_activate_pdn(int cid)
+int nas_proc_activate_pdn(nas_user_t *user, int cid)
 {
   LOG_FUNC_IN;
 
   int rc = RETURNok;
 
-  if ( !emm_main_is_attached() ) {
+  if ( !emm_main_is_attached(user->emm_data) ) {
     /*
      * If the UE is not attached to the network, perform EPS attach
      * procedure prior to attempt to request any PDN connectivity
      */
     LOG_TRACE(WARNING, "NAS-PROC  - UE is not attached to the network");
-    rc = nas_proc_attach();
-  } else if (emm_main_is_emergency()) {
+    rc = nas_proc_attach(user);
+  } else if (emm_main_is_emergency(user->emm_data)) {
     /* The UE is attached for emergency bearer services; It shall not
      * request a PDN connection to any other PDN */
     LOG_TRACE(WARNING,"NAS-PROC  - Attached for emergency bearer services");
@@ -977,13 +971,13 @@ int nas_proc_activate_pdn(int cid)
   if (rc != RETURNerror) {
     if (cid > 0) {
       /* Activate only the specified PDN context */
-      rc = _nas_proc_activate(cid, FALSE);
+      rc = _nas_proc_activate(user, cid, FALSE);
     } else {
       cid = 1;
 
       /* Activate all defined PDN contexts */
-      while ((rc != RETURNerror) && (cid < esm_main_get_nb_pdns_max()+1)) {
-        rc = _nas_proc_activate(cid++, TRUE);
+      while ((rc != RETURNerror) && (cid < esm_main_get_nb_pdns_max(user->esm_data)+1)) {
+        rc = _nas_proc_activate(user, cid++, TRUE);
       }
     }
   }
@@ -1021,7 +1015,7 @@ int nas_proc_activate_pdn(int cid)
  **      Others:    _nas_proc_data                             **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_cell_info(int found, tac_t tac, ci_t ci, AcT_t AcT,
+int nas_proc_cell_info(nas_user_t *user, int found, tac_t tac, ci_t ci, AcT_t AcT,
                        uint8_t rsrq, uint8_t rsrp)
 {
   LOG_FUNC_IN;
@@ -1030,8 +1024,8 @@ int nas_proc_cell_info(int found, tac_t tac, ci_t ci, AcT_t AcT,
   int rc;
 
   /* Store LTE signal strength/quality measurement data */
-  _nas_proc_data.rsrq = rsrq;
-  _nas_proc_data.rsrp = rsrp;
+  user->proc.rsrq = rsrq;
+  user->proc.rsrp = rsrp;
 
   /*
    * Notify the EMM procedure call manager that cell information
@@ -1044,7 +1038,7 @@ int nas_proc_cell_info(int found, tac_t tac, ci_t ci, AcT_t AcT,
   emm_sap.u.emm_as.u.cell_info.tac = tac;
   emm_sap.u.emm_as.u.cell_info.cellID = ci;
 
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1066,7 +1060,7 @@ int nas_proc_cell_info(int found, tac_t tac, ci_t ci, AcT_t AcT,
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_establish_cnf(const Byte_t *data, uint32_t len)
+int nas_proc_establish_cnf(nas_user_t *user, const Byte_t *data, uint32_t len)
 {
   LOG_FUNC_IN;
 
@@ -1081,7 +1075,7 @@ int nas_proc_establish_cnf(const Byte_t *data, uint32_t len)
   emm_sap.primitive = EMMAS_ESTABLISH_CNF;
   emm_sap.u.emm_as.u.establish.NASmsg.length = len;
   emm_sap.u.emm_as.u.establish.NASmsg.value = (uint8_t *)data;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1103,7 +1097,7 @@ int nas_proc_establish_cnf(const Byte_t *data, uint32_t len)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_establish_rej(void)
+int nas_proc_establish_rej(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -1116,7 +1110,7 @@ int nas_proc_establish_rej(void)
    * from lower layers
    */
   emm_sap.primitive = EMMAS_ESTABLISH_REJ;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1136,7 +1130,7 @@ int nas_proc_establish_rej(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_release_ind(int cause)
+int nas_proc_release_ind(nas_user_t *user, int cause)
 {
   LOG_FUNC_IN;
 
@@ -1149,7 +1143,7 @@ int nas_proc_release_ind(int cause)
    */
   emm_sap.primitive = EMMAS_RELEASE_IND;
   emm_sap.u.emm_as.u.release.cause = cause;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1170,7 +1164,7 @@ int nas_proc_release_ind(int cause)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_ul_transfer_cnf(void)
+int nas_proc_ul_transfer_cnf(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -1183,10 +1177,10 @@ int nas_proc_ul_transfer_cnf(void)
    * receiver side
    */
   emm_sap.primitive = EMMAS_DATA_IND;
-  emm_sap.u.emm_as.u.data.ueid = 0;
+  emm_sap.u.emm_as.u.data.ueid = user->ueid;
   emm_sap.u.emm_as.u.data.delivered = TRUE;
   emm_sap.u.emm_as.u.data.NASmsg.length = 0;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1207,7 +1201,7 @@ int nas_proc_ul_transfer_cnf(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_ul_transfer_rej(void)
+int nas_proc_ul_transfer_rej(nas_user_t *user)
 {
   LOG_FUNC_IN;
 
@@ -1220,10 +1214,10 @@ int nas_proc_ul_transfer_rej(void)
    * from lower layers
    */
   emm_sap.primitive = EMMAS_DATA_IND;
-  emm_sap.u.emm_as.u.data.ueid = 0;
+  emm_sap.u.emm_as.u.data.ueid = user->ueid;
   emm_sap.u.emm_as.u.data.delivered = FALSE;
   emm_sap.u.emm_as.u.data.NASmsg.length = 0;
-  rc = emm_sap_send(&emm_sap);
+  rc = emm_sap_send(user, &emm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1244,7 +1238,7 @@ int nas_proc_ul_transfer_rej(void)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-int nas_proc_dl_transfer_ind(const Byte_t *data, uint32_t len)
+int nas_proc_dl_transfer_ind(nas_user_t *user, const Byte_t *data, uint32_t len)
 {
   LOG_FUNC_IN;
 
@@ -1257,11 +1251,11 @@ int nas_proc_dl_transfer_ind(const Byte_t *data, uint32_t len)
      * indication has been received from the Access-Stratum sublayer
      */
     emm_sap.primitive = EMMAS_DATA_IND;
-    emm_sap.u.emm_as.u.data.ueid = 0;
+    emm_sap.u.emm_as.u.data.ueid = user->ueid;
     emm_sap.u.emm_as.u.data.delivered = TRUE;
     emm_sap.u.emm_as.u.data.NASmsg.length = len;
     emm_sap.u.emm_as.u.data.NASmsg.value = (uint8_t *)data;
-    rc = emm_sap_send(&emm_sap);
+    rc = emm_sap_send(user, &emm_sap);
   }
 
   LOG_FUNC_RETURN (rc);
@@ -1291,7 +1285,7 @@ int nas_proc_dl_transfer_ind(const Byte_t *data, uint32_t len)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _nas_proc_activate(int cid, int apply_to_all)
+static int _nas_proc_activate(nas_user_t *user, int cid, int apply_to_all)
 {
   LOG_FUNC_IN;
 
@@ -1301,7 +1295,7 @@ static int _nas_proc_activate(int cid, int apply_to_all)
   esm_sap_t esm_sap;
 
   /* Get PDN context parameters */
-  rc = esm_main_get_pdn(cid, &esm_sap.data.pdn_connect.pdn_type,
+  rc = esm_main_get_pdn(user->esm_data, cid, &esm_sap.data.pdn_connect.pdn_type,
                         &esm_sap.data.pdn_connect.apn,
                         &esm_sap.data.pdn_connect.is_emergency, &active);
 
@@ -1323,7 +1317,7 @@ static int _nas_proc_activate(int cid, int apply_to_all)
   }
 
   if (esm_sap.data.pdn_connect.is_emergency) {
-    if (esm_main_has_emergency()) {
+    if (esm_main_has_emergency(user->esm_data)) {
       /* There is already a PDN connection for emergency
        * bearer services established; the UE shall not
        * request an additional PDN connection for emer-
@@ -1342,7 +1336,7 @@ static int _nas_proc_activate(int cid, int apply_to_all)
   esm_sap.is_standalone = TRUE;
   esm_sap.data.pdn_connect.is_defined = TRUE;
   esm_sap.data.pdn_connect.cid = cid;
-  rc = esm_sap_send(&esm_sap);
+  rc = esm_sap_send(user, &esm_sap);
 
   LOG_FUNC_RETURN (rc);
 }
@@ -1364,7 +1358,7 @@ static int _nas_proc_activate(int cid, int apply_to_all)
  **      Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-static int _nas_proc_deactivate(int cid, int apply_to_all)
+static int _nas_proc_deactivate(nas_user_t *user, int cid, int apply_to_all)
 {
   LOG_FUNC_IN;
 
@@ -1375,7 +1369,7 @@ static int _nas_proc_deactivate(int cid, int apply_to_all)
   int active = FALSE;
 
   /* Get PDN context parameters */
-  rc = esm_main_get_pdn(cid, &pdn_type, &apn, &emergency, &active);
+  rc = esm_main_get_pdn(user->esm_data, cid, &pdn_type, &apn, &emergency, &active);
 
   if (rc != RETURNok) {
     /* No any context is defined for the specified PDN */
@@ -1393,7 +1387,7 @@ static int _nas_proc_deactivate(int cid, int apply_to_all)
     LOG_FUNC_RETURN (RETURNok);
   }
 
-  if (esm_main_get_nb_pdns() > 1) {
+  if (esm_main_get_nb_pdns(user->esm_data) > 1) {
     /*
      * Notify ESM that all EPS bearers towards the specified PDN
      * has to be released
@@ -1401,7 +1395,7 @@ static int _nas_proc_deactivate(int cid, int apply_to_all)
     esm_sap_t esm_sap;
     esm_sap.primitive = ESM_PDN_DISCONNECT_REQ;
     esm_sap.data.pdn_disconnect.cid = cid;
-    rc = esm_sap_send(&esm_sap);
+    rc = esm_sap_send(user, &esm_sap);
   } else {
     /* For EPS, if an attempt is made to disconnect the last PDN
      * connection, then the MT responds with an error */
diff --git a/openair3/NAS/UE/nas_proc.h b/openair3/NAS/UE/nas_proc.h
index 4514983df88d28f981c435469475fe83f9119028..bf8dec9e58a19e81986a3d0eb4f598ea9ae5ea47 100644
--- a/openair3/NAS/UE/nas_proc.h
+++ b/openair3/NAS/UE/nas_proc.h
@@ -40,6 +40,10 @@ Description NAS procedure call manager
 
 #include "commonDef.h"
 #include "networkDef.h"
+#include "user_defs.h"
+#include "emm_main.h"
+#include "esm_ebr.h"
+#include "esmData.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -57,10 +61,10 @@ Description NAS procedure call manager
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void nas_proc_initialize(emm_indication_callback_t emm_cb,
+void nas_proc_initialize(nas_user_t *user, emm_indication_callback_t emm_cb,
                          esm_indication_callback_t esm_cb, const char *imei);
 
-void nas_proc_cleanup(void);
+void nas_proc_cleanup(nas_user_t *user);
 
 /*
  * --------------------------------------------------------------------------
@@ -68,39 +72,39 @@ void nas_proc_cleanup(void);
  * --------------------------------------------------------------------------
  */
 
-int nas_proc_enable_s1_mode(void);
-int nas_proc_disable_s1_mode(void);
-int nas_proc_get_eps(int *stat);
+int nas_proc_enable_s1_mode(nas_user_t *user);
+int nas_proc_disable_s1_mode(nas_user_t *user);
+int nas_proc_get_eps(nas_user_t *user, int *stat);
 
-int nas_proc_get_imsi(char *imsi_str);
-int nas_proc_get_msisdn(char *msisdn_str, int *ton_npi);
+int nas_proc_get_imsi(emm_data_t *emm_data, char *imsi_str);
+int nas_proc_get_msisdn(nas_user_t *user, char *msisdn_str, int *ton_npi);
 
-int nas_proc_get_signal_quality(int *rsrq, int *rsrp);
+int nas_proc_get_signal_quality(nas_user_t *user, int *rsrq, int *rsrp);
 
-int nas_proc_register(int mode, int format, const network_plmn_t *oper, int AcT);
-int nas_proc_deregister(void);
-int nas_proc_get_reg_data(int *mode, int *selected, int format,
+int nas_proc_register(nas_user_t *user, int mode, int format, const network_plmn_t *oper, int AcT);
+int nas_proc_deregister(nas_user_t *user);
+int nas_proc_get_reg_data(nas_user_t *user, int *mode, int *selected, int format,
                           network_plmn_t *oper, int *AcT);
-int nas_proc_get_oper_list(const char **oper_list);
+int nas_proc_get_oper_list(nas_user_t *user, const char **oper_list);
 
-int nas_proc_get_reg_status(int *stat);
-int nas_proc_get_loc_info(char *tac, char *ci, int *AcT);
+int nas_proc_get_reg_status(nas_user_t *user, int *stat);
+int nas_proc_get_loc_info(nas_user_t *user, char *tac, char *ci, int *AcT);
 
-int nas_proc_detach(int switch_off);
-int nas_proc_attach(void);
-int nas_proc_get_attach_status(void);
+int nas_proc_detach(nas_user_t *user, int switch_off);
+int nas_proc_attach(nas_user_t *user);
+int nas_proc_get_attach_status(nas_user_t *user);
 
-int nas_proc_reset_pdn(int cid);
-int nas_proc_set_pdn(int cid, int type, const char *apn, int ipv4_addr,
+int nas_proc_reset_pdn(nas_user_t *user, int cid);
+int nas_proc_set_pdn(nas_user_t *user, int cid, int type, const char *apn, int ipv4_addr,
                      int emergency, int p_cscf, int im_cn_signal);
-int nas_proc_get_pdn_range(void);
-int nas_proc_get_pdn_status(int *cids, int *states, int n_pdn_max);
-int nas_proc_get_pdn_param(int *cids, int *types, const char **apns,
+int nas_proc_get_pdn_range(esm_data_t *esm_data);
+int nas_proc_get_pdn_status(nas_user_t *user, int *cids, int *states, int n_pdn_max);
+int nas_proc_get_pdn_param(esm_data_t *esm_data, int *cids, int *types, const char **apns,
                            int n_pdn_max);
-int nas_proc_get_pdn_addr(int cid, int *cids, const char **addr1,
+int nas_proc_get_pdn_addr(nas_user_t *user, int cid, int *cids, const char **addr1,
                           const char **addr2, int n_addr_max);
-int nas_proc_deactivate_pdn(int cid);
-int nas_proc_activate_pdn(int cid);
+int nas_proc_deactivate_pdn(nas_user_t *user, int cid);
+int nas_proc_activate_pdn(nas_user_t *user, int cid);
 
 /*
  * --------------------------------------------------------------------------
@@ -108,17 +112,17 @@ int nas_proc_activate_pdn(int cid);
  * --------------------------------------------------------------------------
  */
 
-int nas_proc_cell_info(int found, tac_t tac, ci_t ci, AcT_t rat, uint8_t rsrp,
+int nas_proc_cell_info(nas_user_t *user, int found, tac_t tac, ci_t ci, AcT_t rat, uint8_t rsrp,
                        uint8_t rsrq);
 
-int nas_proc_establish_cnf(const Byte_t *data, uint32_t len);
-int nas_proc_establish_rej(void);
+int nas_proc_establish_cnf(nas_user_t *user, const Byte_t *data, uint32_t len);
+int nas_proc_establish_rej(nas_user_t *user);
 
-int nas_proc_release_ind(int cause);
+int nas_proc_release_ind(nas_user_t *user, int cause);
 
-int nas_proc_ul_transfer_cnf(void);
-int nas_proc_ul_transfer_rej(void);
-int nas_proc_dl_transfer_ind(const Byte_t *data, uint32_t len);
+int nas_proc_ul_transfer_cnf(nas_user_t *user);
+int nas_proc_ul_transfer_rej(nas_user_t *user);
+int nas_proc_dl_transfer_ind(nas_user_t *user, const Byte_t *data, uint32_t len);
 
 
 
diff --git a/openair3/NAS/UE/nas_proc_defs.h b/openair3/NAS/UE/nas_proc_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..1ce2bfd929df7734eafd738df34787c388b263f6
--- /dev/null
+++ b/openair3/NAS/UE/nas_proc_defs.h
@@ -0,0 +1,43 @@
+#ifndef _NAS_PROC_DEFS_H
+#define _NAS_PROC_DEFS_H
+
+/*
+ * Local NAS data
+ */
+typedef struct {
+  /* EPS capibility status */
+  int EPS_capability_status;
+  /* Reference signal received quality    */
+  int rsrq;
+  /* Reference signal received power      */
+  int rsrp;
+} proc_data_t;
+
+/*
+ * MT SIM pending status (see ETSI TS 127 007 V10.6.0, Note 2)
+ * Commands which interact with MT that are accepted when MT is pending SIM PIN,
+ * SIM PUK, or PH-SIM are: +CGMI, +CGMM, +CGMR, +CGSN, D112; (emergency call),
+ * +CPAS, +CFUN, +CPIN, +CPINR, +CDIS (read and test command only), and +CIND
+ * (read and test command only).
+*/
+typedef enum {
+  NAS_USER_READY, /* MT is not pending for any password   */
+  NAS_USER_SIM_PIN,   /* MT is waiting SIM PIN to be given    */
+  NAS_USER_SIM_PUK,   /* MT is waiting SIM PUK to be given    */
+  NAS_USER_PH_SIM_PIN /* MT is waiting phone-to-SIM card
+             * password to be given         */
+} nas_user_sim_status;
+
+/*
+ * The local UE context
+ */
+typedef struct {
+  /* Firmware version number          */
+  const char *version;
+  /* SIM pending status           */
+  nas_user_sim_status sim_status;
+  /* Level of functionality           */
+  int fun;
+} nas_user_context_t;
+
+#endif
diff --git a/openair3/NAS/UE/nas_ue_task.c b/openair3/NAS/UE/nas_ue_task.c
index 9d097fc3897030e8491271e7cf89e472c4c95e2f..2a42b38a7248a3bfe24eea3bae51b9d958b76a4d 100644
--- a/openair3/NAS/UE/nas_ue_task.c
+++ b/openair3/NAS/UE/nas_ue_task.c
@@ -19,26 +19,32 @@
  *      contact@openairinterface.org
  */
 
+#include "utils.h"
 #if defined(ENABLE_ITTI)
 # include "assertions.h"
 # include "intertask_interface.h"
 # include "nas_ue_task.h"
 # include "UTIL/LOG/log.h"
 
-# include "nas_user.h"
+# include "user_defs.h"
 # include "user_api.h"
 # include "nas_parser.h"
 # include "nas_proc.h"
 # include "msc.h"
+# include "memory.h"
 
+#include "nas_user.h"
+
+// FIXME make command line option for NAS_UE_AUTOSTART
 # define NAS_UE_AUTOSTART 1
 
+// FIXME review these externs
 extern unsigned char NB_eNB_INST;
 extern unsigned char NB_UE_INST;
 
-static int user_fd;
+char *make_port_str_from_ueid(const char *base_port_str, int ueid);
 
-static int nas_ue_process_events(struct epoll_event *events, int nb_events)
+static int nas_ue_process_events(nas_user_container_t *users, struct epoll_event *events, int nb_events)
 {
   int event;
   int exit_loop = FALSE;
@@ -48,8 +54,9 @@ static int nas_ue_process_events(struct epoll_event *events, int nb_events)
   for (event = 0; event < nb_events; event++) {
     if (events[event].events != 0) {
       /* If the event has not been yet been processed (not an itti message) */
-      if (events[event].data.fd == user_fd) {
-        exit_loop = nas_user_receive_and_process(&user_fd, NULL);
+      nas_user_t *user = find_user_from_fd(users, events[event].data.fd);
+      if ( user != NULL ) {
+        exit_loop = nas_user_receive_and_process(user, NULL);
       } else {
         LOG_E(NAS, "[UE] Received an event from an unknown fd %d!\n", events[event].data.fd);
       }
@@ -59,6 +66,24 @@ static int nas_ue_process_events(struct epoll_event *events, int nb_events)
   return (exit_loop);
 }
 
+// Initialize user api id and port number
+void nas_user_api_id_initialize(nas_user_t *user) {
+  user_api_id_t *user_api_id = calloc_or_fail(sizeof(user_api_id_t));
+  user->user_api_id = user_api_id;
+  char *port = make_port_str_from_ueid(NAS_PARSER_DEFAULT_USER_PORT_NUMBER, user->ueid);
+  if ( port == NULL ) {
+      LOG_E(NAS, "[UE %d] can't get port from ueid %d !", user->ueid);
+      exit (EXIT_FAILURE);
+  }
+  if (user_api_initialize (user_api_id, NAS_PARSER_DEFAULT_USER_HOSTNAME, port, NULL,
+              NULL) != RETURNok) {
+      LOG_E(NAS, "[UE %d] user interface initialization failed!", user->ueid);
+      exit (EXIT_FAILURE);
+  }
+  free(port);
+  itti_subscribe_event_fd (TASK_NAS_UE, user_api_get_fd(user_api_id));
+}
+
 void *nas_ue_task(void *args_p)
 {
   int                   nb_events;
@@ -68,25 +93,45 @@ void *nas_ue_task(void *args_p)
   instance_t            instance;
   unsigned int          Mod_id;
   int                   result;
+  nas_user_container_t *users=args_p;
 
   itti_mark_task_ready (TASK_NAS_UE);
   MSC_START_USE();
   /* Initialize UE NAS (EURECOM-NAS) */
+  for (int i=0; i < users->count; i++)
   {
-    /* Initialize user interface (to exchange AT commands with user process) */
-    {
-      if (user_api_initialize (NAS_PARSER_DEFAULT_USER_HOSTNAME, NAS_PARSER_DEFAULT_USER_PORT_NUMBER, NULL,
-                               NULL) != RETURNok) {
-        LOG_E(NAS, "[UE] user interface initialization failed!");
-        exit (EXIT_FAILURE);
-      }
+    nas_user_t *user = &users->item[i];
+    user->ueid=i;
+
+    /* Get USIM data application filename */
+    user->usim_data_store = memory_get_path_from_ueid(USIM_API_NVRAM_DIRNAME, USIM_API_NVRAM_FILENAME, user->ueid);
+    if ( user->usim_data_store == NULL ) {
+      LOG_E(NAS, "[UE %d] - Failed to get USIM data application filename", user->ueid, user->ueid);
+      exit(EXIT_FAILURE);
+    }
+
+    /* Get UE's data pathname */
+    user->user_nvdata_store = memory_get_path_from_ueid(USER_NVRAM_DIRNAME, USER_NVRAM_FILENAME, user->ueid);
+    if ( user->user_nvdata_store == NULL ) {
+      LOG_E(NAS, "[UE %d] - Failed to get USIM nvdata filename", user->ueid);
+      exit(EXIT_FAILURE);
+    }
 
-      user_fd = user_api_get_fd ();
-      itti_subscribe_event_fd (TASK_NAS_UE, user_fd);
+    /* Get EMM data pathname */
+    user->emm_nvdata_store = memory_get_path_from_ueid(EMM_NVRAM_DIRNAME, EMM_NVRAM_FILENAME, user->ueid);
+    if ( user->emm_nvdata_store == NULL ) {
+      LOG_E(NAS, "[UE %d] - Failed to get EMM nvdata filename", user->ueid);
+      exit(EXIT_FAILURE);
     }
 
+    /* Initialize user interface (to exchange AT commands with user process) */
+    nas_user_api_id_initialize(user);
+    /* allocate needed structures */
+    user->user_at_commands = calloc_or_fail(sizeof(user_at_commands_t));
+    user->at_response = calloc_or_fail(sizeof(at_response_t));
+    user->lowerlayer_data = calloc_or_fail(sizeof(lowerlayer_data_t));
     /* Initialize NAS user */
-    nas_user_initialize (&user_api_emm_callback, &user_api_esm_callback, FIRMWARE_VERSION);
+    nas_user_initialize (user, &user_api_emm_callback, &user_api_esm_callback, FIRMWARE_VERSION);
   }
 
   /* Set UE activation state */
@@ -105,6 +150,12 @@ void *nas_ue_task(void *args_p)
       msg_name = ITTI_MSG_NAME (msg_p);
       instance = ITTI_MSG_INSTANCE (msg_p);
       Mod_id = instance - NB_eNB_INST;
+      if (instance == INSTANCE_DEFAULT) {
+        printf("%s:%d: FATAL: instance is INSTANCE_DEFAULT, should not happen.\n",
+               __FILE__, __LINE__);
+        abort();
+      }
+      nas_user_t *user = &users->item[Mod_id];
 
       switch (ITTI_MSG_ID(msg_p)) {
       case INITIALIZE_MESSAGE:
@@ -114,7 +165,7 @@ void *nas_ue_task(void *args_p)
           /* Send an activate modem command to NAS like UserProcess should do it */
           char *user_data = "at+cfun=1\r";
 
-          nas_user_receive_and_process (&user_fd, user_data);
+          nas_user_receive_and_process (user, user_data);
         }
 #endif
         break;
@@ -134,7 +185,7 @@ void *nas_ue_task(void *args_p)
         {
           int cell_found = (NAS_CELL_SELECTION_CNF (msg_p).errCode == AS_SUCCESS);
 
-          nas_proc_cell_info (cell_found, NAS_CELL_SELECTION_CNF (msg_p).tac,
+          nas_proc_cell_info (user, cell_found, NAS_CELL_SELECTION_CNF (msg_p).tac,
                               NAS_CELL_SELECTION_CNF (msg_p).cellID, NAS_CELL_SELECTION_CNF (msg_p).rat,
                               NAS_CELL_SELECTION_CNF (msg_p).rsrq, NAS_CELL_SELECTION_CNF (msg_p).rsrp);
         }
@@ -160,7 +211,7 @@ void *nas_ue_task(void *args_p)
 
         if ((NAS_CONN_ESTABLI_CNF (msg_p).errCode == AS_SUCCESS)
             || (NAS_CONN_ESTABLI_CNF (msg_p).errCode == AS_TERMINATED_NAS)) {
-          nas_proc_establish_cnf(NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.data, NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.length);
+          nas_proc_establish_cnf(user, NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.data, NAS_CONN_ESTABLI_CNF (msg_p).nasMsg.length);
 
           /* TODO checks if NAS will free the nas message, better to do it there anyway! */
           // result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), NAS_CONN_ESTABLI_CNF(msg_p).nasMsg.data);
@@ -173,7 +224,7 @@ void *nas_ue_task(void *args_p)
         LOG_I(NAS, "[UE %d] Received %s: cause %u\n", Mod_id, msg_name,
               NAS_CONN_RELEASE_IND (msg_p).cause);
 
-        nas_proc_release_ind (NAS_CONN_RELEASE_IND (msg_p).cause);
+        nas_proc_release_ind (user, NAS_CONN_RELEASE_IND (msg_p).cause);
         break;
 
       case NAS_UPLINK_DATA_CNF:
@@ -181,9 +232,9 @@ void *nas_ue_task(void *args_p)
               NAS_UPLINK_DATA_CNF (msg_p).UEid, NAS_UPLINK_DATA_CNF (msg_p).errCode);
 
         if (NAS_UPLINK_DATA_CNF (msg_p).errCode == AS_SUCCESS) {
-          nas_proc_ul_transfer_cnf ();
+          nas_proc_ul_transfer_cnf (user);
         } else {
-          nas_proc_ul_transfer_rej ();
+          nas_proc_ul_transfer_rej (user);
         }
 
         break;
@@ -192,7 +243,7 @@ void *nas_ue_task(void *args_p)
         LOG_I(NAS, "[UE %d] Received %s: UEid %u, length %u\n", Mod_id, msg_name,
               NAS_DOWNLINK_DATA_IND (msg_p).UEid, NAS_DOWNLINK_DATA_IND (msg_p).nasMsg.length);
 
-        nas_proc_dl_transfer_ind (NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length);
+        nas_proc_dl_transfer_ind (user, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.data, NAS_DOWNLINK_DATA_IND(msg_p).nasMsg.length);
 
         if (0) {
           /* TODO checks if NAS will free the nas message, better to do it there anyway! */
@@ -215,10 +266,41 @@ void *nas_ue_task(void *args_p)
     nb_events = itti_get_events(TASK_NAS_UE, &events);
 
     if ((nb_events > 0) && (events != NULL)) {
-      if (nas_ue_process_events(events, nb_events) == TRUE) {
+      if (nas_ue_process_events(users, events, nb_events) == TRUE) {
         LOG_E(NAS, "[UE] Received exit loop\n");
       }
     }
   }
+
+  free(users);
+  return NULL;
+}
+
+nas_user_t *find_user_from_fd(nas_user_container_t *users, int fd) {
+  for (int i=0; i<users->count; i++) {
+    nas_user_t *user = &users->item[i];
+    if (fd == user_api_get_fd(user->user_api_id)) {
+      return user;
+    }
+  }
+  return NULL;
+}
+
+char *make_port_str_from_ueid(const char *base_port_str, int ueid) {
+  int port;
+  int base_port;
+  char *endptr = NULL;
+
+  base_port = strtol(base_port_str, &endptr, 10);
+  if ( base_port_str == endptr ) {
+    return NULL;
+  }
+
+  port = base_port + ueid;
+  if ( port<1 || port > 65535 ) {
+    return NULL;
+  }
+
+  return itoa(port);
 }
 #endif
diff --git a/openair2/NAS/nas_ue_task.h b/openair3/NAS/UE/nas_ue_task.h
similarity index 80%
rename from openair2/NAS/nas_ue_task.h
rename to openair3/NAS/UE/nas_ue_task.h
index bc580726810cdf702db9f50435bb4dc09085b736..90ef26bd80ffbab5fdd6eac4d00938a8e4fcf706 100644
--- a/openair2/NAS/nas_ue_task.h
+++ b/openair3/NAS/UE/nas_ue_task.h
@@ -22,6 +22,17 @@
 #ifndef NAS_UE_TASK_H_
 #define NAS_UE_TASK_H_
 
+#include "openairinterface5g_limits.h"
+#include "user_defs.h"
+
+// XXX simple array container for multiple users
+typedef struct {
+    size_t count;
+    nas_user_t item[NUMBER_OF_UE_MAX];
+} nas_user_container_t;
+
+nas_user_t *find_user_from_fd(nas_user_container_t *users, int fd);
+
 # if defined(ENABLE_ITTI)
 void *nas_ue_task(void *args_p);
 # endif
diff --git a/openair3/NAS/UE/nas_user.c b/openair3/NAS/UE/nas_user.c
index 7616a48c0528045c02730c3f0560e532cd370a4e..3108858d06f539c4661c0fe27975dec90ff8e3fe 100644
--- a/openair3/NAS/UE/nas_user.c
+++ b/openair3/NAS/UE/nas_user.c
@@ -37,8 +37,7 @@ Description NAS procedure functions triggered by the user
 *****************************************************************************/
 
 
-#include "nas_user.h"
-#include "userDef.h"
+#include "user_defs.h"
 #include "nas_log.h"
 #include "memory.h"
 
@@ -47,6 +46,8 @@ Description NAS procedure functions triggered by the user
 #include "at_error.h"
 #include "user_indication.h"
 #include "nas_proc.h"
+#include "nas_user.h"
+#include "utils.h"
 #include "user_api.h"
 
 #include <string.h> // memset, strncpy, strncmp
@@ -65,30 +66,30 @@ Description NAS procedure functions triggered by the user
  *  Functions executed upon receiving AT command from the user
  * ---------------------------------------------------------------------
  */
-static int _nas_user_proc_cgsn    (const at_command_t *data);
-static int _nas_user_proc_cgmi    (const at_command_t *data);
-static int _nas_user_proc_cgmm    (const at_command_t *data);
-static int _nas_user_proc_cgmr    (const at_command_t *data);
-static int _nas_user_proc_cimi    (const at_command_t *data);
-static int _nas_user_proc_cfun    (const at_command_t *data);
-static int _nas_user_proc_cpin    (const at_command_t *data);
-static int _nas_user_proc_csq     (const at_command_t *data);
-static int _nas_user_proc_cesq    (const at_command_t *data);
-static int _nas_user_proc_cops    (const at_command_t *data);
-static int _nas_user_proc_cgatt   (const at_command_t *data);
-static int _nas_user_proc_creg    (const at_command_t *data);
-static int _nas_user_proc_cgreg   (const at_command_t *data);
-static int _nas_user_proc_cereg   (const at_command_t *data);
-static int _nas_user_proc_cgdcont (const at_command_t *data);
-static int _nas_user_proc_cgact   (const at_command_t *data);
-static int _nas_user_proc_cmee    (const at_command_t *data);
-static int _nas_user_proc_clck    (const at_command_t *data);
-static int _nas_user_proc_cgpaddr (const at_command_t *data);
-static int _nas_user_proc_cnum    (const at_command_t *data);
-static int _nas_user_proc_clac    (const at_command_t *data);
+static int _nas_user_proc_cgsn    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgmi    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgmm    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgmr    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cimi    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cfun    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cpin    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_csq     (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cesq    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cops    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgatt   (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_creg    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgreg   (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cereg   (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgdcont (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgact   (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cmee    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_clck    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cgpaddr (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_cnum    (nas_user_t *user, const at_command_t *data);
+static int _nas_user_proc_clac    (nas_user_t *user, const at_command_t *data);
 
 /* NAS procedures applicable to AT commands */
-typedef int (*_nas_user_procedure_t) (const at_command_t *);
+typedef int (*_nas_user_procedure_t) (nas_user_t *, const at_command_t *);
 
 static _nas_user_procedure_t _nas_user_procedure[AT_COMMAND_ID_MAX] = {
   NULL,
@@ -115,31 +116,12 @@ static _nas_user_procedure_t _nas_user_procedure[AT_COMMAND_ID_MAX] = {
   _nas_user_proc_cgpaddr, /* CGPADDR */
 };
 
-/*
- * Internal representation of data structure returned to the user
- * as the result of NAS procedure function call
- */
-static at_response_t _nas_user_data = {};
-
 /*
  * ---------------------------------------------------------------------
  *              Local UE context
  * ---------------------------------------------------------------------
  */
-/*
- * MT SIM pending status (see ETSI TS 127 007 V10.6.0, Note 2)
- * Commands which interact with MT that are accepted when MT is pending SIM PIN,
- * SIM PUK, or PH-SIM are: +CGMI, +CGMM, +CGMR, +CGSN, D112; (emergency call),
- * +CPAS, +CFUN, +CPIN, +CPINR, +CDIS (read and test command only), and +CIND
- * (read and test command only).
-*/
-typedef enum {
-  NAS_USER_READY, /* MT is not pending for any password   */
-  NAS_USER_SIM_PIN,   /* MT is waiting SIM PIN to be given    */
-  NAS_USER_SIM_PUK,   /* MT is waiting SIM PUK to be given    */
-  NAS_USER_PH_SIM_PIN /* MT is waiting phone-to-SIM card
-             * password to be given         */
-} nas_user_sim_status;
+
 static const char *_nas_user_sim_status_str[] = {
   "READY",
   "SIM PIN",
@@ -147,29 +129,16 @@ static const char *_nas_user_sim_status_str[] = {
   "PH-SIM PIN"
 };
 
-/*
- * The local UE context
- */
-static struct {
-  /* Firmware version number          */
-  const char *version;
-  /* SIM pending status           */
-  nas_user_sim_status sim_status;
-  /* Level of functionality           */
-  int fun;
-} _nas_user_context;
-
-/*
- * ---------------------------------------------------------------------
- *  UE parameters stored in the UE's non-volatile memory device
- * ---------------------------------------------------------------------
- */
-static user_nvdata_t _nas_user_nvdata;
-
 /****************************************************************************/
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
+void _nas_user_context_initialize(nas_user_context_t *nas_user_context, const char *version) {
+  nas_user_context->version = version;
+  nas_user_context->sim_status = NAS_USER_SIM_PIN;
+  nas_user_context->fun = AT_CFUN_FUN_DEFAULT;
+}
+
 /****************************************************************************
  **                                                                        **
  ** Name:    nas_user_initialize()                                     **
@@ -183,38 +152,26 @@ static user_nvdata_t _nas_user_nvdata;
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    None                                       **
- **          Others:    _nas_user_nvdata, _nas_user_context        **
  **                                                                        **
  ***************************************************************************/
-void nas_user_initialize(emm_indication_callback_t emm_cb,
+void nas_user_initialize(nas_user_t *user, emm_indication_callback_t emm_cb,
                          esm_indication_callback_t esm_cb, const char *version)
 {
   LOG_FUNC_IN;
 
-  /* Get UE's data pathname */
-  char *path = memory_get_path(USER_NVRAM_DIRNAME, USER_NVRAM_FILENAME);
-
-  if (path == NULL) {
-    LOG_TRACE(ERROR, "USR-MAIN  - Failed to get UE's data pathname");
-  }
+  user->nas_user_nvdata = calloc_or_fail(sizeof(user_nvdata_t));
 
   /* Get UE data stored in the non-volatile memory device */
-  else {
-    int rc = memory_read(path, &_nas_user_nvdata, sizeof(user_nvdata_t));
-
-    if (rc != RETURNok) {
-      LOG_TRACE(ERROR, "USR-MAIN  - Failed to read %s", path);
-    }
-
-    free(path);
+  int rc = memory_read(user->user_nvdata_store, user->nas_user_nvdata, sizeof(user_nvdata_t));
+  if (rc != RETURNok) {
+    LOG_TRACE(ERROR, "USR-MAIN  - Failed to read %s", user->nas_user_nvdata);
   }
 
-  _nas_user_context.version = version;
-  _nas_user_context.sim_status = NAS_USER_SIM_PIN;
-  _nas_user_context.fun = AT_CFUN_FUN_DEFAULT;
+  user->nas_user_context = calloc_or_fail(sizeof(nas_user_context_t));
+  _nas_user_context_initialize(user->nas_user_context, version);
 
   /* Initialize the internal NAS processing data */
-  nas_proc_initialize(emm_cb, esm_cb, _nas_user_nvdata.IMEI);
+  nas_proc_initialize(user, emm_cb, esm_cb, user->nas_user_nvdata->IMEI);
 
   LOG_FUNC_OUT;
 }
@@ -232,7 +189,7 @@ void nas_user_initialize(emm_indication_callback_t emm_cb,
  ** Outputs:     Return:        FALSE, TRUE                                **
  **                                                                        **
  ***************************************************************************/
-int nas_user_receive_and_process(int *fd, char *message)
+int nas_user_receive_and_process(nas_user_t *user, char *message)
 {
   LOG_FUNC_IN;
 
@@ -240,13 +197,14 @@ int nas_user_receive_and_process(int *fd, char *message)
   int nb_command;
   int bytes;
   int i;
+  user_api_id_t *user_api_id = user->user_api_id;
 
   if (message != NULL) {
     /* Set the message in receive buffer (Use to simulate reception of data from UserProcess) */
-    bytes = user_api_set_data(message);
+    bytes = user_api_set_data(user_api_id, message);
   } else {
     /* Read the user data message */
-    bytes = user_api_read_data (*fd);
+    bytes = user_api_read_data (user_api_id);
 
     if (bytes == RETURNerror) {
       /* Failed to read data from the user application layer;
@@ -263,11 +221,11 @@ int nas_user_receive_and_process(int *fd, char *message)
   }
 
   /* Decode the user data message */
-  nb_command = user_api_decode_data (bytes);
+  nb_command = user_api_decode_data (user_api_id, user->user_at_commands, bytes);
 
   for (i = 0; i < nb_command; i++) {
     /* Get the user data to be processed */
-    const void *data = user_api_get_data (i);
+    const void *data = user_api_get_data (user->user_at_commands, i);
 
     if (data == NULL) {
       /* Failed to get user data at the given index;
@@ -279,7 +237,7 @@ int nas_user_receive_and_process(int *fd, char *message)
     }
 
     /* Process the user data message */
-    ret_code = nas_user_process_data (data);
+    ret_code = nas_user_process_data (user, data);
 
     if (ret_code != RETURNok) {
       /* The user data message has not been successfully
@@ -293,7 +251,7 @@ int nas_user_receive_and_process(int *fd, char *message)
     /* Send response to UserProcess (If not in simulated reception) */
     if (message == NULL) {
       /* Encode the user data message */
-      bytes = user_api_encode_data (nas_user_get_data (), i == nb_command - 1);
+      bytes = user_api_encode_data (user->user_api_id, nas_user_get_data (user), i == nb_command - 1);
 
       if (bytes == RETURNerror) {
         /* Failed to encode the user data message;
@@ -302,7 +260,7 @@ int nas_user_receive_and_process(int *fd, char *message)
       }
 
       /* Send the data message to the user */
-      bytes = user_api_send_data (*fd, bytes);
+      bytes = user_api_send_data (user_api_id, bytes);
 
       if (bytes == RETURNerror) {
         /* Failed to send data to the user application layer;
@@ -331,12 +289,12 @@ int nas_user_receive_and_process(int *fd, char *message)
  ** Outputs:     None                                                      **
  **      Return:    RETURNok if the command has been success-  **
  **             fully executed; RETURNerror otherwise      **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-int nas_user_process_data(const void *data)
+int nas_user_process_data(nas_user_t *user, const void *data)
 {
   LOG_FUNC_IN;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNerror;
 
@@ -355,18 +313,18 @@ int nas_user_process_data(const void *data)
     nas_procedure = _nas_user_procedure[user_data->id];
 
     if (nas_procedure != NULL) {
-      ret_code = (*nas_procedure)(user_data);
+      ret_code = (*nas_procedure)(user, user_data);
     } else {
       /* AT command related to result format only */
-      _nas_user_data.id = user_data->id;
-      _nas_user_data.type = user_data->type;
-      _nas_user_data.mask = user_data->mask;
-      _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+      at_response->id = user_data->id;
+      at_response->type = user_data->type;
+      at_response->mask = user_data->mask;
+      at_response->cause_code = AT_ERROR_SUCCESS;
       ret_code = RETURNok;
     }
   } else {
     LOG_TRACE(ERROR, "USR-MAIN  - Data to be processed is null");
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
   }
 
   LOG_FUNC_RETURN (ret_code);
@@ -389,10 +347,10 @@ int nas_user_process_data(const void *data)
  **          Others:    None                                       **
  **                                                                        **
  ***************************************************************************/
-const void *nas_user_get_data(void)
+const void *nas_user_get_data(nas_user_t *user)
 {
   LOG_FUNC_IN;
-  LOG_FUNC_RETURN ((void *) &_nas_user_data);
+  LOG_FUNC_RETURN ((void *) user->at_response);
 }
 
 /****************************************************************************/
@@ -411,30 +369,29 @@ const void *nas_user_get_data(void)
  **      ning the IMEI.                                            **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_nvdata                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgsn(const at_command_t *data)
+static int _nas_user_proc_cgsn(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgsn_resp_t *cgsn = &_nas_user_data.response.cgsn;
+  at_cgsn_resp_t *cgsn = &at_response->response.cgsn;
   memset(cgsn, 0, sizeof(at_cgsn_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGSN_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGSN_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
     /* Get the Product Serial Number Identification (IMEI) */
-    strncpy(cgsn->sn, _nas_user_nvdata.IMEI,
+    strncpy(cgsn->sn, user->nas_user_nvdata->IMEI,
             AT_RESPONSE_INFO_TEXT_SIZE);
     break;
 
@@ -445,7 +402,7 @@ static int _nas_user_proc_cgsn(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGSN command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -463,30 +420,29 @@ static int _nas_user_proc_cgsn(const at_command_t *data)
  **      le Equipment to which it is connected to.                 **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_nvdata                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgmi(const at_command_t *data)
+static int _nas_user_proc_cgmi(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgmi_resp_t *cgmi = &_nas_user_data.response.cgmi;
+  at_cgmi_resp_t *cgmi = &at_response->response.cgmi;
   memset(cgmi, 0, sizeof(at_cgmi_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGMI_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGMI_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
     /* Get the Manufacturer identifier */
-    strncpy(cgmi->manufacturer, _nas_user_nvdata.manufacturer,
+    strncpy(cgmi->manufacturer, user->nas_user_nvdata->manufacturer,
             AT_RESPONSE_INFO_TEXT_SIZE);
     break;
 
@@ -497,7 +453,7 @@ static int _nas_user_proc_cgmi(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGMI command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -515,30 +471,29 @@ static int _nas_user_proc_cgmi(const at_command_t *data)
  **      Equipment to which it is connected to.                    **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_nvdata                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgmm(const at_command_t *data)
+static int _nas_user_proc_cgmm(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgmm_resp_t *cgmm = &_nas_user_data.response.cgmm;
+  at_cgmm_resp_t *cgmm = &at_response->response.cgmm;
   memset(cgmm, 0, sizeof(at_cgmm_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGMM_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGMM_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
     /* Get the Model identifier */
-    strncpy(cgmm->model, _nas_user_nvdata.model,
+    strncpy(cgmm->model, user->nas_user_nvdata->model,
             AT_RESPONSE_INFO_TEXT_SIZE);
     break;
 
@@ -549,7 +504,7 @@ static int _nas_user_proc_cgmm(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGMM command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -568,30 +523,30 @@ static int _nas_user_proc_cgmm(const at_command_t *data)
  **      Equipment to which it is connected to.                    **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgmr(const at_command_t *data)
+static int _nas_user_proc_cgmr(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgmr_resp_t *cgmr = &_nas_user_data.response.cgmr;
+  at_cgmr_resp_t *cgmr = &at_response->response.cgmr;
   memset(cgmr, 0, sizeof(at_cgmr_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGMR_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGMR_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
     /* Get the revision identifier */
-    strncpy(cgmr->revision, _nas_user_context.version,
+    strncpy(cgmr->revision, nas_user_context->version,
             AT_RESPONSE_INFO_TEXT_SIZE);
     break;
 
@@ -602,7 +557,7 @@ static int _nas_user_proc_cgmr(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGMR command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -624,39 +579,39 @@ static int _nas_user_proc_cgmr(const at_command_t *data)
  **      ning the IMSI.                                            **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_nvdata                           **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cimi(const at_command_t *data)
+static int _nas_user_proc_cimi(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cimi_resp_t *cimi = &_nas_user_data.response.cimi;
+  at_cimi_resp_t *cimi = &at_response->response.cimi;
   memset(cimi, 0, sizeof(at_cimi_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CIMI_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CIMI_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
     /* Get the International Mobile Subscriber Identity (IMSI) */
-    ret_code = nas_proc_get_imsi(cimi->IMSI);
+    ret_code = nas_proc_get_imsi(user->emm_data, cimi->IMSI);
 
     if (ret_code != RETURNok) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to get IMSI number");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -668,7 +623,7 @@ static int _nas_user_proc_cimi(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CIMI command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -685,27 +640,27 @@ static int _nas_user_proc_cimi(const at_command_t *data)
  **      to different power consumption states.                    **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cfun(const at_command_t *data)
+static int _nas_user_proc_cfun(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cfun_resp_t *cfun = &_nas_user_data.response.cfun;
+  at_cfun_resp_t *cfun = &at_response->response.cfun;
   memset(cfun, 0, sizeof(at_cfun_resp_t));
 
   int fun = AT_CFUN_FUN_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CFUN_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CFUN_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
@@ -721,7 +676,7 @@ static int _nas_user_proc_cfun(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <rst> parameter is not valid"
                   " (%d)", data->command.cfun.rst);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -734,7 +689,7 @@ static int _nas_user_proc_cfun(const at_command_t *data)
          * is not valid; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <fun> parameter is not valid"
                   " (%d)", data->command.cfun.fun);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -751,7 +706,7 @@ static int _nas_user_proc_cfun(const at_command_t *data)
     case AT_CFUN_FULL:
       /* Notify the NAS procedure call manager that the UE is
        * operational */
-      ret_code = nas_proc_enable_s1_mode();
+      ret_code = nas_proc_enable_s1_mode(user);
       break;
 
     default:
@@ -761,14 +716,14 @@ static int _nas_user_proc_cfun(const at_command_t *data)
 
     if (ret_code != RETURNerror) {
       /* Update the functionality level */
-      _nas_user_context.fun = fun;
+      nas_user_context->fun = fun;
     }
 
     break;
 
   case AT_COMMAND_GET:
     /* Get the MT's functionality level */
-    cfun->fun = _nas_user_context.fun;
+    cfun->fun = nas_user_context->fun;
     break;
 
   case AT_COMMAND_TST:
@@ -780,7 +735,7 @@ static int _nas_user_proc_cfun(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CFUN command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -798,25 +753,25 @@ static int _nas_user_proc_cfun(const at_command_t *data)
  **      can be operated.                                          **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_context, _nas_user_data          **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cpin(const at_command_t *data)
+static int _nas_user_proc_cpin(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cpin_resp_t *cpin = &_nas_user_data.response.cpin;
+  at_cpin_resp_t *cpin = &at_response->response.cpin;
   memset(cpin, 0, sizeof(at_cpin_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CPIN_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CPIN_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
@@ -825,24 +780,24 @@ static int _nas_user_proc_cpin(const at_command_t *data)
      * Set command sends to the MT a password which is necessary
      * before it can be operated
      */
-    if (_nas_user_context.sim_status == NAS_USER_SIM_PIN) {
+    if (nas_user_context->sim_status == NAS_USER_SIM_PIN) {
       /* The MT is waiting for PIN password; check the PIN code */
-      if (strncmp(_nas_user_nvdata.PIN,
+      if (strncmp(user->nas_user_nvdata->PIN,
                   data->command.cpin.pin, USER_PIN_SIZE) != 0) {
         /* The PIN code is NOT matching; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - PIN code is not correct "
                   "(%s)", data->command.cpin.pin);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PASSWD;
+        at_response->cause_code = AT_ERROR_INCORRECT_PASSWD;
         ret_code = RETURNerror;
       } else {
         /* The PIN code is matching; update the user's PIN
          * pending status */
-        _nas_user_context.sim_status = NAS_USER_READY;
+        nas_user_context->sim_status = NAS_USER_READY;
       }
     } else {
       /* The MT is NOT waiting for PIN password;
        * return an error message */
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_ALLOWED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_ALLOWED;
       ret_code = RETURNerror;
     }
 
@@ -854,7 +809,7 @@ static int _nas_user_proc_cpin(const at_command_t *data)
      * whether some password is required or not.
      */
     strncpy(cpin->code,
-            _nas_user_sim_status_str[_nas_user_context.sim_status],
+            _nas_user_sim_status_str[nas_user_context->sim_status],
             AT_CPIN_RESP_SIZE);
     break;
 
@@ -866,7 +821,7 @@ static int _nas_user_proc_cpin(const at_command_t *data)
     /* Other types of AT CPIN command are not valid */
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CPIN command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -884,30 +839,30 @@ static int _nas_user_proc_cpin(const at_command_t *data)
  **      Equipment.                                                **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_csq(const at_command_t *data)
+static int _nas_user_proc_csq(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_csq_resp_t *csq = &_nas_user_data.response.csq;
+  at_csq_resp_t *csq = &at_response->response.csq;
   memset(csq, 0, sizeof(at_csq_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CSQ_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CSQ_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -928,7 +883,7 @@ static int _nas_user_proc_csq(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CSQ command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -945,30 +900,30 @@ static int _nas_user_proc_csq(const at_command_t *data)
  **      meters.                                                   **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cesq(const at_command_t *data)
+static int _nas_user_proc_cesq(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cesq_resp_t *cesq = &_nas_user_data.response.cesq;
+  at_cesq_resp_t *cesq = &at_response->response.cesq;
   memset(cesq, 0, sizeof(at_cesq_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CESQ_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CESQ_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -979,7 +934,7 @@ static int _nas_user_proc_cesq(const at_command_t *data)
     cesq->ber  = AT_CESQ_BER_UNKNOWN;
     cesq->rscp = AT_CESQ_RSCP_UNKNOWN;
     cesq->ecno = AT_CESQ_ECNO_UNKNOWN;
-    ret_code = nas_proc_get_signal_quality(&cesq->rsrq, &cesq->rsrp);
+    ret_code = nas_proc_get_signal_quality(user, &cesq->rsrq, &cesq->rsrp);
     break;
 
   case AT_COMMAND_TST:
@@ -991,7 +946,7 @@ static int _nas_user_proc_cesq(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CESQ command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1010,19 +965,19 @@ static int _nas_user_proc_cesq(const at_command_t *data)
  **      slot.                                                     **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cops(const at_command_t *data)
+static int _nas_user_proc_cops(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cops_resp_t *cops = &_nas_user_data.response.cops;
+  at_cops_resp_t *cops = &at_response->response.cops;
   memset(cops, 0, sizeof(at_cops_resp_t));
 
   static int read_format = AT_COPS_FORMAT_DEFAULT;
@@ -1035,15 +990,15 @@ static int _nas_user_proc_cops(const at_command_t *data)
 
   int oper_is_selected;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_NO_PARAM;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_NO_PARAM;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1079,7 +1034,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
         /* <oper> field shall be present */
         if ( !(data->mask & AT_COPS_OPER_MASK) ) {
           LOG_TRACE(ERROR, "USR-MAIN  - <oper> parameter is not present");
-          _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+          at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
           ret_code = RETURNerror;
           break;
         }
@@ -1087,7 +1042,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
         /* <format> field shall be present */
         if ( !(data->mask & AT_COPS_FORMAT_MASK) ) {
           LOG_TRACE(ERROR, "USR-MAIN  - <format> parameter is not present");
-          _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+          at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
           ret_code = RETURNerror;
           break;
         }
@@ -1098,7 +1053,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
           /* The value of <format> field is not valid */
           LOG_TRACE(ERROR, "USR-MAIN  - <format> parameter is not valid (%d)",
                     data->command.cops.format);
-          _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+          at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
           ret_code = RETURNerror;
           break;
         }
@@ -1112,7 +1067,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
             /* The value of <AcT> field is not valid */
             LOG_TRACE(ERROR, "USR-MAIN  - <AcT> parameter is not valid (%d)",
                       data->command.cops.AcT);
-            _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+            at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
             ret_code = RETURNerror;
             break;
           }
@@ -1140,7 +1095,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
             /* The value of <format> field is not valid */
             LOG_TRACE(ERROR, "USR-MAIN  - <format> parameter is not valid (%d)",
                       data->command.cops.format);
-            _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+            at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
             ret_code = RETURNerror;
             break;
           }
@@ -1156,7 +1111,7 @@ static int _nas_user_proc_cops(const at_command_t *data)
 
       default:
         LOG_TRACE(ERROR, "USR-MAIN  - <mode> parameter is not supported (%d)", mode);
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
         ret_code = RETURNerror;
         break;
       }
@@ -1168,22 +1123,22 @@ static int _nas_user_proc_cops(const at_command_t *data)
     if (ret_code != RETURNerror) {
       if (mode == AT_COPS_DEREG) {
         /* Force an attempt to deregister from the network */
-        ret_code = nas_proc_deregister();
+        ret_code = nas_proc_deregister(user);
 
         if (ret_code != RETURNok) {
           LOG_TRACE(ERROR, "USR-MAIN  - Network deregistration failed");
-          _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+          at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
           break;
         }
       } else if (mode != AT_COPS_FORMAT) {
         /* Force an attempt to automatically/manualy select
          * and register the GSM/UMTS/EPS network operator */
-        ret_code = nas_proc_register(mode, format,
+        ret_code = nas_proc_register(user, mode, format,
                                      &data->command.cops.plmn, AcT);
 
         if (ret_code != RETURNok) {
           LOG_TRACE(ERROR, "USR-MAIN  - Network registration failed (<mode>=%d)", mode);
-          _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+          at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
           break;
         }
       }
@@ -1198,14 +1153,14 @@ static int _nas_user_proc_cops(const at_command_t *data)
      */
 
     /* Get the current network registration data */
-    ret_code = nas_proc_get_reg_data(&mode,
+    ret_code = nas_proc_get_reg_data(user, &mode,
                                      &oper_is_selected, read_format,
                                      &cops->get.plmn, &cops->get.AcT);
 
     if (ret_code != RETURNok) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to get registration data (<mode>=%d)",
                 mode);
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       break;
     }
 
@@ -1221,11 +1176,11 @@ static int _nas_user_proc_cops(const at_command_t *data)
     /* Set optional parameter bitmask */
     if (oper_is_selected) {
       cops->get.format = read_format;
-      _nas_user_data.mask |= (AT_COPS_RESP_FORMAT_MASK |
+      at_response->mask |= (AT_COPS_RESP_FORMAT_MASK |
                               AT_COPS_RESP_OPER_MASK);
 
       if (cops->get.AcT != NET_ACCESS_UNAVAILABLE) {
-        _nas_user_data.mask |= AT_COPS_RESP_ACT_MASK;
+        at_response->mask |= AT_COPS_RESP_ACT_MASK;
       }
     }
 
@@ -1236,13 +1191,13 @@ static int _nas_user_proc_cops(const at_command_t *data)
      * Test command returns a set of parameters, each representing
      * an operator present in the network.
      */
-    cops->tst.size = nas_proc_get_oper_list(&cops->tst.data);
+    cops->tst.size = nas_proc_get_oper_list(user, &cops->tst.data);
     break;
 
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+COPS command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1259,30 +1214,30 @@ static int _nas_user_proc_cops(const at_command_t *data)
  **      or detach the MT from, the EPS service.                   **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgatt(const at_command_t *data)
+static int _nas_user_proc_cgatt(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgatt_resp_t *cgatt = &_nas_user_data.response.cgatt;
+  at_cgatt_resp_t *cgatt = &at_response->response.cgatt;
   memset(cgatt, 0, sizeof(at_cgatt_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGATT_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGATT_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1297,7 +1252,7 @@ static int _nas_user_proc_cgatt(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <state> parameter is not valid (%d)",
                   data->command.cgatt.state);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1308,16 +1263,16 @@ static int _nas_user_proc_cgatt(const at_command_t *data)
       ret_code = RETURNerror;
 
       if (data->command.cgatt.state == AT_CGATT_ATTACHED) {
-        ret_code = nas_proc_attach();
+        ret_code = nas_proc_attach(user);
       } else if (data->command.cgatt.state == AT_CGATT_DETACHED) {
-        ret_code = nas_proc_detach(FALSE);
+        ret_code = nas_proc_detach(user, FALSE);
       }
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to attach/detach "
                   "to/from EPS service (<state>=%d)",
                   data->command.cgatt.state);
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
     }
 
@@ -1328,7 +1283,7 @@ static int _nas_user_proc_cgatt(const at_command_t *data)
     /*
      * Read command returns the current EPS service state.
      */
-    if (nas_proc_get_attach_status() != TRUE) {
+    if (nas_proc_get_attach_status(user) != TRUE) {
       cgatt->state = AT_CGATT_DETACHED;
     } else {
       cgatt->state = AT_CGATT_ATTACHED;
@@ -1346,7 +1301,7 @@ static int _nas_user_proc_cgatt(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGATT command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1364,32 +1319,32 @@ static int _nas_user_proc_cgatt(const at_command_t *data)
  **      location information in GERA/UTRA/E-UTRA Network.         **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_creg(const at_command_t *data)
+static int _nas_user_proc_creg(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_creg_resp_t *creg = &_nas_user_data.response.creg;
+  at_creg_resp_t *creg = &at_response->response.creg;
   memset(creg, 0, sizeof(at_creg_resp_t));
 
   static int n = AT_CREG_N_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_NO_PARAM;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_NO_PARAM;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1406,7 +1361,7 @@ static int _nas_user_proc_creg(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <n> parameter is not valid"
                   " (%d)", data->command.creg.n);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1432,7 +1387,7 @@ static int _nas_user_proc_creg(const at_command_t *data)
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR,
                   "USR-MAIN  - Failed to disable logging of network notification");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1447,7 +1402,7 @@ static int _nas_user_proc_creg(const at_command_t *data)
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to enable logging of registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1474,11 +1429,11 @@ static int _nas_user_proc_creg(const at_command_t *data)
     case AT_CREG_OFF:
     case AT_CREG_ON:
       /* Get network registration status */
-      ret_code = nas_proc_get_reg_status(&creg->stat);
+      ret_code = nas_proc_get_reg_status(user, &creg->stat);
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to get registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1495,7 +1450,7 @@ static int _nas_user_proc_creg(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CREG command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1513,32 +1468,32 @@ static int _nas_user_proc_creg(const at_command_t *data)
  **      information in GERA/UTRA Network.                         **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgreg(const at_command_t *data)
+static int _nas_user_proc_cgreg(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgreg_resp_t *cgreg = &_nas_user_data.response.cgreg;
+  at_cgreg_resp_t *cgreg = &at_response->response.cgreg;
   memset(cgreg, 0, sizeof(at_cgreg_resp_t));
 
   static int n = AT_CGREG_N_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_NO_PARAM;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_NO_PARAM;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1555,7 +1510,7 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <n> parameter is not valid"
                   " (%d)", data->command.cgreg.n);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1581,7 +1536,7 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR,
                   "USR-MAIN  - Failed to disable logging of network notification");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1596,7 +1551,7 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to enable logging of registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1623,11 +1578,11 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
     case AT_CGREG_OFF:
     case AT_CGREG_ON:
       /* Get network registration status */
-      ret_code = nas_proc_get_reg_status(&cgreg->stat);
+      ret_code = nas_proc_get_reg_status(user, &cgreg->stat);
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to get registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1644,7 +1599,7 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGREG command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1662,32 +1617,32 @@ static int _nas_user_proc_cgreg(const at_command_t *data)
  **      information in E-UTRA Network.                            **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cereg(const at_command_t *data)
+static int _nas_user_proc_cereg(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cereg_resp_t *cereg = &_nas_user_data.response.cereg;
+  at_cereg_resp_t *cereg = &at_response->response.cereg;
   memset(cereg, 0, sizeof(at_cereg_resp_t));
 
   static int n = AT_CEREG_N_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_NO_PARAM;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_NO_PARAM;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1704,7 +1659,7 @@ static int _nas_user_proc_cereg(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <n> parameter is not valid"
                   " (%d)",  data->command.cereg.n);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1730,7 +1685,7 @@ static int _nas_user_proc_cereg(const at_command_t *data)
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR,
                   "USR-MAIN  - Failed to disable logging of network notification");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1741,7 +1696,7 @@ static int _nas_user_proc_cereg(const at_command_t *data)
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to enable logging of location information");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1753,7 +1708,7 @@ static int _nas_user_proc_cereg(const at_command_t *data)
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to enable logging of registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1776,21 +1731,21 @@ static int _nas_user_proc_cereg(const at_command_t *data)
     switch (n) {
     case AT_CEREG_BOTH:
       /* Get EPS location information  */
-      ret_code = nas_proc_get_loc_info(cereg->tac, cereg->ci,
+      ret_code = nas_proc_get_loc_info(user, cereg->tac, cereg->ci,
                                        &cereg->AcT);
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to get location information");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
         break;
       }
 
       if (cereg->tac[0] != 0) {
-        _nas_user_data.mask |= (AT_CEREG_RESP_TAC_MASK |
+        at_response->mask |= (AT_CEREG_RESP_TAC_MASK |
                                 AT_CEREG_RESP_CI_MASK);
 
         if (cereg->AcT != NET_ACCESS_UNAVAILABLE) {
-          _nas_user_data.mask |= (AT_CEREG_RESP_ACT_MASK);
+          at_response->mask |= (AT_CEREG_RESP_ACT_MASK);
         }
       }
 
@@ -1799,11 +1754,11 @@ static int _nas_user_proc_cereg(const at_command_t *data)
     case AT_CEREG_OFF:
     case AT_CEREG_ON:
       /* Get network registration status */
-      ret_code = nas_proc_get_reg_status(&cereg->stat);
+      ret_code = nas_proc_get_reg_status(user, &cereg->stat);
 
       if (ret_code != RETURNok) {
         LOG_TRACE(ERROR, "USR-MAIN  - Failed to get registration status");
-        _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+        at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       }
 
       break;
@@ -1820,7 +1775,7 @@ static int _nas_user_proc_cereg(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CEREG command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -1843,19 +1798,19 @@ static int _nas_user_proc_cereg(const at_command_t *data)
  **      fault bearer and traffic flows in EPS.                    **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgdcont(const at_command_t *data)
+static int _nas_user_proc_cgdcont(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgdcont_get_t *cgdcont = &_nas_user_data.response.cgdcont.get;
+  at_cgdcont_get_t *cgdcont = &at_response->response.cgdcont.get;
   memset(cgdcont, 0, sizeof(at_cgdcont_resp_t));
 
   int cid = AT_CGDCONT_CID_DEFAULT;
@@ -1867,15 +1822,15 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
   int im_cn_signalling = AT_CGDCONT_IM_CM_DEFAULT;
   int reset_pdn = TRUE;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_NO_PARAM;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_NO_PARAM;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -1889,7 +1844,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <cid> parameter is not valid"
                   " (%d)", data->command.cgdcont.cid);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1909,7 +1864,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <PDN_type> parameter is not "
                   "valid (%s)", data->command.cgdcont.PDP_type);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1928,7 +1883,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
          * not valid; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <d_comp> parameter is not "
                   "valid (%d)", data->command.cgdcont.d_comp);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1943,7 +1898,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
          * not valid; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <h_comp> parameter is not "
                   "valid (%d)", data->command.cgdcont.h_comp);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1959,7 +1914,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
         LOG_TRACE(ERROR, "USR-MAIN  - <IPv4AddrAlloc> parameter "
                   "is not valid (%d)",
                   data->command.cgdcont.IPv4AddrAlloc);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1975,7 +1930,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
         LOG_TRACE(ERROR, "USR-MAIN  - <emergency indication> "
                   "parameter is not valid (%d)",
                   data->command.cgdcont.emergency_indication);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -1991,7 +1946,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
         LOG_TRACE(ERROR, "USR-MAIN  - <P-CSCF_discovery> "
                   "parameter is not valid (%d)",
                   data->command.cgdcont.P_CSCF_discovery);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2008,7 +1963,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
         LOG_TRACE(ERROR, "USR-MAIN  - <IM_CN_Signalling_Flag_Ind> "
                   "parameter is not valid (%d)",
                   data->command.cgdcont.IM_CN_Signalling_Flag_Ind);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2022,10 +1977,10 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
     if (reset_pdn) {
       /* A special form of the set command, +CGDCONT=<cid> causes
        * the values for context number <cid> to become undefined */
-      ret_code = nas_proc_reset_pdn(cid);
+      ret_code = nas_proc_reset_pdn(user, cid);
     } else {
       /* Define a new PDN connection */
-      ret_code = nas_proc_set_pdn(cid, pdn_type, apn,
+      ret_code = nas_proc_set_pdn(user, cid, pdn_type, apn,
                                   ipv4_addr_allocation, emergency,
                                   p_cscf, im_cn_signalling);
     }
@@ -2033,7 +1988,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
     if (ret_code != RETURNok) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to setup PDN connection "
                 "(<cid>=%d)", data->command.cgdcont.cid);
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2043,14 +1998,14 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
      * Read command returns the current settings for each
      * defined PDN connection/default EPS bearer context
      */
-    cgdcont->n_pdns = nas_proc_get_pdn_param(cgdcont->cid,
+    cgdcont->n_pdns = nas_proc_get_pdn_param(user->esm_data, cgdcont->cid,
                       cgdcont->PDP_type,
                       cgdcont->APN,
                       AT_CGDCONT_RESP_SIZE);
 
     if (cgdcont->n_pdns == 0) {
       LOG_TRACE(ERROR, "USR-MAIN  - No any PDN context is defined");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2061,15 +2016,15 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
      */
   {
     /* Get the maximum value of a PDN context identifier */
-    int cid_max = nas_proc_get_pdn_range();
+    int cid_max = nas_proc_get_pdn_range(user->esm_data);
 
     if (cid_max > AT_CGDCONT_RESP_SIZE) {
       /* The range is defined by the user interface */
-      _nas_user_data.response.cgdcont.tst.n_cid =
+      at_response->response.cgdcont.tst.n_cid =
         AT_CGDCONT_RESP_SIZE;
     } else {
       /* The range is defined by the ESM sublayer application */
-      _nas_user_data.response.cgdcont.tst.n_cid = cid_max;
+      at_response->response.cgdcont.tst.n_cid = cid_max;
     }
   }
   break;
@@ -2077,7 +2032,7 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGDCONT command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2095,33 +2050,33 @@ static int _nas_user_proc_cgdcont(const at_command_t *data)
  **      for E-UTRAN                                               **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgact(const at_command_t *data)
+static int _nas_user_proc_cgact(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgact_resp_t *cgact = &_nas_user_data.response.cgact;
+  at_cgact_resp_t *cgact = &at_response->response.cgact;
   memset(cgact, 0, sizeof(at_cgact_resp_t));
 
   int cid = -1;
   int state = AT_CGACT_STATE_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGACT_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGACT_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -2136,7 +2091,7 @@ static int _nas_user_proc_cgact(const at_command_t *data)
          * not valid; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <state> parameter is "
                   "not valid (%d)",  data->command.cgact.state);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2150,7 +2105,7 @@ static int _nas_user_proc_cgact(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <cid> parameter is "
                   "not valid (%d)",  data->command.cgact.cid);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2164,9 +2119,9 @@ static int _nas_user_proc_cgact(const at_command_t *data)
     ret_code = RETURNerror;
 
     if (state == AT_CGACT_DEACTIVATED) {
-      ret_code = nas_proc_deactivate_pdn(cid);
+      ret_code = nas_proc_deactivate_pdn(user, cid);
     } else if (state == AT_CGACT_ACTIVATED) {
-      ret_code = nas_proc_activate_pdn(cid);
+      ret_code = nas_proc_activate_pdn(user, cid);
     }
 
     if (ret_code != RETURNok) {
@@ -2174,7 +2129,7 @@ static int _nas_user_proc_cgact(const at_command_t *data)
                 "(<state>=%d,<cid>=%d)",
                 (state != AT_CGACT_ACTIVATED)? "deactivate" :
                 "activate", state, cid);
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2184,12 +2139,12 @@ static int _nas_user_proc_cgact(const at_command_t *data)
      * The read command returns the current activation states for
      * all the defined PDN/EPS bearer contexts
      */
-    cgact->n_pdns = nas_proc_get_pdn_status(cgact->cid, cgact->state,
+    cgact->n_pdns = nas_proc_get_pdn_status(user, cgact->cid, cgact->state,
                                             AT_CGACT_RESP_SIZE);
 
     if (cgact->n_pdns == 0) {
       LOG_TRACE(ERROR, "USR-MAIN  - No any PDN context is defined");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2204,7 +2159,7 @@ static int _nas_user_proc_cgact(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGACT command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2226,23 +2181,23 @@ static int _nas_user_proc_cgact(const at_command_t *data)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cmee(const at_command_t *data)
+static int _nas_user_proc_cmee(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
 
   int ret_code = RETURNok;
-  at_cmee_resp_t *cmee = &_nas_user_data.response.cmee;
+  at_response_t *at_response = user->at_response;
+  at_cmee_resp_t *cmee = &at_response->response.cmee;
   memset(cmee, 0, sizeof(at_cmee_resp_t));
 
   int n = AT_CMEE_N_DEFAULT;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CMEE_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CMEE_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:    /* ATV0, ATV1 response format commands */
@@ -2260,7 +2215,7 @@ static int _nas_user_proc_cmee(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <n> parameter is not valid"
                   " (%d)", data->command.cmee.n);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2304,7 +2259,7 @@ static int _nas_user_proc_cmee(const at_command_t *data)
 
     if (cmee->n == RETURNerror) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to get format of the final result code");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2318,7 +2273,7 @@ static int _nas_user_proc_cmee(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CMEE command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2339,21 +2294,21 @@ static int _nas_user_proc_cmee(const at_command_t *data)
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_clck(const at_command_t *data)
+static int _nas_user_proc_clck(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_clck_resp_t *clck = &_nas_user_data.response.clck;
+  at_clck_resp_t *clck = &at_response->response.clck;
   memset(clck, 0, sizeof(at_clck_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CLCK_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CLCK_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
@@ -2369,7 +2324,7 @@ static int _nas_user_proc_clck(const at_command_t *data)
       /* Facilities other than SIM is not supported */
       LOG_TRACE(ERROR, "USR-MAIN  - Facility is not supported (%s)",
                 data->command.clck.fac);
-      _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+      at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
       ret_code = RETURNerror;
       break;
     }
@@ -2377,12 +2332,12 @@ static int _nas_user_proc_clck(const at_command_t *data)
     /* Check password parameter */
     if (data->mask & AT_CLCK_PASSWD_MASK) {
       /* Check the PIN code */
-      if (strncmp(_nas_user_nvdata.PIN,
+      if (strncmp(user->nas_user_nvdata->PIN,
                   data->command.clck.passwd, USER_PIN_SIZE) != 0) {
         /* The PIN code is NOT matching; return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - Password is not correct "
                   "(%s)", data->command.clck.passwd);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PASSWD;
+        at_response->cause_code = AT_ERROR_INCORRECT_PASSWD;
         ret_code = RETURNerror;
         break;
       }
@@ -2397,14 +2352,14 @@ static int _nas_user_proc_clck(const at_command_t *data)
         /* unlock requires password */
         LOG_TRACE(ERROR, "USR-MAIN  - unlock mode of operation "
                   "requires a password");
-        _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+        at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
         ret_code = RETURNerror;
         break;
       }
 
       LOG_TRACE(ERROR, "USR-MAIN  - unlock mode of operation "
                 "is not supported");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       ret_code = RETURNerror;
       break;
 
@@ -2415,14 +2370,14 @@ static int _nas_user_proc_clck(const at_command_t *data)
         /* unlock requires password */
         LOG_TRACE(ERROR, "USR-MAIN  - lock mode of operation "
                   "requires a password");
-        _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+        at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
         ret_code = RETURNerror;
         break;
       }
 
       LOG_TRACE(ERROR, "USR-MAIN  - lock mode of operation "
                 "is not supported");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
       ret_code = RETURNerror;
       break;
 
@@ -2434,7 +2389,7 @@ static int _nas_user_proc_clck(const at_command_t *data)
     default:
       LOG_TRACE(ERROR, "USR-MAIN  - <mode> parameter is not valid"
                 " (%d)", data->command.clck.mode);
-      _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+      at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
       ret_code = RETURNerror;
       break;
     }
@@ -2450,7 +2405,7 @@ static int _nas_user_proc_clck(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CLCK command type %d is not supported",
               data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2467,32 +2422,32 @@ static int _nas_user_proc_clck(const at_command_t *data)
  **      for the specified context identifiers                     **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cgpaddr(const at_command_t *data)
+static int _nas_user_proc_cgpaddr(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cgpaddr_resp_t *cgpaddr = &_nas_user_data.response.cgpaddr;
+  at_cgpaddr_resp_t *cgpaddr = &at_response->response.cgpaddr;
   memset(cgpaddr, 0, sizeof(at_cgpaddr_resp_t));
 
   int cid = -1;
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CGPADDR_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CGPADDR_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_SET:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
@@ -2506,7 +2461,7 @@ static int _nas_user_proc_cgpaddr(const at_command_t *data)
          * return an error message */
         LOG_TRACE(ERROR, "USR-MAIN  - <cid> parameter is "
                   "not valid (%d)",  data->command.cgpaddr.cid);
-        _nas_user_data.cause_code = AT_ERROR_INCORRECT_PARAMETERS;
+        at_response->cause_code = AT_ERROR_INCORRECT_PARAMETERS;
         ret_code = RETURNerror;
         break;
       }
@@ -2517,14 +2472,14 @@ static int _nas_user_proc_cgpaddr(const at_command_t *data)
     /*
      * Get the PDP addresses
      */
-    cgpaddr->n_pdns = nas_proc_get_pdn_addr(cid, cgpaddr->cid,
+    cgpaddr->n_pdns = nas_proc_get_pdn_addr(user, cid, cgpaddr->cid,
                                             cgpaddr->PDP_addr_1,
                                             cgpaddr->PDP_addr_2,
                                             AT_CGPADDR_RESP_SIZE);
 
     if (cgpaddr->n_pdns == 0) {
       LOG_TRACE(ERROR, "USR-MAIN  - No any PDN context is defined");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2533,7 +2488,7 @@ static int _nas_user_proc_cgpaddr(const at_command_t *data)
     /*
      * The test command returns a list of defined <cid>s.
      */
-    cgpaddr->n_pdns = nas_proc_get_pdn_addr(cid, cgpaddr->cid,
+    cgpaddr->n_pdns = nas_proc_get_pdn_addr(user, cid, cgpaddr->cid,
                                             cgpaddr->PDP_addr_1,
                                             cgpaddr->PDP_addr_2,
                                             AT_CGPADDR_RESP_SIZE);
@@ -2542,7 +2497,7 @@ static int _nas_user_proc_cgpaddr(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CGPADDR command type %d is "
               "not supported", data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2559,39 +2514,39 @@ static int _nas_user_proc_cgpaddr(const at_command_t *data)
  **      subscriber.                                               **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_cnum(const at_command_t *data)
+static int _nas_user_proc_cnum(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
+  nas_user_context_t *nas_user_context = user->nas_user_context;
+  at_response_t *at_response = user->at_response;
 
   int ret_code = RETURNok;
-  at_cnum_resp_t *cnum = &_nas_user_data.response.cnum;
+  at_cnum_resp_t *cnum = &at_response->response.cnum;
   memset(cnum, 0, sizeof(at_cnum_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CNUM_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CNUM_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
-    if (_nas_user_context.sim_status != NAS_USER_READY) {
-      _nas_user_data.cause_code = AT_ERROR_SIM_PIN_REQUIRED;
+    if (nas_user_context->sim_status != NAS_USER_READY) {
+      at_response->cause_code = AT_ERROR_SIM_PIN_REQUIRED;
       LOG_FUNC_RETURN(RETURNerror);
     }
 
     /* Get the International Mobile Subscriber Identity (IMSI) */
-    ret_code = nas_proc_get_msisdn(cnum->number, &cnum->type);
+    ret_code = nas_proc_get_msisdn(user, cnum->number, &cnum->type);
 
     if (ret_code != RETURNok) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to get MS dialing number");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2603,7 +2558,7 @@ static int _nas_user_proc_cnum(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CNUM command type %d is "
               "not supported", data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
@@ -2620,25 +2575,23 @@ static int _nas_user_proc_cnum(const at_command_t *data)
  **      are available for the user.                               **
  **                                                                        **
  ** Inputs:  data:      Pointer to the AT command data structure   **
- **          Others:    _nas_user_context                          **
  **                                                                        **
  ** Outputs:     None                                                      **
  **      Return:    RETURNok; RETURNerror;                     **
- **          Others:    _nas_user_data                             **
  **                                                                        **
  ***************************************************************************/
-static int _nas_user_proc_clac(const at_command_t *data)
+static int _nas_user_proc_clac(nas_user_t *user, const at_command_t *data)
 {
   LOG_FUNC_IN;
-
+  at_response_t *at_response = user->at_response;
   int ret_code = RETURNok;
-  at_clac_resp_t *clac = &_nas_user_data.response.clac;
+  at_clac_resp_t *clac = &at_response->response.clac;
   memset(clac, 0, sizeof(at_clac_resp_t));
 
-  _nas_user_data.id = data->id;
-  _nas_user_data.type = data->type;
-  _nas_user_data.mask = AT_RESPONSE_CLAC_MASK;
-  _nas_user_data.cause_code = AT_ERROR_SUCCESS;
+  at_response->id = data->id;
+  at_response->type = data->type;
+  at_response->mask = AT_RESPONSE_CLAC_MASK;
+  at_response->cause_code = AT_ERROR_SUCCESS;
 
   switch (data->type) {
   case AT_COMMAND_ACT:
@@ -2648,7 +2601,7 @@ static int _nas_user_proc_clac(const at_command_t *data)
     if (clac->n_acs == 0) {
       LOG_TRACE(ERROR, "USR-MAIN  - Failed to get the list of "
                 "supported AT commands");
-      _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+      at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     }
 
     break;
@@ -2660,7 +2613,7 @@ static int _nas_user_proc_clac(const at_command_t *data)
   default:
     LOG_TRACE(ERROR, "USR-MAIN  - AT+CLAC command type %d is "
               "not supported", data->type);
-    _nas_user_data.cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
+    at_response->cause_code = AT_ERROR_OPERATION_NOT_SUPPORTED;
     ret_code = RETURNerror;
     break;
   }
diff --git a/openair3/NAS/UE/nas_user.h b/openair3/NAS/UE/nas_user.h
index 93b1cc8961bb574581f13fc3289d541478a95cbb..eb7f94a671a0569b988d149fb2ff01fa6fa98235 100644
--- a/openair3/NAS/UE/nas_user.h
+++ b/openair3/NAS/UE/nas_user.h
@@ -40,6 +40,9 @@ Description NAS procedure functions triggered by the user
 
 #include "commonDef.h"
 #include "networkDef.h"
+#include "emm_main.h"
+#include "esm_ebr.h"
+#include "user_defs.h"
 
 /****************************************************************************/
 /*********************  G L O B A L    C O N S T A N T S  *******************/
@@ -57,13 +60,13 @@ Description NAS procedure functions triggered by the user
 /******************  E X P O R T E D    F U N C T I O N S  ******************/
 /****************************************************************************/
 
-void nas_user_initialize(emm_indication_callback_t emm_cb,
+void nas_user_initialize(nas_user_t *user, emm_indication_callback_t emm_cb,
                          esm_indication_callback_t esm_cb, const char *version);
 
-int nas_user_receive_and_process(int * fd, char *message);
+int nas_user_receive_and_process(nas_user_t *user, char *message);
 
-int nas_user_process_data(const void *data);
+int nas_user_process_data(nas_user_t *user, const void *data);
 
-const void *nas_user_get_data(void);
+const void *nas_user_get_data(nas_user_t *nas_user);
 
 #endif /* __NAS_USER_H__*/
diff --git a/openair3/NAS/UE/user_defs.h b/openair3/NAS/UE/user_defs.h
new file mode 100644
index 0000000000000000000000000000000000000000..c27cf1320ee1708e0f9d9e03760f6030e2e18728
--- /dev/null
+++ b/openair3/NAS/UE/user_defs.h
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The OpenAirInterface Software Alliance licenses this file to You under
+ * the OAI Public License, Version 1.0  (the "License"); you may not use this file
+ * except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.openairinterface.org/?page_id=698
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *-------------------------------------------------------------------------------
+ * For more information about the OpenAirInterface (OAI) Software Alliance:
+ *      contact@openairinterface.org
+ */
+
+/*****************************************************************************
+Source      user_defs.h
+
+Version     0.1
+
+Date        2016/07/01
+
+Product     NAS stack
+
+Subsystem   NAS main process
+
+Author      Frederic Leroy
+
+Description NAS type definition to manage a user equipment
+
+*****************************************************************************/
+#ifndef __USER_DEFS_H__
+#define __USER_DEFS_H__
+
+#include "nas_proc_defs.h"
+#include "esmData.h"
+#include "esm_pt_defs.h"
+#include "EMM/emm_fsm_defs.h"
+#include "EMM/emmData.h"
+#include "EMM/Authentication.h"
+#include "EMM/IdleMode_defs.h"
+#include "EMM/LowerLayer_defs.h"
+#include "API/USIM/usim_api.h"
+#include "API/USER/user_api_defs.h"
+#include "SecurityModeControl.h"
+#include "userDef.h"
+#include "at_response.h"
+
+typedef struct {
+  int ueid; /* UE lower layer identifier */
+  proc_data_t proc;
+  // Eps Session Management
+  esm_data_t *esm_data; // ESM internal data (used within ESM only)
+  esm_pt_data_t *esm_pt_data;
+  esm_ebr_data_t *esm_ebr_data;  // EPS bearer contexts
+  default_eps_bearer_context_data_t *default_eps_bearer_context_data;
+  // Eps Mobility Management
+  emm_fsm_state_t emm_fsm_status; // Current EPS Mobility Management status
+  emm_data_t *emm_data; // EPS mobility management data
+  const char *emm_nvdata_store;
+  emm_plmn_list_t *emm_plmn_list; // list of PLMN identities
+  authentication_data_t *authentication_data;
+  security_data_t *security_data; //Internal data used for security mode control procedure
+  // Hardware persistent storage
+  usim_data_t usim_data; // USIM application data
+  const char *usim_data_store; // USIM application data filename
+  user_nvdata_t *nas_user_nvdata; //UE parameters stored in the UE's non-volatile memory device
+  const char *user_nvdata_store; //UE parameters stored in the UE's non-volatile memory device
+  //
+  nas_user_context_t *nas_user_context;
+  at_response_t *at_response; // data structure returned to the user as the result of NAS procedure function call
+  //
+  user_at_commands_t *user_at_commands; //decoded data received from the user application layer
+  user_api_id_t *user_api_id;
+  lowerlayer_data_t *lowerlayer_data;
+} nas_user_t;
+
+#endif
diff --git a/targets/COMMON/create_tasks.c b/targets/COMMON/create_tasks.c
index 9a1a14baebf4669d00937ba795a77c10695ac3dd..c28b9e839c4377755498fc5a1a87e5cef27b78b2 100644
--- a/targets/COMMON/create_tasks.c
+++ b/targets/COMMON/create_tasks.c
@@ -85,7 +85,10 @@ int create_tasks(uint32_t enb_nb, uint32_t ue_nb)
 
 #      if defined(NAS_BUILT_IN_UE)
       if (ue_nb > 0) {
-        if (itti_create_task (TASK_NAS_UE, nas_ue_task, NULL) < 0) {
+        nas_user_container_t *users = calloc(1, sizeof(*users));
+        if (users == NULL) abort();
+        users->count = ue_nb;
+        if (itti_create_task (TASK_NAS_UE, nas_ue_task, users) < 0) {
           LOG_E(NAS, "Create task for NAS UE failed\n");
           return -1;
         }
diff --git a/targets/COMMON/openairinterface5g_limits.h b/targets/COMMON/openairinterface5g_limits.h
new file mode 100644
index 0000000000000000000000000000000000000000..d9590873e82f47ed935c49eb1deb5f8a409f8e74
--- /dev/null
+++ b/targets/COMMON/openairinterface5g_limits.h
@@ -0,0 +1,25 @@
+#ifndef OPENAIRINTERFACE5G_LIMITS_H_
+#define OPENAIRINTERFACE5G_LIMITS_H_
+
+#if defined(CBMIMO1) || defined(EXMIMO) || defined(OAI_USRP)
+	#define NUMBER_OF_eNB_MAX 1
+	#define NUMBER_OF_UE_MAX 16
+	#define NUMBER_OF_CONNECTED_eNB_MAX 3
+#else
+	#define NUMBER_OF_eNB_MAX 7
+	#define NUMBER_OF_UE_MAX 20
+	#define NUMBER_OF_CONNECTED_eNB_MAX 3
+
+	#if STANDALONE==1
+		#define NUMBER_OF_eNB_MAX 3
+		#define NUMBER_OF_UE_MAX 3
+	#endif
+
+	#if LARGE_SCALE
+		#define NUMBER_OF_eNB_MAX 2
+		#define NUMBER_OF_UE_MAX 120
+		#define NUMBER_OF_CONNECTED_eNB_MAX 1 // to save some memory
+	#endif
+#endif
+
+#endif
diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/start_enb_and_ue_virt.bash b/targets/PROJECTS/GENERIC-LTE-EPC/start_enb_and_ue_virt.bash
index 5339af13e490f9f94aff25a537ff68ee1a92ff5f..6ea2d5bb0afe0efc6d64bc08b06b5e1c503b45bc 100755
--- a/targets/PROJECTS/GENERIC-LTE-EPC/start_enb_and_ue_virt.bash
+++ b/targets/PROJECTS/GENERIC-LTE-EPC/start_enb_and_ue_virt.bash
@@ -125,18 +125,18 @@ if [ ! -f $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data ] || [ ! -f $OPENAIR3_DIR/NA
     fi
     echo_success "make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS -f Makefile PROCESS=UE all"
     make  -f Makefile --debug=b --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS PROCESS=UE all
-    rm .ue.nvram
-    rm .usim.nvram
+    rm .ue.nvram0
+    rm .usim.nvram0
     touch /tmp/nas_cleaned
 fi
 
-if [ ! -f .ue.nvram ]; then
-    echo_success "generate .ue_emm.nvram .ue.nvram"
+if [ ! -f .ue.nvram0 ]; then
+    echo_success "generate .ue_emm.nvram0 .ue.nvram0"
     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --gen
 fi
 
-if [ ! -f .usim.nvram ]; then
-    echo_success "generate .usim.nvram"
+if [ ! -f .usim.nvram0 ]; then
+    echo_success "generate .usim.nvram0"
     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/usim_data --gen
 fi
 $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --print
diff --git a/targets/PROJECTS/GENERIC-LTE-EPC/start_ue.bash b/targets/PROJECTS/GENERIC-LTE-EPC/start_ue.bash
index e6d96602e270b5fb3d46ceddcd0291c1916df82c..ff16e27982a0e42954149bab6d3dbbf938bbf1f2 100755
--- a/targets/PROJECTS/GENERIC-LTE-EPC/start_ue.bash
+++ b/targets/PROJECTS/GENERIC-LTE-EPC/start_ue.bash
@@ -68,20 +68,20 @@ export NVRAM_DIR=$THIS_SCRIPT_PATH
 if [ ! -f $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data ]; then
     make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS veryveryclean
     make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS PROCESS=UE
-    rm .ue.nvram
+    rm .ue.nvram0
 fi
 if [ ! -f $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/usim_data ]; then
     make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS veryveryclean
     make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS PROCESS=UE
-    rm .usim.nvram
+    rm .usim.nvram0
 fi
-if [ ! -f .ue.nvram ]; then
-    # generate .ue_emm.nvram .ue.nvram
+if [ ! -f .ue.nvram0 ]; then
+    # generate .ue_emm.nvram0 .ue.nvram0
     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --gen
 fi
 
-if [ ! -f .usim.nvram ]; then
-    # generate .usim.nvram
+if [ ! -f .usim.nvram0 ]; then
+    # generate .usim.nvram0
     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/usim_data --gen
 fi
 $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --print
diff --git a/targets/RT/USER/lte-softmodem.c b/targets/RT/USER/lte-softmodem.c
index b37d5bc1c4cb11ee5e034114a09983e23b34c285..68b8050735e842e92165287656d675b6aea126ae 100644
--- a/targets/RT/USER/lte-softmodem.c
+++ b/targets/RT/USER/lte-softmodem.c
@@ -289,6 +289,11 @@ char uecap_xer[1024],uecap_xer_in=0;
 
 int oaisim_flag=0;
 
+/* see file openair2/LAYER2/MAC/main.c for why abstraction_flag is needed
+ * this is very hackish - find a proper solution
+ */
+uint8_t abstraction_flag=0;
+
 /*---------------------BMC: timespec helpers -----------------------------*/
 
 struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 };
diff --git a/targets/RT/USER/lte-ue.c b/targets/RT/USER/lte-ue.c
index 5c821a4176c2f98277566dd4b4cdf2d993b4377b..3745c61a9e95ae137eac2cd75d7502064ca40eb0 100644
--- a/targets/RT/USER/lte-ue.c
+++ b/targets/RT/USER/lte-ue.c
@@ -190,26 +190,28 @@ void init_UE(int nb_inst) {
     }
     UE->rfdevice.host_type = BBU_HOST;
     //    UE->rfdevice.type      = NONE_DEV;
-    error_code = pthread_create(&UE->proc.pthread_ue, &UE->proc.attr_ue, UE_thread, NULL);
+    error_code = pthread_create(&UE->proc.pthread_ue, &UE->proc.attr_ue, UE_thread, UE);
     
     if (error_code!= 0) {
       LOG_D(HW,"[lte-softmodem.c] Could not allocate UE_thread, error %d\n",error_code);
       return;
     } else {
+      char name[128];
+      sprintf(name, "main UE %d", inst);
       LOG_D(HW, "[lte-softmodem.c] Allocate UE_thread successful\n" );
-      pthread_setname_np( UE->proc.pthread_ue, "main UE" );
+      pthread_setname_np(UE->proc.pthread_ue, name);
     }
   }
 
   printf("UE threads created\n");
-#ifdef USE_MME
-  
+#if 0
+#if defined(ENABLE_USE_MME)
+  extern volatile int start_UE;
   while (start_UE == 0) {
     sleep(1);
   }
-  
 #endif
-  
+#endif
 }
 
 /*!
@@ -611,6 +613,14 @@ static void *UE_thread_synch(void *arg)
 }
 
 
+/* this structure is used to pass both UE phy vars and
+ * proc to the function UE_thread_rxn_txnp4
+ */
+struct rx_tx_thread_data {
+  PHY_VARS_UE    *UE;
+  UE_rxtx_proc_t *proc;
+};
+
 
 /*!
  * \brief This is the UE thread for RX subframe n and TX subframe n+4.
@@ -623,9 +633,10 @@ static void *UE_thread_synch(void *arg)
 static void *UE_thread_rxn_txnp4(void *arg)
 {
   static int UE_thread_rxtx_retval;
-  UE_rxtx_proc_t *proc = (UE_rxtx_proc_t *)arg;
+  struct rx_tx_thread_data *rtd = arg;
+  UE_rxtx_proc_t *proc = rtd->proc;
+  PHY_VARS_UE    *UE   = rtd->UE;
   int ret;
-  PHY_VARS_UE *UE=PHY_vars_UE_g[0][proc->CC_id];
   proc->instance_cnt_rxtx=-1;
 
 
@@ -861,6 +872,7 @@ static void *UE_thread_rxn_txnp4(void *arg)
   }
   
   // thread finished
+  free(arg);
   return &UE_thread_rxtx_retval;
 }
 
@@ -885,7 +897,7 @@ static void *UE_thread_rxn_txnp4(void *arg)
 void *UE_thread(void *arg) {
 
   static int UE_thread_retval;
-  PHY_VARS_UE *UE = PHY_vars_UE_g[0][0];
+  PHY_VARS_UE *UE = arg; //PHY_vars_UE_g[0][0];
   //  int tx_enabled = 0;
   uint32_t rxs=0,txs=0;
   int dummy_rx[UE->frame_parms.nb_antennas_rx][UE->frame_parms.samples_per_tti] __attribute__((aligned(32)));
@@ -948,7 +960,7 @@ void *UE_thread(void *arg) {
 
 #ifdef NAS_UE
   message_p = itti_alloc_new_message(TASK_NAS_UE, INITIALIZE_MESSAGE);
-  itti_send_msg_to_task (TASK_NAS_UE, INSTANCE_DEFAULT, message_p);
+  itti_send_msg_to_task (TASK_NAS_UE, UE->Mod_id + NB_eNB_INST, message_p);
 #endif 
 
   while (!oai_exit) {
@@ -1693,8 +1705,10 @@ void *UE_thread_old(void *arg)
  */
 void init_UE_threads(int inst)
 {
+  struct rx_tx_thread_data *rtd;
+  char name[128];
   PHY_VARS_UE *UE;
- 
+
   UE = PHY_vars_UE_g[inst][0];
 
   pthread_attr_init (&UE->proc.attr_ue);
@@ -1717,12 +1731,23 @@ void init_UE_threads(int inst)
   pthread_cond_init(&UE->proc.proc_rxtx[0].cond_rxtx,NULL);
   pthread_cond_init(&UE->proc.proc_rxtx[1].cond_rxtx,NULL);
   pthread_cond_init(&UE->proc.cond_synch,NULL);
-  pthread_create(&UE->proc.proc_rxtx[0].pthread_rxtx,NULL,UE_thread_rxn_txnp4,(void*)&UE->proc.proc_rxtx[0]);
-  pthread_setname_np( UE->proc.proc_rxtx[0].pthread_rxtx, "rxn_txnp4_even" );
-  pthread_create(&UE->proc.proc_rxtx[1].pthread_rxtx,NULL,UE_thread_rxn_txnp4,(void*)&UE->proc.proc_rxtx[1]);
-  pthread_setname_np( UE->proc.proc_rxtx[1].pthread_rxtx, "rxn_txnp4_odd" );
+  rtd = calloc(1, sizeof(struct rx_tx_thread_data));
+  if (rtd == NULL) abort();
+  rtd->UE = PHY_vars_UE_g[inst][UE->proc.proc_rxtx[0].CC_id];
+  rtd->proc = &UE->proc.proc_rxtx[0];
+  pthread_create(&UE->proc.proc_rxtx[0].pthread_rxtx,NULL,UE_thread_rxn_txnp4,rtd);//(void*)&UE->proc.proc_rxtx[0]);
+  sprintf(name, "rxn_txnp4_even UE %d", inst);
+  pthread_setname_np(UE->proc.proc_rxtx[0].pthread_rxtx, name);
+  rtd = calloc(1, sizeof(struct rx_tx_thread_data));
+  if (rtd == NULL) abort();
+  rtd->UE = PHY_vars_UE_g[inst][UE->proc.proc_rxtx[1].CC_id];
+  rtd->proc = &UE->proc.proc_rxtx[1];
+  pthread_create(&UE->proc.proc_rxtx[1].pthread_rxtx,NULL,UE_thread_rxn_txnp4,rtd);//(void*)&UE->proc.proc_rxtx[1]);
+  sprintf(name, "rxn_txnp4_odd UE %d", inst);
+  pthread_setname_np(UE->proc.proc_rxtx[1].pthread_rxtx, name);
   pthread_create(&UE->proc.pthread_synch,NULL,UE_thread_synch,(void*)UE);
-  pthread_setname_np( UE->proc.pthread_synch, "UE_thread_synch" );
+  sprintf(name, "UE_thread_synch UE %d", inst);
+  pthread_setname_np(UE->proc.pthread_synch, name);
 }
 
 
diff --git a/targets/SIMU/USER/channel_sim.c b/targets/SIMU/USER/channel_sim.c
index 370e50d145257253df3662f8e2dc80f0177a2799..aff1f789eeefe8486457c3b44a2344fdc1320e3e 100644
--- a/targets/SIMU/USER/channel_sim.c
+++ b/targets/SIMU/USER/channel_sim.c
@@ -327,8 +327,8 @@ void do_DL_sig(channel_desc_t *eNB2UE[NUMBER_OF_eNB_MAX][NUMBER_OF_UE_MAX][MAX_N
 	eNB_output_mask[UE_id]=0;
       
 
-	double *r_re_p[2] = {r_re_DL[eNB_id][0],r_re_DL[eNB_id][1]};
-	double *r_im_p[2] = {r_im_DL[eNB_id][0],r_im_DL[eNB_id][1]};
+	double *r_re_p[2] = {r_re_DL[UE_id][0],r_re_DL[UE_id][1]};
+	double *r_im_p[2] = {r_im_DL[UE_id][0],r_im_DL[UE_id][1]};
 
 #ifdef DEBUG_SIM
 	rx_pwr = signal_energy_fp(r_re_p,r_im_p,nb_antennas_rx,frame_parms->ofdm_symbol_size,sf_offset)/(12.0*frame_parms->N_RB_DL);
diff --git a/targets/SIMU/USER/oaisim.c b/targets/SIMU/USER/oaisim.c
index 71bfce44cb1a1ce2ef54927491d10c07ac8e6ed1..02ccefed26989acb363e02440c9ab0e88c54c8f9 100644
--- a/targets/SIMU/USER/oaisim.c
+++ b/targets/SIMU/USER/oaisim.c
@@ -457,7 +457,8 @@ l2l1_task_state_t l2l1_state = L2L1_WAITTING;
 
 extern openair0_timestamp current_eNB_rx_timestamp[NUMBER_OF_eNB_MAX][MAX_NUM_CCs];
 extern openair0_timestamp current_UE_rx_timestamp[NUMBER_OF_UE_MAX][MAX_NUM_CCs];
-
+extern openair0_timestamp last_eNB_rx_timestamp[NUMBER_OF_eNB_MAX][MAX_NUM_CCs];
+extern openair0_timestamp last_UE_rx_timestamp[NUMBER_OF_UE_MAX][MAX_NUM_CCs];
 
 /*------------------------------------------------------------------------------*/
 void *
@@ -590,6 +591,7 @@ l2l1_task (void *args_p)
       switch (ITTI_MSG_ID(message_p)) {
       case INITIALIZE_MESSAGE:
         l2l1_state = L2L1_RUNNING;
+        start_eNB = 1;
         break;
 
       case ACTIVATE_MESSAGE:
@@ -733,57 +735,43 @@ l2l1_task (void *args_p)
 
 	clear_eNB_transport_info (oai_emulation.info.nb_enb_local);
 
-        CC_id=0;
         int all_done=0;
         while (all_done==0) {
-          pthread_mutex_lock(&subframe_mutex);
-          int subframe_eNB_mask_local = subframe_eNB_mask;
-          int subframe_UE_mask_local  = subframe_UE_mask;
-          pthread_mutex_unlock(&subframe_mutex);
-          LOG_D(EMU,"Frame %d, Subframe %d: Checking masks %x,%x\n",frame,sf,subframe_eNB_mask,subframe_UE_mask);
-          if ((subframe_eNB_mask_local == ((1<<NB_eNB_INST)-1)) &&
-              (subframe_UE_mask_local == ((1<<NB_UE_INST)-1)))
-             all_done=1;
-          else
-	    usleep(1500);
+          int i;
+          all_done = 1;
+          for (i = oai_emulation.info.first_enb_local;
+               i < oai_emulation.info.first_enb_local + oai_emulation.info.nb_enb_local;
+               i++)
+            for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++)
+              if (last_eNB_rx_timestamp[i][CC_id] != current_eNB_rx_timestamp[i][CC_id]) {
+                all_done = 0;
+                break;
+              }
+          if (all_done == 1)
+            for (i = 0; i < NB_UE_INST; i++)
+              for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++)
+                if (last_UE_rx_timestamp[i][CC_id] != current_UE_rx_timestamp[i][CC_id]) {
+                  all_done = 0;
+                  break;
+                }
+          if (all_done == 0)
+	    usleep(500);
         }
 
-        //clear subframe masks for next round
-        pthread_mutex_lock(&subframe_mutex);
-        subframe_eNB_mask=0;
-        subframe_UE_mask=0;
-        pthread_mutex_unlock(&subframe_mutex);
-
         // increment timestamps
         for (eNB_inst = oai_emulation.info.first_enb_local;
              (eNB_inst
               < (oai_emulation.info.first_enb_local
                  + oai_emulation.info.nb_enb_local));
              eNB_inst++) {
-	  
-	  current_eNB_rx_timestamp[eNB_inst][CC_id] += PHY_vars_eNB_g[eNB_inst][CC_id]->frame_parms.samples_per_tti;
+          for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++)
+            current_eNB_rx_timestamp[eNB_inst][CC_id] += PHY_vars_eNB_g[eNB_inst][CC_id]->frame_parms.samples_per_tti;
         }
         for (UE_inst = 0; UE_inst<NB_UE_INST;UE_inst++) {
-	  current_UE_rx_timestamp[UE_inst][CC_id] += PHY_vars_UE_g[UE_inst][CC_id]->frame_parms.samples_per_tti;
+          for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++)
+            current_UE_rx_timestamp[UE_inst][CC_id] += PHY_vars_UE_g[UE_inst][CC_id]->frame_parms.samples_per_tti;
         }
 
-
-	if (oai_emulation.info.cli_start_enb[eNB_inst] != 0) {
-	  T(T_ENB_MASTER_TICK, T_INT(eNB_inst), T_INT(frame % 1024), T_INT(slot/2));
-	  /*
-	  LOG_D(EMU,
-		"PHY procedures eNB %d for frame %d, slot %d (subframe TX %d, RX %d) TDD %d/%d Nid_cell %d\n",
-		eNB_inst,
-		frame%MAX_FRAME_NUMBER,
-		2*sf,
-		PHY_vars_eNB_g[eNB_inst][0]->proc[slot >> 1].subframe_tx,
-		PHY_vars_eNB_g[eNB_inst][0]->proc[slot >> 1].subframe_rx,
-		PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.frame_type,
-		PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.tdd_config,
-		PHY_vars_eNB_g[eNB_inst][0]->lte_frame_parms.Nid_cell);
-	  */
-	}
-
         for (eNB_inst = oai_emulation.info.first_enb_local;
              (eNB_inst
               < (oai_emulation.info.first_enb_local
@@ -791,7 +779,6 @@ l2l1_task (void *args_p)
              eNB_inst++) {
           if (oai_emulation.info.cli_start_enb[eNB_inst] != 0) {
         
-	    T(T_ENB_MASTER_TICK, T_INT(eNB_inst), T_INT(frame % 1024), T_INT(sf));
 	    /*
 	    LOG_D(EMU,
 		  "PHY procedures eNB %d for frame %d, subframe %d TDD %d/%d Nid_cell %d\n",
@@ -1223,6 +1210,12 @@ main (int argc, char **argv)
   //Default values if not changed by the user in get_simulation_options();
   pdcp_period = 1;
   omg_period = 1;
+  //Clean ip rule table
+  for(int i =0; i<NUMBER_OF_UE_MAX; i++){
+      char command_line[100];
+      sprintf(command_line, "while ip rule del table %d; do true; done",i+201);
+      system(command_line);
+  }
   // start thread for log gen
   log_thread_init ();
 
@@ -1350,9 +1343,9 @@ main (int argc, char **argv)
 
 #if defined(ENABLE_ITTI)
 
+  // Handle signals until all tasks are terminated
   itti_wait_tasks_end();
 
-
 #else
 
   if (oai_emulation.info.nb_enb_local > 0) {
diff --git a/targets/SIMU/USER/oaisim_functions.c b/targets/SIMU/USER/oaisim_functions.c
index e500c15a3c1d9035ed43e8fcceadafab47320052..c47dc10caf11d95cfa71addff2b4de91f6ad2500 100644
--- a/targets/SIMU/USER/oaisim_functions.c
+++ b/targets/SIMU/USER/oaisim_functions.c
@@ -1036,13 +1036,15 @@ int UE_trx_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) {
 extern pthread_mutex_t subframe_mutex;
 extern int subframe_eNB_mask,subframe_UE_mask;
 
-int eNB_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
-
+int eNB_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc)
+{
+  int ret = nsamps;
   int eNB_id = device->Mod_id;
   int CC_id  = device->CC_id;
 
   int subframe;
-  int sample_count=0;
+  int read_samples, max_samples;
+  openair0_timestamp last = last_eNB_rx_timestamp[eNB_id][CC_id];
 
   *ptimestamp = last_eNB_rx_timestamp[eNB_id][CC_id];
 
@@ -1052,49 +1054,55 @@ int eNB_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void *
 	(*ptimestamp/PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms.samples_per_tti)%10);
   // if we're at a subframe boundary generate UL signals for this eNB
 
-  while (sample_count<nsamps) {
-    while (current_eNB_rx_timestamp[eNB_id][CC_id]<
-	   (nsamps+last_eNB_rx_timestamp[eNB_id][CC_id])) {
-      //      LOG_D(EMU,"eNB: current TS %llu, last TS %llu, sleeping\n",current_eNB_rx_timestamp[eNB_id][CC_id],last_eNB_rx_timestamp[eNB_id][CC_id]);
+  while (nsamps) {
+    while (current_eNB_rx_timestamp[eNB_id][CC_id] == last) {
+      LOG_D(EMU,"eNB: current TS %llu, last TS %llu, sleeping\n",current_eNB_rx_timestamp[eNB_id][CC_id],last_eNB_rx_timestamp[eNB_id][CC_id]);
       usleep(500);
     }
 
-    // tell top-level we are busy
-    pthread_mutex_lock(&subframe_mutex);
-    subframe_eNB_mask|=(1<<eNB_id);
-    pthread_mutex_unlock(&subframe_mutex);
-
-    subframe = (last_eNB_rx_timestamp[eNB_id][CC_id]/PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms.samples_per_tti)%10;
-    LOG_D(EMU,"eNB_trx_read generating UL subframe %d (Ts %llu, current TS %llu)\n",
-	  subframe,(unsigned long long)*ptimestamp,
-	  (unsigned long long)current_eNB_rx_timestamp[eNB_id][CC_id]);
-
-    do_UL_sig(UE2eNB,
-	      enb_data,
-	      ue_data,
-	      subframe,
-	      0,  // abstraction_flag
-	      &PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms,
-	      0,  // frame is only used for abstraction
-	      eNB_id,
-	      CC_id);
-
-    last_eNB_rx_timestamp[eNB_id][CC_id] += PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms.samples_per_tti;
-    sample_count += PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms.samples_per_tti;
-  }
+    read_samples = nsamps;
+    max_samples = current_eNB_rx_timestamp[eNB_id][CC_id]-last;
+    if (read_samples > max_samples)
+      read_samples = max_samples;
 
+    last += read_samples;
+    nsamps -= read_samples;
 
+    if (current_eNB_rx_timestamp[eNB_id][CC_id] == last) {
+      subframe = (last/PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms.samples_per_tti)%10;
+      //subframe = (subframe+9) % 10;
 
-  return(nsamps);
-}
+      LOG_D(PHY,"eNB_trx_read generating UL subframe %d (Ts %llu, current TS %llu)\n",
+            subframe,(unsigned long long)*ptimestamp,
+            (unsigned long long)current_eNB_rx_timestamp[eNB_id][CC_id]);
+    
+      do_UL_sig(UE2eNB,
+                enb_data,
+                ue_data,
+                subframe,
+                0,  // abstraction_flag
+                &PHY_vars_eNB_g[eNB_id][CC_id]->frame_parms,
+                0,  // frame is only used for abstraction
+                eNB_id,
+                CC_id);
+
+      last_eNB_rx_timestamp[eNB_id][CC_id] = last;
+    }
+  }
+  
+  last_eNB_rx_timestamp[eNB_id][CC_id] = last;
 
-int UE_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
+  return ret;
+}
 
+int UE_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc)
+{
+  int ret = nsamps;
   int UE_id = device->Mod_id;
   int CC_id  = device->CC_id;
   int subframe;
-  int sample_count=0;
-  int read_size;
+  int read_samples, max_samples;
+  openair0_timestamp last = last_UE_rx_timestamp[UE_id][CC_id];
 
   *ptimestamp = last_UE_rx_timestamp[UE_id][CC_id];
 
@@ -1103,55 +1111,59 @@ int UE_trx_read(openair0_device *device, openair0_timestamp *ptimestamp, void **
         (unsigned long long)last_UE_rx_timestamp[UE_id][CC_id],
 	cc);
 
-  if (nsamps < PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti)
-    read_size = nsamps;
-  else
-    read_size = PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti;
-
-  while (sample_count<nsamps) {
-    while (current_UE_rx_timestamp[UE_id][CC_id] <
-	   (last_UE_rx_timestamp[UE_id][CC_id]+read_size)) {
-      //LOG_D(EMU,"UE_trx_read : current TS %d, last TS %d, sleeping\n",current_UE_rx_timestamp[UE_id][CC_id],last_UE_rx_timestamp[UE_id][CC_id]);
+  while (nsamps) {
+    /* wait for all processing to be finished */
+    while (1) {
+      PHY_VARS_UE *UE = PHY_vars_UE_g[UE_id][0];
+      int ready = 1;
+      int i;
+      for (i = 0; i < 2; i++)
+        if (UE->proc.proc_rxtx[i].instance_cnt_rxtx >= 0) ready = 0;
+      if (UE->proc.instance_cnt_synch >= 0) ready = 0;
+      if (ready) break;
+      usleep(500);
+    }
+    while (current_UE_rx_timestamp[UE_id][CC_id] == last) {
+      LOG_D(EMU,"UE_trx_read : current TS %d, last TS %d, sleeping\n",current_UE_rx_timestamp[UE_id][CC_id],last_UE_rx_timestamp[UE_id][CC_id]);
 
       usleep(500);
     }
 
     //    LOG_D(EMU,"UE_trx_read : current TS %d, last TS %d, sleeping\n",current_UE_rx_timestamp[UE_id][CC_id],last_UE_rx_timestamp[UE_id][CC_id]);
       
-    // tell top-level we are busy 
-    pthread_mutex_lock(&subframe_mutex);
-    subframe_UE_mask|=(1<<UE_id);
-    pthread_mutex_unlock(&subframe_mutex);
-
-
-    // otherwise we have one subframe here so generate the received signal
-    subframe = (last_UE_rx_timestamp[UE_id][CC_id]/PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti)%10;
-    if ((last_UE_rx_timestamp[UE_id][CC_id]%PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti) > 0)
-      subframe++;
-
-    last_UE_rx_timestamp[UE_id][CC_id] += read_size;
-    sample_count += read_size;
-
-    if (subframe > 9)
-      return(nsamps);
-
-    LOG_D(PHY,"UE_trx_read generating DL subframe %d (Ts %llu, current TS %llu)\n",
-	  subframe,(unsigned long long)*ptimestamp,
-	  (unsigned long long)current_UE_rx_timestamp[UE_id][CC_id]);
-    do_DL_sig(eNB2UE,
-	      enb_data,
-	      ue_data,
-	      subframe,
-	      0, //abstraction_flag,
-	      &PHY_vars_UE_g[UE_id][CC_id]->frame_parms,
-	      UE_id,
-	      CC_id);
-
-
+    read_samples = nsamps;
+    max_samples = current_UE_rx_timestamp[UE_id][CC_id]-last;
+    if (read_samples > max_samples)
+      read_samples = max_samples;
+
+    last += read_samples;
+    nsamps -= read_samples;
+
+    if (current_UE_rx_timestamp[UE_id][CC_id] == last) {
+      // we have one subframe here so generate the received signal
+      subframe = (last/PHY_vars_UE_g[UE_id][CC_id]->frame_parms.samples_per_tti)%10;
+      //subframe = (subframe+9) % 10;
+
+      LOG_D(PHY,"UE_trx_read generating DL subframe %d (Ts %llu, current TS %llu)\n",
+            subframe,(unsigned long long)*ptimestamp,
+            (unsigned long long)current_UE_rx_timestamp[UE_id][CC_id]);
+
+      do_DL_sig(eNB2UE,
+                enb_data,
+                ue_data,
+                subframe,
+                0, //abstraction_flag,
+                &PHY_vars_UE_g[UE_id][CC_id]->frame_parms,
+                UE_id,
+                CC_id);
+
+      last_UE_rx_timestamp[UE_id][CC_id] = last;
+    }
   }
 
+  last_UE_rx_timestamp[UE_id][CC_id] = last;
 
-  return(nsamps);
+  return ret;
 }
 
 int eNB_trx_write(openair0_device *device,openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
diff --git a/targets/build_helper.bash b/targets/build_helper.bash
index 163be3ba53504b764e21aebcccab1f76e5923246..f161275c65d09c1d9a7a5585ceed4326ead50460 100755
--- a/targets/build_helper.bash
+++ b/targets/build_helper.bash
@@ -852,8 +852,8 @@ compile_nas_tools() {
         fi
         echo_success "make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS/tools all"
         make --directory=$OPENAIR3_DIR/NAS/EURECOM-NAS/tools all
-        rm .ue.nvram
-        rm .usim.nvram
+        rm .ue.nvram0
+        rm .usim.nvram0
         touch /tmp/nas_cleaned
 }
 
@@ -1027,13 +1027,13 @@ install_oaisim() {
 
 install_nas_tools() {
     cd $OPENAIR_TARGETS/bin
-    if [ ! -f .ue.nvram ]; then
-        echo_success "generate .ue_emm.nvram .ue.nvram"
+    if [ ! -f .ue.nvram0 ]; then
+        echo_success "generate .ue_emm.nvram0 .ue.nvram0"
         $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --gen
     fi
 
-    if [ ! -f .usim.nvram ]; then
-        echo_success "generate .usim.nvram"
+    if [ ! -f .usim.nvram0 ]; then
+        echo_success "generate .usim.nvram0"
         $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/usim_data --gen
     fi
     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --print
diff --git a/targets/build_oai.bash b/targets/build_oai.bash
index 1303ef9e7e9cba90e231bb3e9203982fe3f2ded7..7a6a179bebc431acb4158b218cee4bcbd8d724ec 100755
--- a/targets/build_oai.bash
+++ b/targets/build_oai.bash
@@ -756,13 +756,13 @@ if [ $RUN -ne 0 ]; then
 		    install_nasmesh
                 else
                     # prepare NAS for UE
-                    if [ ! -f .ue.nvram ]; then
-                        echo_success "generate .ue_emm.nvram .ue.nvram"
+                    if [ ! -f .ue.nvram0 ]; then
+                        echo_success "generate .ue_emm.nvram0 .ue.nvram0"
                         $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --gen
                     fi
 
-                    if [ ! -f .usim.nvram ]; then
-                        echo_success "generate .usim.nvram"
+                    if [ ! -f .usim.nvram0 ]; then
+                        echo_success "generate .usim.nvram0"
                         $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/usim_data --gen
                     fi
                     $OPENAIR3_DIR/NAS/EURECOM-NAS/bin/ue_data --print