update,
This commit is contained in:
BIN
ahhim526/ELEC_3120__P1_Writeup__Fall__2023_.pdf
Normal file
BIN
ahhim526/ELEC_3120__P1_Writeup__Fall__2023_.pdf
Normal file
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
# Use the Google style in this project.
|
||||
BasedOnStyle: Google
|
12
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/.gitignore
vendored
Normal file
12
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
.vagrant
|
||||
*.log
|
||||
*.o
|
||||
project-1_elec3120/client
|
||||
project-1_elec3120/receiver
|
||||
project-1_elec3120/sender
|
||||
project-1_elec3120/server
|
||||
project-1_elec3120/tests/testing_server
|
||||
**/*.pyc
|
||||
**/handin.tar.gz
|
||||
**/*.pcap
|
||||
!**/test.pcap
|
@@ -0,0 +1,24 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
|
||||
fail_fast: false
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.3.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: https://gitlab.com/daverona/pre-commit-cpp
|
||||
rev: 0.8.0 # use the most recent version
|
||||
hooks:
|
||||
- id: clang-format # formatter of C/C++ code based on a style guide: LLVM, Google, Chromium, Mozilla, and WebKit available
|
||||
args: ["-style=Google"]
|
||||
- id: cpplint # linter (or style-error checker) for Google C++ Style Guide
|
||||
- id: cppcheck # static analyzer of C/C++ code
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 22.3.0
|
||||
hooks:
|
||||
- id: black
|
||||
args: ["--line-length", "79"]
|
@@ -0,0 +1,3 @@
|
||||
filter=-legal/copyright
|
||||
filter=-build/include,-build/c++11
|
||||
filter=-readability/casting
|
@@ -0,0 +1,5 @@
|
||||
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.
|
@@ -0,0 +1,155 @@
|
||||
# ELEC 3120 Project 1: TCP in the Wild
|
||||
|
||||
Welcome to Project 1! Please read the handout and starter code thoroughly before you begin. This README contains a quick summary to get your testing environment set up.
|
||||
|
||||
## Setting up the environment
|
||||
|
||||
You can use Vagrant and Docker to automatically setup the environment. This should work both on Linux as well as on macOS (including Apple Silicon macs).
|
||||
|
||||
Start by downloading and installing [Vagrant](https://www.vagrantup.com/downloads) and [Docker](https://docs.docker.com/desktop/). For macOS, you may use homebrew to install Vagrant but **do not** use homebrew to install Docker. Instead, download Docker Desktop from the link above.
|
||||
|
||||
Install Vagrant and Docker:
|
||||
```bash
|
||||
# In Azure instance terminal under Ubuntu environment
|
||||
# In the project directory
|
||||
bash install_env.sh
|
||||
```
|
||||
|
||||
Once you have both Vagrant and Docker Desktop installed, navigate inside this repo and run:
|
||||
|
||||
```bash
|
||||
# In your project root folder where your Vagrantfile is located
|
||||
vagrant up --provider=docker # builds the server and client containers using Docker.
|
||||
vagrant ssh {client | server} # connects to either the client or server using SSH.
|
||||
```
|
||||
|
||||
Vagrant keeps all files synchronized between your host machine and the two containers. In other words, the code will update automatically on the containers as you edit it on your computer. Similarly, debugging files and other files generated on the containers will automatically appear on your host machine.
|
||||
|
||||
## Files
|
||||
The following files have been provided for you:
|
||||
|
||||
* `backend.c`: This file contains the backend code that will run in a separate thread from the application. This is where most of your logic should go. The backend should deal with most of the TCP functionality, including the state machine, timeouts, retransmissions, buffering, congestion control, etc.
|
||||
|
||||
* `cmu_tcp.c`: This contains the main socket functions required of your TCP socket including reading, writing, opening and closing. Since TCP needs to works asynchronously with the application, these functions are relatively simple and interact with the backend running in a separate thread.
|
||||
|
||||
* `Vagrantfile`: Defines the structure, IP addresses, and dependencies in the containers. Feel free to modify this file to add any additional testing tools as you see fit. Remember to document your changes in `tests.txt`!
|
||||
|
||||
* `README.md`: A description of your files, as well as your algorithm, if you choose to implement it in CP2.
|
||||
|
||||
* `tests.txt`: A brief writeup describing your testing strategy, and any tools you used in the process of testing.
|
||||
|
||||
* `gen_graph.py`: Takes in a PCAP file and generates a graph. Feel free to modify this file to profile Reno and your own algorithm in CP2.
|
||||
|
||||
* `capture_packets.sh`: Captures packets from the server and client containers and saves them to a PCAP file.
|
||||
|
||||
* `tcp.lua`: A Lua plugin that allows Wireshark to decode CMU-TCP headers. Copy the file to the directory described in <https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html> to use the plugin.
|
||||
|
||||
* `test_cp1.py`: Test script for CP1 that is executed with `make test`. You should add your own tests to this file.
|
||||
|
||||
* `test_cp2.py`: Test script for CP2 that can be executed with `make test`. You should add your own tests to this file.
|
||||
|
||||
* `grading.h`: These are variables that we will use to test your implementation. We will be replacing this file when running tests, and hence you should test your implementation with different values.
|
||||
|
||||
* `server.c`: An application using the server side of your transport protocol. We will test your code using a different server program, so do not keep any variables or functions here that your protocol uses.
|
||||
|
||||
* `client.c`: An application using the client side of your transport protocol. We will test your code using a different client application, so do not keep any variables or functions here that your protocol uses.
|
||||
|
||||
* `cmu_packet.h`: This file describes the basic packet format and header and provides helper functions that will help you handle packets. You are not allowed to modify this file! The scripts that we provide to help you graph your packet traces rely on this file being unchanged. All the communication between your server and client will use UDP as the underlying protocol. All packets will begin with the common header described as follows:
|
||||
|
||||
* Course Number [4 bytes]
|
||||
* Source Port [2 bytes]
|
||||
* Destination Port [2 bytes]
|
||||
* Sequence Number [4 bytes]
|
||||
* Acknowledgement Number [4 bytes]
|
||||
* Header Length [2 bytes]
|
||||
* Packet Length [2 bytes]
|
||||
* Flags [1 byte]
|
||||
* Advertised Window [2 bytes]
|
||||
* Extension length [2 bytes]
|
||||
* Extension Data [You may use it when designing your own congestion control algorithm]
|
||||
|
||||
## Manual test
|
||||
You can manually test your code by running the server and client applications in the containers while also capturing packets using `capture_packets.sh`. You can then use the same script or [Wireshark](https://www.wireshark.org/#download) to view the packets.
|
||||
|
||||
Open two terminals. One will be used to access the server container and the other will be used to access the client container.
|
||||
|
||||
On the **server** terminal:
|
||||
```bash
|
||||
# Access the server container and navigate to the project directory.
|
||||
vagrant ssh server
|
||||
cd /vagrant/project-1_elec3120/
|
||||
|
||||
# Compile.
|
||||
make
|
||||
|
||||
# Start packet capture.
|
||||
./utils/capture_packets.sh start cap.pcap
|
||||
|
||||
# Start the server.
|
||||
./server
|
||||
```
|
||||
|
||||
On the **client** terminal:
|
||||
```bash
|
||||
# Access the client container and navigate to the project directory.
|
||||
vagrant ssh client
|
||||
cd /vagrant/project-1_elec3120/
|
||||
|
||||
# Note that you don't need to recompile the code here as we already did it on the server and the code is synchronized between the two containers.
|
||||
|
||||
# Start the client.
|
||||
./client
|
||||
```
|
||||
|
||||
Once the server is done. Stop the packet capture and analyze the packets. In the **server** terminal:
|
||||
```bash
|
||||
# Stop the packet capture.
|
||||
./utils/capture_packets.sh stop cap.pcap
|
||||
|
||||
# Analyze the packets.
|
||||
./utils/capture_packets.sh analyze cap.pcap
|
||||
```
|
||||
|
||||
You can also access the capture file (`cap.pcap` in this example) from your host machine and open it with Wireshark. You should use the `utils/tcp.lua` plugin to decode CMU-TCP headers. To do so, copy the file to the directory described in <https://www.wireshark.org/docs/wsug_html_chunked/ChPluginFolders.html>.
|
||||
|
||||
## Automatic tests
|
||||
|
||||
You should also run automatic tests. On either the client or server container, navigate to `/vagrant/project-1_elec3120/` and run:
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
Note that the test files are _incomplete_! You are expected to build upon them and write more extensive tests (doing so will help you write better code and save you grief during debugging)!
|
||||
|
||||
## Submission
|
||||
|
||||
To submit your code to Gradescope, make sure that all the files that should be included are committed and then run:
|
||||
```bash
|
||||
./utils/prepare_submission.sh
|
||||
```
|
||||
|
||||
This will generate a file called `handin.tar.gz` at the repository's root directory. Upload this file to Gradescope.
|
||||
|
||||
## [Optional] Automatic code formatting and analysis
|
||||
We provided a [pre-commit](https://pre-commit.com/) configuration file that you can use to automatically format and statically check your code whenever you commit. Either use the pre-commit already installed in one of the containers or install it in your local machine:
|
||||
```bash
|
||||
pip3 install pre-commit
|
||||
```
|
||||
|
||||
And then run the following command in the root directory of this repo to install the pre-commit hook:
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
|
||||
Now, pre-commit will automatically run whenever you commit. If you want to run it manually, you can run:
|
||||
```bash
|
||||
make format
|
||||
```
|
||||
|
||||
## [Optional]SSH login remote server
|
||||
|
||||
Using username and password to log in to your instance:
|
||||
```bash
|
||||
ssh username@IP_address
|
||||
```
|
||||
For convenience, you can also use the public key to login without password: [ssh_setting](ssh_connection.md)
|
56
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/Vagrantfile
vendored
Normal file
56
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/Vagrantfile
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
$VAGRANT_EXTRA_STEPS = <<~SCRIPT
|
||||
git config --global --add safe.directory '*'
|
||||
echo "cd /vagrant" >> /home/vagrant/.bashrc
|
||||
SCRIPT
|
||||
|
||||
$SET_NETWORK = <<~'SCRIPT'
|
||||
IFNAME=$(ifconfig | grep -B1 10.0.1. | grep -o "^\w*")
|
||||
echo "export IFNAME=$IFNAME" >> /home/vagrant/.bashrc
|
||||
sudo echo "export IFNAME=$IFNAME" >> /root/.bashrc
|
||||
|
||||
sudo tcset $IFNAME --rate 100Mbps --delay 20ms
|
||||
sudo sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' \
|
||||
/etc/ssh/sshd_config
|
||||
sudo service sshd restart
|
||||
SCRIPT
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
config.ssh.forward_agent = true
|
||||
config.vm.synced_folder "project-1_elec3120", "/vagrant/project-1_elec3120"
|
||||
config.vm.provision "shell",
|
||||
inline: "sudo /vagrant/project-1_elec3120/setup/setup.sh"
|
||||
config.vm.provision "shell", inline: $VAGRANT_EXTRA_STEPS
|
||||
|
||||
config.ssh.insert_key = false
|
||||
|
||||
config.vm.provider "docker" do |docker, override|
|
||||
override.vm.box = nil
|
||||
docker.image = "rofrano/vagrant-provider:ubuntu-22.04"
|
||||
docker.remains_running = true
|
||||
docker.has_ssh = true
|
||||
docker.privileged = true
|
||||
docker.create_args = ["--cgroupns=host"]
|
||||
docker.volumes = ["/sys/fs/cgroup:/sys/fs/cgroup:rw"]
|
||||
end
|
||||
|
||||
config.vm.provider "virtualbox" do |v, override|
|
||||
override.vm.box = "ubuntu/jammy64"
|
||||
end
|
||||
|
||||
config.vm.define :client, primary: true do |host|
|
||||
host.vm.hostname = "client"
|
||||
host.vm.network "private_network", ip: "10.0.1.2", netmask: 8,
|
||||
mac: "080027a7feb1", virtualbox__intnet: "15441"
|
||||
host.vm.provision "shell", inline: $SET_NETWORK
|
||||
end
|
||||
|
||||
config.vm.define :server do |host|
|
||||
host.vm.hostname = "server"
|
||||
host.vm.network "private_network", ip: "10.0.1.1", netmask: 8,
|
||||
mac: "08002722471c", virtualbox__intnet: "15441"
|
||||
host.vm.provision "shell", inline: $SET_NETWORK
|
||||
end
|
||||
end
|
@@ -0,0 +1,28 @@
|
||||
# install vagrant
|
||||
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
|
||||
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
|
||||
sudo apt update && sudo apt install -y vagrant
|
||||
|
||||
# install docker
|
||||
|
||||
# Add Docker's official GPG key:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates curl gnupg
|
||||
sudo install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
sudo chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
|
||||
# Add the repository to Apt sources:
|
||||
echo \
|
||||
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
|
||||
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
sudo apt-get update
|
||||
|
||||
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
|
||||
# add docker permition
|
||||
sudo groupadd -f docker
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
|
BIN
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/md_img/ssh_config.png
(Stored with Git LFS)
Normal file
BIN
ahhim526/_ref/computer-networks-project1_elec3120-c33fe69569bd/md_img/ssh_config.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -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")
|
@@ -0,0 +1,88 @@
|
||||
<h1 style="text-align: center;">SSH Key Configuration Guide</h1>
|
||||
|
||||
This guide will walk you through the process of configuration of SSH key for remote SSH connections to servers on both Windows and Linux. Fast-connecting to a sever using SSH public key authentication is a SSH connection trick that makes it easy to connect to a remote server.
|
||||
|
||||
## Generating SSH Keys
|
||||
### Windows
|
||||
|
||||
#### Step 1: Open Powershell Bash
|
||||
|
||||
Open the Powershell terminal to prepare for SSH key generation.
|
||||
|
||||
#### Step 2: Generate SSH Keys
|
||||
|
||||
In the Powershell terminal, use the following command to generate SSH keys:
|
||||
|
||||
```bash
|
||||
# In powershell bash
|
||||
ssh-keygen
|
||||
```
|
||||
Save it by default, the system will save the keys to [your home directory]/.ssh/id_rsa. Unless you are an expert you should use the default option and press Enter.
|
||||
|
||||
#### Step 3: View SSH Keys
|
||||
|
||||
To view the generated SSH keys, run the following command:
|
||||
|
||||
```bash
|
||||
# In powershell bash
|
||||
cat ~/.ssh/id_rsa.pub
|
||||
```
|
||||
|
||||
Copy the displayed key and configure it on your server.
|
||||
|
||||
### Linux-like systems
|
||||
|
||||
#### Step 1: Open Terminal
|
||||
|
||||
Open the terminal to prepare for SSH key generation.
|
||||
|
||||
#### Step 2: Generate SSH Keys
|
||||
|
||||
In the terminal, use the following command to generate SSH keys:
|
||||
|
||||
```bash
|
||||
# In termianl
|
||||
ssh-keygen
|
||||
```
|
||||
|
||||
Follow the prompts to input the file save path and a passphrase. Generally, you can accept the default values by pressing Enter.
|
||||
|
||||
#### Step 3: View SSH Keys
|
||||
|
||||
To view the generated SSH keys, run the following command:
|
||||
|
||||
```bash
|
||||
# In termianl
|
||||
cat ~/.ssh/id_rsa.pub
|
||||
```
|
||||
|
||||
Copy the displayed key and configure it on your server.
|
||||
|
||||
## Server Configuration
|
||||
|
||||
On your Azure instance/server which your code running on, add the SSH publish key you generated to the `~/.ssh/authorized_keys` file to allow remote SSH access. Ensure that your server has SSH access properly configured.
|
||||
|
||||
If you don't have the `authorized_keys` file under the `~/.ssh/`, just create a blank one:
|
||||
|
||||
```bash
|
||||
# In the server terminal, use the following command
|
||||
touch ~/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
On your own PC, write the alias of the server in the `~/.ssh/config` file:
|
||||
|
||||

|
||||
|
||||
```bash
|
||||
Host elec3120us # the alias for server
|
||||
HostName 20.38.43.192 # server IP address
|
||||
User elec3120 # server usrname
|
||||
```
|
||||
|
||||
You have now successfully generated SSH keys and are ready for remote SSH connections to your server by typing the following to connect to server _elec3120@20.38.43.192_:
|
||||
|
||||
```bash
|
||||
ssh elec3120us
|
||||
```
|
||||
|
||||
We hope this guide is helpful!
|
@@ -0,0 +1 @@
|
||||
{"dependencies":[["ipaddr",[">= 1.2.4"]],["rubyntlm",["~> 0.6.0",">= 0.6.3"]],["nori",["~> 2.0"]],["multi_json",["~> 1.14"]],["little-plugger",["~> 1.1"]],["logging",[">= 1.6.1","< 3.0"]],["httpclient",["~> 2.2",">= 2.2.0.2"]],["rexml",["~> 3.2"]],["builder",[">= 2.1.2"]],["gyoku",["~> 1.0"]],["ffi",["~> 1.9"]],["gssapi",["~> 1.2"]],["erubi",[">= 0"]],["winrm",[">= 2.3.6","< 3.0"]],["rubyzip",["~> 2.3.2"]],["winrm-fs",[">= 1.3.5","< 2.0"]],["winrm-elevated",[">= 1.2.3","< 2.0"]],["wdm",["~> 0.1.1"]],["log4r",["~> 1.1.9","< 1.1.11"]],["excon",["~> 0.73"]],["vagrant_cloud",["~> 3.1.1"]],["pairing_heap",[">= 0.3.0"]],["stream",["~> 0.5.3"]],["rgl",["~> 0.5.10"]],["rb-kqueue",["~> 0.2.0"]],["net-ssh",["~> 7.0"]],["net-scp",["~> 4.0"]],["net-sftp",["~> 4.0"]],["date",[">= 0"]],["time",[">= 0"]],["io-wait",[">= 0"]],["timeout",[">= 0"]],["net-protocol",[">= 0"]],["net-ftp",["~> 0.2"]],["mime-types-data",["~> 3.2015"]],["mime-types",["~> 3.3"]],["rb-inotify",["~> 0.9",">= 0.9.10"]],["rb-fsevent",["~> 0.10",">= 0.10.3"]],["listen",["~> 3.7"]],["concurrent-ruby",["~> 1.0"]],["i18n",["~> 1.12"]],["hashicorp-checkpoint",["~> 0.1.5"]],["google-protobuf",["~> 3.23"]],["googleapis-common-protos-types",["~> 1.3"]],["grpc",["~> 1.56.0"]],["ed25519",["~> 1.3.0"]],["childprocess",["~> 4.1.0"]],["bcrypt_pbkdf",["~> 1.1"]],["vagrant",[">= 1.9.2"]],["netrc",["~> 0.8"]],["domain_name",["~> 0.5"]],["http-cookie",[">= 1.0.2","< 2.0"]],["http-accept",[">= 1.7.0","< 2.0"]],["rest-client",[">= 1.6.0"]],["vagrant-share",["= 2.0.0"]]],"checksum":"1ae33a57574240b0c1da847fb943e2eec9d44bbee4792fb20a14462bc766d076","vagrant_version":"2.4.1"}
|
@@ -0,0 +1 @@
|
||||
1.5:cb3e5e76-03f3-48ba-9686-2671cb60aa3b
|
@@ -0,0 +1 @@
|
||||
1713594943
|
@@ -0,0 +1 @@
|
||||
{"name":"ubuntu/jammy64","version":"20240417.0.0","provider":"virtualbox","directory":"boxes/ubuntu-VAGRANTSLASH-jammy64/20240417.0.0/virtualbox"}
|
@@ -0,0 +1 @@
|
||||
0
|
@@ -0,0 +1 @@
|
||||
cb3e5e76-03f3-48ba-9686-2671cb60aa3b
|
@@ -0,0 +1 @@
|
||||
2198e36e6f2b4f3483c39f13b7ab405f
|
@@ -0,0 +1,8 @@
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAA
|
||||
AAtzc2gtZWQyNTUxOQAAACAIKEfG3PaOX2SXKEgj9m2wF9oT+Tm38WRa3P3k
|
||||
2wvIrQAAAJCDuT7Gg7k+xgAAAAtzc2gtZWQyNTUxOQAAACAIKEfG3PaOX2SX
|
||||
KEgj9m2wF9oT+Tm38WRa3P3k2wvIrQAAAEC70ZbHu/ean0p89XwO0ebyTGfR
|
||||
O7GXhZ0U8uww7SYCiwgoR8bc9o5fZJcoSCP2bbAX2hP5ObfxZFrc/eTbC8it
|
||||
AAAAB3ZhZ3JhbnQBAgMEBQY=
|
||||
-----END OPENSSH PRIVATE KEY-----
|
@@ -0,0 +1 @@
|
||||
{"virtualbox":{"/vagrant_data":{"guestpath":"/vagrant_data","hostpath":"D:/_workspace/comission-playlist/ahhim526/_ref/vagrant_helloworld/data","disabled":false,"__vagrantfile":true},"/vagrant":{"guestpath":"/vagrant","hostpath":"D:/_workspace/comission-playlist/ahhim526/_ref/vagrant_helloworld","disabled":false,"__vagrantfile":true}}}
|
@@ -0,0 +1 @@
|
||||
D:/_workspace/comission-playlist/ahhim526/_ref/vagrant_helloworld
|
12
ahhim526/_ref/vagrant_helloworld/.vagrant/rgloader/loader.rb
Normal file
12
ahhim526/_ref/vagrant_helloworld/.vagrant/rgloader/loader.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
# This file loads the proper rgloader/loader.rb file that comes packaged
|
||||
# with Vagrant so that encoded files can properly run with Vagrant.
|
||||
|
||||
if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]
|
||||
require File.expand_path(
|
||||
"rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"])
|
||||
else
|
||||
raise "Encoded files can't be read outside of the Vagrant installer."
|
||||
end
|
80
ahhim526/_ref/vagrant_helloworld/Vagrantfile
vendored
Normal file
80
ahhim526/_ref/vagrant_helloworld/Vagrantfile
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
# All Vagrant configuration is done below. The "2" in Vagrant.configure
|
||||
# configures the configuration version (we support older styles for
|
||||
# backwards compatibility). Please don't change it unless you know what
|
||||
# you're doing.
|
||||
Vagrant.configure("2") do |config|
|
||||
# The most common configuration options are documented and commented below.
|
||||
# For a complete reference, please see the online documentation at
|
||||
# https://docs.vagrantup.com.
|
||||
|
||||
# Every Vagrant development environment requires a box. You can search for
|
||||
# boxes at https://vagrantcloud.com/search.
|
||||
config.vm.box = "ubuntu/jammy64"
|
||||
|
||||
# Disable automatic box update checking. If you disable this, then
|
||||
# boxes will only be checked for updates when the user runs
|
||||
# `vagrant box outdated`. This is not recommended.
|
||||
# config.vm.box_check_update = false
|
||||
|
||||
# Create a forwarded port mapping which allows access to a specific port
|
||||
# within the machine from a port on the host machine. In the example below,
|
||||
# accessing "localhost:8080" will access port 80 on the guest machine.
|
||||
# NOTE: This will enable public access to the opened port
|
||||
config.vm.network "forwarded_port", guest: 80, host: 8080
|
||||
|
||||
# Create a forwarded port mapping which allows access to a specific port
|
||||
# within the machine from a port on the host machine and only allow access
|
||||
# via 127.0.0.1 to disable public access
|
||||
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
|
||||
|
||||
# Create a private network, which allows host-only access to the machine
|
||||
# using a specific IP.
|
||||
# config.vm.network "private_network", ip: "192.168.33.10"
|
||||
|
||||
# Create a public network, which generally matched to bridged network.
|
||||
# Bridged networks make the machine appear as another physical device on
|
||||
# your network.
|
||||
# config.vm.network "public_network"
|
||||
|
||||
# Share an additional folder to the guest VM. The first argument is
|
||||
# the path on the host to the actual folder. The second argument is
|
||||
# the path on the guest to mount the folder. And the optional third
|
||||
# argument is a set of non-required options.
|
||||
config.vm.synced_folder "./data", "/vagrant_data"
|
||||
|
||||
# Disable the default share of the current code directory. Doing this
|
||||
# provides improved isolation between the vagrant box and your host
|
||||
# by making sure your Vagrantfile isn't accessible to the vagrant box.
|
||||
# If you use this you may want to enable additional shared subfolders as
|
||||
# shown above.
|
||||
# config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||
|
||||
# Provider-specific configuration so you can fine-tune various
|
||||
# backing providers for Vagrant. These expose provider-specific options.
|
||||
# Example for VirtualBox:
|
||||
#
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
# # Display the VirtualBox GUI when booting the machine
|
||||
# vb.gui = true
|
||||
#
|
||||
# # Customize the amount of memory on the VM:
|
||||
vb.memory = "2048"
|
||||
end
|
||||
#
|
||||
# View the documentation for the provider you are using for more
|
||||
# information on available options.
|
||||
|
||||
# Enable provisioning with a shell script. Additional provisioners such as
|
||||
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
|
||||
# documentation for more information about their specific syntax and use.
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
apt-get update
|
||||
apt-get install -y apache2
|
||||
|
||||
hostnamectl set-hostname louistest-vm
|
||||
|
||||
SHELL
|
||||
end
|
1
ahhim526/_ref/vagrant_helloworld/data/helloworld.txt
Normal file
1
ahhim526/_ref/vagrant_helloworld/data/helloworld.txt
Normal file
@@ -0,0 +1 @@
|
||||
3
|
9
ahhim526/_ref/vagrant_helloworld/init.bat
Normal file
9
ahhim526/_ref/vagrant_helloworld/init.bat
Normal file
@@ -0,0 +1,9 @@
|
||||
vagrant init hashicorp/bionic64
|
||||
vagrant box add hashicorp/bionic64
|
||||
vagrant up
|
||||
vagrant ssh
|
||||
vagrant destroy
|
||||
vagrant box list
|
||||
vagrant box remove hashicorp/bionic64
|
||||
vagrant plugin install vagrant-share
|
||||
vagrant share
|
5
ahhim526/_ref/vagrant_helloworld/rebuild.bat
Normal file
5
ahhim526/_ref/vagrant_helloworld/rebuild.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
vagrant destroy -f
|
||||
vagrant up
|
||||
@REM vagrant plugin install vagrant-share
|
||||
|
||||
echo "done"
|
7
ahhim526/gitUpdate.bat
Normal file
7
ahhim526/gitUpdate.bat
Normal file
@@ -0,0 +1,7 @@
|
||||
git status .
|
||||
|
||||
@pause
|
||||
|
||||
git add .
|
||||
git commit -m"update ahhim526,"
|
||||
start git push
|
3
ahhim526/meta.md
Normal file
3
ahhim526/meta.md
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
tags: HKUST, ELEC3120, vagrant
|
||||
---
|
Reference in New Issue
Block a user