update,
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
TOP_DIR = .
|
||||
INC_DIR = $(TOP_DIR)/inc
|
||||
SRC_DIR = $(TOP_DIR)/src
|
||||
BUILD_DIR = $(TOP_DIR)/build
|
||||
CC=gcc
|
||||
FLAGS = -pthread -fPIC -g -ggdb -pedantic -Wall -Wextra -DDEBUG -I$(INC_DIR)
|
||||
OBJS = $(BUILD_DIR)/cmu_packet.o $(BUILD_DIR)/cmu_tcp.o $(BUILD_DIR)/backend.o
|
||||
|
||||
all: server client tests/testing_server
|
||||
|
||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
$(CC) $(FLAGS) -c -o $@ $<
|
||||
|
||||
server: $(OBJS) $(SRC_DIR)/server.c
|
||||
$(CC) $(FLAGS) $(SRC_DIR)/server.c -o server $(OBJS)
|
||||
|
||||
client: $(OBJS) $(SRC_DIR)/client.c
|
||||
$(CC) $(FLAGS) $(SRC_DIR)/client.c -o client $(OBJS)
|
||||
|
||||
tests/testing_server: $(OBJS)
|
||||
$(CC) $(FLAGS) tests/testing_server.c -o tests/testing_server $(OBJS)
|
||||
|
||||
format:
|
||||
pre-commit run --all-files
|
||||
|
||||
test:
|
||||
sudo -E python3 tests/test_cp1.py
|
||||
sudo -E python3 tests/test_cp1_basic_ack_packets.py
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/*.o peer client server
|
||||
rm -f tests/testing_server
|
@@ -0,0 +1,72 @@
|
||||
#!/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 scapy.all import rdpcap, Raw, IP
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# Change this to be your pcap file
|
||||
# You can capture a pcap file with wireshark or tcpdump
|
||||
# https://support.rackspace.com/how-to/capturing-packets-with-tcpdump/
|
||||
FILE_TO_READ = "capture.pcap"
|
||||
|
||||
packets = rdpcap(FILE_TO_READ)
|
||||
packet_list = []
|
||||
times = []
|
||||
base = 0
|
||||
server_port = 15441
|
||||
num_packets = 0
|
||||
|
||||
# This script assumes that only the client is sending data to the server.
|
||||
|
||||
for packet in packets:
|
||||
payload = packet[Raw].load
|
||||
|
||||
if IP not in packet:
|
||||
continue
|
||||
|
||||
if int.from_bytes(payload[:4], byteorder="big") != 15441:
|
||||
continue
|
||||
|
||||
# Count the number of data packets going into the network.
|
||||
if packet[IP].dport == server_port:
|
||||
hlen = int.from_bytes(payload[16:18], byteorder="big")
|
||||
plen = int.from_bytes(payload[18:20], byteorder="big")
|
||||
if plen > hlen: # Data packet
|
||||
num_packets = num_packets + 1
|
||||
time = packet.time
|
||||
if base == 0:
|
||||
base = time
|
||||
packet_list.append(num_packets)
|
||||
times.append(time - base)
|
||||
|
||||
# Count the number of ACKs from server to client.
|
||||
elif packet[IP].sport == server_port:
|
||||
mask = int.from_bytes(payload[20:21], byteorder="big")
|
||||
if (mask & 4) == 4: # ACK PACKET
|
||||
num_packets = max(num_packets - 1, 0)
|
||||
time = packet.time
|
||||
if base == 0:
|
||||
base = time
|
||||
packet_list.append(num_packets)
|
||||
times.append(time - base)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# https://matplotlib.org/users/pyplot_tutorial.html for how to format and
|
||||
# make a good quality graph.
|
||||
print(packet_list)
|
||||
plt.scatter(times, packet_list)
|
||||
plt.savefig("graph.pdf")
|
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
*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 defines the function signatures for the CMU-TCP backend that should
|
||||
* be exposed. The backend runs in a different thread and handles all the socket
|
||||
* operations separately from the application.
|
||||
*/
|
||||
|
||||
#ifndef PROJECT_1_ELEC3120_INC_BACKEND_H_
|
||||
#define PROJECT_1_ELEC3120_INC_BACKEND_H_
|
||||
|
||||
/**
|
||||
* Launches the CMU-TCP backend.
|
||||
*
|
||||
* @param in the socket to be used for backend processing.
|
||||
*/
|
||||
void* begin_backend(void* in);
|
||||
|
||||
#endif // PROJECT_1_ELEC3120_INC_BACKEND_H_
|
@@ -0,0 +1,182 @@
|
||||
/**
|
||||
*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.
|
||||
*
|
||||
* Defines a CMU-TCP packet and define helper functions to create and manipulate
|
||||
* packets.
|
||||
*
|
||||
* Do NOT modify this file.
|
||||
*/
|
||||
|
||||
#ifndef PROJECT_1_ELEC3120_INC_CMU_PACKET_H_
|
||||
#define PROJECT_1_ELEC3120_INC_CMU_PACKET_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t identifier; // Identifier for the CMU-TCP protocol.
|
||||
uint16_t source_port; // Source port.
|
||||
uint16_t destination_port; // Destination port.
|
||||
uint32_t seq_num; // Sequence number.
|
||||
uint32_t ack_num; // Acknowledgement number.
|
||||
uint16_t hlen; // Header length.
|
||||
uint16_t plen; // Packet length.
|
||||
uint8_t flags; // Flags.
|
||||
uint16_t advertised_window; // Advertised window.
|
||||
uint16_t extension_length; // Extension length.
|
||||
uint8_t extension_data[]; // Extension data.
|
||||
} __attribute__((__packed__)) cmu_tcp_header_t;
|
||||
|
||||
#define SYN_FLAG_MASK 0x8
|
||||
#define ACK_FLAG_MASK 0x4
|
||||
#define FIN_FLAG_MASK 0x2
|
||||
#define IDENTIFIER 15441
|
||||
|
||||
// Maximum Segment Size. Make sure to update this if your CCA requires extension
|
||||
// data for all packets, as this reduces the payload and thus the MSS.
|
||||
#define MSS (MAX_LEN - sizeof(cmu_tcp_header_t))
|
||||
|
||||
/* Helper functions to get/set fields in the header */
|
||||
|
||||
uint16_t get_src(cmu_tcp_header_t* header);
|
||||
uint16_t get_dst(cmu_tcp_header_t* header);
|
||||
uint32_t get_seq(cmu_tcp_header_t* header);
|
||||
uint32_t get_ack(cmu_tcp_header_t* header);
|
||||
uint16_t get_hlen(cmu_tcp_header_t* header);
|
||||
uint16_t get_plen(cmu_tcp_header_t* header);
|
||||
uint8_t get_flags(cmu_tcp_header_t* header);
|
||||
uint16_t get_advertised_window(cmu_tcp_header_t* header);
|
||||
uint16_t get_extension_length(cmu_tcp_header_t* header);
|
||||
uint8_t* get_extension_data(cmu_tcp_header_t* header);
|
||||
|
||||
void set_src(cmu_tcp_header_t* header, uint16_t src);
|
||||
void set_dst(cmu_tcp_header_t* header, uint16_t dst);
|
||||
void set_seq(cmu_tcp_header_t* header, uint32_t seq);
|
||||
void set_ack(cmu_tcp_header_t* header, uint32_t ack);
|
||||
void set_hlen(cmu_tcp_header_t* header, uint16_t hlen);
|
||||
void set_plen(cmu_tcp_header_t* header, uint16_t plen);
|
||||
void set_flags(cmu_tcp_header_t* header, uint8_t flags);
|
||||
void set_advertised_window(cmu_tcp_header_t* header,
|
||||
uint16_t advertised_window);
|
||||
void set_extension_length(cmu_tcp_header_t* header, uint16_t extension_length);
|
||||
void set_extension_data(cmu_tcp_header_t* header, uint8_t* extension_data);
|
||||
|
||||
/**
|
||||
* Sets all the header fields.
|
||||
*
|
||||
* Review TCP headers for more information about what each field means.
|
||||
*
|
||||
* @param header The header to set the fields.
|
||||
* @param src Source port.
|
||||
* @param dst Destination port.
|
||||
* @param seq Sequence number.
|
||||
* @param ack Acknowledgement number.
|
||||
* @param hlen Header length.
|
||||
* @param plen Packet length.
|
||||
* @param flags Packet flags.
|
||||
* @param advertised_window Advertised window.
|
||||
* @param extension_length Header extension length.
|
||||
* @param extension_data Header extension data.
|
||||
*/
|
||||
void set_header(cmu_tcp_header_t* header, uint16_t src, uint16_t dst,
|
||||
uint32_t seq, uint32_t ack, uint16_t hlen, uint16_t plen,
|
||||
uint8_t flags, uint16_t adv_window, uint16_t ext,
|
||||
uint8_t* ext_data);
|
||||
|
||||
/**
|
||||
* Gets a pointer to the packet payload.
|
||||
*
|
||||
* @param pkt The packet to get the payload.
|
||||
*
|
||||
* @return A pointer to the payload.
|
||||
*/
|
||||
uint8_t* get_payload(uint8_t* pkt);
|
||||
|
||||
/**
|
||||
* Gets the length of the packet payload.
|
||||
*
|
||||
* @param pkt The packet to get the payload length.
|
||||
*
|
||||
* @return The length of the payload.
|
||||
*/
|
||||
uint16_t get_payload_len(uint8_t* pkt);
|
||||
|
||||
/**
|
||||
* Sets the packet payload.
|
||||
*
|
||||
* @param pkt The packet to set the payload.
|
||||
* @param payload A pointer to the payload to be set.
|
||||
* @param payload_len The length of the payload.
|
||||
*/
|
||||
void set_payload(uint8_t* pkt, uint8_t* payload, uint16_t payload_len);
|
||||
|
||||
/**
|
||||
* Allocates and initializes a packet.
|
||||
*
|
||||
* @param src The source port.
|
||||
* @param dst The destination port.
|
||||
* @param seq The sequence number.
|
||||
* @param ack The acknowledgement number.
|
||||
* @param hlen The header length.
|
||||
* @param plen The packet length.
|
||||
* @param flags The flags.
|
||||
* @param adv_window The advertised window.
|
||||
* @param ext_len The header extension length.
|
||||
* @param ext_data The header extension data.
|
||||
* @param payload The payload.
|
||||
* @param payload_len The length of the payload.
|
||||
*
|
||||
* @return A pointer to the newly allocated packet. User must `free` after use.
|
||||
*/
|
||||
uint8_t* create_packet(uint16_t src, uint16_t dst, uint32_t seq, uint32_t ack,
|
||||
uint16_t hlen, uint16_t plen, uint8_t flags,
|
||||
uint16_t adv_window, uint16_t ext_len, uint8_t* ext_data,
|
||||
uint8_t* payload, uint16_t payload_len);
|
||||
|
||||
/**
|
||||
* Checks if a given sequence number comes before another sequence number.
|
||||
*
|
||||
* @param seq1 the first sequence number.
|
||||
* @param seq2 the second sequence number.
|
||||
* @return 1 if seq1 comes before seq2, 0 otherwise.
|
||||
*/
|
||||
static inline int before(uint32_t seq1, uint32_t seq2) {
|
||||
return (int32_t)(seq1 - seq2) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given sequence number comes after another sequence number.
|
||||
*
|
||||
* @param seq1 the first sequence number.
|
||||
* @param seq2 the second sequence number.
|
||||
* @return 1 if seq1 comes after seq2, 0 otherwise.
|
||||
*/
|
||||
static inline int after(uint32_t seq1, uint32_t seq2) {
|
||||
return before(seq2, seq1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given sequence number is between two others.
|
||||
*
|
||||
* @param seq the sequence number.
|
||||
* @param low the lower bound.
|
||||
* @param high the upper bound.
|
||||
* @return 1 if low <= seq <= high, 0 otherwise.
|
||||
*/
|
||||
static inline int between(uint32_t seq, uint32_t low, uint32_t high) {
|
||||
return high - low >= seq - low;
|
||||
}
|
||||
|
||||
#endif // PROJECT_1_ELEC3120_INC_CMU_PACKET_H_
|
@@ -0,0 +1,145 @@
|
||||
/**
|
||||
*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 defines the API for the CMU TCP implementation.
|
||||
*/
|
||||
|
||||
#ifndef PROJECT_1_ELEC3120_INC_CMU_TCP_H_
|
||||
#define PROJECT_1_ELEC3120_INC_CMU_TCP_H_
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cmu_packet.h"
|
||||
#include "grading.h"
|
||||
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_ERROR -1
|
||||
#define EXIT_FAILURE 1
|
||||
|
||||
typedef struct {
|
||||
uint32_t next_seq_expected;
|
||||
uint32_t last_ack_received;
|
||||
pthread_mutex_t ack_lock;
|
||||
} window_t;
|
||||
|
||||
/**
|
||||
* CMU-TCP socket types. (DO NOT CHANGE.)
|
||||
*/
|
||||
typedef enum {
|
||||
TCP_INITIATOR = 0,
|
||||
TCP_LISTENER = 1,
|
||||
} cmu_socket_type_t;
|
||||
|
||||
/**
|
||||
* This structure holds the state of a socket. You may modify this structure as
|
||||
* you see fit to include any additional state you need for your implementation.
|
||||
*/
|
||||
typedef struct {
|
||||
int socket;
|
||||
pthread_t thread_id;
|
||||
uint16_t my_port;
|
||||
struct sockaddr_in conn;
|
||||
uint8_t* received_buf;
|
||||
int received_len;
|
||||
pthread_mutex_t recv_lock;
|
||||
pthread_cond_t wait_cond;
|
||||
uint8_t* sending_buf;
|
||||
int sending_len;
|
||||
cmu_socket_type_t type;
|
||||
pthread_mutex_t send_lock;
|
||||
int dying;
|
||||
pthread_mutex_t death_lock;
|
||||
window_t window;
|
||||
} cmu_socket_t;
|
||||
|
||||
/*
|
||||
* DO NOT CHANGE THE DECLARATIONS BELOW
|
||||
*/
|
||||
|
||||
/**
|
||||
* Read mode flags supported by a CMU-TCP socket.
|
||||
*/
|
||||
typedef enum {
|
||||
NO_FLAG = 0, // Default behavior: block indefinitely until data is available.
|
||||
NO_WAIT, // Return immediately if no data is available.
|
||||
TIMEOUT, // Block until data is available or the timeout is reached.
|
||||
} cmu_read_mode_t;
|
||||
|
||||
/**
|
||||
* Constructs a CMU-TCP socket.
|
||||
*
|
||||
* An Initiator socket is used to connect to a Listener socket.
|
||||
*
|
||||
* @param sock The structure with the socket state. It will be initialized by
|
||||
* this function.
|
||||
* @param socket_type Indicates the type of socket: Listener or Initiator.
|
||||
* @param port Port to either connect to, or bind to. (Based on socket_type.)
|
||||
* @param server_ip IP address of the server to connect to. (Only used if the
|
||||
* socket is an initiator.)
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int cmu_socket(cmu_socket_t* sock, const cmu_socket_type_t socket_type,
|
||||
const int port, const char* server_ip);
|
||||
|
||||
/**
|
||||
* Closes a CMU-TCP socket.
|
||||
*
|
||||
* @param sock The socket to close.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int cmu_close(cmu_socket_t* sock);
|
||||
|
||||
/**
|
||||
* Reads data from a CMU-TCP socket.
|
||||
*
|
||||
* If there is data available in the socket buffer, it is placed in the
|
||||
* destination buffer.
|
||||
*
|
||||
* @param sock The socket to read from.
|
||||
* @param buf The buffer to read into.
|
||||
* @param length The maximum number of bytes to read.
|
||||
* @param flags Flags that determine how the socket should wait for data. Check
|
||||
* `cmu_read_mode_t` for more information. `TIMEOUT` is not
|
||||
* implemented for CMU-TCP.
|
||||
*
|
||||
* @return The number of bytes read on success, -1 on error.
|
||||
*/
|
||||
int cmu_read(cmu_socket_t* sock, void* buf, const int length,
|
||||
cmu_read_mode_t flags);
|
||||
|
||||
/**
|
||||
* Writes data to a CMU-TCP socket.
|
||||
*
|
||||
* @param sock The socket to write to.
|
||||
* @param buf The data to write.
|
||||
* @param length The number of bytes to write.
|
||||
*
|
||||
* @return 0 on success, -1 on error.
|
||||
*/
|
||||
int cmu_write(cmu_socket_t* sock, const void* buf, int length);
|
||||
|
||||
/*
|
||||
* You can declare more functions after this point if you need to.
|
||||
*/
|
||||
|
||||
#endif // PROJECT_1_ELEC3120_INC_CMU_TCP_H_
|
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
*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 files defines constants used for grading. Do NOT modify this file as
|
||||
* any changes will be overwritten by the autograder.
|
||||
*/
|
||||
|
||||
#ifndef PROJECT_1_ELEC3120_INC_GRADING_H_
|
||||
#define PROJECT_1_ELEC3120_INC_GRADING_H_
|
||||
|
||||
/*
|
||||
* DO NOT CHANGE THIS FILE
|
||||
* This contains the variables for your TCP implementation
|
||||
* and we will replace this file during the autolab testing with new variables.
|
||||
*/
|
||||
|
||||
// packet lengths
|
||||
#define MAX_LEN 1400
|
||||
|
||||
// window variables
|
||||
#define WINDOW_INITIAL_WINDOW_SIZE (MSS * 16)
|
||||
#define WINDOW_INITIAL_SSTHRESH (MSS * 64)
|
||||
#define WINDOW_INITIAL_RTT 3000 // ms
|
||||
#define WINDOW_INITIAL_ADVERTISED MSS
|
||||
|
||||
// Max TCP Buffer
|
||||
#define MAX_NETWORK_BUFFER 65535 // (2^16 - 1) bytes
|
||||
|
||||
#endif // PROJECT_1_ELEC3120_INC_GRADING_H_
|
@@ -0,0 +1,56 @@
|
||||
attrs==22.1.0
|
||||
bcrypt==4.0.0
|
||||
certifi==2022.9.24
|
||||
cffi==1.15.1
|
||||
chardet==5.0.0
|
||||
charset-normalizer==2.1.1
|
||||
contourpy==1.0.5
|
||||
cryptography==38.0.1
|
||||
cycler==0.11.0
|
||||
DataProperty==0.55.0
|
||||
dbus-python==1.2.18
|
||||
distro==1.6.0
|
||||
docker==6.0.0
|
||||
fabric==2.7.1
|
||||
fonttools==4.37.4
|
||||
humanreadable==0.1.0
|
||||
idna==3.4
|
||||
iniconfig==1.1.1
|
||||
invoke==1.7.3
|
||||
kiwisolver==1.4.4
|
||||
loguru==0.6.0
|
||||
matplotlib==3.6.0
|
||||
mbstrdecoder==1.1.1
|
||||
msgfy==0.2.0
|
||||
numpy==1.23.3
|
||||
packaging==21.3
|
||||
paramiko==2.11.0
|
||||
path==16.5.0
|
||||
pathlib2==2.3.7.post1
|
||||
pathvalidate==2.5.2
|
||||
Pillow==9.2.0
|
||||
pluggy==1.0.0
|
||||
py==1.11.0
|
||||
pycparser==2.21
|
||||
PyGObject==3.42.0
|
||||
PyNaCl==1.5.0
|
||||
pyparsing==3.0.9
|
||||
pyroute2==0.7.3
|
||||
pytest==7.1.3
|
||||
python-dateutil==2.8.2
|
||||
pytz==2022.4
|
||||
requests==2.28.1
|
||||
scapy==2.4.5
|
||||
SimpleSQLite==1.3.0
|
||||
six==1.16.0
|
||||
sqliteschema==1.3.0
|
||||
ssh-import-id==5.11
|
||||
subprocrunner==2.0.0
|
||||
tabledata==1.3.0
|
||||
tcconfig==0.28.0
|
||||
tomli==2.0.1
|
||||
typepy==1.3.0
|
||||
typing==3.7.4.3
|
||||
urllib3==1.26.12
|
||||
voluptuous==0.13.1
|
||||
websocket-client==1.4.1
|
BIN
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/project-1_elec3120/sample_data.png
(Stored with Git LFS)
Normal file
BIN
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/project-1_elec3120/sample_data.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
sudo apt-get update
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential vim \
|
||||
emacs tree tmux git gdb valgrind python3-dev libffi-dev libssl-dev \
|
||||
clang-format iperf3 tshark iproute2 iputils-ping net-tools tcpdump cppcheck
|
||||
|
||||
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y python3 python3-pip \
|
||||
python-tk libpython3.10-dev libcairo2 libcairo2-dev pre-commit
|
||||
|
||||
pip3 install --upgrade pip
|
||||
pip3 install -r $SCRIPT_DIR/../requirements.txt
|
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
*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 the CMU-TCP backend. The backend runs in a different
|
||||
* thread and handles all the socket operations separately from the application.
|
||||
*
|
||||
* This is where most of your code should go. Feel free to modify any function
|
||||
* in this file.
|
||||
*/
|
||||
|
||||
#include "backend.h"
|
||||
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "cmu_packet.h"
|
||||
#include "cmu_tcp.h"
|
||||
|
||||
#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
|
||||
|
||||
/**
|
||||
* Tells if a given sequence number has been acknowledged by the socket.
|
||||
*
|
||||
* @param sock The socket to check for acknowledgements.
|
||||
* @param seq Sequence number to check.
|
||||
*
|
||||
* @return 1 if the sequence number has been acknowledged, 0 otherwise.
|
||||
*/
|
||||
int has_been_acked(cmu_socket_t *sock, uint32_t seq) {
|
||||
int result;
|
||||
while (pthread_mutex_lock(&(sock->window.ack_lock)) != 0) {
|
||||
}
|
||||
result = after(sock->window.last_ack_received, seq);
|
||||
pthread_mutex_unlock(&(sock->window.ack_lock));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the socket information to represent the newly received packet.
|
||||
*
|
||||
* In the current stop-and-wait implementation, this function also sends an
|
||||
* acknowledgement for the packet.
|
||||
*
|
||||
* @param sock The socket used for handling packets received.
|
||||
* @param pkt The packet data received by the socket.
|
||||
*/
|
||||
void handle_message(cmu_socket_t *sock, uint8_t *pkt) {
|
||||
cmu_tcp_header_t *hdr = (cmu_tcp_header_t *)pkt;
|
||||
uint8_t flags = get_flags(hdr);
|
||||
|
||||
switch (flags) {
|
||||
case ACK_FLAG_MASK: {
|
||||
uint32_t ack = get_ack(hdr);
|
||||
if (after(ack, sock->window.last_ack_received)) {
|
||||
sock->window.last_ack_received = ack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
socklen_t conn_len = sizeof(sock->conn);
|
||||
uint32_t seq = sock->window.last_ack_received;
|
||||
|
||||
// No payload.
|
||||
uint8_t *payload = NULL;
|
||||
uint16_t payload_len = 0;
|
||||
|
||||
// No extension.
|
||||
uint16_t ext_len = 0;
|
||||
uint8_t *ext_data = NULL;
|
||||
|
||||
uint16_t src = sock->my_port;
|
||||
uint16_t dst = ntohs(sock->conn.sin_port);
|
||||
uint32_t ack = get_seq(hdr) + get_payload_len(pkt);
|
||||
uint16_t hlen = sizeof(cmu_tcp_header_t);
|
||||
uint16_t plen = hlen + payload_len;
|
||||
uint8_t flags = ACK_FLAG_MASK;
|
||||
uint16_t adv_window = 1;
|
||||
uint8_t *response_packet =
|
||||
create_packet(src, dst, seq, ack, hlen, plen, flags, adv_window,
|
||||
ext_len, ext_data, payload, payload_len);
|
||||
|
||||
sendto(sock->socket, response_packet, plen, 0,
|
||||
(struct sockaddr *)&(sock->conn), conn_len);
|
||||
free(response_packet);
|
||||
|
||||
seq = get_seq(hdr);
|
||||
|
||||
if (seq == sock->window.next_seq_expected) {
|
||||
sock->window.next_seq_expected = seq + get_payload_len(pkt);
|
||||
payload_len = get_payload_len(pkt);
|
||||
payload = get_payload(pkt);
|
||||
|
||||
// Make sure there is enough space in the buffer to store the payload.
|
||||
sock->received_buf =
|
||||
realloc(sock->received_buf, sock->received_len + payload_len);
|
||||
memcpy(sock->received_buf + sock->received_len, payload, payload_len);
|
||||
sock->received_len += payload_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the socket received any data.
|
||||
*
|
||||
* It first peeks at the header to figure out the length of the packet and then
|
||||
* reads the entire packet.
|
||||
*
|
||||
* @param sock The socket used for receiving data on the connection.
|
||||
* @param flags Flags that determine how the socket should wait for data. Check
|
||||
* `cmu_read_mode_t` for more information.
|
||||
*/
|
||||
void check_for_data(cmu_socket_t *sock, cmu_read_mode_t flags) {
|
||||
cmu_tcp_header_t hdr;
|
||||
uint8_t *pkt;
|
||||
socklen_t conn_len = sizeof(sock->conn);
|
||||
ssize_t len = 0;
|
||||
uint32_t plen = 0, buf_size = 0, n = 0;
|
||||
|
||||
while (pthread_mutex_lock(&(sock->recv_lock)) != 0) {
|
||||
}
|
||||
switch (flags) {
|
||||
case NO_FLAG:
|
||||
len = recvfrom(sock->socket, &hdr, sizeof(cmu_tcp_header_t), MSG_PEEK,
|
||||
(struct sockaddr *)&(sock->conn), &conn_len);
|
||||
break;
|
||||
case TIMEOUT: {
|
||||
// Using `poll` here so that we can specify a timeout.
|
||||
struct pollfd ack_fd;
|
||||
ack_fd.fd = sock->socket;
|
||||
ack_fd.events = POLLIN;
|
||||
// Timeout after 3 seconds.
|
||||
if (poll(&ack_fd, 1, 3000) <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fallthrough.
|
||||
case NO_WAIT:
|
||||
len = recvfrom(sock->socket, &hdr, sizeof(cmu_tcp_header_t),
|
||||
MSG_DONTWAIT | MSG_PEEK, (struct sockaddr *)&(sock->conn),
|
||||
&conn_len);
|
||||
break;
|
||||
default:
|
||||
perror("ERROR unknown flag");
|
||||
}
|
||||
if (len >= (ssize_t)sizeof(cmu_tcp_header_t)) {
|
||||
plen = get_plen(&hdr);
|
||||
pkt = malloc(plen);
|
||||
while (buf_size < plen) {
|
||||
n = recvfrom(sock->socket, pkt + buf_size, plen - buf_size, 0,
|
||||
(struct sockaddr *)&(sock->conn), &conn_len);
|
||||
buf_size = buf_size + n;
|
||||
}
|
||||
handle_message(sock, pkt);
|
||||
free(pkt);
|
||||
}
|
||||
pthread_mutex_unlock(&(sock->recv_lock));
|
||||
}
|
||||
|
||||
/**
|
||||
* Breaks up the data into packets and sends a single packet at a time.
|
||||
*
|
||||
* You should most certainly update this function in your implementation.
|
||||
*
|
||||
* @param sock The socket to use for sending data.
|
||||
* @param data The data to be sent.
|
||||
* @param buf_len The length of the data being sent.
|
||||
*/
|
||||
void single_send(cmu_socket_t *sock, uint8_t *data, int buf_len) {
|
||||
uint8_t *msg;
|
||||
uint8_t *data_offset = data;
|
||||
size_t conn_len = sizeof(sock->conn);
|
||||
|
||||
int sockfd = sock->socket;
|
||||
if (buf_len > 0) {
|
||||
while (buf_len != 0) {
|
||||
uint16_t payload_len = MIN(buf_len, MSS);
|
||||
|
||||
uint16_t src = sock->my_port;
|
||||
uint16_t dst = ntohs(sock->conn.sin_port);
|
||||
uint32_t seq = sock->window.last_ack_received;
|
||||
uint32_t ack = sock->window.next_seq_expected;
|
||||
uint16_t hlen = sizeof(cmu_tcp_header_t);
|
||||
uint16_t plen = hlen + payload_len;
|
||||
uint8_t flags = 0;
|
||||
uint16_t adv_window = 1;
|
||||
uint16_t ext_len = 0;
|
||||
uint8_t *ext_data = NULL;
|
||||
uint8_t *payload = data_offset;
|
||||
|
||||
msg = create_packet(src, dst, seq, ack, hlen, plen, flags, adv_window,
|
||||
ext_len, ext_data, payload, payload_len);
|
||||
buf_len -= payload_len;
|
||||
|
||||
while (1) {
|
||||
// FIXME: This is using stop and wait, can we do better?
|
||||
sendto(sockfd, msg, plen, 0, (struct sockaddr *)&(sock->conn),
|
||||
conn_len);
|
||||
check_for_data(sock, TIMEOUT);
|
||||
if (has_been_acked(sock, seq)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data_offset += payload_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *begin_backend(void *in) {
|
||||
cmu_socket_t *sock = (cmu_socket_t *)in;
|
||||
int death, buf_len, send_signal;
|
||||
uint8_t *data;
|
||||
|
||||
while (1) {
|
||||
while (pthread_mutex_lock(&(sock->death_lock)) != 0) {
|
||||
}
|
||||
death = sock->dying;
|
||||
pthread_mutex_unlock(&(sock->death_lock));
|
||||
|
||||
while (pthread_mutex_lock(&(sock->send_lock)) != 0) {
|
||||
}
|
||||
buf_len = sock->sending_len;
|
||||
|
||||
if (death && buf_len == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (buf_len > 0) {
|
||||
data = malloc(buf_len);
|
||||
memcpy(data, sock->sending_buf, buf_len);
|
||||
sock->sending_len = 0;
|
||||
free(sock->sending_buf);
|
||||
sock->sending_buf = NULL;
|
||||
pthread_mutex_unlock(&(sock->send_lock));
|
||||
single_send(sock, data, buf_len);
|
||||
free(data);
|
||||
} else {
|
||||
pthread_mutex_unlock(&(sock->send_lock));
|
||||
}
|
||||
|
||||
check_for_data(sock, NO_WAIT);
|
||||
|
||||
while (pthread_mutex_lock(&(sock->recv_lock)) != 0) {
|
||||
}
|
||||
|
||||
send_signal = sock->received_len > 0;
|
||||
|
||||
pthread_mutex_unlock(&(sock->recv_lock));
|
||||
|
||||
if (send_signal) {
|
||||
pthread_cond_signal(&(sock->wait_cond));
|
||||
}
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
}
|
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
*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 client. Its purpose is to provide
|
||||
* simple test cases and demonstrate how the sockets will be used.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "cmu_tcp.h"
|
||||
|
||||
void functionality(cmu_socket_t *sock) {
|
||||
uint8_t buf[9898];
|
||||
int read;
|
||||
FILE *fp;
|
||||
|
||||
cmu_write(sock, "hi there", 8);
|
||||
cmu_write(sock, " https://www.youtube.com/watch?v=dQw4w9WgXcQ", 44);
|
||||
cmu_write(sock, " https://www.youtube.com/watch?v=Yb6dZ1IFlKc", 44);
|
||||
cmu_write(sock, " https://www.youtube.com/watch?v=xvFZjo5PgG0", 44);
|
||||
cmu_write(sock, " https://www.youtube.com/watch?v=8ybW48rKBME", 44);
|
||||
cmu_write(sock, " https://www.youtube.com/watch?v=xfr64zoBTAQ", 45);
|
||||
cmu_read(sock, buf, 200, NO_FLAG);
|
||||
|
||||
cmu_write(sock, "hi there", 9);
|
||||
cmu_read(sock, buf, 200, NO_FLAG);
|
||||
printf("R: %s\n", buf);
|
||||
|
||||
read = cmu_read(sock, buf, 200, NO_WAIT);
|
||||
printf("Read: %d\n", read);
|
||||
|
||||
fp = fopen("/vagrant/project-1_elec3120/src/cmu_tcp.c", "rb");
|
||||
read = 1;
|
||||
while (read > 0) {
|
||||
read = fread(buf, 1, 2000, fp);
|
||||
if (read > 0) {
|
||||
cmu_write(sock, buf, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (cmu_socket(&socket, TCP_INITIATOR, portno, serverip) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
functionality(&socket);
|
||||
|
||||
if (cmu_close(&socket) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
*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 helper functions to create and manipulate packets.
|
||||
*
|
||||
* Do NOT modify this file.
|
||||
*/
|
||||
|
||||
#include "cmu_packet.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
uint16_t get_src(cmu_tcp_header_t* header) {
|
||||
return ntohs(header->source_port);
|
||||
}
|
||||
|
||||
uint16_t get_dst(cmu_tcp_header_t* header) {
|
||||
return ntohs(header->destination_port);
|
||||
}
|
||||
|
||||
uint32_t get_seq(cmu_tcp_header_t* header) { return ntohl(header->seq_num); }
|
||||
|
||||
uint32_t get_ack(cmu_tcp_header_t* header) { return ntohl(header->ack_num); }
|
||||
|
||||
uint16_t get_hlen(cmu_tcp_header_t* header) { return ntohs(header->hlen); }
|
||||
|
||||
uint16_t get_plen(cmu_tcp_header_t* header) { return ntohs(header->plen); }
|
||||
|
||||
uint8_t get_flags(cmu_tcp_header_t* header) { return header->flags; }
|
||||
|
||||
uint16_t get_advertised_window(cmu_tcp_header_t* header) {
|
||||
return ntohs(header->advertised_window);
|
||||
}
|
||||
|
||||
uint16_t get_extension_length(cmu_tcp_header_t* header) {
|
||||
return ntohs(header->extension_length);
|
||||
}
|
||||
|
||||
uint8_t* get_extension_data(cmu_tcp_header_t* header) {
|
||||
return (uint8_t*)(header + 1);
|
||||
}
|
||||
|
||||
void set_src(cmu_tcp_header_t* header, uint16_t src) {
|
||||
header->source_port = htons(src);
|
||||
}
|
||||
|
||||
void set_dst(cmu_tcp_header_t* header, uint16_t dst) {
|
||||
header->destination_port = htons(dst);
|
||||
}
|
||||
|
||||
void set_seq(cmu_tcp_header_t* header, uint32_t seq) {
|
||||
header->seq_num = htonl(seq);
|
||||
}
|
||||
|
||||
void set_ack(cmu_tcp_header_t* header, uint32_t ack) {
|
||||
header->ack_num = htonl(ack);
|
||||
}
|
||||
|
||||
void set_hlen(cmu_tcp_header_t* header, uint16_t hlen) {
|
||||
header->hlen = htons(hlen);
|
||||
}
|
||||
|
||||
void set_plen(cmu_tcp_header_t* header, uint16_t plen) {
|
||||
header->plen = htons(plen);
|
||||
}
|
||||
|
||||
void set_flags(cmu_tcp_header_t* header, uint8_t flags) {
|
||||
header->flags = flags;
|
||||
}
|
||||
|
||||
void set_advertised_window(cmu_tcp_header_t* header, uint16_t adv_window) {
|
||||
header->advertised_window = htons(adv_window);
|
||||
}
|
||||
|
||||
void set_extension_length(cmu_tcp_header_t* header, uint16_t ext) {
|
||||
header->extension_length = htons(ext);
|
||||
}
|
||||
|
||||
void set_extension_data(cmu_tcp_header_t* header, uint8_t* ext_data) {
|
||||
memcpy(header->extension_data, ext_data, get_extension_length(header));
|
||||
}
|
||||
|
||||
void set_header(cmu_tcp_header_t* header, uint16_t src, uint16_t dst,
|
||||
uint32_t seq, uint32_t ack, uint16_t hlen, uint16_t plen,
|
||||
uint8_t flags, uint16_t adv_window, uint16_t ext,
|
||||
uint8_t* ext_data) {
|
||||
header->identifier = htonl(IDENTIFIER);
|
||||
header->source_port = htons(src);
|
||||
header->destination_port = htons(dst);
|
||||
header->seq_num = htonl(seq);
|
||||
header->ack_num = htonl(ack);
|
||||
header->hlen = htons(hlen);
|
||||
header->plen = htons(plen);
|
||||
header->flags = flags;
|
||||
header->advertised_window = htons(adv_window);
|
||||
header->extension_length = htons(ext);
|
||||
|
||||
memcpy(header->extension_data, ext_data, ext);
|
||||
}
|
||||
|
||||
uint8_t* get_payload(uint8_t* pkt) {
|
||||
cmu_tcp_header_t* header = (cmu_tcp_header_t*)pkt;
|
||||
uint16_t ext_len = get_extension_length(header);
|
||||
int offset = sizeof(cmu_tcp_header_t) + ext_len;
|
||||
return (uint8_t*)header + offset;
|
||||
}
|
||||
|
||||
uint16_t get_payload_len(uint8_t* pkt) {
|
||||
cmu_tcp_header_t* header = (cmu_tcp_header_t*)pkt;
|
||||
return get_plen(header) - get_hlen(header);
|
||||
}
|
||||
|
||||
void set_payload(uint8_t* pkt, uint8_t* payload, uint16_t payload_len) {
|
||||
cmu_tcp_header_t* header = (cmu_tcp_header_t*)pkt;
|
||||
uint16_t ext_len = get_extension_length(header);
|
||||
int offset = sizeof(cmu_tcp_header_t) + ext_len;
|
||||
memcpy((uint8_t*)header + offset, payload, payload_len);
|
||||
}
|
||||
|
||||
uint8_t* create_packet(uint16_t src, uint16_t dst, uint32_t seq, uint32_t ack,
|
||||
uint16_t hlen, uint16_t plen, uint8_t flags,
|
||||
uint16_t adv_window, uint16_t ext_len, uint8_t* ext_data,
|
||||
uint8_t* payload, uint16_t payload_len) {
|
||||
if (hlen < sizeof(cmu_tcp_header_t)) {
|
||||
return NULL;
|
||||
}
|
||||
if (plen < hlen) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t* packet = malloc(sizeof(cmu_tcp_header_t) + payload_len);
|
||||
if (packet == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cmu_tcp_header_t* header = (cmu_tcp_header_t*)packet;
|
||||
set_header(header, src, dst, seq, ack, hlen, plen, flags, adv_window, ext_len,
|
||||
ext_data);
|
||||
|
||||
uint8_t* pkt_payload = get_payload(packet);
|
||||
memcpy(pkt_payload, payload, payload_len);
|
||||
|
||||
return packet;
|
||||
}
|
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
*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 the high-level API for CMU-TCP sockets.
|
||||
*/
|
||||
|
||||
#include "cmu_tcp.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "backend.h"
|
||||
|
||||
int cmu_socket(cmu_socket_t *sock, const cmu_socket_type_t socket_type,
|
||||
const int port, const char *server_ip) {
|
||||
int sockfd, optval;
|
||||
socklen_t len;
|
||||
struct sockaddr_in conn, my_addr;
|
||||
len = sizeof(my_addr);
|
||||
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd < 0) {
|
||||
perror("ERROR opening socket");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
sock->socket = sockfd;
|
||||
sock->received_buf = NULL;
|
||||
sock->received_len = 0;
|
||||
pthread_mutex_init(&(sock->recv_lock), NULL);
|
||||
|
||||
sock->sending_buf = NULL;
|
||||
sock->sending_len = 0;
|
||||
pthread_mutex_init(&(sock->send_lock), NULL);
|
||||
|
||||
sock->type = socket_type;
|
||||
sock->dying = 0;
|
||||
pthread_mutex_init(&(sock->death_lock), NULL);
|
||||
|
||||
// FIXME: Sequence numbers should be randomly initialized. The next expected
|
||||
// sequence number should be initialized according to the SYN packet from the
|
||||
// other side of the connection.
|
||||
sock->window.last_ack_received = 0;
|
||||
sock->window.next_seq_expected = 0;
|
||||
pthread_mutex_init(&(sock->window.ack_lock), NULL);
|
||||
|
||||
if (pthread_cond_init(&sock->wait_cond, NULL) != 0) {
|
||||
perror("ERROR condition variable not set\n");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
switch (socket_type) {
|
||||
case TCP_INITIATOR:
|
||||
if (server_ip == NULL) {
|
||||
perror("ERROR server_ip NULL");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.sin_family = AF_INET;
|
||||
conn.sin_addr.s_addr = inet_addr(server_ip);
|
||||
conn.sin_port = htons(port);
|
||||
sock->conn = conn;
|
||||
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
my_addr.sin_port = 0;
|
||||
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) < 0) {
|
||||
perror("ERROR on binding");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TCP_LISTENER:
|
||||
memset(&conn, 0, sizeof(conn));
|
||||
conn.sin_family = AF_INET;
|
||||
conn.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
conn.sin_port = htons((uint16_t)port);
|
||||
|
||||
optval = 1;
|
||||
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval,
|
||||
sizeof(int));
|
||||
if (bind(sockfd, (struct sockaddr *)&conn, sizeof(conn)) < 0) {
|
||||
perror("ERROR on binding");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
sock->conn = conn;
|
||||
break;
|
||||
|
||||
default:
|
||||
perror("Unknown Flag");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
getsockname(sockfd, (struct sockaddr *)&my_addr, &len);
|
||||
sock->my_port = ntohs(my_addr.sin_port);
|
||||
|
||||
pthread_create(&(sock->thread_id), NULL, begin_backend, (void *)sock);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int cmu_close(cmu_socket_t *sock) {
|
||||
while (pthread_mutex_lock(&(sock->death_lock)) != 0) {
|
||||
}
|
||||
sock->dying = 1;
|
||||
pthread_mutex_unlock(&(sock->death_lock));
|
||||
|
||||
pthread_join(sock->thread_id, NULL);
|
||||
|
||||
if (sock != NULL) {
|
||||
if (sock->received_buf != NULL) {
|
||||
free(sock->received_buf);
|
||||
}
|
||||
if (sock->sending_buf != NULL) {
|
||||
free(sock->sending_buf);
|
||||
}
|
||||
} else {
|
||||
perror("ERROR null socket\n");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
return close(sock->socket);
|
||||
}
|
||||
|
||||
int cmu_read(cmu_socket_t *sock, void *buf, int length, cmu_read_mode_t flags) {
|
||||
uint8_t *new_buf;
|
||||
int read_len = 0;
|
||||
|
||||
if (length < 0) {
|
||||
perror("ERROR negative length");
|
||||
return EXIT_ERROR;
|
||||
}
|
||||
|
||||
while (pthread_mutex_lock(&(sock->recv_lock)) != 0) {
|
||||
}
|
||||
|
||||
switch (flags) {
|
||||
case NO_FLAG:
|
||||
while (sock->received_len == 0) {
|
||||
pthread_cond_wait(&(sock->wait_cond), &(sock->recv_lock));
|
||||
}
|
||||
// Fall through.
|
||||
case NO_WAIT:
|
||||
if (sock->received_len > 0) {
|
||||
if (sock->received_len > length)
|
||||
read_len = length;
|
||||
else
|
||||
read_len = sock->received_len;
|
||||
|
||||
memcpy(buf, sock->received_buf, read_len);
|
||||
if (read_len < sock->received_len) {
|
||||
new_buf = malloc(sock->received_len - read_len);
|
||||
memcpy(new_buf, sock->received_buf + read_len,
|
||||
sock->received_len - read_len);
|
||||
free(sock->received_buf);
|
||||
sock->received_len -= read_len;
|
||||
sock->received_buf = new_buf;
|
||||
} else {
|
||||
free(sock->received_buf);
|
||||
sock->received_buf = NULL;
|
||||
sock->received_len = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
perror("ERROR Unknown flag.\n");
|
||||
read_len = EXIT_ERROR;
|
||||
}
|
||||
pthread_mutex_unlock(&(sock->recv_lock));
|
||||
return read_len;
|
||||
}
|
||||
|
||||
int cmu_write(cmu_socket_t *sock, const void *buf, int length) {
|
||||
while (pthread_mutex_lock(&(sock->send_lock)) != 0) {
|
||||
}
|
||||
if (sock->sending_buf == NULL)
|
||||
sock->sending_buf = malloc(length);
|
||||
else
|
||||
sock->sending_buf = realloc(sock->sending_buf, length + sock->sending_len);
|
||||
memcpy(sock->sending_buf + sock->sending_len, buf, length);
|
||||
sock->sending_len += length;
|
||||
|
||||
pthread_mutex_unlock(&(sock->send_lock));
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
*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. Its purpose is to provide
|
||||
* simple test cases and demonstrate how the sockets will be used.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cmu_tcp.h"
|
||||
|
||||
#define BUF_SIZE 10000
|
||||
|
||||
/*
|
||||
* Param: sock - used for reading and writing to a connection
|
||||
*
|
||||
* Purpose: To provide some simple test cases and demonstrate how
|
||||
* the sockets will be used.
|
||||
*
|
||||
*/
|
||||
void functionality(cmu_socket_t *sock) {
|
||||
uint8_t buf[BUF_SIZE];
|
||||
FILE *fp;
|
||||
int n;
|
||||
|
||||
n = cmu_read(sock, buf, BUF_SIZE, NO_FLAG);
|
||||
printf("R: %s\n", buf);
|
||||
printf("N: %d\n", n);
|
||||
cmu_write(sock, "hi there", 9);
|
||||
n = cmu_read(sock, buf, 200, NO_FLAG);
|
||||
printf("R: %s\n", buf);
|
||||
printf("N: %d\n", n);
|
||||
cmu_write(sock, "https://www.youtube.com/watch?v=dQw4w9WgXcQ", 44);
|
||||
|
||||
sleep(1);
|
||||
n = cmu_read(sock, buf, BUF_SIZE, NO_FLAG);
|
||||
printf("N: %d\n", n);
|
||||
fp = fopen("/tmp/file.c", "w");
|
||||
fwrite(buf, 1, n, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (cmu_socket(&socket, TCP_LISTENER, portno, serverip) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
functionality(&socket);
|
||||
|
||||
if (cmu_close(&socket) < 0) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
@@ -0,0 +1 @@
|
||||
Describe your tests here
|
@@ -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;
|
||||
}
|
@@ -0,0 +1 @@
|
||||
*.pcap
|
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
HOST=$(hostname)
|
||||
IFNAME=$(ifconfig | grep -B1 10.0.1. | grep -o "^\w*")
|
||||
FUNCTION_TO_RUN=$1
|
||||
PCAP_NAME=$2
|
||||
|
||||
if [ -z "$FUNCTION_TO_RUN" ]
|
||||
then
|
||||
echo "usage: ./capture_packets.sh < start | stop | analyze > PCAP_NAME"
|
||||
echo "Expecting name of function to run: start, stop, or analyze."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$PCAP_NAME" ]
|
||||
then
|
||||
PCAP_NAME=$HOST.pcap
|
||||
echo NO PCAP_NAME PARAM SO USING DEFAULT FILE: $PCAP_NAME
|
||||
fi
|
||||
|
||||
start() {
|
||||
sudo tcpdump -i $IFNAME -w $PCAP_NAME udp > /dev/null 2> /dev/null < \
|
||||
/dev/null &
|
||||
}
|
||||
|
||||
stop() {
|
||||
sudo pkill -f "tcpdump -i $IFNAME -w $PCAP_NAME udp"
|
||||
}
|
||||
|
||||
analyze() {
|
||||
tshark -X lua_script:$DIR/tcp.lua -R "cmutcp and not icmp" -r $PCAP_NAME \
|
||||
-T fields \
|
||||
-e frame.time_relative \
|
||||
-e ip.src \
|
||||
-e cmutcp.source_port \
|
||||
-e ip.dst \
|
||||
-e cmutcp.destination_port \
|
||||
-e cmutcp.seq_num \
|
||||
-e cmutcp.ack_num \
|
||||
-e cmutcp.hlen \
|
||||
-e cmutcp.plen \
|
||||
-e cmutcp.flags \
|
||||
-e cmutcp.advertised_window \
|
||||
-e cmutcp.extension_length \
|
||||
-E header=y -E separator=, \
|
||||
-2
|
||||
}
|
||||
|
||||
$FUNCTION_TO_RUN
|
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
||||
|
||||
echo "Preparing submission..."
|
||||
echo "Make sure to commit all files that you want to submit."
|
||||
|
||||
cd $SCRIPT_DIR/../..
|
||||
git archive -o handin.tar.gz HEAD
|
||||
|
||||
echo "Upload handin.tar.gz to Gradescope."
|
@@ -0,0 +1,72 @@
|
||||
-- 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.
|
||||
|
||||
tcp = Proto("cmutcp", "CMU TCP")
|
||||
|
||||
local f_identifier = ProtoField.uint32("cmutcp.identifier", "Identifier")
|
||||
local f_source_port = ProtoField.uint16("cmutcp.source_port", "Source Port")
|
||||
local f_destination_port = ProtoField.uint16("cmutcp.destination_port", "Destination Port")
|
||||
local f_seq_num = ProtoField.uint32("cmutcp.seq_num", "Sequence Number")
|
||||
local f_ack_num = ProtoField.uint32("cmutcp.ack_num", "ACK Number")
|
||||
local f_hlen = ProtoField.uint16("cmutcp.hlen", "Header Length")
|
||||
local f_plen = ProtoField.uint16("cmutcp.plen", "Packet Length")
|
||||
local f_flags = ProtoField.uint8("cmutcp.flags", "Flags")
|
||||
local f_advertised_window = ProtoField.uint16("cmutcp.advertised_window", "Advertised Window")
|
||||
local f_extension_length = ProtoField.uint16("cmutcp.extension_length", "Extension Length")
|
||||
local f_extension_data = ProtoField.string("cmutcp.extension_data", "Extension Data")
|
||||
|
||||
tcp.fields = { f_identifier, f_source_port, f_destination_port, f_seq_num, f_ack_num, f_hlen, f_plen, f_flags, f_advertised_window, f_extension_length , f_extension_data}
|
||||
|
||||
function tcp.dissector(tvb, pInfo, root) -- Tvb, Pinfo, TreeItem
|
||||
if (tvb:len() ~= tvb:reported_len()) then
|
||||
return 0 -- ignore partially captured packets
|
||||
-- this can/may be re-enabled only for unfragmented UDP packets
|
||||
end
|
||||
|
||||
local t = root:add(tcp, tvb(0,25))
|
||||
t:add(f_identifier, tvb(0,4))
|
||||
t:add(f_source_port, tvb(4,2))
|
||||
t:add(f_destination_port, tvb(6,2))
|
||||
t:add(f_seq_num, tvb(8,4))
|
||||
t:add(f_ack_num, tvb(12,4))
|
||||
t:add(f_hlen, tvb(16,2))
|
||||
t:add(f_plen, tvb(18,2))
|
||||
local f = t:add(f_flags, tvb(20,1))
|
||||
t:add(f_advertised_window, tvb(21,2))
|
||||
t:add(f_extension_length, tvb(23,2))
|
||||
local extension_length = tvb(23,1):int()
|
||||
t:add(f_extension_data, tvb(25,extension_length))
|
||||
|
||||
|
||||
local flag = tvb(20,1):uint()
|
||||
|
||||
if bit.band(flag, 8) ~= 0 then
|
||||
f:add(tvb(20,1), "SYN")
|
||||
end
|
||||
if bit.band(flag, 4) ~= 0 then
|
||||
f:add(tvb(20,1), "ACK")
|
||||
end
|
||||
if bit.band(flag, 2) ~= 0 then
|
||||
f:add(tvb(20,1), "FIN")
|
||||
end
|
||||
|
||||
pInfo.cols.protocol = "CMU TCP"
|
||||
end
|
||||
|
||||
-- have to put the port for the server here
|
||||
local udpDissectorTable = DissectorTable.get("udp.port")
|
||||
udpDissectorTable:add("15441", tcp)
|
||||
|
||||
io.stderr:write("tcp.lua is successfully loaded\n")
|
Reference in New Issue
Block a user