diff --git a/cmake_targets/CMakeLists.txt b/cmake_targets/CMakeLists.txt
index 34b868642d966cd58f1a4491169a67cfb0ee5dff..c2d5168406997f5a13a7e6de70428689f70e7f1d 100644
--- a/cmake_targets/CMakeLists.txt
+++ b/cmake_targets/CMakeLists.txt
@@ -242,6 +242,7 @@ add_boolean_option(UE_DEBUG_TRACE      False "Activate UE debug trace")
 add_boolean_option(UE_TIMING_TRACE     False "Activate UE timing trace")
 add_boolean_option(DISABLE_LOG_X       False "Deactivate all LOG_* macros")
 add_boolean_option(USRP_REC_PLAY       False "Enable USRP record playback mode")
+add_boolean_option(UE_NAS_USE_TUN      False "Enable UE NAS TUN device instead of ue_ip.ko")
 
 add_boolean_option(DEBUG_CONSOLE False "makes debugging easier, disables stdout/stderr buffering")
 
diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai
index d7b98bce413beb48f97f9447b80fe913c88618f1..440ff13b988c2daa7a1400dfb7b0ba5353fe2b59 100755
--- a/cmake_targets/build_oai
+++ b/cmake_targets/build_oai
@@ -67,6 +67,7 @@ UE_TIMING_TRACE="False"
 DISABLE_LOG_X="False"
 USRP_REC_PLAY="False"
 BUILD_ECLIPSE=0
+UE_NAS_USE_TUN="False"
 trap handle_ctrl_c INT
 
 function print_help() {
@@ -156,6 +157,8 @@ Options
    Build eclipse project files. Paths are auto corrected by fixprj.sh
 --usrp-recplay
    Build for I/Q record-playback modes
+--ue-nas-use-tun
+   Use TUN devices for the UEs instead of ue_ip.ko
 Usage (first build):
  oaisim (eNB + UE): ./build_oai -I  --oaisim -x --install-system-files
  Eurecom EXMIMO + COTS UE : ./build_oai -I  --eNB -x --install-system-files
@@ -347,6 +350,10 @@ function main() {
             USRP_REC_PLAY="True"
             echo_info "Enabling USRP record playback mode"
             shift 1;;
+        --ue-nas-use-tun)
+            UE_NAS_USE_TUN="True"
+            echo_info "Enabling UE NAS TUN device usage instead of ue_ip.ko"
+            shift 1;;
         -h | --help)
             print_help
             exit 1;;
