diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/.gitignore b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..232ccd1d8c8fa7bb09c62bfe30ee8baf3ec12f4f
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/.gitignore
@@ -0,0 +1,2 @@
+target/
+logs/
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.lock b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.lock
new file mode 100644
index 0000000000000000000000000000000000000000..7ccc49374a0461302584facf2ad8140f4b19e8b3
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.lock
@@ -0,0 +1,106 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bincode"
+version = "1.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
+
+[[package]]
+name = "memchr"
+version = "2.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+
+[[package]]
+name = "netem-evaluator"
+version = "0.1.0"
+dependencies = [
+ "bincode",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.92"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
+
+[[package]]
+name = "serde"
+version = "1.0.216"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.216"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.90"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.toml b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..61442fe3afd6b9ae5b339ea0e0c8d55fea00372e
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "netem-evaluator"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+bincode = "1.3"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Makefile b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..fd61ebc1e1a3438ba7853cc4ba2416e4f903f319
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/Makefile
@@ -0,0 +1,19 @@
+
+export NUMBER_OF_NETWORK_NAMESPACES = 1
+
+CARGO = $(shell which cargo)
+
+run_evaluator: teardown setup
+	cargo build --release
+	mkdir -p logs
+	sudo --preserve-env ip netns exec srv_ns_1 ${CARGO} run --release --bin server > logs/netem_data.log &
+	sudo --preserve-env ip netns exec cli_ns_1 ${CARGO} run --release --bin client
+	sleep 1
+	sudo --preserve-env pkill -f 'cargo run --release --bin server'
+	$(MAKE) teardown
+
+setup:
+	sudo --preserve-env src/scripts/setup_ns.sh
+
+teardown:
+	- src/scripts/remove_ns.sh
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/client.rs b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/client.rs
new file mode 100644
index 0000000000000000000000000000000000000000..9836553545ebf3d34064706dfb3d24ad151190d1
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/client.rs
@@ -0,0 +1,93 @@
+use std::error::Error;
+use std::net::UdpSocket;
+use std::time::{SystemTime, UNIX_EPOCH};
+
+use netem_evaluator::Message;
+use netem_evaluator::NetemParameters;
+
+const TIME_BETWEEN_MESSAGES_IN_MICRO_SECONDS: u64 = 10000;
+const MESSAGES_TO_SEND: u32 = 1000;
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let socket = UdpSocket::bind("0.0.0.0:0")?;
+    let target = "10.0.0.1:6789";
+    socket.connect(target)?;
+    let mut id = 0;
+
+    // TEST GOAL: find out if there is a difference, between no netem, and netem with 0 delay
+    // There is a very small difference of 0.03ms on average
+    let mut netem_parameters = NetemParameters::new();
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+
+    netem_evaluator::remove_qdiscs();
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, None)?;
+
+    netem_evaluator::create_qdiscs();
+    send_messages_to_server(
+        &socket,
+        &mut id,
+        MESSAGES_TO_SEND,
+        Some(NetemParameters::new()),
+    )?;
+
+    // TEST GOAL: find out how good the delay of netem works
+    // The delay is pretty accurate
+    netem_parameters = NetemParameters::new().with_delay(1);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(2);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(4);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(10);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(20);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(50);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+    netem_parameters = NetemParameters::new().with_delay(100);
+    netem_evaluator::set_network_parameters(netem_parameters.clone());
+    send_messages_to_server(&socket, &mut id, MESSAGES_TO_SEND, Some(netem_parameters))?;
+
+    Ok(())
+}
+
+fn send_messages_to_server(
+    socket: &UdpSocket,
+    id: &mut u128,
+    messages_to_send: u32,
+    netem_parameters: Option<NetemParameters>,
+) -> Result<(), Box<dyn Error>> {
+    let mut buffer = [0u8; 1000];
+    for _ in 0..messages_to_send {
+        *id += 1;
+        // Get high-precision timestamp
+        // Using SystemTime for monotonic timestamps that are consistent across processes
+        let message = Message {
+            id: *id,
+            timestamp: SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos(),
+            netem_parameters: netem_parameters.clone(),
+        };
+
+        let mut cursor = std::io::Cursor::new(&mut buffer[..]);
+        bincode::serialize_into(&mut cursor, &message)?;
+
+        socket.send(&buffer)?;
+        println!("{message:?}");
+        // println!("{:02x?}", &buffer[0..100]);
+
+        buffer.fill(0);
+        std::thread::sleep(std::time::Duration::from_micros(
+            TIME_BETWEEN_MESSAGES_IN_MICRO_SECONDS,
+        ));
+    }
+
+    std::thread::sleep(std::time::Duration::from_secs(1));
+    Ok(())
+}
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/server.rs b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/server.rs
new file mode 100644
index 0000000000000000000000000000000000000000..035ea5062429782ffa2449f0fd3bb37b73257e49
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/bin/server.rs
@@ -0,0 +1,36 @@
+use std::error::Error;
+use std::net::UdpSocket;
+use std::time::{SystemTime, UNIX_EPOCH};
+
+use netem_evaluator::Message;
+
+fn main() -> Result<(), Box<dyn Error>> {
+    let socket = UdpSocket::bind("0.0.0.0:6789")?;
+    eprintln!("Listening on :6789");
+
+    let mut buffer = [0u8; 1000];
+
+    loop {
+        match socket.recv(&mut buffer) {
+            Ok(received) => {
+                let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
+
+                let mut message: Message = bincode::deserialize(&buffer[..received])?;
+                message.timestamp = now - message.timestamp; // Abuse timestamp field to store time difference
+
+                // let time_diff_micros = (now - message.timestamp) as f64 / 1000.;
+                // let id = message.id;
+                // println!("Received packet #{id} - Time difference: {time_diff_micros:.3} µs");
+                println!("{}", serde_json::to_string(&message)?);
+                let server_time_per_request =
+                    SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos() - now;
+                // println!(
+                //     "{} ms time needed for server to handle request",
+                //     server_time_per_request as f64 / 1000000.
+                // );
+                assert!(server_time_per_request < 10000000); // if this bites you, you should choose a higher value for TIME_BETWEEN_MESSAGES_IN_MICRO_SECONDS
+            }
+            Err(e) => eprintln!("Error receiving: {}", e),
+        }
+    }
+}
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/lib.rs b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0ab806c14d2548a473c23dcf7ce1ca9851216615
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/lib.rs
@@ -0,0 +1,245 @@
+use serde::{Deserialize, Serialize};
+use std::process::Command;
+
+const NETWORK_NAMESPACES: u32 = 1;
+
+const SRV_NS: &str = "srv_ns";
+const CLI_NS: &str = "cli_ns";
+const SRV_VE: &str = "srv_ve";
+const CLI_VE: &str = "cli_ve";
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct Message {
+    pub id: u128,
+    pub timestamp: u128,
+    pub netem_parameters: Option<NetemParameters>,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct NetemParameters {
+    pub srv_rate: u32,
+    pub srv_delay: u32,
+    pub srv_jitter: u32,
+    pub srv_pkt_loss: u32,
+    pub srv_duplicate: u32,
+    pub srv_corrupt: u32,
+    pub srv_reorder: u32,
+    pub cli_rate: u32,
+    pub cli_delay: u32,
+    pub cli_jitter: u32,
+    pub cli_pkt_loss: u32,
+    pub cli_duplicate: u32,
+    pub cli_corrupt: u32,
+    pub cli_reorder: u32,
+}
+
+// impl NetemParameters {
+//     pub fn from_json(json_str: &str) -> Result<NetemParameters, serde_json::Error> {
+//         serde_json::from_str(json_str)
+//     }
+
+//     pub fn to_json(&self) -> Result<String, serde_json::Error> {
+//         serde_json::to_string(self)
+//     }
+// }
+
+impl NetemParameters {
+    pub fn new() -> NetemParameters {
+        NetemParameters {
+            srv_rate: 1000,
+            srv_delay: 0,
+            srv_jitter: 0,
+            srv_pkt_loss: 0,
+            srv_duplicate: 0,
+            srv_corrupt: 0,
+            srv_reorder: 0,
+            cli_rate: 1000,
+            cli_delay: 0,
+            cli_jitter: 0,
+            cli_pkt_loss: 0,
+            cli_duplicate: 0,
+            cli_corrupt: 0,
+            cli_reorder: 0,
+        }
+    }
+
+    pub fn with_rate(mut self, rate: u32) -> NetemParameters {
+        self.srv_rate = rate;
+        self.cli_rate = rate;
+        self
+    }
+
+    pub fn with_delay(mut self, delay: u32) -> NetemParameters {
+        self.srv_delay = delay;
+        self.cli_delay = delay;
+        self
+    }
+
+    pub fn with_jitter(mut self, jitter: u32) -> NetemParameters {
+        self.srv_jitter = jitter;
+        self.cli_jitter = jitter;
+        self
+    }
+
+    pub fn with_pkt_loss(mut self, pkt_loss: u32) -> NetemParameters {
+        self.srv_pkt_loss = pkt_loss;
+        self.cli_pkt_loss = pkt_loss;
+        self
+    }
+
+    pub fn with_duplicate(mut self, duplicate: u32) -> NetemParameters {
+        self.srv_duplicate = duplicate;
+        self.cli_duplicate = duplicate;
+        self
+    }
+
+    pub fn with_corrupt(mut self, corrupt: u32) -> NetemParameters {
+        self.srv_corrupt = corrupt;
+        self.cli_corrupt = corrupt;
+        self
+    }
+
+    pub fn with_reorder(mut self, reorder: u32) -> NetemParameters {
+        self.srv_reorder = reorder;
+        self.cli_reorder = reorder;
+        self
+    }
+}
+
+pub fn set_network_parameters(parameters: NetemParameters) {
+    for i in 1..(NETWORK_NAMESPACES + 1) {
+        change_qdisc(
+            format!("{SRV_NS}_{i}"),
+            format!("{SRV_VE}"),
+            parameters.srv_rate,
+            parameters.srv_delay,
+            parameters.srv_jitter,
+            parameters.srv_pkt_loss,
+            parameters.srv_duplicate,
+            parameters.srv_corrupt,
+            parameters.srv_reorder,
+        );
+        change_qdisc(
+            format!("{CLI_NS}_{i}"),
+            format!("{CLI_VE}"),
+            parameters.cli_rate,
+            parameters.cli_delay,
+            parameters.cli_jitter,
+            parameters.cli_pkt_loss,
+            parameters.cli_duplicate,
+            parameters.cli_corrupt,
+            parameters.cli_reorder,
+        );
+    }
+}
+
+fn change_qdisc(
+    ns: String,
+    dev: String,
+    rate: u32,
+    delay: u32,
+    jitter: u32,
+    pkt_loss: u32,
+    duplicate: u32,
+    corrupt: u32,
+    reorder: u32,
+) {
+    let rate = format!("{rate}mbit");
+    let delay = format!("{delay}ms");
+    let jitter = format!("{jitter}ms");
+    let pkt_loss = format!("{pkt_loss}%");
+    let duplicate = format!("{duplicate}%");
+    let corrupt = format!("{corrupt}%");
+    let reorder = format!("{reorder}%");
+
+    let args = [
+        "netns",
+        "exec",
+        &ns,
+        "tc",
+        "qdisc",
+        "change",
+        "dev",
+        &dev,
+        "root",
+        "netem",
+        "limit",
+        "1000",
+        "rate",
+        &rate,
+        "delay",
+        &delay,
+        &jitter,
+        "loss",
+        &pkt_loss,
+        "duplicate",
+        &duplicate,
+        "corrupt",
+        &corrupt,
+        "reorder",
+        &reorder,
+    ];
+    println!("> ip {args:?}");
+
+    let output = Command::new("ip")
+        .args(&args)
+        .output()
+        .expect("failed to execute command");
+    println!("{output:?}");
+}
+
+pub fn create_qdiscs() {
+    for i in 1..(NETWORK_NAMESPACES + 1) {
+        create_qdisc(format!("{SRV_NS}_{i}"), format!("{SRV_VE}"));
+        create_qdisc(format!("{CLI_NS}_{i}"), format!("{CLI_VE}"));
+    }
+    set_network_parameters(NetemParameters::new());
+}
+fn create_qdisc(ns: String, dev: String) {
+    let args = [
+        "netns", "exec", &ns, "tc", "qdisc", "add", "dev", &dev, "root", "netem",
+    ];
+    println!("> ip {args:?}");
+
+    let output = Command::new("ip")
+        .args(&args)
+        .output()
+        .expect("failed to execute command");
+    println!("{output:?}");
+}
+
+pub fn remove_qdiscs() {
+    for i in 1..(NETWORK_NAMESPACES + 1) {
+        remove_qdisc(format!("{SRV_NS}_{i}"), format!("{SRV_VE}"));
+        remove_qdisc(format!("{CLI_NS}_{i}"), format!("{CLI_VE}"));
+    }
+}
+fn remove_qdisc(ns: String, dev: String) {
+    let args = [
+        "netns", "exec", &ns, "tc", "qdisc", "del", "dev", &dev, "root",
+    ];
+    println!("> ip {args:?}");
+
+    let output = Command::new("ip")
+        .args(&args)
+        .output()
+        .expect("failed to execute command");
+    println!("{output:?}");
+}
+
+pub fn show_qdiscs() {
+    for i in 1..(NETWORK_NAMESPACES + 1) {
+        show_qdisc(format!("{SRV_NS}_{i}"), format!("{SRV_VE}"));
+        show_qdisc(format!("{CLI_NS}_{i}"), format!("{CLI_VE}"));
+    }
+}
+fn show_qdisc(ns: String, dev: String) {
+    let args = ["netns", "exec", &ns, "tc", "qdisc", "show", "dev", &dev];
+    println!("> ip {args:?}");
+
+    let output = Command::new("ip")
+        .args(&args)
+        .output()
+        .expect("failed to execute command");
+    println!("{output:?}");
+}
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/main.rs b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/main.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e7a11a969c037e00a796aafeff6258501ec15e9a
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+    println!("Hello, world!");
+}
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/analyze.py b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/analyze.py
new file mode 100644
index 0000000000000000000000000000000000000000..a93c4d388d13898e47de206087f0362de2215c1a
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/analyze.py
@@ -0,0 +1,111 @@
+# format of json is {"id":1,"timestamp":186514,"netem_parameters":{"srv_rate":1000,"srv_delay":0,"srv_jitter":0,"srv_pkt_loss":0,"srv_duplicate":0,"srv_corrupt":0,"srv_reorder":0,"cli_rate":1000,"cli_delay":0,"cli_jitter":0,"cli_pkt_loss":0,"cli_duplicate":0,"cli_corrupt":0,"cli_reorder":0}}
+
+# write a function that reads the json file into a pandas dataframe
+import json
+import pandas as pd
+
+
+def main():
+    df = read_json_file("logs/netem_data.log")
+    # print(df)
+    summarized = summarize_data(df)
+    summarized = summarized.drop(
+        columns=["srv_reorder", "srv_corrupt", "srv_duplicate"]
+    )
+    print(summarized)
+
+
+def read_json_file(file_path):
+    data = []
+    with open(file_path, "r") as file:
+        for line in file:
+            data.append(json.loads(line))
+
+    df = pd.json_normalize(data)
+    df.rename(columns={"timestamp": "time_ns"}, inplace=True)
+    df["time_ms"] = df["time_ns"] / 1000000
+
+    # make netem_parameters a boolean
+    df = df.astype({"netem_parameters": "bool"})
+    # set netem_parameters to true if all netem_parameters.* are some value
+    netem_columns = [col for col in df.columns if col.startswith("netem_parameters.")]
+    df["netem_parameters"] = df[netem_columns].notnull().all(axis=1)
+
+    df = df[df["time_ms"] < 1e30]  # filter out unreasonable large values
+
+    return df
+
+
+def summarize_data(df):
+    summarized = pd.DataFrame()
+
+    # print the row with index 1001
+    # print(df.loc[1])
+    # print(df.loc[5])
+
+    # find out each set of distinct values for all netem_parameters.*
+    netem_columns = [col for col in df.columns if col.startswith("netem_parameters")]
+    distinct_rows = df[netem_columns].drop_duplicates()
+    # print("Distinct rows for netem parameters:")
+    # print(distinct_rows)
+
+    for row in distinct_rows.iterrows():
+        # Extract the current `netem_parameters` values
+        entry = row[1]
+        current_netem_params = entry.to_dict()
+
+        if current_netem_params["netem_parameters"] == True:
+            mask = df[netem_columns] == current_netem_params
+            # print(mask)
+            # print(mask.all(axis=1))
+            filtered_df = df[mask.all(axis=1)]
+        else:
+            mask = df["netem_parameters"] == False
+            # print(mask)
+            filtered_df = df[mask]
+
+        # print(filtered_df)
+
+        time_ms_max = filtered_df["time_ms"].max()
+        time_ms_min = filtered_df["time_ms"].min()
+        time_ms_avg = filtered_df["time_ms"].mean()
+        time_ms_std = filtered_df["time_ms"].std()
+        count = filtered_df["time_ms"].count()
+        netem_parameters = filtered_df["netem_parameters"].iloc[0]
+        srv_rate = filtered_df["netem_parameters.srv_rate"].iloc[0]
+        srv_delay = filtered_df["netem_parameters.srv_delay"].iloc[0]
+        srv_jitter = filtered_df["netem_parameters.srv_jitter"].iloc[0]
+        srv_pkt_loss = filtered_df["netem_parameters.srv_pkt_loss"].iloc[0]
+        srv_duplicate = filtered_df["netem_parameters.srv_duplicate"].iloc[0]
+        srv_corrupt = filtered_df["netem_parameters.srv_corrupt"].iloc[0]
+        srv_reorder = filtered_df["netem_parameters.srv_reorder"].iloc[0]
+
+        summarized = pd.concat(
+            [
+                summarized,
+                pd.DataFrame(
+                    {
+                        "time_ms_min": time_ms_min,
+                        "time_ms_max": time_ms_max,
+                        "time_ms_avg": time_ms_avg,
+                        "time_ms_std": time_ms_std,
+                        "count": count,
+                        "netem_parameters": netem_parameters,
+                        "srv_rate": srv_rate,
+                        "srv_delay": srv_delay,
+                        "srv_jitter": srv_jitter,
+                        "srv_pkt_loss": srv_pkt_loss,
+                        "srv_duplicate": srv_duplicate,
+                        "srv_corrupt": srv_corrupt,
+                        "srv_reorder": srv_reorder,
+                    },
+                    index=[0],
+                ),
+            ],
+            ignore_index=True,
+        )
+
+    return summarized
+
+
+main()
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/remove_ns.sh b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/remove_ns.sh
new file mode 100755
index 0000000000000000000000000000000000000000..764842eaf80c1c5f8eaca4778048228e4a14df3c
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/remove_ns.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+set -x
+
+##########################
+# Remove network namespaces
+##########################
+for i in $(seq 1 ${NUMBER_OF_NETWORK_NAMESPACES}); do
+    sudo ip netns del srv_ns_${i}
+    sudo ip netns del cli_ns_${i}
+done
diff --git a/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/setup_ns.sh b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/setup_ns.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6d2ab507706b23727ad3f9879036f4534e2c69fc
--- /dev/null
+++ b/pq-tls-benchmark-framework/emulation-exp/code/netem-evaluator/src/scripts/setup_ns.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+set -ex
+
+# Code taken from benchmarking-pqc-in-tls project.
+
+##########################
+# Setup network namespaces
+##########################
+
+# NUMBER_OF_NETWORK_NAMESPACES is set in setup.sh
+echo "Setting up ${NUMBER_OF_NETWORK_NAMESPACES} network namespaces"
+
+# Server
+SERVER_VETH=srv_ve
+SERVER_VETH_LL_ADDR=00:00:00:00:00:01
+
+# Client
+CLIENT_VETH=cli_ve
+CLIENT_VETH_LL_ADDR=00:00:00:00:00:02
+
+for i in $(seq 1 ${NUMBER_OF_NETWORK_NAMESPACES}); do
+    SERVER_NS=srv_ns_${i}
+    CLIENT_NS=cli_ns_${i}
+
+    ip netns add ${SERVER_NS}
+    ip netns add ${CLIENT_NS}
+
+    # Add virtual link of types VETH
+    ip link add \
+        name ${SERVER_VETH} \
+        address ${SERVER_VETH_LL_ADDR} \
+        netns ${SERVER_NS} type veth \
+        peer name ${CLIENT_VETH} \
+        address ${CLIENT_VETH_LL_ADDR} \
+        netns ${CLIENT_NS}
+
+    ip netns exec ${SERVER_NS} \
+        ip link set dev ${SERVER_VETH} up
+    ip netns exec ${SERVER_NS} \
+        ip link set dev lo up
+    ip netns exec ${SERVER_NS} \
+        ip addr add 10.0.0.1/24 dev ${SERVER_VETH}
+
+    ip netns exec ${CLIENT_NS} \
+        ip link set dev ${CLIENT_VETH} up
+    ip netns exec ${CLIENT_NS} \
+        ip link set dev lo up
+    ip netns exec ${CLIENT_NS} \
+        ip addr add 10.0.0.2/24 dev ${CLIENT_VETH}
+
+    # Add neighbour objects for IP connection
+    ip netns exec ${SERVER_NS} \
+        ip neigh add 10.0.0.2 \
+        lladdr ${CLIENT_VETH_LL_ADDR} \
+        dev ${SERVER_VETH}
+    ip netns exec ${CLIENT_NS} \
+        ip neigh add 10.0.0.1 \
+        lladdr ${SERVER_VETH_LL_ADDR} \
+        dev ${CLIENT_VETH}
+
+    # Turn off optimizations that dent realism.
+    ip netns exec ${CLIENT_NS} \
+        ethtool -K ${CLIENT_VETH} gso off gro off tso off
+
+    ip netns exec ${SERVER_NS} \
+        ethtool -K ${SERVER_VETH} gso off gro off tso off
+
+    # Add netem as qdisc for traffic control
+    ip netns exec ${CLIENT_NS} \
+        tc qdisc add \
+        dev ${CLIENT_VETH} \
+        root netem
+    ip netns exec ${SERVER_NS} \
+        tc qdisc add \
+        dev ${SERVER_VETH} \
+        root netem
+done
+
+# $DATE and $DEBUG is set by setup.sh
+# Only set up tshark for the first network namespace
+if [ "$DEBUG" == "true" ]; then
+    mkdir -p -m 777 captures && touch captures/capture_${DATE}.pcap && chmod a+w captures/capture_${DATE}.pcap
+    ip netns exec cli_ns_1 tshark -i ${CLIENT_VETH} -w captures/capture_${DATE}.pcap &
+fi