From 149c7296ffed7709f89fcf97d0fd9a2515d223f5 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:00:35 +0000 Subject: [PATCH 01/30] add fail check for convert for rando cloud connectins --- 5GTC/main.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) 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): """ From c99f29c713e0a019ed521fd3ae79f1d870ebec68 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:30:25 +0000 Subject: [PATCH 02/30] improved client and server for better tests --- 5GTC/pkg/test/test_pyroute2.py | 59 ++++++++++++++ 5GTC/pkg/test/util.py | 136 +++++++++++++++++++++++++++++++++ evaluation/mptcp_client.c | 32 ++++++++ evaluation/server.py | 92 ++++++++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 5GTC/pkg/test/test_pyroute2.py create mode 100644 5GTC/pkg/test/util.py create mode 100644 evaluation/server.py diff --git a/5GTC/pkg/test/test_pyroute2.py b/5GTC/pkg/test/test_pyroute2.py new file mode 100644 index 0000000..1096b0b --- /dev/null +++ b/5GTC/pkg/test/test_pyroute2.py @@ -0,0 +1,59 @@ +# Tests for modified Pyroute2 library + +import unittest +import pyroute2.netlink.generic.mptcp as mptcp +from 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] + with SysctlContext('net.mptcp.pm_type', 1): + 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/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 4234539..1f0e517 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,6 +34,8 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 +#define MAGIC_NUMBER 0xCAFEBABE + int BYTES_READ = 0; int BYTES_WRITTEN = 0; int LOOP = 1; @@ -158,6 +160,36 @@ 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(4); + memset(magic, 0, 4); + magic[0] = MAGIC_NUMBER; + if (write(sockfd, magic, 4) < 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); + while (LOOP) { char* buffer = malloc(buffer_size); memset(buffer, 0, buffer_size); diff --git a/evaluation/server.py b/evaluation/server.py new file mode 100644 index 0000000..750c5f0 --- /dev/null +++ b/evaluation/server.py @@ -0,0 +1,92 @@ +import socket +import os +import threading +import logging +from collections import namedtuple + +logger = logging.getLogger("mptcp_server") + +MAGIC_NUMBER = 0xCAFEBABE + +CLIENT_TYPES = { + 0: "UPLINK", + 1: "DOWNLINK", + 2: "ECHO" +} + +def generate_random_data_buffer(size): + return os.urandom(size) + +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): + 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: + logger.error("Invalid connection") + 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(sock): + # Read magic number + magic_number = sock.recv(4) + if magic_number != MAGIC_NUMBER: + logger.error("Invalid magic number") + return None + # Read client type (1 byte) + client_type = sock.recv(1) + if client_type not in CLIENT_TYPES: + logger.error("Invalid client type") + return None + # Read buffer size (4 bytes) + buffer_size = sock.recv(4) + if buffer_size < 0: + logger.error("Invalid buffer size") + return None + return ClientConnection(sock, int.from_bytes(client_type, "big"), int.from_bytes(buffer_size, "big")) + + + def handle_client(conn: ClientConnection): + # If client is an echo client, just echo the data back as we receive it + if conn.type == 2: + 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 == 0: + 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 == 1: + while True: + data = generate_random_data_buffer(conn.buffer_size) + conn.socket.sendall(data) + From 83234d294efa04522966cf53d94225c97ccaf8f1 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:33:02 +0000 Subject: [PATCH 03/30] server main --- evaluation/server.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/evaluation/server.py b/evaluation/server.py index 750c5f0..ccee3b5 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -1,5 +1,6 @@ import socket import os +import sys import threading import logging from collections import namedtuple @@ -90,3 +91,8 @@ def handle_client(conn: ClientConnection): data = generate_random_data_buffer(conn.buffer_size) conn.socket.sendall(data) +if __name__ == "__main__": + logging.basicConfig(level=logging.DEBUG) + host, port = sys.argv[1], int(sys.argv[2]) + server = MPTCPServer(host, port) + server.run() \ No newline at end of file From 2027a520b048b6d0c543aaacbc450e66100decdc Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:33:30 +0000 Subject: [PATCH 04/30] server help --- evaluation/server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evaluation/server.py b/evaluation/server.py index ccee3b5..a4d6d80 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -93,6 +93,9 @@ def handle_client(conn: ClientConnection): 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 From a7bbd6ad157c1f6e907e416a6d9e1df0168b84fd Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:34:02 +0000 Subject: [PATCH 05/30] log --- evaluation/server.py | 1 + 1 file changed, 1 insertion(+) diff --git a/evaluation/server.py b/evaluation/server.py index a4d6d80..a1b5d09 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -34,6 +34,7 @@ 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) From 23941d1d2e612b167a5c5bc499063efc06ee7c2b Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:36:26 +0000 Subject: [PATCH 06/30] mnagic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 1f0e517..3172f0c 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,7 +34,7 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 -#define MAGIC_NUMBER 0xCAFEBABE +#define MAGIC_NUMBER 0xBEEF int BYTES_READ = 0; int BYTES_WRITTEN = 0; From 262fdadb1e8cb3cf521a1487c4670725fa168f56 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:37:23 +0000 Subject: [PATCH 07/30] mnagic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 3172f0c..61c7c0c 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,7 +34,7 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 -#define MAGIC_NUMBER 0xBEEF +#define MAGIC_NUMBER "MPTCP" int BYTES_READ = 0; int BYTES_WRITTEN = 0; From af7d37b9d0b8406246f165726d2e7c5ff7cc0e2b Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:39:40 +0000 Subject: [PATCH 08/30] mnagic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 61c7c0c..98d31bd 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,7 +34,7 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 -#define MAGIC_NUMBER "MPTCP" +#define MAGIC_NUMBER 1432 int BYTES_READ = 0; int BYTES_WRITTEN = 0; From e6bae37d539f02b3abf41d1c4f783247fb1e52ac Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:40:37 +0000 Subject: [PATCH 09/30] mnagic number --- evaluation/mptcp_client.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 98d31bd..56e1158 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,7 +34,7 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 -#define MAGIC_NUMBER 1432 +#define MAGIC_NUMBER 0xBEEF int BYTES_READ = 0; int BYTES_WRITTEN = 0; @@ -163,7 +163,7 @@ int main(int argc, char **argv) // Send the server the magic number so it knows we're an MPTCP client char *magic = malloc(4); memset(magic, 0, 4); - magic[0] = MAGIC_NUMBER; + memcpy(magic, MAGIC_NUMBER, 4); if (write(sockfd, magic, 4) < 0) { log_color(RED, "write() failed for magic number"); return -1; From 1e5468238b02aafbb6203a4893f0667a69b4c1bc Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:41:33 +0000 Subject: [PATCH 10/30] mnagic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 56e1158..fc82e92 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -163,7 +163,7 @@ int main(int argc, char **argv) // Send the server the magic number so it knows we're an MPTCP client char *magic = malloc(4); memset(magic, 0, 4); - memcpy(magic, MAGIC_NUMBER, 4); + memcpy(magic, &MAGIC_NUMBER, 4); if (write(sockfd, magic, 4) < 0) { log_color(RED, "write() failed for magic number"); return -1; From 7ae5888c193c939685bcc4474742870444c768c1 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:44:53 +0000 Subject: [PATCH 11/30] mnagic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index fc82e92..887c8d9 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -34,8 +34,8 @@ #define DOWNLINK_CLIENT 1 #define ECHO_CLIENT 2 -#define MAGIC_NUMBER 0xBEEF +int MAGIC_NUMBER = 1423; int BYTES_READ = 0; int BYTES_WRITTEN = 0; int LOOP = 1; From 6fae49600063f17231d62e65127b4c5985448534 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:45:19 +0000 Subject: [PATCH 12/30] server magic number --- evaluation/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/server.py b/evaluation/server.py index a1b5d09..5850d42 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -7,7 +7,7 @@ logger = logging.getLogger("mptcp_server") -MAGIC_NUMBER = 0xCAFEBABE +MAGIC_NUMBER = 1423 CLIENT_TYPES = { 0: "UPLINK", From 3462439f4e74df3b1f539fe0a517bd0b9f592ae0 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:46:52 +0000 Subject: [PATCH 13/30] server --- evaluation/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index 5850d42..59485d7 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -53,7 +53,7 @@ def cleanup(self): t.join() self.sock.close() - def read_client_header(sock): + def read_client_header(self, sock): # Read magic number magic_number = sock.recv(4) if magic_number != MAGIC_NUMBER: @@ -72,7 +72,7 @@ def read_client_header(sock): return ClientConnection(sock, int.from_bytes(client_type, "big"), int.from_bytes(buffer_size, "big")) - def handle_client(conn: ClientConnection): + def handle_client(self, conn: ClientConnection): # If client is an echo client, just echo the data back as we receive it if conn.type == 2: while True: From ed17e13f98a1fe8bb2f2164a8d7ab1d1436fdc6b Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:47:43 +0000 Subject: [PATCH 14/30] server logging --- evaluation/server.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index 59485d7..40bdc2e 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -41,7 +41,6 @@ def run(self): # Create connection object conn = self.read_client_header(client) if conn is None: - logger.error("Invalid connection") client.close() continue # Handle connection @@ -57,17 +56,17 @@ def read_client_header(self, sock): # Read magic number magic_number = sock.recv(4) if magic_number != MAGIC_NUMBER: - logger.error("Invalid magic number") + logger.error("Invalid magic number: %d" % magic_number) return None # Read client type (1 byte) client_type = sock.recv(1) if client_type not in CLIENT_TYPES: - logger.error("Invalid client type") + logger.error("Invalid client type: %d" % client_type) return None # Read buffer size (4 bytes) buffer_size = sock.recv(4) if buffer_size < 0: - logger.error("Invalid buffer size") + logger.error("Invalid buffer size: %d" % buffer_size) return None return ClientConnection(sock, int.from_bytes(client_type, "big"), int.from_bytes(buffer_size, "big")) From 4b4e036e4933c557af3b5a4660bdf72a0e1932a0 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:48:44 +0000 Subject: [PATCH 15/30] convert values in header from bytes --- evaluation/server.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evaluation/server.py b/evaluation/server.py index 40bdc2e..64fe5e8 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -55,20 +55,23 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number magic_number = sock.recv(4) + magic_number = int.from_bytes(magic_number, "big") if magic_number != MAGIC_NUMBER: logger.error("Invalid magic number: %d" % magic_number) return None # Read client type (1 byte) client_type = sock.recv(1) + client_type = int.from_bytes(client_type, "big") if client_type not in CLIENT_TYPES: 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, "big") if buffer_size < 0: logger.error("Invalid buffer size: %d" % buffer_size) return None - return ClientConnection(sock, int.from_bytes(client_type, "big"), int.from_bytes(buffer_size, "big")) + return ClientConnection(sock, client_type, buffer_size) def handle_client(self, conn: ClientConnection): From e320c049d8c97ed46c7ac06b85c21bfee686ee6d Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:50:44 +0000 Subject: [PATCH 16/30] magic number --- evaluation/mptcp_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index 887c8d9..ea62137 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -35,7 +35,7 @@ #define ECHO_CLIENT 2 -int MAGIC_NUMBER = 1423; +int MAGIC_NUMBER = 0xBEEF; int BYTES_READ = 0; int BYTES_WRITTEN = 0; int LOOP = 1; From 633b88aaad01b5fa5263a823bb733ac2cfec3c42 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:51:45 +0000 Subject: [PATCH 17/30] read 2 bytes for magic number --- evaluation/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/server.py b/evaluation/server.py index 64fe5e8..3c846c8 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -54,7 +54,7 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number - magic_number = sock.recv(4) + magic_number = sock.recv(2) magic_number = int.from_bytes(magic_number, "big") if magic_number != MAGIC_NUMBER: logger.error("Invalid magic number: %d" % magic_number) From f7b74767db3e92830b8ee9a05c97078cce0467ef Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:55:29 +0000 Subject: [PATCH 18/30] memcpy 2 bytes --- evaluation/mptcp_client.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index ea62137..f014358 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -161,10 +161,10 @@ int main(int argc, char **argv) signal(SIGINT, sigint_handler); // Send the server the magic number so it knows we're an MPTCP client - char *magic = malloc(4); - memset(magic, 0, 4); - memcpy(magic, &MAGIC_NUMBER, 4); - if (write(sockfd, magic, 4) < 0) { + 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; } From bae9265334f6a427505cdb836cc2fcfb174ae119 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 16:58:16 +0000 Subject: [PATCH 19/30] little endian --- evaluation/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index 3c846c8..cc1a7fe 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -55,19 +55,19 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number magic_number = sock.recv(2) - magic_number = int.from_bytes(magic_number, "big") + magic_number = int.from_bytes(magic_number, "little") if magic_number != MAGIC_NUMBER: logger.error("Invalid magic number: %d" % magic_number) return None # Read client type (1 byte) client_type = sock.recv(1) - client_type = int.from_bytes(client_type, "big") + client_type = int.from_bytes(client_type, "little") if client_type not in CLIENT_TYPES: 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, "big") + buffer_size = int.from_bytes(buffer_size, "little") if buffer_size < 0: logger.error("Invalid buffer size: %d" % buffer_size) return None From fcf889997a5ffe29262f3926ee15d258796db684 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:00:34 +0000 Subject: [PATCH 20/30] server magic number --- evaluation/server.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index cc1a7fe..d6bab78 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -7,7 +7,7 @@ logger = logging.getLogger("mptcp_server") -MAGIC_NUMBER = 1423 +MAGIC_NUMBER = 0xBEEF CLIENT_TYPES = { 0: "UPLINK", @@ -55,19 +55,19 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number magic_number = sock.recv(2) - magic_number = int.from_bytes(magic_number, "little") + magic_number = int.from_bytes(magic_number, "big") if magic_number != MAGIC_NUMBER: logger.error("Invalid magic number: %d" % magic_number) return None # Read client type (1 byte) client_type = sock.recv(1) - client_type = int.from_bytes(client_type, "little") + client_type = int.from_bytes(client_type, "big") if client_type not in CLIENT_TYPES: 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") + buffer_size = int.from_bytes(buffer_size, "big") if buffer_size < 0: logger.error("Invalid buffer size: %d" % buffer_size) return None From d2ea1c1ec661e6fb99f4bd5d13bc8e5bdffdfbef Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:01:13 +0000 Subject: [PATCH 21/30] log --- evaluation/server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evaluation/server.py b/evaluation/server.py index d6bab78..bf2f3e8 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -57,7 +57,7 @@ def read_client_header(self, sock): magic_number = sock.recv(2) magic_number = int.from_bytes(magic_number, "big") if magic_number != MAGIC_NUMBER: - logger.error("Invalid magic number: %d" % magic_number) + logger.error("Invalid magic number: %d != " % magic_number, MAGIC_NUMBER) return None # Read client type (1 byte) client_type = sock.recv(1) From dab95f1bdf0777e5ddb38350a82b7af7076bed2e Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:02:13 +0000 Subject: [PATCH 22/30] little endian --- evaluation/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index bf2f3e8..7e5c21d 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -55,19 +55,19 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number magic_number = sock.recv(2) - magic_number = int.from_bytes(magic_number, "big") + magic_number = int.from_bytes(magic_number, "litte") if magic_number != MAGIC_NUMBER: logger.error("Invalid magic number: %d != " % magic_number, MAGIC_NUMBER) return None # Read client type (1 byte) client_type = sock.recv(1) - client_type = int.from_bytes(client_type, "big") + client_type = int.from_bytes(client_type, "litte") if client_type not in CLIENT_TYPES: 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, "big") + buffer_size = int.from_bytes(buffer_size, "litte") if buffer_size < 0: logger.error("Invalid buffer size: %d" % buffer_size) return None From fafb4c1a200c1fbe5665b1a1589d4a30057aab82 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:02:41 +0000 Subject: [PATCH 23/30] server magic number log --- evaluation/server.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evaluation/server.py b/evaluation/server.py index 7e5c21d..2c4daf8 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -57,7 +57,8 @@ def read_client_header(self, sock): magic_number = sock.recv(2) magic_number = int.from_bytes(magic_number, "litte") if magic_number != MAGIC_NUMBER: - logger.error("Invalid magic number: %d != " % 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) From 1dea841b07d7e587f361a91ec89662cbffbdc6d5 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:03:17 +0000 Subject: [PATCH 24/30] server magic number log --- evaluation/server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evaluation/server.py b/evaluation/server.py index 2c4daf8..9a674db 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -55,20 +55,20 @@ def cleanup(self): def read_client_header(self, sock): # Read magic number magic_number = sock.recv(2) - magic_number = int.from_bytes(magic_number, "litte") + 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, "litte") + client_type = int.from_bytes(client_type, "little") if client_type not in CLIENT_TYPES: 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, "litte") + buffer_size = int.from_bytes(buffer_size, "little") if buffer_size < 0: logger.error("Invalid buffer size: %d" % buffer_size) return None From 9cd2da2a0f65cf7d06d0b842318bc307e41e763b Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 17:05:20 +0000 Subject: [PATCH 25/30] echo fix --- evaluation/server.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evaluation/server.py b/evaluation/server.py index 9a674db..83091b7 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -78,6 +78,9 @@ def read_client_header(self, sock): def handle_client(self, conn: ClientConnection): # If client is an echo client, just echo the data back as we receive it if conn.type == 2: + # 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: From 5519bd12f7a48e51ac78da44c3ad2359dcd8b0be Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 22:23:29 +0000 Subject: [PATCH 26/30] python client --- .gitignore | 1 - .vscode/settings.json | 6 +- 5GTC/pkg/performance_logger/plot.py | 11 ++- 5GTC/pkg/test/__init__.py | 0 5GTC/pkg/test/test_pyroute2.py | 21 ++--- 5GTC/plot.py | 8 +- 5GTC/requirements.txt | 3 +- Makefile | 16 ++-- evaluation/__init__.py | 0 evaluation/client.py | 139 ++++++++++++++++++++++++++++ evaluation/server.py | 32 +++---- evaluation/util.py | 22 +++++ 12 files changed, 216 insertions(+), 43 deletions(-) create mode 100644 5GTC/pkg/test/__init__.py create mode 100644 evaluation/__init__.py create mode 100644 evaluation/client.py create mode 100644 evaluation/util.py diff --git a/.gitignore b/.gitignore index 49412c6..7b238d1 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/pkg/performance_logger/plot.py b/5GTC/pkg/performance_logger/plot.py index bcc71cc..10a7501 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], (0, number_iterations - len(subflow_data[i])), "constant" + ) ax.plot(subflow_data[i]) # Set the title and labels ax.set_title(title if title else feature) 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 index 1096b0b..3a4750c 100644 --- a/5GTC/pkg/test/test_pyroute2.py +++ b/5GTC/pkg/test/test_pyroute2.py @@ -2,7 +2,7 @@ import unittest import pyroute2.netlink.generic.mptcp as mptcp -from test.util import ( +from pkg.test.util import ( SysctlContext, TestMPTCPServer, TestMPTCPClient, @@ -41,16 +41,15 @@ def test_create(self): ips = interface_ips(DEFAULT_NETWORK_INTERFACE) ips.remove(DEFAULT_NETWORK_IP) ip = ips[0] - with SysctlContext('net.mptcp.pm_type', 1): - 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) + 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 diff --git a/5GTC/plot.py b/5GTC/plot.py index e560b56..95cdadf 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} @@ -50,8 +50,12 @@ def plot_subflow_info_from_db( 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/5GTC/requirements.txt b/5GTC/requirements.txt index e3d2ede..d5f7fd8 100644 --- a/5GTC/requirements.txt +++ b/5GTC/requirements.txt @@ -1,4 +1,5 @@ flask matplotlib numpy -scapy \ No newline at end of file +scapy +pyyaml \ No newline at end of file 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..400263a --- /dev/null +++ b/evaluation/client.py @@ -0,0 +1,139 @@ +import socket +import logging +import threading +import signal +import time +import sys +import argparse + +from evaluation.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/server.py b/evaluation/server.py index 83091b7..790518d 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -1,23 +1,19 @@ import socket -import os import sys import threading import logging from collections import namedtuple +from evaluation.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") -MAGIC_NUMBER = 0xBEEF - -CLIENT_TYPES = { - 0: "UPLINK", - 1: "DOWNLINK", - 2: "ECHO" -} - -def generate_random_data_buffer(size): - return os.urandom(size) - ClientConnection = namedtuple("ClientConnection", ["socket", "type", "buffer_size"]) class MPTCPServer: @@ -63,21 +59,21 @@ def read_client_header(self, sock): # Read client type (1 byte) client_type = sock.recv(1) client_type = int.from_bytes(client_type, "little") - if client_type not in CLIENT_TYPES: + 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" % buffer_size) - return None + 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 == 2: + if conn.type == CLIENT_TYPE_ECHO: # Be the first to send data data = generate_random_data_buffer(conn.buffer_size) conn.socket.sendall(data) @@ -87,13 +83,13 @@ def handle_client(self, conn: ClientConnection): break conn.socket.sendall(data) # If client is an uplink client, read data from the socket and never send anything back - elif conn.type == 0: + 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 == 1: + elif conn.type == CLIENT_TYPE_DOWNLINK: while True: data = generate_random_data_buffer(conn.buffer_size) conn.socket.sendall(data) 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) From cb443ef02a88f9264fe45c47cec540bfa9b113bd Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 22:25:31 +0000 Subject: [PATCH 27/30] eval path --- evaluation/client.py | 2 +- evaluation/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evaluation/client.py b/evaluation/client.py index 400263a..f685a2c 100644 --- a/evaluation/client.py +++ b/evaluation/client.py @@ -6,7 +6,7 @@ import sys import argparse -from evaluation.util import ( +from .util import ( generate_random_data_buffer, MAGIC_NUMBER, DEFAULT_BUFFER_SIZE, diff --git a/evaluation/server.py b/evaluation/server.py index 790518d..55089b8 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -3,7 +3,7 @@ import threading import logging from collections import namedtuple -from evaluation.util import ( +from .util import ( MAGIC_NUMBER, DEFAULT_BUFFER_SIZE, CLIENT_TYPE_UPLINK, From 15585d389cafd73727df10f77e600cf06f8bae6b Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 22:26:16 +0000 Subject: [PATCH 28/30] eval path --- evaluation/client.py | 2 +- evaluation/server.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evaluation/client.py b/evaluation/client.py index f685a2c..38a6b33 100644 --- a/evaluation/client.py +++ b/evaluation/client.py @@ -6,7 +6,7 @@ import sys import argparse -from .util import ( +from util import ( generate_random_data_buffer, MAGIC_NUMBER, DEFAULT_BUFFER_SIZE, diff --git a/evaluation/server.py b/evaluation/server.py index 55089b8..6ca76ef 100644 --- a/evaluation/server.py +++ b/evaluation/server.py @@ -3,7 +3,7 @@ import threading import logging from collections import namedtuple -from .util import ( +from util import ( MAGIC_NUMBER, DEFAULT_BUFFER_SIZE, CLIENT_TYPE_UPLINK, From 3873e7f58a6a02e033a9d00e186bd33c2b96437a Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 22:43:25 +0000 Subject: [PATCH 29/30] CLIENT ME LEAK --- evaluation/client.py | 8 ++++++++ evaluation/mptcp_client.c | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/evaluation/client.py b/evaluation/client.py index 38a6b33..4559b74 100644 --- a/evaluation/client.py +++ b/evaluation/client.py @@ -1,3 +1,11 @@ +""" +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 diff --git a/evaluation/mptcp_client.c b/evaluation/mptcp_client.c index f014358..5f4b104 100644 --- a/evaluation/mptcp_client.c +++ b/evaluation/mptcp_client.c @@ -190,10 +190,9 @@ int main(int argc, char **argv) } 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); @@ -227,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); From 6c6f51ea4895a950074e407cfa49421b6e9d7e47 Mon Sep 17 00:00:00 2001 From: Matan Broner Date: Fri, 12 May 2023 23:05:42 +0000 Subject: [PATCH 30/30] plot padding --- 5GTC/pkg/performance_logger/plot.py | 3 ++- 5GTC/plot.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/5GTC/pkg/performance_logger/plot.py b/5GTC/pkg/performance_logger/plot.py index 10a7501..dc96a6e 100644 --- a/5GTC/pkg/performance_logger/plot.py +++ b/5GTC/pkg/performance_logger/plot.py @@ -85,7 +85,7 @@ def plot_subflow_features( 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], (0, number_iterations - len(subflow_data[i])), "constant" + subflow_data[i], (number_iterations - len(subflow_data[i]), 0), "constant" ) ax.plot(subflow_data[i]) # Set the title and labels @@ -107,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/plot.py b/5GTC/plot.py index 95cdadf..e19116b 100644 --- a/5GTC/plot.py +++ b/5GTC/plot.py @@ -45,7 +45,7 @@ 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)