Add network emulation feature.

Issue-ID: SIM-78
Change-Id: I20a9ad92ed09316ab65b9ef74cdbd93b1082a1f6
Signed-off-by: Alex Stancu <alexandru.stancu@highstreet-technologies.com>
diff --git a/ntsimulator/deploy/base/build_ntsim-ng.sh b/ntsimulator/deploy/base/build_ntsim-ng.sh
index fe7a367..aa24594 100755
--- a/ntsimulator/deploy/base/build_ntsim-ng.sh
+++ b/ntsimulator/deploy/base/build_ntsim-ng.sh
@@ -49,6 +49,7 @@
     "utils/http_client.c"
     "utils/nts_utils.c"
     "utils/nc_client.c"
+    "utils/network_emulation.c"
     "features/ves_pnf_registration/ves_pnf_registration.c"
     "features/ves_heartbeat/ves_heartbeat.c"
     "features/ves_file_ready/ves_file_ready.c"
diff --git a/ntsimulator/deploy/base/ubuntu.Dockerfile b/ntsimulator/deploy/base/ubuntu.Dockerfile
index 354ef0c..cacd9d1 100644
--- a/ntsimulator/deploy/base/ubuntu.Dockerfile
+++ b/ntsimulator/deploy/base/ubuntu.Dockerfile
@@ -161,9 +161,9 @@
     openssh-client \
     vsftpd \
     openssh-server \
