diff --git a/.gitignore b/.gitignore index 86c3f3e..48f7a77 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -test *.db *.journal figures/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 24b4f4a..32ed56d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,9 @@ "files.associations": { "convert_util.h": "c", "mptcp.h": "c" - } + }, + "[python]": { + "editor.defaultFormatter": "ms-python.python" + }, + "python.formatting.provider": "none" } \ No newline at end of file diff --git a/5GTC/main.py b/5GTC/main.py index 6b2bd93..02c77c2 100644 --- a/5GTC/main.py +++ b/5GTC/main.py @@ -92,7 +92,9 @@ def run_nonblocking(self): logger.debug("Accepted connection from {} with fd={}".format(addr, client_sock.fileno())) logger.debug(client_sock) - self.handle_connection(client_sock) + success = self.handle_connection(client_sock) + if not success: + client_sock.close() else: # Read from the socket self.read_and_forward(s) @@ -103,18 +105,23 @@ def handle_connection(self, client_sock): Handle a new connection from a client Parses the Convert header and handles the TLVs """ - # Read the Convert header from the client (TCP Fast Open) - convert = read_convert_header(client_sock) - if convert is None: - logger.error("Error reading Convert header from client") - return - - # Go through the TLVs and handle them - for tlv in convert.tlvs: - if CONVERT_TLVS[tlv.type] == "connect": - self.handle_tlv_connect(tlv, client_sock) - else: - logger.debug("Handled TLV: {}".format(CONVERT_TLVS[tlv.type])) + try: + # Read the Convert header from the client (TCP Fast Open) + convert = read_convert_header(client_sock) + if convert is None: + logger.error("Error reading Convert header from client") + return + + # Go through the TLVs and handle them + for tlv in convert.tlvs: + if CONVERT_TLVS[tlv.type] == "connect": + self.handle_tlv_connect(tlv, client_sock) + else: + logger.debug("Handled TLV: {}".format(CONVERT_TLVS[tlv.type])) + return True + except Exception as e: + logger.error("Error handling Convert Protocol header: {}".format(e)) + return False def handle_tlv_connect(self, tlv, client_sock): """ diff --git a/5GTC/pkg/performance_logger/plot.py b/5GTC/pkg/performance_logger/plot.py index bcc71cc..dc96a6e 100644 --- a/5GTC/pkg/performance_logger/plot.py +++ b/5GTC/pkg/performance_logger/plot.py @@ -64,24 +64,29 @@ def plot_subflow_features( # Get subflow data for the feature subflow_data = [subflow[feature] for subflow in data.values()] + number_iterations = max([len(subflow) for subflow in subflow_data]) + # If fx_time, plot the x axis as time (in minutes) if fx_time: iterations_per_minute = (60 * 1000) / iteration_ms # Number of ticks = (ms per iteration) * (number of iterations) all divided by 60,000 ms - num_minutes = ((len(subflow_data[0]) * iteration_ms) / 1000) / 60 + num_minutes = ((len(number_iterations) * iteration_ms) / 1000) / 60 # Round up to the nearest minute num_minutes = int(np.ceil(num_minutes)) ticks_loc = np.arange( - 0, len(subflow_data[0]) + iterations_per_minute, iterations_per_minute + 0, len(number_iterations) + iterations_per_minute, iterations_per_minute ) ax.set_xticks(ticks_loc) ax.set_xticklabels(np.arange(0, num_minutes + 1, 1)) ax.set_xlabel(xlabel if xlabel else "Time (minutes)") else: - x = np.arange(0, len(subflow_data[0])) ax.set_xlabel(xlabel if xlabel else "Iteration") # Plot the data for i in range(len(subflow_data)): + # Left pad the data with 0s so that all subflows have the same length + subflow_data[i] = np.pad( + subflow_data[i], (number_iterations - len(subflow_data[i]), 0), "constant" + ) ax.plot(subflow_data[i]) # Set the title and labels ax.set_title(title if title else feature) @@ -102,6 +107,7 @@ def plot_transfer(subflow_data, iteration_ms=500, subflow_keys=None): # Create a new figure fig = Figure() ax = fig.add_subplot(111) + number_iterations = max([len(subflow_data[subflow]["tcpi_bytes_acked"]) for subflow in subflow_data]) for subflow in subflow_data: # Calculate the throughput iterations_per_minute = (60 * 1000) / iteration_ms diff --git a/5GTC/pkg/test/__init__.py b/5GTC/pkg/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/5GTC/pkg/test/test_pyroute2.py b/5GTC/pkg/test/test_pyroute2.py new file mode 100644 index 0000000..3a4750c --- /dev/null +++ b/5GTC/pkg/test/test_pyroute2.py @@ -0,0 +1,58 @@ +# Tests for modified Pyroute2 library + +import unittest +import pyroute2.netlink.generic.mptcp as mptcp +from pkg.test.util import ( + SysctlContext, + TestMPTCPServer, + TestMPTCPClient, + interface_ips, + mptcp_info_get_attr, + DEFAULT_NETWORK_INTERFACE, + DEFAULT_NETWORK_IP +) + +class TestMPTCP(unittest.TestCase): + def setUp(self): + self.mptcp = mptcp.MPTCP() + + def tearDown(self): + self.mptcp.close() + + def test_create(self): + # Sanity check + self.assertIsInstance(self.mptcp, mptcp.MPTCP) + + # Check that the default network interface has more than one IP address + self.assertGreater(len(interface_ips(DEFAULT_NETWORK_INTERFACE)), 1) + + # Create server + server = TestMPTCPServer(DEFAULT_NETWORK_IP) + server.listen() + + # Create the connection + client = TestMPTCPClient(DEFAULT_NETWORK_IP, server.port) + self.assertTrue(client.is_connected()) + + # Create the new subflow + token = mptcp_info_get_attr(client.sock, 'token') + + # Choose an IP that is not the IP used by the client socket already + ips = interface_ips(DEFAULT_NETWORK_INTERFACE) + ips.remove(DEFAULT_NETWORK_IP) + ip = ips[0] + res = self.mptcp.create( + local_ip=ip, + local_port=0, + local_id=1, + remote_ip=server.ip, + remote_port=server.port, + token=token + ) + print(res) + + + # Kill the client and server + client.close() + server.close() + diff --git a/5GTC/pkg/test/util.py b/5GTC/pkg/test/util.py new file mode 100644 index 0000000..97d60cd --- /dev/null +++ b/5GTC/pkg/test/util.py @@ -0,0 +1,136 @@ +import socket +import threading +import subprocess +from pyroute2 import NDB +from mptcp_util import * + +IPV4 = 2 + +# TODO: configure this through command line arguments +DEFAULT_NETWORK_INTERFACE = "enp0s8" +DEFAULT_NETWORK_IP = "192.168.2.221" + +class SysctlContext: + """ + Allows execution of code with a temporary sysctl variable value. + Example: + with SysctlContext("net.mptcp.mptcp_enabled", 0): + # Code that runs with mptcp disabled + """ + def __init__(self, key, value): + self.key = key + self.value = value + + def __enter__(self): + # Save the current value of the sysctl variable + self.old_value = subprocess.check_output(['sysctl', '-n', self.key]).decode().strip() + + # Set the new value of the sysctl variable + subprocess.check_call(['sysctl', '-w', f"{self.key}={self.value}"]) + + # Read back the value to make sure it was set correctly + new_value = subprocess.check_output(['sysctl', '-n', self.key]).decode().strip() + + # Print the old and new values + print(f"sysctl {self.key} was {self.old_value}, now {new_value}") + + def __exit__(self, exc_type, exc_val, exc_tb): + # Restore the old value of the sysctl variable + subprocess.check_call(['sysctl', '-w', f"{self.key}={self.old_value}"]) + +class TestMPTCPServer: + """ + This class is used to create a MPTCP server for testing purposes. + It starts a server in a background thread and listens for incoming connections. + After receiving a connection, it echoes the data back to the client. + """ + + def __init__(self, ip): + self.ip = ip + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind((self.ip, 0)) + self.port = None + + def __del__(self): + self.sock.close() + if self.thread: + self.thread.join() + + def listen(self): + # Start the server in a background thread and store the thread object + self._running = True + self.thread = threading.Thread(target=self._listen) + self.thread.start() + # Wait for the server to start listening, blocking call + while self.port == None: + pass + + def close(self): + self._running = False + self.sock.close() + if self.thread: + self.thread.join() + + def _listen(self): + self.sock.listen(10) + self.port = self.sock.getsockname()[1] + while self._running: + conn, addr = self.sock.accept() + # Mirror the data back to the client + data = conn.recv(1024) + if not data: + break + conn.sendall(data) + conn.close() + + +class TestMPTCPClient: + """ + This class is used to create a MPTCP client for testing purposes. + It connects to a given server and sends data to it. + """ + def __init__(self, server_ip, server_port): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) + self.sock.connect((server_ip, server_port)) + + def __del__(self): + self.close() + + def close(self): + self.sock.close() + + def send(self, data): + self.sock.sendall(data) + + def is_connected(self): + return self.sock.fileno() != -1 + +def interface_ips(ifname): + """ + Return a list of IP addresses for the given interface name. + """ + ips = [] + with NDB() as ndb: + # Get the interface object by name + iface = ndb.interfaces.get(ifname) + + if not iface: + raise Exception("Interface not found: {}".format(ifname)) + + # Get all IP addresses (both virtual and not) for the interface + for ipaddr in iface.ipaddr: + # If IP address is IPV4, add it to the list + if ipaddr['family'] == IPV4: + ips.append(ipaddr['address']) + + return ips + +def mptcp_info_get_attr(sock, attr): + """ + Get the value of a MPTCP socket attribute. + """ + # Get the MPTCP connection token + attr = 'mptcpi_' + attr + mptcp_info = get_mptcp_info(sock.fileno()) + return mptcp_info[attr] \ No newline at end of file diff --git a/5GTC/plot.py b/5GTC/plot.py index e560b56..e19116b 100644 --- a/5GTC/plot.py +++ b/5GTC/plot.py @@ -3,7 +3,7 @@ from pkg.performance_logger.db import DB FIGURES_DIR = os.path.join(os.path.dirname(__file__), "figures") -DB_NAME = "performance_log.db" +DB_PATH = "performance_log.db" ITERATION_TIME_MS = 500 @@ -14,7 +14,7 @@ def plot_subflow_info_from_db( Plot the subflow info from a session_id """ is_transfer_rate = field == "transfer_rate" - db = DB({"db_path": DB_NAME, "delete_db_on_exit": False}) + db = DB({"db_path": DB_PATH, "delete_db_on_exit": False}) subflows = db.read_all_subflows(iteration_id) key = "tcpi_bytes_acked" if is_transfer_rate else field data = {sf[1]: {key: []} for sf in subflows} @@ -45,13 +45,17 @@ def plot_subflow_info_from_db( if __name__ == "__main__": parser = argparse.ArgumentParser(description="Plot the data from a performance log") - parser.add_argument("--iteration_id", type=str, help="The iteration id to plot") + parser.add_argument("--iteration-id", type=str, help="The iteration id to plot") parser.add_argument("--field", type=str, help="The field to plot") parser.add_argument("--xlabel", type=str, help="The x-axis label", default=None) parser.add_argument("--ylabel", type=str, help="The y-axis label", default=None) parser.add_argument("--title", type=str, help="The title of the plot", default=None) + parser.add_argument("--db-path", type=str, help="The path to the database", default=DB_PATH) args = parser.parse_args() if not os.path.exists(FIGURES_DIR): os.makedirs(FIGURES_DIR) + + DB_PATH = args.db_path + plot_subflow_info_from_db(args.iteration_id, args.field, args.xlabel, args.ylabel, args.title) diff --git a/Makefile b/Makefile index 7e35b82..4f5479e 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ CC = gcc DEV_CFLAGS = -Wno-unused-variable -Wno-unused-function CFLAGS = -fPIC -Wall -Wextra $(DEV_CFLAGS) LDFLAGS = -shared +ENV_NAME = venv CLIENT_SRCS = lib_convert/convert_client.c lib_convert/convert_util.c CLIENT_OBJS = $(CLIENT_SRCS:.c=.o) @@ -19,14 +20,17 @@ $(CLIENT_TARGET): $(CLIENT_OBJS) $(CC) $(CFLAGS) -c $< -o $@ setup_transport_converter: - python3 -m venv venv - . venv/bin/activate - pip install -r ./5GTC/requirements.txt - pip install ./5GTC/pkg/mptcp_util + python3 -m venv $(ENV_NAME) + $(ENV_NAME)/bin/pip install --upgrade pip + $(ENV_NAME)/bin/pip install -r ./5GTC/requirements.txt + $(ENV_NAME)/bin/pip install install ./5GTC/pkg/mptcp_util + $(ENV_NAME)/bin/pip install install ./pyroute2 run_transport_converter: - . venv/bin/activate - cd 5GTC && sudo -E python3 main.py + cd 5GTC && ../$(ENV_NAME)/bin/python3 ./main.py + +test_transport_converter: + cd 5GTC && sudo -E ../$(ENV_NAME)/bin/python3 -m unittest discover .PHONY: clean clean: diff --git a/evaluation/__init__.py b/evaluation/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/evaluation/client.py b/evaluation/client.py new file mode 100644 index 0000000..4559b74 --- /dev/null +++ b/evaluation/client.py @@ -0,0 +1,147 @@ +""" +NOTE: This has not been successfully tested. +Python wraps the basic socket(), connect(), etc. calls in a higher-level abstraction called a socket object. +We cannot currently intercept these calls using lib_convert. + +This is left over in a broken state from Matan's testing! +""" + +import socket +import logging +import threading +import signal +import time +import sys +import argparse + +from util import ( + generate_random_data_buffer, + MAGIC_NUMBER, + DEFAULT_BUFFER_SIZE, + CLIENT_TYPE_UPLINK, + CLIENT_TYPE_DOWNLINK, + CLIENT_TYPE_ECHO, + COLORS +) + +logger = logging.getLogger("mptcp_client") + +def log(msg, level="INFO"): + color = COLORS["NONE"] + if level == "INFO": + color = COLORS["GREEN"] + elif level == "WARNING": + color = COLORS["YELLOW"] + elif level == "ERROR": + color = COLORS["RED"] + logger.log(level, "%s%s%s" % (color, msg, COLORS["NONE"])) + +class MPTCPClient: + def __init__(self, bind, host, port, client_type=CLIENT_TYPE_ECHO, buffer_size=DEFAULT_BUFFER_SIZE): + self.host = host + self.port = port + self.client_type = client_type + self.buffer_size = buffer_size + self.bind = bind + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind(self.bind) + self.sock.connect((self.host, self.port)) + self._read_data = b"" + self._total_bytes_sent = 0 + self._total_bytes_received = 0 + self.loop = True + self.authenticate() + + def __del__(self): + self.sock.close() + if self.thread: + self.thread.join() + + def authenticate(self): + log("Authenticating with server...", "INFO") + # Send magic number + self.sock.send(MAGIC_NUMBER.to_bytes(2, "little")) + # Send cllog("Total bytes sent: %d" % self._total_bytes_sent) + log("Total bytes received: %d" % self._total_bytes_received) + self.sock.send(self.client_type.to_bytes(1, "little")) + # Send buffer size + self.sock.send(self.buffer_size.to_bytes(4, "little")) + + def run(self): + # Start a background thread to report metrics + self.thread = threading.Thread(target=self.report_metrics) + self.thread.start() + # Set a SIGINT handler to stop the client + signal.signal(signal.SIGINT, self.stop) + while self.loop: + if self.client_type in [CLIENT_TYPE_DOWNLINK, CLIENT_TYPE_ECHO]: + self.handle_downlink() + if self.client_type in [CLIENT_TYPE_UPLINK, CLIENT_TYPE_ECHO]: + self.handle_uplink() + + def stop(self, signum, frame): + self.loop = False + log("Stopping client...", "WARNING") + + def handle_downlink(self): + """ + Handle downlink traffic. Store received data in self._read_data. + """ + data = self.sock.recv(self.buffer_size) + self._read_data = data + self._total_bytes_received += len(data) + + def handle_uplink(self): + """ + Handle uplink traffic. Send random data from buffer. + """ + if not self._read_data: + data = generate_random_data_buffer(self.buffer_size) + else: + data = self._read_data + self.sock.send(data) + self._total_bytes_sent += len(data) + + def report_metrics(self): + """ + Report metrics to stdout. + """ + while self.loop: + log("Total bytes sent: %d" % self._total_bytes_sent, "INFO") + log("Total bytes received: %d" % self._total_bytes_received, "INFO") + time.sleep(1) + +if __name__ == "__main__": + if len(sys.argv) < 3: + print("Usage: python3 client.py [--bind ] [--client-type ] [--buffer-size ]") + print("\t: Host to connect to") + print("\t: Port to connect to") + print("\t--bind : Bind to this address") + print("\t--client-type : Client type [0: uplink, 1: downlink, 2: echo]") + print("\t--buffer-size : Buffer size") + sys.exit(1) + parser = argparse.ArgumentParser(description="MPTCP Client") + parser.add_argument("host", type=str, help="Host to connect to") + parser.add_argument("port", type=int, help="Port to connect to") + parser.add_argument("--bind", type=str, help="Bind to this address") + parser.add_argument("--client-type", type=int, help="Client type") + parser.add_argument("--buffer-size", type=int, help="Buffer size") + args = parser.parse_args() + + if not args.bind: + log("No bind address specified. Using 0.0.0.0", "WARNING") + args.bind = "0.0.0.0" + if not args.client_type or args.client_type not in [CLIENT_TYPE_UPLINK, CLIENT_TYPE_DOWNLINK, CLIENT_TYPE_ECHO]: + log("No client type specified. Using echo client", "WARNING") + args.client_type = CLIENT_TYPE_ECHO + if not args.buffer_size: + log("No buffer size specified. Using default buffer size", "WARNING") + args.buffer_size = DEFAULT_BUFFER_SIZE + (args.bind, 0) + client = MPTCPClient((args.bind, 0), args.host, args.port, args.client_type, args.buffer_size) + client.run() + + + + \ No newline at end of file diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 4234539..5f4b104 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,6 +34,8 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 + +int MAGIC_NUMBER = 0xBEEF; int BYTES_READ = 0; int BYTES_WRITTEN = 0; int LOOP = 1; @@ -158,10 +160,39 @@ int main(int argc, char **argv) // Set up signal handler for keyboard interrupt signal(SIGINT, sigint_handler); + // Send the server the magic number so it knows we're an MPTCP client + char *magic = malloc(2); + memset(magic, 0, 2); + memcpy(magic, &MAGIC_NUMBER, 2); + if (write(sockfd, magic, 2) < 0) { + log_color(RED, "write() failed for magic number"); + return -1; + } + free(magic); + + // Send the server a 1 byte message to tell it what type of client we are + char *client_type = malloc(1); + memset(client_type, 0, 1); + client_type[0] = CLIENT_TYPE; + if (write(sockfd, client_type, 1) < 0) { + log_color(RED, "write() failed for client type"); + return -1; + } + free(client_type); + + // Send the server the buffer size we're using + char *buffer_size_str = malloc(4); + memset(buffer_size_str, 0, 4); + memcpy(buffer_size_str, &buffer_size, 4); + if (write(sockfd, buffer_size_str, 4) < 0) { + log_color(RED, "write() failed for buffer size"); + return -1; + } + free(buffer_size_str); + + char* buffer = malloc(buffer_size); while (LOOP) { - char* buffer = malloc(buffer_size); memset(buffer, 0, buffer_size); - // if downlink client or echo client, read from server if (CLIENT_TYPE == DOWNLINK_CLIENT || CLIENT_TYPE == ECHO_CLIENT) { int bytes_read = read(sockfd, buffer, buffer_size); @@ -195,6 +226,7 @@ int main(int argc, char **argv) memset(buffer, 0, buffer_size); } } + free(buffer); log_color(BLUE, "Closing connection..."); close(sockfd); pthread_join(thread_id, NULL); diff --git a/evaluation/server.py b/evaluation/server.py new file mode 100644 index 0000000..6ca76ef --- /dev/null +++ b/evaluation/server.py @@ -0,0 +1,104 @@ +import socket +import sys +import threading +import logging +from collections import namedtuple +from util import ( + MAGIC_NUMBER, + DEFAULT_BUFFER_SIZE, + CLIENT_TYPE_UPLINK, + CLIENT_TYPE_DOWNLINK, + CLIENT_TYPE_ECHO, + generate_random_data_buffer +) + +logger = logging.getLogger("mptcp_server") + +ClientConnection = namedtuple("ClientConnection", ["socket", "type", "buffer_size"]) + +class MPTCPServer: + def __init__(self, host, port): + self.host = host + self.port = port + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_MPTCP) + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.sock.bind((self.host, self.port)) + self.sock.listen(200) + self.threads = [] + + def __del__(self): + self.cleanup() + + def run(self): + logger.info("Listening on %s:%d" % (self.host, self.port)) + while True: + client, addr = self.sock.accept() + logger.info("New connection from %s:%d" % addr) + # Create connection object + conn = self.read_client_header(client) + if conn is None: + client.close() + continue + # Handle connection + t = threading.Thread(target=self.handle_client, args=(conn,)).start() + self.threads.append(t) + + def cleanup(self): + for t in self.threads: + t.join() + self.sock.close() + + def read_client_header(self, sock): + # Read magic number + magic_number = sock.recv(2) + magic_number = int.from_bytes(magic_number, "little") + if magic_number != MAGIC_NUMBER: + logger.error("Invalid magic number: %d" % magic_number) + logger.error("Expected: %d" % MAGIC_NUMBER) + return None + # Read client type (1 byte) + client_type = sock.recv(1) + client_type = int.from_bytes(client_type, "little") + if client_type not in [CLIENT_TYPE_UPLINK, CLIENT_TYPE_DOWNLINK, CLIENT_TYPE_ECHO]]: + logger.error("Invalid client type: %d" % client_type) + return None + # Read buffer size (4 bytes) + buffer_size = sock.recv(4) + buffer_size = int.from_bytes(buffer_size, "little") + if buffer_size < 0: + logger.error("Invalid buffer size: %d. Using default buffer size." % buffer_size) + buffer_size = DEFAULT_BUFFER_SIZE + return ClientConnection(sock, client_type, buffer_size) + + + def handle_client(self, conn: ClientConnection): + # If client is an echo client, just echo the data back as we receive it + if conn.type == CLIENT_TYPE_ECHO: + # Be the first to send data + data = generate_random_data_buffer(conn.buffer_size) + conn.socket.sendall(data) + while True: + data = conn.socket.recv(conn.buffer_size) + if not data: + break + conn.socket.sendall(data) + # If client is an uplink client, read data from the socket and never send anything back + elif conn.type == CLIENT_TYPE_UPLINK: + while True: + data = conn.socket.recv(conn.buffer_size) + if not data: + break + # If client is a downlink client, send random data to the client + elif conn.type == CLIENT_TYPE_DOWNLINK: + while True: + data = generate_random_data_buffer(conn.buffer_size) + conn.socket.sendall(data) + +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + if len(sys.argv) != 3: + print("Usage: python3 server.py ") + sys.exit(1) + host, port = sys.argv[1], int(sys.argv[2]) + server = MPTCPServer(host, port) + server.run() \ No newline at end of file diff --git a/evaluation/util.py b/evaluation/util.py new file mode 100644 index 0000000..4062075 --- /dev/null +++ b/evaluation/util.py @@ -0,0 +1,22 @@ +import os + +MAGIC_NUMBER = 0xBEEF +DEFAULT_BUFFER_SIZE = 1024 + +CLIENT_TYPE_UPLINK = 0 +CLIENT_TYPE_DOWNLINK = 1 +CLIENT_TYPE_ECHO = 2 + +COLORS = { + "RED": "\033[91m", + "GREEN": "\033[92m", + "YELLOW": "\033[93m", + "BLUE": "\033[94m", + "PURPLE": "\033[95m", + "CYAN": "\033[96m", + "WHITE": "\033[97m", + "NONE": "\033[0m", +} + +def generate_random_data_buffer(size): + return os.urandom(size)