@@ -696,6 +703,7 @@ function main() {
     echo "set ( RRC_ASN1_VERSION \"${REL}\")" >>  $cmake_file
     echo "set ( ENABLE_VCD_FIFO $VCD_TIMING )" >>  $cmake_file
     echo "set ( T_TRACER $T_TRACER )"          >>  $cmake_file
+    echo "set ( UE_NAS_USE_TUN $UE_NAS_USE_TUN )" >>  $cmake_file
     echo 'include(${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt)' >> $cmake_file
     [ "$CLEAN" = "1" ] && rm -rf $DIR/$oaisim_build_dir/build
     mkdir -p $DIR/$oaisim_build_dir/build
diff --git a/openair1/SIMULATION/ETH_TRANSPORT/netlink_init.c b/openair1/SIMULATION/ETH_TRANSPORT/netlink_init.c
index b578721a1a0ffed4b79f7956551d8ae3fe49c00c..b90961a1c092fa84cdc6ee1fd2e057369fe44060 100644
--- a/openair1/SIMULATION/ETH_TRANSPORT/netlink_init.c
+++ b/openair1/SIMULATION/ETH_TRANSPORT/netlink_init.c
@@ -39,6 +39,13 @@
 #include <fcntl.h>
 #include <errno.h>
 #include "platform_constants.h"
+#ifdef UE_NAS_USE_TUN
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include "openairinterface5g_limits.h"
+#endif
 
 char nl_rx_buf[NL_MAX_PAYLOAD];
 
@@ -47,12 +54,121 @@ struct nlmsghdr *nas_nlh_tx = NULL;
 struct nlmsghdr *nas_nlh_rx = NULL;
 struct iovec nas_iov_tx;
 struct iovec nas_iov_rx = {nl_rx_buf, sizeof(nl_rx_buf)};
+#ifdef UE_NAS_USE_TUN
+int nas_sock_fd[NUMBER_OF_UE_MAX];
+#else
 int nas_sock_fd;
+#endif
 struct msghdr nas_msg_tx;
 struct msghdr nas_msg_rx;
 
 #define GRAAL_NETLINK_ID 31
 
+#ifdef UE_NAS_USE_TUN
+
+static int tun_alloc(char *dev)
+{
+  struct ifreq ifr;
+  int fd, err;
+
+  if( (fd = open("/dev/net/tun", O_RDWR)) < 0 ) {
+    printf("[TUN] failed to open /dev/net/tun\n");
+    return -1;
+  }
+
+  memset(&ifr, 0, sizeof(ifr));
+
+  /* Flags: IFF_TUN   - TUN device (no Ethernet headers)
+   *        IFF_TAP   - TAP device
+   *
+   *        IFF_NO_PI - Do not provide packet information
+   */
+  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
+  if( *dev )
+     strncpy(ifr.ifr_name, dev, IFNAMSIZ);
+
+  if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ){
+     close(fd);
+     return err;
+  }
+  strcpy(dev, ifr.ifr_name);
+  return fd;
+}
+
+int netlink_init(void)
+{
+  int i;
+  int ret;
+
+  char ifname[64];
+  for (i = 0; i < NUMBER_OF_UE_MAX; i++) {
+    sprintf(ifname, "oip%d", i+1);
+    nas_sock_fd[i] = tun_alloc(ifname);
+
+    if (nas_sock_fd[i] == -1) {
+      printf("[NETLINK] Error opening socket %d (%d:%s)\n",nas_sock_fd[i],errno, strerror(errno));
+#if defined(LINK_ENB_PDCP_TO_IP_DRIVER)
+      exit(1);
+#endif
+    }
+
+    printf("[NETLINK]Opened socket with fd %d\n",nas_sock_fd[i]);
+
+#if !defined(PDCP_USE_NETLINK_QUEUES)
+    ret = fcntl(nas_sock_fd[i],F_SETFL,O_NONBLOCK);
+
+    if (ret == -1) {
+      printf("[NETLINK] Error fcntl (%d:%s)\n",errno, strerror(errno));
+#if defined(LINK_ENB_PDCP_TO_IP_DRIVER)
+      exit(1);
+#endif
+    }
+
+#endif
+
+    memset(&nas_src_addr, 0, sizeof(nas_src_addr));
+    nas_src_addr.nl_family = AF_NETLINK;
+    nas_src_addr.nl_pid = 1;//getpid();  /* self pid */
+    nas_src_addr.nl_groups = 0;  /* not in mcast groups */
+    ret = bind(nas_sock_fd[i], (struct sockaddr*)&nas_src_addr, sizeof(nas_src_addr));
+
+
+
+    memset(&nas_dest_addr, 0, sizeof(nas_dest_addr));
+    nas_dest_addr.nl_family = AF_NETLINK;
+    nas_dest_addr.nl_pid = 0;   /* For Linux Kernel */
+    nas_dest_addr.nl_groups = 0; /* unicast */
+
+    // TX PART
+    nas_nlh_tx=(struct nlmsghdr *)malloc(NLMSG_SPACE(NL_MAX_PAYLOAD));
+    memset(nas_nlh_tx, 0, NLMSG_SPACE(NL_MAX_PAYLOAD));
+    /* Fill the netlink message header */
+    nas_nlh_tx->nlmsg_len = NLMSG_SPACE(NL_MAX_PAYLOAD);
+    nas_nlh_tx->nlmsg_pid = 1;//getpid();  /* self pid */
+    nas_nlh_tx->nlmsg_flags = 0;
+
+    nas_iov_tx.iov_base = (void *)nas_nlh_tx;
+    nas_iov_tx.iov_len = nas_nlh_tx->nlmsg_len;
+    memset(&nas_msg_tx,0,sizeof(nas_msg_tx));
+    nas_msg_tx.msg_name = (void *)&nas_dest_addr;
+    nas_msg_tx.msg_namelen = sizeof(nas_dest_addr);
+    nas_msg_tx.msg_iov = &nas_iov_tx;
+    nas_msg_tx.msg_iovlen = 1;
+
+
+    // RX PART
+    memset(&nas_msg_rx,0,sizeof(nas_msg_rx));
+    nas_msg_rx.msg_name = (void *)&nas_src_addr;
+    nas_msg_rx.msg_namelen = sizeof(nas_src_addr);
+    nas_msg_rx.msg_iov = &nas_iov_rx;
+    nas_msg_rx.msg_iovlen = 1;
+  }
+
+  return 1;
+}
+
+#else /* UE_NAS_USE_TUN */
+
 int netlink_init(void)
 {
   int ret;
@@ -119,3 +235,5 @@ int netlink_init(void)
 
   return(nas_sock_fd);
 }