+    iproute2 \
     && rm -rf /var/lib/apt/lists/*
     
-
 ARG BUILD_WITH_DEBUG
 ENV BUILD_WITH_DEBUG=${BUILD_WITH_DEBUG}
 RUN if [ -n "${BUILD_WITH_DEBUG}" ]; then DEBIAN_FRONTEND="noninteractive" apt-get install -y gdb valgrind nano mc && unset BUILD_WITH_DEBUG; fi
diff --git a/ntsimulator/deploy/base/yang/nts-network-function.yang b/ntsimulator/deploy/base/yang/nts-network-function.yang
index 626038d..69c39fc 100644
--- a/ntsimulator/deploy/base/yang/nts-network-function.yang
+++ b/ntsimulator/deploy/base/yang/nts-network-function.yang
@@ -21,6 +21,12 @@
   description
     "This module contains YANG definitions for the Network Topology Simulator - Network Functions";
 
+  revision 2021-05-17 {
+    description
+      "Added support for network emulation.";
+    reference
+      "O-RAN-SC SIM project";
+  }
   revision 2021-03-26 {
     description
       "Added info container, and expanted feature control mechanism and status.";
@@ -183,6 +189,139 @@
       uses ntsc:netconf-config-g;
       uses ntsc:ves-config-g;
     }
+    container network-emulation {
+      description
+        "Container which encompasses the details of the network emulation.";
+      leaf limit {
+        type uint16 {
+          range "16 .. max";
+        }
+        units "packets";
+        default "1000";
+        description
+          "Maximum number of packets the qdisc may hold queued at a time.";
+      }
+      container delay {
+        description
+          "Adds the chosen delay to the packets outgoing to chosen network interface.";
+        leaf time {
+          type uint16;
+          units "miliseconds (ms)";
+          description
+            "Delay time between packets.";
+        }
+        leaf jitter {
+          type uint16;
+          units "miliseconds (ms)";
+          description
+            "Delay jitter.";
+        }
+        leaf correlation {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Delay correlation.";
+        }
+        leaf distribution {
+          type enumeration {
+            enum uniform {
+              description
+                "Uniform delay distribution.";
+            }
+            enum normal {
+              description
+                "Normal delay distribution.";
+            }
+            enum pareto {
+              description
+                "Pareto delay distribution.";
+            }
+            enum paretonormal {
+              description
+                "Paretonormal delay distribution.";
+            }
+          }
+          description
+            "Delay distribution.";
+        }
+      }
+      leaf loss {
+        type uint8 {
+          range "0 .. 100";
+        }
+        units "percentage";
+        description
+          "Adds an independent loss probability to the packets outgoing from the chosen network interface.";
+      }
+      container corruption {
+        description
+          "Allows the emulation of random noise introducing an error in a random position for a chosen percent of packets.";
+        leaf percentage {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Corruption percentage.";
+        }
+        leaf correlation {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Corruption correlation.";
+        }
+      }
+      container duplication {
+        description
+          "Using this option the chosen percent of packets is duplicated before queuing them.";
+        leaf percentage {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Duplication percentage.";
+        }
+        leaf correlation {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Duplication correlation.";
+        }
+      }
+      container reordering {
+        description
+          "Used together with delay; a percentage of packets are sent immediately (with defined correlation) while the others are delayed (by specified delay).";
+        leaf percentage {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Reordering percentage.";
+        }
+        leaf correlation {
+          type uint8 {
+            range "0 .. 100";
+          }
+          units "percentage";
+          description
+            "Reordering correlation.";
+        }
+      }
+      leaf rate {
+        type uint16;
+        units "kbits";
+        description
+          "Delay packets based on packet size.";
+      }
+    }
     container sdn-controller {
       description
         "Groups details about the SDN Controller.";
diff --git a/ntsimulator/deploy/blank/container-tag.yaml b/ntsimulator/deploy/blank/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/blank/container-tag.yaml
+++ b/ntsimulator/deploy/blank/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/deploy/nts-manager/container-tag.yaml b/ntsimulator/deploy/nts-manager/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/nts-manager/container-tag.yaml
+++ b/ntsimulator/deploy/nts-manager/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/deploy/o-ran-du/container-tag.yaml b/ntsimulator/deploy/o-ran-du/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/o-ran-du/container-tag.yaml
+++ b/ntsimulator/deploy/o-ran-du/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/deploy/o-ran-ru-fh/container-tag.yaml b/ntsimulator/deploy/o-ran-ru-fh/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/o-ran-ru-fh/container-tag.yaml
+++ b/ntsimulator/deploy/o-ran-ru-fh/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/deploy/o-ran/container-tag.yaml b/ntsimulator/deploy/o-ran/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/o-ran/container-tag.yaml
+++ b/ntsimulator/deploy/o-ran/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/deploy/x-ran/container-tag.yaml b/ntsimulator/deploy/x-ran/container-tag.yaml
index 7377987..edd6735 100644
--- a/ntsimulator/deploy/x-ran/container-tag.yaml
+++ b/ntsimulator/deploy/x-ran/container-tag.yaml
@@ -1,2 +1,2 @@
 ---
-tag: 1.3.3
\ No newline at end of file
+tag: 1.3.4
\ No newline at end of file
diff --git a/ntsimulator/ntsim-ng/core/app/app_common.c b/ntsimulator/ntsim-ng/core/app/app_common.c
index 74be703..e74b655 100644
--- a/ntsimulator/ntsim-ng/core/app/app_common.c
+++ b/ntsimulator/ntsim-ng/core/app/app_common.c
@@ -20,6 +20,7 @@
 #include "supervisor.h"
 #include "utils/log_utils.h"
 #include "utils/sys_utils.h"
+#include "utils/network_emulation.h"
 #include <stdio.h>
 #include <assert.h>
 
@@ -28,6 +29,8 @@
 #include "core/framework.h"
 
 static int app_common_populate_info(void);
+static int app_common_populate_network_emulation_info(void);
+static int app_common_populate_network_emulation_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
 
 int app_common_init(void) {
     assert_session();
@@ -38,20 +41,35 @@
         return NTS_ERR_FAILED;
     }
 
+    network_emulation_init();
+
+    rc = app_common_populate_network_emulation_info();
+    if(rc != NTS_ERR_OK) {
+        log_error("app_common_populate_network_emulation() failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_module_change_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH, app_common_populate_network_emulation_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_UPDATE, &session_subscription);
+    if(rc != SR_ERR_OK) {
+        log_error("could not subscribe to faults");
+        return NTS_ERR_FAILED;
+    }
+
     return NTS_ERR_OK;
 }
 
 static int app_common_populate_info(void) {
     int rc;
+
     if (framework_environment.nts.build_time && strlen(framework_environment.nts.build_time) > 0) {
-        rc  = sr_set_item_str(session_operational, NTS_NF_INFO_BUILD_TIME_XPATH, framework_environment.nts.build_time, 0, 0);
+        rc  = sr_set_item_str(session_operational, NTS_NF_INFO_SCHEMA_XPATH"/build-time", framework_environment.nts.build_time, 0, 0);
         if(rc != SR_ERR_OK) {
             log_error("sr_set_item_str failed\n");
             return NTS_ERR_FAILED;
         }
     }
 
-    rc = sr_set_item_str(session_operational, NTS_NF_INFO_VERSION_XPATH, framework_environment.nts.version, 0, 0);
+    rc = sr_set_item_str(session_operational, NTS_NF_INFO_SCHEMA_XPATH"/version", framework_environment.nts.version, 0, 0);
     if(rc != SR_ERR_OK) {
         log_error("sr_set_item_str failed\n");
         return NTS_ERR_FAILED;
@@ -65,3 +83,213 @@
 
     return NTS_ERR_OK;
 }
+
+static int app_common_populate_network_emulation_info(void) {
+    int rc;
+
+    rc  = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/limit", NETWORK_EMULATION_DEFAULT_LIMIT, 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/time", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/jitter", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/correlation", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/distribution", "normal", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/loss", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/corruption/percentage", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/corruption/correlation", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/duplication/percentage", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/duplication/correlation", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/reordering/percentage", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/reordering/correlation", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_set_item_str(session_running, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/rate", "0", 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_set_item_str failed\n");
+        return NTS_ERR_FAILED;
+    }
+
+    rc = sr_apply_changes(session_running, 0, 0);
+    if(rc != SR_ERR_OK) {
+        log_error("sr_apply_changes failed: %s\n", sr_strerror(rc));
+        return NTS_ERR_FAILED;
+    }
+
+    return NTS_ERR_OK;
+}
+
+static int app_common_populate_network_emulation_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data) {
+    
+    if(event == SR_EV_UPDATE) {
+        sr_change_iter_t *it = 0;
+        int rc = SR_ERR_OK;
+        sr_change_oper_t oper;
+        sr_val_t *old_value = 0;
+        sr_val_t *new_value = 0;
+
+        rc = sr_get_changes_iter(session, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"//.", &it);
+        if(rc != SR_ERR_OK) {
+            log_error("sr_get_changes_iter failed\n");
+            return SR_ERR_VALIDATION_FAILED;
+        }
+
+        uint16_t delay_time = 0;
+        uint16_t delay_jitter = 0;
+
+        while((rc = sr_get_change_next(session, it, &oper, &old_value, &new_value)) == SR_ERR_OK) {
+            if(new_value->xpath && (strstr(new_value->xpath, "/delay/time"))) {
+                delay_time = new_value->data.uint16_val;
+            }
+
+            if(new_value->xpath && (strstr(new_value->xpath, "/delay/jitter"))) {
+                delay_jitter = new_value->data.uint16_val;
+            }
+            sr_free_val(old_value);
+            sr_free_val(new_value);
+        }
+
+        sr_free_change_iter(it);
+
+        if((delay_time == 0) || (delay_jitter == 0)) {
+            rc = sr_set_item_str(session, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/distribution", "normal", 0, 0);
+            if(rc != SR_ERR_OK) {
+                log_error("sr_set_item failed\n");
+                return SR_ERR_VALIDATION_FAILED;
+            }
+        }
+
+        if(delay_time == 0) {
+            rc = sr_set_item_str(session, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/jitter", "0", 0, 0);
+            if(rc != SR_ERR_OK) {
+                log_error("sr_set_item failed\n");
+                return SR_ERR_VALIDATION_FAILED;
+            }
+
+            rc = sr_set_item_str(session, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"/delay/correlation", "0", 0, 0);
+            if(rc != SR_ERR_OK) {
+                log_error("sr_set_item failed\n");
+                return SR_ERR_VALIDATION_FAILED;
+            }
+        }
+    }
+    else if(event == SR_EV_DONE) {
+        sr_val_t *values = NULL;
+        size_t count = 0;
+        
+        int rc = sr_get_items(session, NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH"//.", 0, 0, &values, &count);
+        if (rc != SR_ERR_OK) {
+            log_error("sr_get_items failed\n");
+            return rc;
+        }
+
+        network_emultation_settings_t s;
+
+        for(size_t i = 0; i < count; i++) {
+            if(strstr(values[i].xpath, "/limit")) {
+                s.limit = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/delay/time")) {
+                s.delay.time = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/delay/jitter")) {
+                s.delay.jitter = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/delay/correlation")) {
+                s.delay.correlation = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/delay/distribution")) {
+                s.delay.distribution = strdup(values[i].data.string_val);
+            }
+            else if(strstr(values[i].xpath, "/loss")) {
+                s.loss = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/corruption/percentage")) {
+                s.corruption.percentage = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/corruption/correlation")) {
+                s.corruption.correlation = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/duplication/percentage")) {
+                s.duplication.percentage = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/duplication/correlation")) {
+                s.duplication.correlation = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/reordering/percentage")) {
+                s.reordering.percentage = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/reordering/correlation")) {
+                s.reordering.correlation = values[i].data.uint16_val;
+            }
+            else if(strstr(values[i].xpath, "/rate")) {
+                s.rate = values[i].data.uint16_val;
+            }
+        }
+
+        sr_free_values(values, count);
+        if(network_emulation_update(&s) != NTS_ERR_OK) {
+            log_error("network_emulation_update() failed\n");
+            free(s.delay.distribution);
+            return SR_ERR_OPERATION_FAILED;
+        }
+        free(s.delay.distribution);
+    }
+
+    return SR_ERR_OK;
+}
diff --git a/ntsimulator/ntsim-ng/core/app/network_function.c b/ntsimulator/ntsim-ng/core/app/network_function.c
index af80c94..2a5ff0f 100644
--- a/ntsimulator/ntsim-ng/core/app/network_function.c
+++ b/ntsimulator/ntsim-ng/core/app/network_function.c
@@ -56,8 +56,7 @@
 static int network_function_faults_clear_cb(sr_session_ctx_t *session, const char *path, const sr_val_t *input, const size_t input_cnt, sr_event_t event, uint32_t request_id, sr_val_t **output, size_t *output_cnt, void *private_data);
 static int network_function_faults_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
 static int network_function_faults_count_get_items_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data);
-
-static int network_function_started_features_get_items_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data);
+static int network_function_info_get_items_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data);
 
 static int network_function_change_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data);
 
@@ -131,13 +130,13 @@
     rc = sr_module_change_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_FAULT_GENERATION_SCHEMA_XPATH, network_function_faults_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &session_subscription);
     if(rc != SR_ERR_OK) {
         log_error("could not subscribe to faults");
-        return 0;
+        return NTS_ERR_FAILED;
     }
 
     rc = sr_oper_get_items_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_FAULT_COUNT_LIST_SCHEMA_XPATH, network_function_faults_count_get_items_cb, NULL, SR_SUBSCR_CTX_REUSE, &session_subscription);
     if(rc != SR_ERR_OK) {
         log_error("could not subscribe to oper faults: %s\n", sr_strerror(rc));
-        return 0;
+        return NTS_ERR_FAILED;
     }
 
     rc = sr_rpc_subscribe(session_running, NTS_NF_RPC_FAULTS_CLEAR_SCHEMA_XPATH, network_function_faults_clear_cb, 0, 0, SR_SUBSCR_CTX_REUSE, &session_subscription);
@@ -146,23 +145,22 @@
         return NTS_ERR_FAILED;
     }
 
-    rc = sr_oper_get_items_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_INFO_STARTED_FEATURES_SCHEMA_XPATH, network_function_started_features_get_items_cb, NULL, SR_SUBSCR_CTX_REUSE, &session_subscription);
+    rc = sr_oper_get_items_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_INFO_SCHEMA_XPATH, network_function_info_get_items_cb, NULL, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_OPER_MERGE, &session_subscription);
     if(rc != SR_ERR_OK) {
         log_error("could not subscribe to oper started-features: %s\n", sr_strerror(rc));
-        return 0;
+        return NTS_ERR_FAILED;
     }
-    
 
     //subscribe to any changes on the main
-    rc = sr_module_change_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_NETWORK_FUNCTION_SCHEMA_XPATH, network_function_change_cb, NULL, 0, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_UPDATE, &session_subscription);
+    rc = sr_module_change_subscribe(session_running, NTS_NETWORK_FUNCTION_MODULE, NTS_NF_NETWORK_FUNCTION_SCHEMA_XPATH, network_function_change_cb, NULL, 1, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_UPDATE, &session_subscription);
     if(rc != SR_ERR_OK) {
-        log_error("could not subscribe to simulation changes\n");
+        log_error("could not subscribe to simulation changes: %s\n", sr_strerror(rc));
         return NTS_ERR_FAILED;
     }
 
     rc = faults_init();
     if(rc != NTS_ERR_OK) {
-        log_error("faults_init error\n", sr_strerror(rc));
+        log_error("faults_init error\n");
         return NTS_ERR_FAILED;
     }
 
@@ -636,44 +634,50 @@
     return SR_ERR_OK;
 }
 
-static int network_function_started_features_get_items_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) {
-    char value[1024];
-    value[0] = 0;
+static int network_function_info_get_items_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) {
+    char started_features[1024];
+    started_features[0] = 0;
 
     if(ves_file_ready_feature_get_status()) {
-        strcat(value, "ves-file-ready ");
+        strcat(started_features, "ves-file-ready ");
     }
     
     if(ves_pnf_registration_feature_get_status()) {
-        strcat(value, "ves-pnf-registration ");
+        strcat(started_features, "ves-pnf-registration ");
     }
 
     if(ves_heartbeat_feature_get_status()) {
-        strcat(value, "ves-heartbeat ");
+        strcat(started_features, "ves-heartbeat ");
     }
     
     if(manual_notification_feature_get_status()) {
-        strcat(value, "manual-notification-generation ");
+        strcat(started_features, "manual-notification-generation ");
     }
 
     if(netconf_call_home_feature_get_status()) {
-        strcat(value, "netconf-call-home ");
+        strcat(started_features, "netconf-call-home ");
     }
     
     if(web_cut_through_feature_get_status()) {
-        strcat(value, "web-cut-through ");
+        strcat(started_features, "web-cut-through ");
     }
     
-    if(strlen(value)) {
-        value[strlen(value) - 1] = 0;
+    if(strlen(started_features)) {
+        started_features[strlen(started_features) - 1] = 0;
     }
 
-    *parent = lyd_new_path(NULL, sr_get_context(sr_session_get_connection(session)), NTS_NF_INFO_STARTED_FEATURES_SCHEMA_XPATH, value, 0, 0);
+    *parent = lyd_new_path(NULL, sr_get_context(sr_session_get_connection(session)), NTS_NF_INFO_SCHEMA_XPATH, 0, 0, 0);
     if(*parent == 0) {
         log_error("lyd_new_path failed\n");
         return SR_ERR_OPERATION_FAILED;
     }
 
+    struct lyd_node *n = lyd_new_leaf(*parent, (*parent)->schema->module, "started-features", started_features);
+    if(n == 0) {
+        log_error("lyd_new_leaf failed\n");
+        return SR_ERR_OPERATION_FAILED;
+    }
+
     return SR_ERR_OK;
 }
 
diff --git a/ntsimulator/ntsim-ng/core/datastore/generate.c b/ntsimulator/ntsim-ng/core/datastore/generate.c
index 1fa255c..285dbcf 100644
--- a/ntsimulator/ntsim-ng/core/datastore/generate.c
+++ b/ntsimulator/ntsim-ng/core/datastore/generate.c
@@ -182,41 +182,42 @@
         }
     }
 
-    char **xpaths = 0;
-    int instance_count = datastore_schema_get_xpaths(&xpaths);
-    if(instance_count < 0) {
-        log_error("datastore_schema_get_xpaths failed\n");
-        return NTS_ERR_FAILED;
-    }
+    if(framework_config.datastore_populate.random_generation_enabled) {
+        char **xpaths = 0;
+        int instance_count = datastore_schema_get_xpaths(&xpaths);
+        if(instance_count < 0) {
+            log_error("datastore_schema_get_xpaths failed\n");
+            return NTS_ERR_FAILED;
+        }
 
-    //exclude pre-populated modules
-    struct lyd_node *elem;
-    LY_TREE_FOR(job.operational, elem) {
-        for(int i = 0; i < instance_count; i++) {
-            if(strstr(xpaths[i], elem->schema->module->name) == (xpaths[i] + 1)) {  //xpaths[i] is "/module:container"
-                free(xpaths[i]);
+        //exclude pre-populated modules
+        struct lyd_node *elem;
+        LY_TREE_FOR(job.operational, elem) {
+            for(int i = 0; i < instance_count; i++) {
+                if(strstr(xpaths[i], elem->schema->module->name) == (xpaths[i] + 1)) {  //xpaths[i] is "/module:container"
+                    free(xpaths[i]);
 
-                instance_count--;
-                for(int j = i; j < instance_count; j++) {
-                    xpaths[j] = xpaths[j + 1];
+                    instance_count--;
+                    for(int j = i; j < instance_count; j++) {
+                        xpaths[j] = xpaths[j + 1];
+                    }
+
+                    break;
                 }
-
-                break;
             }
         }
-    }
 
-    generate_instance_t *instance = (generate_instance_t *)malloc(sizeof(generate_instance_t) * instance_count);
-    if(!instance) {
-        log_error("bad malloc\n");
-        for(int i = 0; i < instance_count; i++) {
-            free(xpaths[i]);
+        generate_instance_t *instance = (generate_instance_t *)malloc(sizeof(generate_instance_t) * instance_count);
+        if(!instance) {
+            log_error("bad malloc\n");
+            for(int i = 0; i < instance_count; i++) {
+                free(xpaths[i]);
+            }
+            free(xpaths);
+            return NTS_ERR_FAILED;
         }
-        free(xpaths);
-        return NTS_ERR_FAILED;
-    }
     
-    if(framework_config.datastore_populate.random_generation_enabled) {
+
         //RANDOM generate everything
         for(int i = 0; i < instance_count; i++) {
             log_add_verbose(1, "generating "LOG_COLOR_BOLD_YELLOW"%s"LOG_COLOR_RESET" data...\n", xpaths[i]);
@@ -309,6 +310,20 @@
             log_error("generate_validate failed\n");
             return rc;
         }
+
+        for(int i = 0; i < instance_count; i++) {
+            log_add(1, "%d ", i);
+
+            free(instance[i].modules);
+            free(instance[i].xpath);
+
+            free(xpaths[i]);
+        }
+        free(xpaths);
+        free(job.late_resolve_instance);
+        free(job.late_resolve_schema);
+        free(job.late_resolve_parent_o);
+        free(job.late_resolve_parent_r);
     }
 
     //export generated data
@@ -321,19 +336,7 @@
 
     //cleanup
     log_add_verbose(1, LOG_COLOR_BOLD_YELLOW"datastore_generate_data() cleaning up... "LOG_COLOR_RESET);
-    for(int i = 0; i < instance_count; i++) {
-        log_add(1, "%d ", i);
-
-        free(instance[i].modules);
-        free(instance[i].xpath);
-
-        free(xpaths[i]);
-    }
-    free(xpaths);
-    free(job.late_resolve_instance);
-    free(job.late_resolve_schema);
-    free(job.late_resolve_parent_o);
-    free(job.late_resolve_parent_r);
+    
 
     lyd_free_withsiblings(job.operational);
     lyd_free_withsiblings(job.running);
@@ -347,8 +350,12 @@
 int datastore_generate_external(void) {
     char cmd[512];
     sprintf(cmd, "%s --generate", framework_arguments.argv[0]);
-    system(cmd);
-    return NTS_ERR_OK;
+    if(system(cmd) == 0) {
+        return NTS_ERR_OK;
+    }
+    else {
+        return NTS_ERR_FAILED;
+    }
 }
 
 
diff --git a/ntsimulator/ntsim-ng/core/docker.c b/ntsimulator/ntsim-ng/core/docker.c
index 3d5faa2..aea7ae8 100644
--- a/ntsimulator/ntsim-ng/core/docker.c
+++ b/ntsimulator/ntsim-ng/core/docker.c
@@ -555,9 +555,33 @@
         return NTS_ERR_FAILED;
     }
 
+    cJSON *capAdd = cJSON_CreateArray();
+    if(capAdd == 0) {
+        log_error("could not create JSON array: CapAdd\n");
+        cJSON_Delete(postDataJson);
+        return NTS_ERR_FAILED;
+    }
+    if(cJSON_AddItemToObject(hostConfig, "CapAdd", capAdd) == 0) {
+        log_error("cJSON_AddItemToObject failed\n");
+        cJSON_Delete(postDataJson);
+        return NTS_ERR_FAILED;
+    }
+    cJSON *net_admin = cJSON_CreateString("NET_ADMIN");
+    if(net_admin == 0) {
+        log_error("could not create JSON string\n");
+        cJSON_Delete(postDataJson);
+        return NTS_ERR_FAILED;
+    }
+    if(cJSON_AddItemToArray(capAdd, net_admin) == 0) {
+        log_error("cJSON_AddItemToArray failed\n");
+        cJSON_Delete(postDataJson);
+        return NTS_ERR_FAILED;
+    }
+    
+
     cJSON *portBindings = cJSON_CreateObject();
     if(portBindings == 0) {
-        printf("could not create JSON object: PortBindings");
+        log_error("could not create JSON object: PortBindings\n");
         cJSON_Delete(postDataJson);
         return NTS_ERR_FAILED;
     }
diff --git a/ntsimulator/ntsim-ng/core/xpath.h b/ntsimulator/ntsim-ng/core/xpath.h
index b0514ff..3c57770 100644
--- a/ntsimulator/ntsim-ng/core/xpath.h
+++ b/ntsimulator/ntsim-ng/core/xpath.h
@@ -26,9 +26,7 @@
 #define NTS_MANAGER_VES_ENDPOINT_CONFIG_XPATH                   "/nts-manager:simulation/ves-endpoint"
 
 #define NTS_NETWORK_FUNCTION_MODULE                             "nts-network-function"
-#define NTS_NF_INFO_BUILD_TIME_XPATH                            "/nts-network-function:info/build-time"
-#define NTS_NF_INFO_STARTED_FEATURES_SCHEMA_XPATH               "/nts-network-function:info/started-features"
-#define NTS_NF_INFO_VERSION_XPATH                               "/nts-network-function:info/version"
+#define NTS_NF_INFO_SCHEMA_XPATH                                "/nts-network-function:info"
 #define NTS_NF_NETWORK_FUNCTION_SCHEMA_XPATH                    "/nts-network-function:simulation/network-function"
 #define NTS_NF_NETWORK_FUNCTION_FTYPE_SCHEMA_XPATH              "/nts-network-function:simulation/network-function/function-type"
 #define NTS_NF_NETWORK_FUNCTION_MPAM_SCHEMA_XPATH               "/nts-network-function:simulation/network-function/mount-point-addressing-method"
@@ -41,6 +39,7 @@
 #define NTS_NF_VES_FAULTS_ENABLED_SCHEMA_XPATH                  "/nts-network-function:simulation/network-function/ves/faults-enabled"
 #define NTS_NF_VES_HEARTBEAT_SCHEMA_XPATH                       "/nts-network-function:simulation/network-function/ves/heartbeat-period"
 #define NTS_NF_VES_PNF_REGISTRATION_SCHEMA_XPATH                "/nts-network-function:simulation/network-function/ves/pnf-registration"
+#define NTS_NF_NETWORK_EMULATION_SCHEMA_XPATH                   "/nts-network-function:simulation/network-emulation"
 #define NTS_NF_VES_ENDPOINT_CONFIG_XPATH                        "/nts-network-function:simulation/ves-endpoint"
 #define NTS_NF_SDN_CONTROLLER_CONFIG_XPATH                      "/nts-network-function:simulation/sdn-controller"
 
@@ -49,6 +48,7 @@
 #define NTS_NF_RPC_FAULTS_CLEAR_SCHEMA_XPATH                    "/nts-network-function:clear-fault-counters"
 #define NTS_NF_RPC_MANUAL_NOTIF_SCHEMA_XPATH                    "/nts-network-function:invoke-notification"
 #define NTS_NF_RPC_FILE_READY_SCHEMA_XPATH                      "/nts-network-function:invoke-ves-pm-file-ready"
+#define NTS_NF_RPC_SET_NETWORK_LATENCY_SCHEMA_XPATH             "/nts-network-function:set-network-latency"
 
 #define IETF_KEYSTORE_MODULE                                    "ietf-keystore"
 #define IETF_KEYSTORE_SCHEMA_XPATH                              "/ietf-keystore:keystore"
diff --git a/ntsimulator/ntsim-ng/features/ves_file_ready/ves_file_ready.c b/ntsimulator/ntsim-ng/features/ves_file_ready/ves_file_ready.c
index f344f9a..12a7e4d 100644
--- a/ntsimulator/ntsim-ng/features/ves_file_ready/ves_file_ready.c
+++ b/ntsimulator/ntsim-ng/features/ves_file_ready/ves_file_ready.c
@@ -130,7 +130,7 @@
             failed++;
         }
     }
-    
+
     rc = sr_session_stop(current_session);
     if(rc != SR_ERR_OK) {
         log_error("could not stop sysrepo session\n");
diff --git a/ntsimulator/ntsim-ng/utils/http_client.c b/ntsimulator/ntsim-ng/utils/http_client.c
index 4578dd4..dd15a6d 100644
--- a/ntsimulator/ntsim-ng/utils/http_client.c
+++ b/ntsimulator/ntsim-ng/utils/http_client.c
@@ -160,7 +160,7 @@
     }
 
     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
-    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);     //seconds timeout for a connection
+    curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);     //seconds timeout for a connection
     curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);            //seconds timeout for an operation
     curl_easy_setopt(curl, CURLOPT_FRESH_CONNECT, 1L);
     curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L);
diff --git a/ntsimulator/ntsim-ng/utils/network_emulation.c b/ntsimulator/ntsim-ng/utils/network_emulation.c
new file mode 100644
index 0000000..94af8c8
--- /dev/null
+++ b/ntsimulator/ntsim-ng/utils/network_emulation.c
@@ -0,0 +1,44 @@
+/*************************************************************************
+*
+* Copyright 2020 highstreet technologies GmbH and others
+*
+* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+*
+* 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.
+***************************************************************************/
+
+#define _GNU_SOURCE
+
+#include "network_emulation.h"
+#include "log_utils.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+int network_emulation_init(void) {
+    system("tc qdisc add dev eth0 root netem limit "NETWORK_EMULATION_DEFAULT_LIMIT" delay 0ms > /dev/null");
+    return NTS_ERR_OK;
+}
+
+int network_emulation_update(const network_emultation_settings_t *s) {
+    assert(s);
+
+    char command[512];
+    if(s->delay.time && s->delay.jitter) {
+        sprintf(command, "tc qdisc change dev eth0 root netem limit %d delay %dms %dms %d%% distribution %s loss random %d%% corrupt %d%% %d%% duplicate %d%% %d%% reorder %d%% %d%% rate %dkbit > /dev/null", s->limit, s->delay.time, s->delay.jitter, s->delay.correlation, s->delay.distribution, s->loss, s->corruption.percentage, s->corruption.correlation, s->duplication.percentage, s->duplication.correlation, s->reordering.percentage, s->reordering.correlation, s->rate);
+    }
+    else {
+        sprintf(command, "tc qdisc change dev eth0 root netem limit %d delay %dms %dms %d%% loss random %d%% corrupt %d%% %d%% duplicate %d%% %d%% reorder %d%% %d%% rate %dkbit > /dev/null", s->limit, s->delay.time, s->delay.jitter, s->delay.correlation, s->loss, s->corruption.percentage, s->corruption.correlation, s->duplication.percentage, s->duplication.correlation, s->reordering.percentage, s->reordering.correlation, s->rate);
+    }
+    log_add_verbose(2, "chaning netem: %s\n", command);
+    int rc = system(command);
+    return rc;
+}
diff --git a/ntsimulator/ntsim-ng/utils/network_emulation.h b/ntsimulator/ntsim-ng/utils/network_emulation.h
new file mode 100644
index 0000000..474dbec
--- /dev/null
+++ b/ntsimulator/ntsim-ng/utils/network_emulation.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+*
+* Copyright 2020 highstreet technologies GmbH and others
+*
+* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+*
+* 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.
+***************************************************************************/
+
+#pragma once
+
+#include <stdint.h>
+
+typedef struct {
+    uint16_t limit;
+
+    struct {
+        uint16_t time;
+        uint16_t jitter;
+        uint16_t correlation;
+        char *distribution;
+    } delay;
+
+    uint16_t loss;
+
+    struct {
+        uint16_t percentage;
+        uint16_t correlation;
+    } corruption;
+
+    struct {
+        uint16_t percentage;
+        uint16_t correlation;
+    } duplication;
+
+    struct {
+        uint16_t percentage;
+        uint16_t correlation;
+    } reordering;
+
+    uint16_t rate;
+} network_emultation_settings_t;
+
+#define NETWORK_EMULATION_DEFAULT_LIMIT         "1000"
+
+int network_emulation_init(void);
+int network_emulation_update(const network_emultation_settings_t *s);