feat(dscp): initial commit

This commit is contained in:
Sebastian Rust
2025-01-29 12:13:15 +01:00
commit 3acc8a6318
8 changed files with 194 additions and 0 deletions

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM martenseemann/quic-network-simulator-endpoint:latest
# download and build your QUIC implementation
# [ DO WORK HERE ]
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y python3 python3-loguru
RUN ln -s /usr/bin/python3 /usr/bin/python
# copy run script and run it
COPY run_endpoint.sh .
RUN chmod +x run_endpoint.sh
COPY sender.py .
COPY server.py .
ENTRYPOINT [ "./run_endpoint.sh" ]

0
README.md Normal file
View File

6
compose.yml Normal file
View File

@@ -0,0 +1,6 @@
services:
dscp-toy:
image: git.rust.cloud/dscp/toy:latest
build:
context: .
dockerfile: Dockerfile

9
pyproject.toml Normal file
View File

@@ -0,0 +1,9 @@
[project]
name = "test"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"loguru>=0.7.2",
]

17
run_endpoint.sh Normal file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Set up the routing needed for the simulation
/setup.sh
# The following variables are available for use:
# - ROLE contains the role of this execution context, client or server
# - SERVER_PARAMS contains user-supplied command line parameters
# - CLIENT_PARAMS contains user-supplied command line parameters
if [ "$ROLE" == "client" ]; then
# Wait for the simulator to start up.
/wait-for-it.sh sim:57832 -s -t 30
python sender.py $CLIENT_PARAMS
elif [ "$ROLE" == "server" ]; then
python server.py $SERVER_PARAMS
fi

68
sender.py Normal file
View File

@@ -0,0 +1,68 @@
from argparse import ArgumentParser
from socket import socket, AF_INET, SOCK_DGRAM, IPPROTO_IP, IP_TOS
from time import sleep
import re
import time
import sys
from loguru import logger
def parse_bandwidth(input_str):
pattern = r'^(\d+(?:\.\d+)?)\s*([kKmMgGtT])bps$'
match = re.match(pattern, input_str)
if match:
value = float(match.group(1))
unit = match.group(2).lower()
multipliers = {
'k': 1e3,
'm': 1e6,
'g': 1e9,
't': 1e12
}
multiplier = multipliers.get(unit, 1.0)
return value * multiplier
else:
raise ValueError(f"Invalid bandwidth: {input_str}")
def main():
parser = ArgumentParser()
parser.add_argument('--host', type=str, default='localhost')
parser.add_argument('--port', type=int, default=12345)
parser.add_argument("--log-level", type=str, default="INFO")
parser.add_argument("--bandwidth", type=str, default="1Mbps")
parser.add_argument("--duration", type=int, default=10)
parser.add_argument("--dscp", type=str, default="")
args = parser.parse_args()
logger.remove()
logger.add(sys.stderr, level=args.log_level)
PACKET_SIZE = 1280
bandwidth = parse_bandwidth(args.bandwidth)
time_between_packets = PACKET_SIZE / bandwidth
packet_cnt = 0
logger.info(f"Connecting to {args.host}:{args.port}")
sock = socket(AF_INET, SOCK_DGRAM)
dscp = int(args.dscp) if args.dscp else 0
if dscp:
sock.setsockopt(IPPROTO_IP, IP_TOS, dscp << 2)
CODE_WORD = "CAFEABBA"
payload = (CODE_WORD + "A" * (PACKET_SIZE - len(CODE_WORD))).encode()
t0 = time.time()
while True:
logger.debug("Sending message:")
sock.sendto(payload, (args.host, args.port))
packet_cnt += 1
t1 = time.time() - t0
if t1 > args.duration:
break
time_to_sleep = time_between_packets * packet_cnt - t1
if time_to_sleep > 0:
sleep(time_between_packets * packet_cnt - t1)
else:
logger.warning("We are too slow: {}s", -time_to_sleep)
if __name__ == '__main__':
main()

37
server.py Normal file
View File

@@ -0,0 +1,37 @@
import socket
import argparse
import time
import sys
def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(description="UDP Packet Receiver with Timeout")
parser.add_argument('--port', type=int, required=True, help="Port to listen for UDP packets.")
parser.add_argument('--timeout', type=int, required=True, help="Timeout in seconds to shut down if no packets are received.")
args = parser.parse_args()
# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(("", args.port))
print(f"Listening for UDP packets on port {args.port}...")
# Set the timeout for the socket
sock.settimeout(args.timeout)
try:
while True:
try:
# Wait for a UDP packet
data, addr = sock.recvfrom(1024) # Buffer size is 1024 bytes
except socket.timeout:
print(f"No packets received for {args.timeout} seconds. Shutting down.")
break
except KeyboardInterrupt:
print("\nShutting down due to user interrupt.")
finally:
sock.close()
print("Socket closed.")
if __name__ == "__main__":
main()

44
uv.lock generated Normal file
View File

@@ -0,0 +1,44 @@
version = 1
requires-python = ">=3.12"
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
]
[[package]]
name = "loguru"
version = "0.7.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
{ name = "win32-setctime", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/30/d87a423766b24db416a46e9335b9602b054a72b96a88a241f2b09b560fa8/loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac", size = 145103 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/03/0a/4f6fed21aa246c6b49b561ca55facacc2a44b87d65b8b92362a8e99ba202/loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb", size = 62549 },
]
[[package]]
name = "test"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "loguru" },
]
[package.metadata]
requires-dist = [{ name = "loguru", specifier = ">=0.7.2" }]
[[package]]
name = "win32-setctime"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/6b/dd/f95a13d2b235a28d613ba23ebad55191514550debb968b46aab99f2e3a30/win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2", size = 3676 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/e6/a7d828fef907843b2a5773ebff47fb79ac0c1c88d60c0ca9530ee941e248/win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad", size = 3604 },
]