+
+#endif /* UE_NAS_USE_TUN */
diff --git a/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c b/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
index 2de1b3fc743a4bd4ca41d9de64866fa0a8c79285..50663cdc4fc8da1ade52b30871b7decc2581ad6f 100644
--- a/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
+++ b/openair2/LAYER2/PDCP_v10.1.0/pdcp_fifo.c
@@ -72,7 +72,11 @@ extern struct nlmsghdr *nas_nlh_tx;
 extern struct nlmsghdr *nas_nlh_rx;
 extern struct iovec nas_iov_tx;
 extern struct iovec nas_iov_rx;
+#ifdef UE_NAS_USE_TUN
+extern int nas_sock_fd[NUMBER_OF_UE_MAX];
+#else
 extern int nas_sock_fd;
+#endif
 extern struct msghdr nas_msg_tx;
 extern struct msghdr nas_msg_rx;
 
@@ -239,7 +243,11 @@ int pdcp_fifo_flush_sdus(const protocol_ctxt_t* const  ctxt_pP)
           nas_nlh_tx->nlmsg_len += pdcp_output_sdu_bytes_to_write;
           VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_UE_PDCP_FLUSH_SIZE, pdcp_output_sdu_bytes_to_write);
           VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_FLUSH_BUFFER, 1 );
+#ifdef UE_NAS_USE_TUN
+          ret = write(nas_sock_fd[ctxt_pP->module_id], &(sdu_p->data[sizeof(pdcp_data_ind_header_t)]), pdcp_output_sdu_bytes_to_write);
+#else
           ret = sendmsg(nas_sock_fd,&nas_msg_tx,0);
+#endif
           VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_FLUSH_BUFFER, 0 );
           VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_UE_PDCP_FLUSH_ERR, ret );
 
