Skip to content
Snippets Groups Projects
Commit 67eb82f3 authored by Bartolomeo Berend Müller's avatar Bartolomeo Berend Müller
Browse files

Added netem-evaluator

parent a6aa8bb6
No related branches found
No related tags found
No related merge requests found
Showing
with 720 additions and 0 deletions
target/
logs/
# 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"
[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"
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
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(())
}
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),
}
}
}
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:?}");
}
fn main() {
println!("Hello, world!");
}
# 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()
#!/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
#!/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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment