update,
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
# Copyright (C) 2023 Carnegie Mellon University and
|
||||
# Hong Kong University of Science and Technology
|
||||
|
||||
# This repository is adopted from the TCP in the
|
||||
# Wild course project from the Computer Networks
|
||||
# course taught at Carnegie Mellon University, and
|
||||
# is used for the Computer Networks (ELEC 3120)
|
||||
# course taught at Hong Kong University of Science
|
||||
# and Technology.
|
||||
|
||||
# No part of the project may be copied and/or
|
||||
# distributed without the express permission of
|
||||
# the course staff. Everyone is prohibited from
|
||||
# releasing their forks in any public places.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from scapy.all import (
|
||||
Packet,
|
||||
IntField,
|
||||
ShortField,
|
||||
StrLenField,
|
||||
ByteEnumField,
|
||||
bind_layers,
|
||||
)
|
||||
from scapy.layers.l2 import Ether
|
||||
from scapy.layers.inet import IP, UDP
|
||||
|
||||
CODE_DIR = "/vagrant/project-1_elec3120"
|
||||
PCAP = "/vagrant/project-1_elec3120/tests/test.pcap"
|
||||
IFNAME = os.getenv("IFNAME")
|
||||
|
||||
# Which host are we running this pytest script on, server or client?
|
||||
# If we are running pytest on the server VM, we want the testing host to be the
|
||||
# client VM and vice versa.
|
||||
HOSTNAME = subprocess.check_output("hostname").strip()
|
||||
if HOSTNAME.decode("utf-8") == "client":
|
||||
TESTING_HOSTNAME = "server"
|
||||
HOSTNAME = "client"
|
||||
elif HOSTNAME.decode("utf-8") == "server":
|
||||
TESTING_HOSTNAME = "client"
|
||||
HOSTNAME = "server"
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"Unexpected hostname: {HOSTNAME}. You must run these tests in the "
|
||||
f"client or server VM."
|
||||
)
|
||||
|
||||
# You might need to update these for the network setting on your VMs.
|
||||
IP_ADDRS = {"client": "10.0.1.2", "server": "10.0.1.1"}
|
||||
MAC_ADDRS = {"client": "08:00:27:a7:fe:b1", "server": "08:00:27:22:47:1c"}
|
||||
HOST_IP = IP_ADDRS[HOSTNAME]
|
||||
HOST_MAC = MAC_ADDRS[HOSTNAME]
|
||||
HOST_PORT = 1234
|
||||
TESTING_HOST_IP = IP_ADDRS[TESTING_HOSTNAME]
|
||||
TESTING_HOST_MAC = MAC_ADDRS[TESTING_HOSTNAME]
|
||||
TESTING_HOST_PORT = 15441
|
||||
|
||||
# We can use these commands to start/stop the testing server in a background
|
||||
# process.
|
||||
START_TESTING_SERVER_CMD = (
|
||||
"tmux new -s pytest_server -d /vagrant/"
|
||||
"project-1_elec3120/tests/testing_server"
|
||||
)
|
||||
STOP_TESTING_SERVER_CMD = "tmux kill-session -t pytest_server"
|
||||
|
||||
# Default scapy packets headers we'll use to send packets.
|
||||
eth = Ether(src=HOST_MAC, dst=TESTING_HOST_MAC)
|
||||
ip = IP(src=HOST_IP, dst=TESTING_HOST_IP)
|
||||
udp = UDP(sport=HOST_PORT, dport=TESTING_HOST_PORT)
|
||||
|
||||
FIN_MASK = 0x2
|
||||
ACK_MASK = 0x4
|
||||
SYN_MASK = 0x8
|
||||
|
||||
TIMEOUT = 3
|
||||
|
||||
"""
|
||||
These tests assume there is only one connection in the PCAP
|
||||
and expects the PCAP to be collected on the server. All of
|
||||
the basic tests pass on the starter code, without you having
|
||||
to make any changes. You will need to add to these tests as
|
||||
you add functionality to your implementation. It is also
|
||||
important to understand what the given tests are testing for!
|
||||
"""
|
||||
|
||||
|
||||
# we can make CMUTCP packets using scapy
|
||||
class CMUTCP(Packet):
|
||||
name = "CMU TCP"
|
||||
fields_desc = [
|
||||
IntField("identifier", 15441),
|
||||
ShortField("source_port", HOST_PORT),
|
||||
ShortField("destination_port", TESTING_HOST_PORT),
|
||||
IntField("seq_num", 0),
|
||||
IntField("ack_num", 0),
|
||||
ShortField("hlen", 25),
|
||||
ShortField("plen", 25),
|
||||
ByteEnumField(
|
||||
"flags",
|
||||
0,
|
||||
{
|
||||
FIN_MASK: "FIN",
|
||||
ACK_MASK: "ACK",
|
||||
SYN_MASK: "SYN",
|
||||
FIN_MASK | ACK_MASK: "FIN ACK",
|
||||
SYN_MASK | ACK_MASK: "SYN ACK",
|
||||
},
|
||||
),
|
||||
ShortField("advertised_window", 1),
|
||||
ShortField("extension_length", 0),
|
||||
StrLenField(
|
||||
"extension_data",
|
||||
None,
|
||||
length_from=lambda pkt: pkt.extension_length,
|
||||
),
|
||||
]
|
||||
|
||||
def answers(self, other):
|
||||
return isinstance(other, CMUTCP)
|
||||
|
||||
|
||||
bind_layers(UDP, CMUTCP)
|
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2023 Carnegie Mellon University and
|
||||
# Hong Kong University of Science and Technology
|
||||
|
||||
# This repository is adopted from the TCP in the
|
||||
# Wild course project from the Computer Networks
|
||||
# course taught at Carnegie Mellon University, and
|
||||
# is used for the Computer Networks (ELEC 3120)
|
||||
# course taught at Hong Kong University of Science
|
||||
# and Technology.
|
||||
|
||||
# No part of the project may be copied and/or
|
||||
# distributed without the express permission of
|
||||
# the course staff. Everyone is prohibited from
|
||||
# releasing their forks in any public places.
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from scapy.all import rdpcap
|
||||
from fabric import Connection
|
||||
|
||||
from common import PCAP, CMUTCP, ACK_MASK, IP_ADDRS
|
||||
|
||||
|
||||
def test_pcap_packets_max_size():
|
||||
"""Basic test: Check packets are smaller than max size"""
|
||||
print("Running test_pcap_packets_max_size()")
|
||||
print(
|
||||
"Please note that it's now testing on a sample test.pcap file. "
|
||||
"You should generate your own pcap file and run this test."
|
||||
)
|
||||
packets = rdpcap(PCAP)
|
||||
if len(packets) <= 10:
|
||||
print("Test Failed")
|
||||
return
|
||||
for pkt in packets:
|
||||
if CMUTCP in pkt:
|
||||
if len(pkt[CMUTCP]) > 1400:
|
||||
print("Found packet with length greater than max size")
|
||||
print("Test Failed")
|
||||
return
|
||||
print("Test passed")
|
||||
|
||||
|
||||
def test_pcap_acks():
|
||||
"""Basic test: Check that every data packet sent has a corresponding ACK
|
||||
Ignore handshake packets.
|
||||
"""
|
||||
print("Running test_pcap_acks()")
|
||||
print(
|
||||
"Please note that it's now testing on a sample test.pcap file. "
|
||||
"You should generate your own pcap file and run this test."
|
||||
)
|
||||
packets = rdpcap(PCAP)
|
||||
if len(packets) <= 10:
|
||||
print("Test Failed")
|
||||
return
|
||||
|
||||
expected_acks = []
|
||||
ack_nums = []
|
||||
for pkt in packets:
|
||||
if CMUTCP in pkt:
|
||||
# Ignore handshake packets, should test in a different test.
|
||||
if pkt[CMUTCP].flags == 0:
|
||||
payload_len = pkt[CMUTCP].plen - pkt[CMUTCP].hlen
|
||||
expected_acks.append(pkt[CMUTCP].seq_num + payload_len)
|
||||
elif pkt[CMUTCP].flags == ACK_MASK:
|
||||
ack_nums.append(pkt[CMUTCP].ack_num)
|
||||
|
||||
# Probably not the best way to do this test!
|
||||
if set(expected_acks) == set(ack_nums):
|
||||
print("Test Passed")
|
||||
else:
|
||||
print("Test Failed")
|
||||
|
||||
|
||||
# This will try to run the server and client code.
|
||||
def test_run_server_client():
|
||||
"""Basic test: Run server and client, and initiate the file transfer."""
|
||||
print("Running test_run_server_client()")
|
||||
|
||||
# We are using `tmux` to run the server and client in the background.
|
||||
#
|
||||
# This might also help you debug your code if the test fails. You may call
|
||||
# `getchar()` in your code to pause the program at any point and then use
|
||||
# `tmux attach -t pytest_server` or `tmux attach -t pytest_client` to
|
||||
# attach to the relevant TMUX session and see the output.
|
||||
|
||||
start_server_cmd = (
|
||||
"tmux new -s pytest_server -d /vagrant/project-1_elec3120/server"
|
||||
)
|
||||
start_client_cmd = (
|
||||
"tmux new -s pytest_client -d /vagrant/project-1_elec3120/client"
|
||||
)
|
||||
stop_server_cmd = "tmux kill-session -t pytest_server"
|
||||
stop_client_cmd = "tmux kill-session -t pytest_client"
|
||||
|
||||
failed = False
|
||||
|
||||
original_file = Path("/vagrant/project-1_elec3120/src/cmu_tcp.c")
|
||||
received_file = Path("/tmp/file.c")
|
||||
|
||||
received_file.unlink(missing_ok=True)
|
||||
|
||||
with (
|
||||
Connection(
|
||||
host=IP_ADDRS["server"],
|
||||
user="vagrant",
|
||||
connect_kwargs={"password": "vagrant"},
|
||||
) as server_conn,
|
||||
Connection(
|
||||
host=IP_ADDRS["client"],
|
||||
user="vagrant",
|
||||
connect_kwargs={"password": "vagrant"},
|
||||
) as client_conn,
|
||||
):
|
||||
try:
|
||||
server_conn.run(start_server_cmd)
|
||||
server_conn.run("tmux has-session -t pytest_server")
|
||||
|
||||
client_conn.run(start_client_cmd)
|
||||
client_conn.run("tmux has-session -t pytest_client")
|
||||
|
||||
# Exit when server finished receiving file.
|
||||
server_conn.run(
|
||||
"while tmux has-session -t pytest_server; do sleep 1; done",
|
||||
hide=True,
|
||||
)
|
||||
except Exception:
|
||||
failed = True
|
||||
|
||||
try:
|
||||
client_conn.run("tmux has-session -t pytest_client", hide=True)
|
||||
print("stop client")
|
||||
client_conn.run(stop_client_cmd, hide=True)
|
||||
except Exception:
|
||||
# Ignore error here that may occur if client already shut down.
|
||||
pass
|
||||
try:
|
||||
server_conn.local("tmux has-session -t pytest_server", hide=True)
|
||||
print("stop server")
|
||||
server_conn.local(stop_server_cmd, hide=True)
|
||||
except Exception:
|
||||
# Ignore error here that may occur if server already shut down.
|
||||
pass
|
||||
if failed:
|
||||
print("Test failed: Error running server or client")
|
||||
return
|
||||
|
||||
# Compare SHA256 hashes of the files.
|
||||
server_hash_result = server_conn.run(f"sha256sum {received_file}")
|
||||
client_hash_result = client_conn.run(f"sha256sum {original_file}")
|
||||
|
||||
if not server_hash_result.ok or not client_hash_result.ok:
|
||||
print("Test failed: Error getting file hashes")
|
||||
return
|
||||
|
||||
server_hash = server_hash_result.stdout.split()[0]
|
||||
client_hash = client_hash_result.stdout.split()[0]
|
||||
|
||||
if server_hash != client_hash:
|
||||
print("Test failed: File hashes do not match")
|
||||
return
|
||||
|
||||
print("Test passed")
|
||||
|
||||
|
||||
def test_basic_reliable_data_transfer():
|
||||
"""Basic test: Check that when you run server and client starter code
|
||||
that the input file equals the output file
|
||||
"""
|
||||
# Can you think of how you can test this? Give it a try!
|
||||
pass
|
||||
|
||||
|
||||
def test_basic_retransmit():
|
||||
"""Basic test: Check that when a packet is lost, it's retransmitted"""
|
||||
# Can you think of how you can test this? Give it a try!
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_pcap_packets_max_size()
|
||||
test_pcap_acks()
|
||||
test_run_server_client()
|
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (C) 2023 Carnegie Mellon University and
|
||||
# Hong Kong University of Science and Technology
|
||||
|
||||
# This repository is adopted from the TCP in the
|
||||
# Wild course project from the Computer Networks
|
||||
# course taught at Carnegie Mellon University, and
|
||||
# is used for the Computer Networks (ELEC 3120)
|
||||
# course taught at Hong Kong University of Science
|
||||
# and Technology.
|
||||
|
||||
# No part of the project may be copied and/or
|
||||
# distributed without the express permission of
|
||||
# the course staff. Everyone is prohibited from
|
||||
# releasing their forks in any public places.
|
||||
from fabric import Connection
|
||||
from scapy.all import sr1, Raw
|
||||
|
||||
from common import (
|
||||
CMUTCP,
|
||||
ACK_MASK,
|
||||
TIMEOUT,
|
||||
IFNAME,
|
||||
SYN_MASK,
|
||||
START_TESTING_SERVER_CMD,
|
||||
STOP_TESTING_SERVER_CMD,
|
||||
TESTING_HOST_IP,
|
||||
ip,
|
||||
udp,
|
||||
)
|
||||
|
||||
payloads = ["pa", "pytest 1234567"]
|
||||
|
||||
|
||||
def test_basic_ack_packets():
|
||||
print("Running test_basic_ack_packets()")
|
||||
"""Basic test: Check if when you send data packets, the server responds
|
||||
with correct ack packet with a correct ack number.
|
||||
"""
|
||||
print("Running test_sequence_number()")
|
||||
for payload in payloads:
|
||||
print("Testing payload size " + str(len(payload)))
|
||||
with Connection(
|
||||
host=TESTING_HOST_IP,
|
||||
user="vagrant",
|
||||
connect_kwargs={"password": "vagrant"},
|
||||
) as conn:
|
||||
try:
|
||||
conn.run(START_TESTING_SERVER_CMD)
|
||||
conn.run("tmux has-session -t pytest_server")
|
||||
syn_pkt = (
|
||||
ip /
|
||||
udp /
|
||||
CMUTCP(plen=25, seq_num=1000, flags=SYN_MASK)
|
||||
)
|
||||
syn_ack_pkt = sr1(syn_pkt, timeout=TIMEOUT, iface=IFNAME)
|
||||
|
||||
if (
|
||||
syn_ack_pkt is None
|
||||
or syn_ack_pkt[CMUTCP].flags != SYN_MASK | ACK_MASK
|
||||
or syn_ack_pkt[CMUTCP].ack_num != 1000 + 1
|
||||
):
|
||||
print(
|
||||
"Listener (server) did not properly respond to SYN "
|
||||
"packet."
|
||||
)
|
||||
print("Test Failed")
|
||||
conn.run(STOP_TESTING_SERVER_CMD)
|
||||
return
|
||||
|
||||
print(syn_ack_pkt[CMUTCP].seq_num)
|
||||
|
||||
ack_pkt = (
|
||||
ip /
|
||||
udp /
|
||||
CMUTCP(
|
||||
plen=25,
|
||||
seq_num=1001,
|
||||
ack_num=syn_ack_pkt[CMUTCP].seq_num + 1,
|
||||
flags=ACK_MASK,
|
||||
)
|
||||
)
|
||||
empty_pkt = sr1(ack_pkt, timeout=0.5, iface=IFNAME)
|
||||
|
||||
if empty_pkt is not None:
|
||||
print("Listener (server) should not respond to ack pkt.")
|
||||
print("Test Failed")
|
||||
conn.run(STOP_TESTING_SERVER_CMD)
|
||||
return
|
||||
|
||||
data_pkt = (
|
||||
ip /
|
||||
udp /
|
||||
CMUTCP(
|
||||
plen=25 + len(payload),
|
||||
seq_num=1001,
|
||||
ack_num=syn_ack_pkt[CMUTCP].seq_num + 1,
|
||||
flags=ACK_MASK,
|
||||
)
|
||||
/ Raw(load=payload)
|
||||
)
|
||||
|
||||
server_ack_pkt = sr1(data_pkt, timeout=TIMEOUT, iface=IFNAME)
|
||||
|
||||
if (
|
||||
server_ack_pkt is None
|
||||
or server_ack_pkt[CMUTCP].flags != ACK_MASK
|
||||
or server_ack_pkt[CMUTCP].ack_num != 1001 + len(payload)
|
||||
):
|
||||
print(
|
||||
"Listener (server) did not properly respond to data "
|
||||
"packet."
|
||||
)
|
||||
print("Test Failed")
|
||||
conn.run(STOP_TESTING_SERVER_CMD)
|
||||
return
|
||||
|
||||
finally:
|
||||
try:
|
||||
conn.run(STOP_TESTING_SERVER_CMD)
|
||||
except Exception:
|
||||
# Ignore error here that may occur if server stopped.
|
||||
pass
|
||||
print("Test Passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_basic_ack_packets()
|
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
*Copyright (C) 2023 Carnegie Mellon University and
|
||||
*Hong Kong University of Science and Technology
|
||||
|
||||
*This repository is adopted from the TCP in the
|
||||
*Wild course project from the Computer Networks
|
||||
*course taught at Carnegie Mellon University, and
|
||||
*is used for the Computer Networks (ELEC 3120)
|
||||
*course taught at Hong Kong University of Science
|
||||
*and Technology.
|
||||
|
||||
*No part of the project may be copied and/or
|
||||
*distributed without the express permission of
|
||||
*the course staff. Everyone is prohibited from
|
||||
*releasing their forks in any public places.
|
||||
*
|
||||
* This file implements a simple CMU-TCP server used for testing.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmu_tcp.h"
|
||||
|
||||
/*
|
||||
* Param: sock - used for reading and writing to a connection
|
||||
*
|
||||
*
|
||||
*/
|
||||
void functionality(cmu_socket_t *sock) {
|
||||
uint8_t buf[9898];
|
||||
FILE *fp;
|
||||
int n;
|
||||
int read;
|
||||
|
||||
// Wait to hear from an initiator
|
||||
n = 0;
|
||||
while (n == 0) {
|
||||
n = cmu_read(sock, buf, 9898, NO_FLAG);
|
||||
}
|
||||
|
||||
printf("read something %d\n", n);
|
||||
// Send over a random file
|
||||
fp = fopen("/vagrant/project-1_elec3120/tests/random.input", "rb");
|
||||
read = 1;
|
||||
while (read > 0) {
|
||||
read = fread(buf, 1, 2000, fp);
|
||||
if (read > 0) cmu_write(sock, buf, read);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Param: argc - count of command line arguments provided
|
||||
* Param: argv - values of command line arguments provided
|
||||
*
|
||||
* Purpose: To provide a sample listener for the TCP connection.
|
||||
*
|
||||
*/
|
||||
int main() {
|
||||
int portno;
|
||||
char *serverip;
|
||||
char *serverport;
|
||||
cmu_socket_t socket;
|
||||
|
||||
serverip = getenv("server15441");
|
||||
if (!serverip) {
|
||||
serverip = "10.0.1.1";
|
||||
}
|
||||
|
||||
serverport = getenv("serverport15441");
|
||||
if (!serverport) {
|
||||
serverport = "15441";
|
||||
}
|
||||
portno = (uint16_t)atoi(serverport);
|
||||
|
||||
printf("starting initiator\n");
|
||||
if (cmu_socket(&socket, TCP_LISTENER, portno, serverip) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
printf("finished socket\n");
|
||||
functionality(&socket);
|
||||
|
||||
sleep(5);
|
||||
|
||||
if (cmu_close(&socket) < 0) exit(EXIT_FAILURE);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
Reference in New Issue
Block a user