@@ -372,6 +380,62 @@ int pdcp_fifo_flush_sdus(const protocol_ctxt_t* const  ctxt_pP)
 //-----------------------------------------------------------------------------
 int pdcp_fifo_read_input_sdus (const protocol_ctxt_t* const  ctxt_pP)
 {
+#ifdef UE_NAS_USE_TUN
+  protocol_ctxt_t ctxt = *ctxt_pP;
+  hash_key_t key = HASHTABLE_NOT_A_KEY_VALUE;
+  hashtable_rc_t h_rc;
+  pdcp_t* pdcp_p = NULL;
+  int len;
+  rb_id_t rab_id = DEFAULT_RAB_ID;
+
+  do {
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_READ, 1 );
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_READ_BUFFER, 1 );
+    len = read(nas_sock_fd[ctxt_pP->module_id], &nl_rx_buf, NL_MAX_PAYLOAD);
+    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_READ_BUFFER, 0 );
+
+    if (len<=0) continue;
+    LOG_D(PDCP, "PDCP_COLL_KEY_DEFAULT_DRB_VALUE(module_id=%d, rnti=%x, enb_flag=%d)\n",
+          ctxt.module_id, ctxt.rnti, ctxt.enb_flag);
+    key = PDCP_COLL_KEY_DEFAULT_DRB_VALUE(ctxt.module_id, ctxt.rnti, ctxt.enb_flag);
+    h_rc = hashtable_get(pdcp_coll_p, key, (void**)&pdcp_p);
+    if (h_rc == HASH_TABLE_OK) {
+      LOG_D(PDCP, "[FRAME %5u][UE][NETLINK][IP->PDCP] INST %d: Received socket with length %d on Rab %d \n",
+            ctxt.frame, ctxt.instance, len, rab_id);
+
+      LOG_D(PDCP, "[FRAME %5u][UE][IP][INSTANCE %u][RB %u][--- PDCP_DATA_REQ / %d Bytes --->][PDCP][MOD %u][UE %u][RB %u]\n",
+            ctxt.frame, ctxt.instance, rab_id, len, ctxt.module_id,
+            ctxt.rnti, rab_id);
+      MSC_LOG_RX_MESSAGE((ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_PDCP_ENB:MSC_PDCP_UE,
+                         (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_IP_ENB:MSC_IP_UE,
+                         NULL, 0,
+                         MSC_AS_TIME_FMT" DATA-REQ inst %u rb %u rab %u size %u",
+                         MSC_AS_TIME_ARGS(ctxt_pP),
+                         ctxt.instance, rab_id, rab_id, len);
+
+      pdcp_data_req(&ctxt, SRB_FLAG_NO, rab_id, RLC_MUI_UNDEFINED,
+                    RLC_SDU_CONFIRM_NO, len, nl_rx_buf,
+                    PDCP_TRANSMISSION_MODE_DATA);
+    } else {
+      MSC_LOG_RX_DISCARDED_MESSAGE(
+      (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_PDCP_ENB:MSC_PDCP_UE,
+      (ctxt_pP->enb_flag == ENB_FLAG_YES) ? MSC_IP_ENB:MSC_IP_UE,
+      NULL,
+      0,
+      MSC_AS_TIME_FMT" DATA-REQ inst %u rb %u rab %u size %u",
+      MSC_AS_TIME_ARGS(ctxt_pP),
+      ctxt.instance, rab_id, rab_id, len);
+      LOG_D(PDCP,
+            "[FRAME %5u][UE][IP][INSTANCE %u][RB %u][--- PDCP_DATA_REQ / %d Bytes ---X][PDCP][MOD %u][UE %u][RB %u] NON INSTANCIATED INSTANCE key 0x%"PRIx64", DROPPED\n",
+            ctxt.frame, ctxt.instance, rab_id, len, ctxt.module_id,
+            ctxt.rnti, rab_id, key);
+    }
+  } while (len > 0);
+  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_FIFO_READ, 0 );
+  return len;
+
+#else /* UE_NAS_USE_TUN */
+
 #ifdef PDCP_USE_NETLINK
   protocol_ctxt_t                ctxt_cpy = *ctxt_pP;
   protocol_ctxt_t                ctxt;
@@ -764,6 +828,7 @@ int pdcp_fifo_read_input_sdus (const protocol_ctxt_t* const  ctxt_pP)
 #else // neither PDCP_USE_NETLINK nor PDCP_USE_RT_FIFO
   return 0;
 #endif // PDCP_USE_NETLINK
+#endif /* #else UE_NAS_USE_TUN */
 }