CS144 Lab checkpoint 5 starter code
This commit is contained in:
parent
9d00589b3c
commit
b0006b42f7
@ -5,5 +5,6 @@ add_sponge_exec (tcp_native stream_copy)
|
||||
add_sponge_exec (tun)
|
||||
add_sponge_exec (tcp_udp stream_copy)
|
||||
add_sponge_exec (tcp_ipv4 stream_copy)
|
||||
add_sponge_exec (tcp_ip_ethernet stream_copy)
|
||||
add_sponge_exec (webget)
|
||||
add_sponge_exec (tcp_benchmark)
|
||||
|
||||
142
apps/tcp_ip_ethernet.cc
Normal file
142
apps/tcp_ip_ethernet.cc
Normal file
@ -0,0 +1,142 @@
|
||||
#include "bidirectional_stream_copy.hh"
|
||||
#include "tcp_config.hh"
|
||||
#include "tcp_sponge_socket.hh"
|
||||
#include "tun.hh"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
using namespace std;
|
||||
|
||||
constexpr const char *TAP_DFLT = "tap10";
|
||||
const string LOCAL_ADDRESS_DFLT = "169.254.10.9";
|
||||
const string GATEWAY_DFLT = "169.254.10.1";
|
||||
|
||||
static void show_usage(const char *argv0, const char *msg) {
|
||||
cout << "Usage: " << argv0 << " [options] <host> <port>\n\n"
|
||||
<< " Option Default\n"
|
||||
<< " -- --\n\n"
|
||||
|
||||
<< " -a <addr> Set IP source address (client mode only) " << LOCAL_ADDRESS_DFLT << "\n"
|
||||
<< " -s <port> Set TCP source port (client mode only) (random)\n\n"
|
||||
<< " -n <addr> Set IP next-hop address " << GATEWAY_DFLT << "\n"
|
||||
|
||||
<< " -w <winsz> Use a window of <winsz> bytes " << TCPConfig::MAX_PAYLOAD_SIZE
|
||||
<< "\n\n"
|
||||
|
||||
<< " -t <tmout> Set rt_timeout to tmout " << TCPConfig::TIMEOUT_DFLT << "\n\n"
|
||||
|
||||
<< " -d <tapdev> Connect to tap <tapdev> " << TAP_DFLT << "\n\n"
|
||||
|
||||
<< " -h Show this message.\n\n";
|
||||
|
||||
if (msg != nullptr) {
|
||||
cout << msg;
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
static void check_argc(int argc, char **argv, int curr, const char *err) {
|
||||
if (curr + 3 >= argc) {
|
||||
show_usage(argv[0], err);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static tuple<TCPConfig, FdAdapterConfig, Address, string> get_config(int argc, char **argv) {
|
||||
TCPConfig c_fsm{};
|
||||
FdAdapterConfig c_filt{};
|
||||
string tapdev = TAP_DFLT;
|
||||
|
||||
int curr = 1;
|
||||
|
||||
string source_address = LOCAL_ADDRESS_DFLT;
|
||||
string source_port = to_string(uint16_t(random_device()()));
|
||||
string next_hop_address = GATEWAY_DFLT;
|
||||
|
||||
while (argc - curr > 2) {
|
||||
if (strncmp("-a", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -a requires one argument.");
|
||||
source_address = argv[curr + 1];
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-s", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -s requires one argument.");
|
||||
source_port = argv[curr + 1];
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-n", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -n requires one argument.");
|
||||
next_hop_address = argv[curr + 1];
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-w", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -w requires one argument.");
|
||||
c_fsm.recv_capacity = strtol(argv[curr + 1], nullptr, 0);
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-t", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -t requires one argument.");
|
||||
c_fsm.rt_timeout = strtol(argv[curr + 1], nullptr, 0);
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-d", argv[curr], 3) == 0) {
|
||||
check_argc(argc, argv, curr, "ERROR: -t requires one argument.");
|
||||
tapdev = argv[curr + 1];
|
||||
curr += 2;
|
||||
|
||||
} else if (strncmp("-h", argv[curr], 3) == 0) {
|
||||
show_usage(argv[0], nullptr);
|
||||
exit(0);
|
||||
|
||||
} else {
|
||||
show_usage(argv[0], string("ERROR: unrecognized option " + string(argv[curr])).c_str());
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// parse positional command-line arguments
|
||||
c_filt.destination = {argv[curr], argv[curr + 1]};
|
||||
c_filt.source = {source_address, source_port};
|
||||
|
||||
Address next_hop{next_hop_address, "0"};
|
||||
|
||||
return make_tuple(c_fsm, c_filt, next_hop, tapdev);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
if (argc < 3) {
|
||||
show_usage(argv[0], "ERROR: required arguments are missing.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// choose a random local Ethernet address (and make sure it's private, i.e. not owned by a manufacturer)
|
||||
EthernetAddress local_ethernet_address;
|
||||
for (auto &byte : local_ethernet_address) {
|
||||
byte = random_device()(); // use a random local Ethernet address
|
||||
}
|
||||
local_ethernet_address.at(0) |= 0x02; // "10" in last two binary digits marks a private Ethernet address
|
||||
local_ethernet_address.at(0) &= 0xfe;
|
||||
|
||||
auto [c_fsm, c_filt, next_hop, tap_dev_name] = get_config(argc, argv);
|
||||
|
||||
TCPOverIPv4OverEthernetSpongeSocket tcp_socket(TCPOverIPv4OverEthernetAdapter(
|
||||
TCPOverIPv4OverEthernetAdapter(TapFD(tap_dev_name), local_ethernet_address, c_filt.source, next_hop)));
|
||||
|
||||
tcp_socket.connect(c_fsm, c_filt);
|
||||
|
||||
bidirectional_stream_copy(tcp_socket);
|
||||
tcp_socket.wait_until_closed();
|
||||
} catch (const exception &e) {
|
||||
cerr << "Exception: " << e.what() << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
46
libsponge/network_interface.cc
Normal file
46
libsponge/network_interface.cc
Normal file
@ -0,0 +1,46 @@
|
||||
#include "network_interface.hh"
|
||||
|
||||
#include "arp_message.hh"
|
||||
#include "ethernet_frame.hh"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
// Dummy implementation of a network interface
|
||||
// Translates from {IP datagram, next hop address} to link-layer frame, and from link-layer frame to IP datagram
|
||||
|
||||
// For Lab 5, please replace with a real implementation that passes the
|
||||
// automated checks run by `make check_lab5`.
|
||||
|
||||
// You will need to add private members to the class declaration in `network_interface.hh`
|
||||
|
||||
template <typename... Targs>
|
||||
void DUMMY_CODE(Targs &&... /* unused */) {}
|
||||
|
||||
using namespace std;
|
||||
|
||||
//! \param[in] ethernet_address Ethernet (what ARP calls "hardware") address of the interface
|
||||
//! \param[in] ip_address IP (what ARP calls "protocol") address of the interface
|
||||
NetworkInterface::NetworkInterface(const EthernetAddress ðernet_address, const Address &ip_address)
|
||||
: _ethernet_address(ethernet_address), _ip_address(ip_address) {
|
||||
cerr << "DEBUG: Network interface has Ethernet address " << to_string(_ethernet_address) << " and IP address "
|
||||
<< ip_address.ip() << "\n";
|
||||
}
|
||||
|
||||
//! \param[in] dgram the IPv4 datagram to be sent
|
||||
//! \param[in] next_hop the IP address of the interface to send it to (typically a router or default gateway, but may also be another host if directly connected to the same network as the destination)
|
||||
//! (Note: the Address type can be converted to a uint32_t (raw 32-bit IP address) with the Address::ipv4_numeric() method.)
|
||||
void NetworkInterface::send_datagram(const InternetDatagram &dgram, const Address &next_hop) {
|
||||
// convert IP address of next hop to raw 32-bit representation (used in ARP header)
|
||||
const uint32_t next_hop_ip = next_hop.ipv4_numeric();
|
||||
|
||||
DUMMY_CODE(dgram, next_hop, next_hop_ip);
|
||||
}
|
||||
|
||||
//! \param[in] frame the incoming Ethernet frame
|
||||
optional<InternetDatagram> NetworkInterface::recv_frame(const EthernetFrame &frame) {
|
||||
DUMMY_CODE(frame);
|
||||
return {};
|
||||
}
|
||||
|
||||
//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method
|
||||
void NetworkInterface::tick(const size_t ms_since_last_tick) { DUMMY_CODE(ms_since_last_tick); }
|
||||
67
libsponge/network_interface.hh
Normal file
67
libsponge/network_interface.hh
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef SPONGE_LIBSPONGE_NETWORK_INTERFACE_HH
|
||||
#define SPONGE_LIBSPONGE_NETWORK_INTERFACE_HH
|
||||
|
||||
#include "ethernet_frame.hh"
|
||||
#include "tcp_over_ip.hh"
|
||||
#include "tun.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
||||
//! \brief A "network interface" that connects IP (the internet layer, or network layer)
|
||||
//! with Ethernet (the network access layer, or link layer).
|
||||
|
||||
//! This module is the lowest layer of a TCP/IP stack
|
||||
//! (connecting IP with the lower-layer network protocol,
|
||||
//! e.g. Ethernet). But the same module is also used repeatedly
|
||||
//! as part of a router: a router generally has many network
|
||||
//! interfaces, and the router's job is to route Internet datagrams
|
||||
//! between the different interfaces.
|
||||
|
||||
//! The network interface translates datagrams (coming from the
|
||||
//! "customer," e.g. a TCP/IP stack or router) into Ethernet
|
||||
//! frames. To fill in the Ethernet destination address, it looks up
|
||||
//! the Ethernet address of the next IP hop of each datagram, making
|
||||
//! requests with the [Address Resolution Protocol](\ref rfc::rfc826).
|
||||
//! In the opposite direction, the network interface accepts Ethernet
|
||||
//! frames, checks if they are intended for it, and if so, processes
|
||||
//! the the payload depending on its type. If it's an IPv4 datagram,
|
||||
//! the network interface passes it up the stack. If it's an ARP
|
||||
//! request or reply, the network interface processes the frame
|
||||
//! and learns or replies as necessary.
|
||||
class NetworkInterface {
|
||||
private:
|
||||
//! Ethernet (known as hardware, network-access-layer, or link-layer) address of the interface
|
||||
EthernetAddress _ethernet_address;
|
||||
|
||||
//! IP (known as internet-layer or network-layer) address of the interface
|
||||
Address _ip_address;
|
||||
|
||||
//! outbound queue of Ethernet frames that the NetworkInterface wants sent
|
||||
std::queue<EthernetFrame> _frames_out{};
|
||||
|
||||
public:
|
||||
//! \brief Construct a network interface with given Ethernet (network-access-layer) and IP (internet-layer) addresses
|
||||
NetworkInterface(const EthernetAddress ðernet_address, const Address &ip_address);
|
||||
|
||||
//! \brief Access queue of Ethernet frames awaiting transmission
|
||||
std::queue<EthernetFrame> &frames_out() { return _frames_out; }
|
||||
|
||||
//! \brief Sends an IPv4 datagram, encapsulated in an Ethernet frame (if it knows the Ethernet destination address).
|
||||
|
||||
//! Will need to use [ARP](\ref rfc::rfc826) to look up the Ethernet destination address for the next hop
|
||||
//! ("Sending" is accomplished by pushing the frame onto the frames_out queue.)
|
||||
void send_datagram(const InternetDatagram &dgram, const Address &next_hop);
|
||||
|
||||
//! \brief Receives an Ethernet frame and responds appropriately.
|
||||
|
||||
//! If type is IPv4, returns the datagram.
|
||||
//! If type is ARP request, learn a mapping from the "sender" fields, and send an ARP reply.
|
||||
//! If type is ARP reply, learn a mapping from the "sender" fields.
|
||||
std::optional<InternetDatagram> recv_frame(const EthernetFrame &frame);
|
||||
|
||||
//! \brief Called periodically when time elapses
|
||||
void tick(const size_t ms_since_last_tick);
|
||||
};
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_NETWORK_INTERFACE_HH
|
||||
113
libsponge/tap.sh
Executable file
113
libsponge/tap.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
show_usage () {
|
||||
echo "Usage: $0 <start | stop | restart | check> [tapnum ...]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
start_tap () {
|
||||
local TAPNUM="$1" TAPDEV="tap$1" LLADDR="02:B0:1D:FA:CE:"`printf "%02x" $1`
|
||||
ip tuntap add mode tap user "${SUDO_USER}" name "${TAPDEV}"
|
||||
ip link set "${TAPDEV}" address "${LLADDR}"
|
||||
|
||||
ip addr add "${TUN_IP_PREFIX}.${TAPNUM}.1/24" dev "${TAPDEV}"
|
||||
ip link set dev "${TAPDEV}" up
|
||||
ip route change "${TUN_IP_PREFIX}.${TAPNUM}.0/24" dev "${TAPDEV}" rto_min 10ms
|
||||
|
||||
# Apply NAT (masquerading) only to traffic from CS144's network devices
|
||||
iptables -t nat -A PREROUTING -s ${TUN_IP_PREFIX}.${TAPNUM}.0/24 -j CONNMARK --set-mark ${TAPNUM}
|
||||
iptables -t nat -A POSTROUTING -j MASQUERADE -m connmark --mark ${TAPNUM}
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
}
|
||||
|
||||
stop_tap () {
|
||||
local TAPDEV="tap$1"
|
||||
iptables -t nat -D PREROUTING -s ${TUN_IP_PREFIX}.${1}.0/24 -j CONNMARK --set-mark ${1}
|
||||
iptables -t nat -D POSTROUTING -j MASQUERADE -m connmark --mark ${1}
|
||||
ip tuntap del mode tap name "$TAPDEV"
|
||||
}
|
||||
|
||||
start_all () {
|
||||
while [ ! -z "$1" ]; do
|
||||
local INTF="$1"; shift
|
||||
start_tap "$INTF"
|
||||
done
|
||||
}
|
||||
|
||||
stop_all () {
|
||||
while [ ! -z "$1" ]; do
|
||||
local INTF="$1"; shift
|
||||
stop_tap "$INTF"
|
||||
done
|
||||
}
|
||||
|
||||
restart_all() {
|
||||
stop_all "$@"
|
||||
start_all "$@"
|
||||
}
|
||||
|
||||
check_tap () {
|
||||
[ "$#" != 1 ] && { echo "bad params in check_tap"; exit 1; }
|
||||
local TAPDEV="tap${1}"
|
||||
# make sure tap is healthy: device is up, ip_forward is set, and iptables is configured
|
||||
ip link show ${TAPDEV} &>/dev/null || return 1
|
||||
[ "$(cat /proc/sys/net/ipv4/ip_forward)" = "1" ] || return 2
|
||||
}
|
||||
|
||||
check_sudo () {
|
||||
if [ "$SUDO_USER" = "root" ]; then
|
||||
echo "please execute this script as a regular user, not as root"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$SUDO_USER" ]; then
|
||||
# if the user didn't call us with sudo, re-execute
|
||||
exec sudo $0 "$MODE" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# check arguments
|
||||
if [ -z "$1" ] || ([ "$1" != "start" ] && [ "$1" != "stop" ] && [ "$1" != "restart" ] && [ "$1" != "check" ]); then
|
||||
show_usage
|
||||
fi
|
||||
MODE=$1; shift
|
||||
|
||||
# set default argument
|
||||
if [ "$#" = "0" ]; then
|
||||
set -- 10
|
||||
fi
|
||||
|
||||
# execute 'check' before trying to sudo
|
||||
# - like start, but exit successfully if everything is OK
|
||||
if [ "$MODE" = "check" ]; then
|
||||
declare -a INTFS
|
||||
MODE="start"
|
||||
while [ ! -z "$1" ]; do
|
||||
INTF="$1"; shift
|
||||
check_tap ${INTF}
|
||||
RET=$?
|
||||
if [ "$RET" = "0" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$((RET > 1))" = "1" ]; then
|
||||
MODE="restart"
|
||||
fi
|
||||
INTFS+=($INTF)
|
||||
done
|
||||
|
||||
# address only the interfaces that need it
|
||||
set -- "${INTFS[@]}"
|
||||
if [ "$#" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
echo -e "[$0] Bringing up tunnels ${INTFS[@]}:"
|
||||
fi
|
||||
|
||||
# sudo if necessary
|
||||
check_sudo "$@"
|
||||
|
||||
# get configuration
|
||||
. "$(dirname "$0")"/etc/tunconfig
|
||||
|
||||
# start, stop, or restart all intfs
|
||||
eval "${MODE}_all" "$@"
|
||||
88
libsponge/tcp_helpers/arp_message.cc
Normal file
88
libsponge/tcp_helpers/arp_message.cc
Normal file
@ -0,0 +1,88 @@
|
||||
#include "arp_message.hh"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ParseResult ARPMessage::parse(const Buffer buffer) {
|
||||
NetParser p{buffer};
|
||||
|
||||
if (p.buffer().size() < ARPMessage::LENGTH) {
|
||||
return ParseResult::PacketTooShort;
|
||||
}
|
||||
|
||||
hardware_type = p.u16();
|
||||
protocol_type = p.u16();
|
||||
hardware_address_size = p.u8();
|
||||
protocol_address_size = p.u8();
|
||||
opcode = p.u16();
|
||||
|
||||
if (not supported()) {
|
||||
return ParseResult::Unsupported;
|
||||
}
|
||||
|
||||
// read sender addresses (Ethernet and IP)
|
||||
for (auto &byte : sender_ethernet_address) {
|
||||
byte = p.u8();
|
||||
}
|
||||
sender_ip_address = p.u32();
|
||||
|
||||
// read target addresses (Ethernet and IP)
|
||||
for (auto &byte : target_ethernet_address) {
|
||||
byte = p.u8();
|
||||
}
|
||||
target_ip_address = p.u32();
|
||||
|
||||
return p.get_error();
|
||||
}
|
||||
|
||||
bool ARPMessage::supported() const {
|
||||
return hardware_type == TYPE_ETHERNET and protocol_type == EthernetHeader::TYPE_IPv4 and
|
||||
hardware_address_size == sizeof(EthernetHeader::src) and protocol_address_size == sizeof(IPv4Header::src) and
|
||||
((opcode == OPCODE_REQUEST) or (opcode == OPCODE_REPLY));
|
||||
}
|
||||
|
||||
string ARPMessage::serialize() const {
|
||||
if (not supported()) {
|
||||
throw runtime_error(
|
||||
"ARPMessage::serialize(): unsupported field combination (must be Ethernet/IP, and request or reply)");
|
||||
}
|
||||
|
||||
string ret;
|
||||
NetUnparser::u16(ret, hardware_type);
|
||||
NetUnparser::u16(ret, protocol_type);
|
||||
NetUnparser::u8(ret, hardware_address_size);
|
||||
NetUnparser::u8(ret, protocol_address_size);
|
||||
NetUnparser::u16(ret, opcode);
|
||||
|
||||
/* write sender addresses */
|
||||
for (auto &byte : sender_ethernet_address) {
|
||||
NetUnparser::u8(ret, byte);
|
||||
}
|
||||
NetUnparser::u32(ret, sender_ip_address);
|
||||
|
||||
/* write target addresses */
|
||||
for (auto &byte : target_ethernet_address) {
|
||||
NetUnparser::u8(ret, byte);
|
||||
}
|
||||
NetUnparser::u32(ret, target_ip_address);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
string ARPMessage::to_string() const {
|
||||
stringstream ss{};
|
||||
string opcode_str = "(unknown type)";
|
||||
if (opcode == OPCODE_REQUEST) {
|
||||
opcode_str = "REQUEST";
|
||||
}
|
||||
if (opcode == OPCODE_REPLY) {
|
||||
opcode_str = "REPLY";
|
||||
}
|
||||
ss << "opcode=" << opcode_str << ", sender=" << ::to_string(sender_ethernet_address) << "/"
|
||||
<< inet_ntoa({htobe32(sender_ip_address)}) << ", target=" << ::to_string(target_ethernet_address) << "/"
|
||||
<< inet_ntoa({htobe32(target_ip_address)});
|
||||
return ss.str();
|
||||
}
|
||||
47
libsponge/tcp_helpers/arp_message.hh
Normal file
47
libsponge/tcp_helpers/arp_message.hh
Normal file
@ -0,0 +1,47 @@
|
||||
#ifndef SPONGE_LIBSPONGE_ARP_MESSAGE_HH
|
||||
#define SPONGE_LIBSPONGE_ARP_MESSAGE_HH
|
||||
|
||||
#include "ethernet_header.hh"
|
||||
#include "ipv4_header.hh"
|
||||
|
||||
using EthernetAddress = std::array<uint8_t, 6>;
|
||||
|
||||
//! \brief [ARP](\ref rfc::rfc826) message
|
||||
struct ARPMessage {
|
||||
static constexpr size_t LENGTH = 28; //!< ARP message length in bytes
|
||||
static constexpr uint16_t TYPE_ETHERNET = 1; //!< ARP type for Ethernet/Wi-Fi as link-layer protocol
|
||||
static constexpr uint16_t OPCODE_REQUEST = 1;
|
||||
static constexpr uint16_t OPCODE_REPLY = 2;
|
||||
|
||||
//! \name ARPheader fields
|
||||
//!@{
|
||||
uint16_t hardware_type = TYPE_ETHERNET; //!< Type of the link-layer protocol (generally Ethernet/Wi-Fi)
|
||||
uint16_t protocol_type = EthernetHeader::TYPE_IPv4; //!< Type of the Internet-layer protocol (generally IPv4)
|
||||
uint8_t hardware_address_size = sizeof(EthernetHeader::src);
|
||||
uint8_t protocol_address_size = sizeof(IPv4Header::src);
|
||||
uint16_t opcode{}; //!< Request or reply
|
||||
|
||||
EthernetAddress sender_ethernet_address{};
|
||||
uint32_t sender_ip_address{};
|
||||
|
||||
EthernetAddress target_ethernet_address{};
|
||||
uint32_t target_ip_address{};
|
||||
//!@}
|
||||
|
||||
//! Parse the ARP message from a string
|
||||
ParseResult parse(const Buffer buffer);
|
||||
|
||||
//! Serialize the ARP message to a string
|
||||
std::string serialize() const;
|
||||
|
||||
//! Return a string containing the ARP message in human-readable format
|
||||
std::string to_string() const;
|
||||
|
||||
//! Is this type of ARP message supported by the parser?
|
||||
bool supported() const;
|
||||
};
|
||||
|
||||
//! \struct ARPMessage
|
||||
//! This struct can be used to parse an existing ARP message or to create a new one.
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_ETHERNET_HEADER_HH
|
||||
24
libsponge/tcp_helpers/ethernet_frame.cc
Normal file
24
libsponge/tcp_helpers/ethernet_frame.cc
Normal file
@ -0,0 +1,24 @@
|
||||
#include "ethernet_frame.hh"
|
||||
|
||||
#include "parser.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ParseResult EthernetFrame::parse(const Buffer buffer) {
|
||||
NetParser p{buffer};
|
||||
_header.parse(p);
|
||||
_payload = p.buffer();
|
||||
|
||||
return p.get_error();
|
||||
}
|
||||
|
||||
BufferList EthernetFrame::serialize() const {
|
||||
BufferList ret;
|
||||
ret.append(_header.serialize());
|
||||
ret.append(_payload);
|
||||
return ret;
|
||||
}
|
||||
30
libsponge/tcp_helpers/ethernet_frame.hh
Normal file
30
libsponge/tcp_helpers/ethernet_frame.hh
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef SPONGE_LIBSPONGE_ETHERNET_FRAME_HH
|
||||
#define SPONGE_LIBSPONGE_ETHERNET_FRAME_HH
|
||||
|
||||
#include "buffer.hh"
|
||||
#include "ethernet_header.hh"
|
||||
|
||||
//! \brief Ethernet frame
|
||||
class EthernetFrame {
|
||||
private:
|
||||
EthernetHeader _header{};
|
||||
BufferList _payload{};
|
||||
|
||||
public:
|
||||
//! \brief Parse the frame from a string
|
||||
ParseResult parse(const Buffer buffer);
|
||||
|
||||
//! \brief Serialize the frame to a string
|
||||
BufferList serialize() const;
|
||||
|
||||
//! \name Accessors
|
||||
//!@{
|
||||
const EthernetHeader &header() const { return _header; }
|
||||
EthernetHeader &header() { return _header; }
|
||||
|
||||
const BufferList &payload() const { return _payload; }
|
||||
BufferList &payload() { return _payload; }
|
||||
//!@}
|
||||
};
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_ETHERNET_FRAME_HH
|
||||
83
libsponge/tcp_helpers/ethernet_header.cc
Normal file
83
libsponge/tcp_helpers/ethernet_header.cc
Normal file
@ -0,0 +1,83 @@
|
||||
#include "ethernet_header.hh"
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
ParseResult EthernetHeader::parse(NetParser &p) {
|
||||
if (p.buffer().size() < EthernetHeader::LENGTH) {
|
||||
return ParseResult::PacketTooShort;
|
||||
}
|
||||
|
||||
/* read destination address */
|
||||
for (auto &byte : dst) {
|
||||
byte = p.u8();
|
||||
}
|
||||
|
||||
/* read source address */
|
||||
for (auto &byte : src) {
|
||||
byte = p.u8();
|
||||
}
|
||||
|
||||
/* read the frame's type (e.g. IPv4, ARP, or something else) */
|
||||
type = p.u16();
|
||||
|
||||
return p.get_error();
|
||||
}
|
||||
|
||||
string EthernetHeader::serialize() const {
|
||||
string ret;
|
||||
ret.reserve(LENGTH);
|
||||
|
||||
/* write destination address */
|
||||
for (auto &byte : dst) {
|
||||
NetUnparser::u8(ret, byte);
|
||||
}
|
||||
|
||||
/* write source address */
|
||||
for (auto &byte : src) {
|
||||
NetUnparser::u8(ret, byte);
|
||||
}
|
||||
|
||||
/* write the frame's type (e.g. IPv4, ARP or something else) */
|
||||
NetUnparser::u16(ret, type);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! \returns A string with a textual representation of an Ethernet address
|
||||
string to_string(const EthernetAddress address) {
|
||||
stringstream ss{};
|
||||
for (auto it = address.begin(); it != address.end(); it++) {
|
||||
ss.width(2);
|
||||
ss << setfill('0') << hex << int(*it);
|
||||
if (it != address.end() - 1) {
|
||||
ss << ":";
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
//! \returns A string with the header's contents
|
||||
string EthernetHeader::to_string() const {
|
||||
stringstream ss{};
|
||||
ss << "dst=" << ::to_string(dst);
|
||||
ss << ", src=" << ::to_string(src);
|
||||
ss << ", type=";
|
||||
switch (type) {
|
||||
case TYPE_IPv4:
|
||||
ss << "IPv4";
|
||||
break;
|
||||
case TYPE_ARP:
|
||||
ss << "ARP";
|
||||
break;
|
||||
default:
|
||||
ss << "[unknown type " << hex << type << "!]";
|
||||
break;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
43
libsponge/tcp_helpers/ethernet_header.hh
Normal file
43
libsponge/tcp_helpers/ethernet_header.hh
Normal file
@ -0,0 +1,43 @@
|
||||
#ifndef SPONGE_LIBSPONGE_ETHERNET_HEADER_HH
|
||||
#define SPONGE_LIBSPONGE_ETHERNET_HEADER_HH
|
||||
|
||||
#include "parser.hh"
|
||||
|
||||
#include <array>
|
||||
|
||||
//! Helper type for an Ethernet address (an array of six bytes)
|
||||
using EthernetAddress = std::array<uint8_t, 6>;
|
||||
|
||||
//! Ethernet broadcast address (ff:ff:ff:ff:ff:ff)
|
||||
constexpr EthernetAddress ETHERNET_BROADCAST = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
//! Printable representation of an EthernetAddress
|
||||
std::string to_string(const EthernetAddress address);
|
||||
|
||||
//! \brief Ethernet frame header
|
||||
struct EthernetHeader {
|
||||
static constexpr size_t LENGTH = 14; //!< Ethernet header length in bytes
|
||||
static constexpr uint16_t TYPE_IPv4 = 0x800; //!< Type number for [IPv4](\ref rfc::rfc791)
|
||||
static constexpr uint16_t TYPE_ARP = 0x806; //!< Type number for [ARP](\ref rfc::rfc826)
|
||||
|
||||
//! \name Ethernet header fields
|
||||
//!@{
|
||||
EthernetAddress dst;
|
||||
EthernetAddress src;
|
||||
uint16_t type;
|
||||
//!@}
|
||||
|
||||
//! Parse the Ethernet fields from the provided NetParser
|
||||
ParseResult parse(NetParser &p);
|
||||
|
||||
//! Serialize the Ethernet fields to a string
|
||||
std::string serialize() const;
|
||||
|
||||
//! Return a string containing a header in human-readable format
|
||||
std::string to_string() const;
|
||||
};
|
||||
|
||||
//! \struct EthernetHeader
|
||||
//! This struct can be used to parse an existing Ethernet header or to create a new one.
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_ETHERNET_HEADER_HH
|
||||
@ -1,5 +1,6 @@
|
||||
#include "tcp_sponge_socket.hh"
|
||||
|
||||
#include "network_interface.hh"
|
||||
#include "parser.hh"
|
||||
#include "tun.hh"
|
||||
#include "util.hh"
|
||||
@ -211,7 +212,7 @@ void TCPSpongeSocket<AdaptT>::connect(const TCPConfig &c_tcp, const FdAdapterCon
|
||||
|
||||
_datagram_adapter.config_mut() = c_ad;
|
||||
|
||||
cerr << "DEBUG: Connecting to " << c_ad.destination.to_string() << "... ";
|
||||
cerr << "DEBUG: Connecting to " << c_ad.destination.to_string() << "...\n";
|
||||
_tcp->connect();
|
||||
|
||||
const TCPState expected_state = TCPState::State::SYN_SENT;
|
||||
@ -222,7 +223,7 @@ void TCPSpongeSocket<AdaptT>::connect(const TCPConfig &c_tcp, const FdAdapterCon
|
||||
}
|
||||
|
||||
_tcp_loop([&] { return _tcp->state() == TCPState::State::SYN_SENT; });
|
||||
cerr << "done.\n";
|
||||
cerr << "Successfully connected to " << c_ad.destination.to_string() << ".\n";
|
||||
|
||||
_tcp_thread = thread(&TCPSpongeSocket::_tcp_main, this);
|
||||
}
|
||||
@ -240,12 +241,12 @@ void TCPSpongeSocket<AdaptT>::listen_and_accept(const TCPConfig &c_tcp, const Fd
|
||||
_datagram_adapter.config_mut() = c_ad;
|
||||
_datagram_adapter.set_listening(true);
|
||||
|
||||
cerr << "DEBUG: Listening for incoming connection... ";
|
||||
cerr << "DEBUG: Listening for incoming connection...\n";
|
||||
_tcp_loop([&] {
|
||||
const auto s = _tcp->state();
|
||||
return (s == TCPState::State::LISTEN or s == TCPState::State::SYN_RCVD or s == TCPState::State::SYN_SENT);
|
||||
});
|
||||
cerr << "new connection from " << _datagram_adapter.config().destination.to_string() << ".\n";
|
||||
cerr << "New connection from " << _datagram_adapter.config().destination.to_string() << ".\n";
|
||||
|
||||
_tcp_thread = thread(&TCPSpongeSocket::_tcp_main, this);
|
||||
}
|
||||
@ -275,6 +276,9 @@ template class TCPSpongeSocket<TCPOverUDPSocketAdapter>;
|
||||
//! Specialization of TCPSpongeSocket for TCPOverIPv4OverTunFdAdapter
|
||||
template class TCPSpongeSocket<TCPOverIPv4OverTunFdAdapter>;
|
||||
|
||||
//! Specialization of TCPSpongeSocket for TCPOverIPv4OverEthernetAdapter
|
||||
template class TCPSpongeSocket<TCPOverIPv4OverEthernetAdapter>;
|
||||
|
||||
//! Specialization of TCPSpongeSocket for LossyTCPOverUDPSocketAdapter
|
||||
template class TCPSpongeSocket<LossyTCPOverUDPSocketAdapter>;
|
||||
|
||||
@ -293,3 +297,34 @@ void CS144TCPSocket::connect(const Address &address) {
|
||||
|
||||
TCPOverIPv4SpongeSocket::connect(tcp_config, multiplexer_config);
|
||||
}
|
||||
|
||||
static const string LOCAL_TAP_IP_ADDRESS = "169.254.10.9";
|
||||
static const string LOCAL_TAP_NEXT_HOP_ADDRESS = "169.254.10.1";
|
||||
|
||||
EthernetAddress random_private_ethernet_address() {
|
||||
EthernetAddress addr;
|
||||
for (auto &byte : addr) {
|
||||
byte = random_device()(); // use a random local Ethernet address
|
||||
}
|
||||
addr.at(0) |= 0x02; // "10" in last two binary digits marks a private Ethernet address
|
||||
addr.at(0) &= 0xfe;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
FullStackSocket::FullStackSocket()
|
||||
: TCPOverIPv4OverEthernetSpongeSocket(TCPOverIPv4OverEthernetAdapter(TapFD("tap10"),
|
||||
random_private_ethernet_address(),
|
||||
Address(LOCAL_TAP_IP_ADDRESS, "0"),
|
||||
Address(LOCAL_TAP_NEXT_HOP_ADDRESS, "0"))) {}
|
||||
|
||||
void FullStackSocket::connect(const Address &address) {
|
||||
TCPConfig tcp_config;
|
||||
tcp_config.rt_timeout = 100;
|
||||
|
||||
FdAdapterConfig multiplexer_config;
|
||||
multiplexer_config.source = {LOCAL_TAP_IP_ADDRESS, to_string(uint16_t(random_device()()))};
|
||||
multiplexer_config.destination = address;
|
||||
|
||||
TCPOverIPv4OverEthernetSpongeSocket::connect(tcp_config, multiplexer_config);
|
||||
}
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
#include "eventloop.hh"
|
||||
#include "fd_adapter.hh"
|
||||
#include "file_descriptor.hh"
|
||||
#include "network_interface.hh"
|
||||
#include "tcp_config.hh"
|
||||
#include "tcp_connection.hh"
|
||||
#include "tcp_over_ip.hh"
|
||||
#include "tuntap_adapter.hh"
|
||||
|
||||
#include <atomic>
|
||||
@ -23,9 +23,11 @@ class TCPSpongeSocket : public LocalStreamSocket {
|
||||
//! Stream socket for reads and writes between owner and TCP thread
|
||||
LocalStreamSocket _thread_data;
|
||||
|
||||
protected:
|
||||
//! Adapter to underlying datagram socket (e.g., UDP or IP)
|
||||
AdaptT _datagram_adapter;
|
||||
|
||||
private:
|
||||
//! Set up the TCPConnection and the event loop
|
||||
void _initialize_TCP(const TCPConfig &config);
|
||||
|
||||
@ -96,6 +98,8 @@ class TCPSpongeSocket : public LocalStreamSocket {
|
||||
|
||||
using TCPOverUDPSpongeSocket = TCPSpongeSocket<TCPOverUDPSocketAdapter>;
|
||||
using TCPOverIPv4SpongeSocket = TCPSpongeSocket<TCPOverIPv4OverTunFdAdapter>;
|
||||
using TCPOverIPv4OverEthernetSpongeSocket = TCPSpongeSocket<TCPOverIPv4OverEthernetAdapter>;
|
||||
|
||||
using LossyTCPOverUDPSpongeSocket = TCPSpongeSocket<LossyTCPOverUDPSocketAdapter>;
|
||||
using LossyTCPOverIPv4SpongeSocket = TCPSpongeSocket<LossyTCPOverIPv4OverTunFdAdapter>;
|
||||
|
||||
@ -126,4 +130,14 @@ class CS144TCPSocket : public TCPOverIPv4SpongeSocket {
|
||||
void connect(const Address &address);
|
||||
};
|
||||
|
||||
//! Helper class that makes a TCPOverIPv4overEthernetSpongeSocket behave more like a (kernel) TCPSocket
|
||||
class FullStackSocket : public TCPOverIPv4OverEthernetSpongeSocket {
|
||||
public:
|
||||
//! Construct a TCP (stream) socket, using the CS144 TCPConnection object,
|
||||
//! that encapsulates TCP segments in IP datagrams, then encapsulates
|
||||
//! those IP datagrams in Ethernet frames sent to the Ethernet address of the next hop.
|
||||
FullStackSocket();
|
||||
void connect(const Address &address);
|
||||
};
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_TCP_SPONGE_SOCKET_HH
|
||||
|
||||
@ -2,5 +2,58 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
//! \param[in] tap Raw network device that will be owned by the adapter
|
||||
//! \param[in] eth_address Ethernet address (local address) of the adapter
|
||||
//! \param[in] ip_address IP address (local address) of the adapter
|
||||
//! \param[in] next_hop IP address of the next hop (typically a router or default gateway)
|
||||
TCPOverIPv4OverEthernetAdapter::TCPOverIPv4OverEthernetAdapter(TapFD &&tap,
|
||||
const EthernetAddress ð_address,
|
||||
const Address &ip_address,
|
||||
const Address &next_hop)
|
||||
: _tap(move(tap)), _interface(eth_address, ip_address), _next_hop(next_hop) {
|
||||
// Linux seems to ignore the first frame sent on a TAP device, so send a dummy frame to prime the pump :-(
|
||||
EthernetFrame dummy_frame;
|
||||
_tap.write(dummy_frame.serialize());
|
||||
}
|
||||
|
||||
optional<TCPSegment> TCPOverIPv4OverEthernetAdapter::read() {
|
||||
// Read Ethernet frame from the raw device
|
||||
EthernetFrame frame;
|
||||
if (frame.parse(_tap.read()) != ParseResult::NoError) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Give the frame to the NetworkInterface. Get back an Internet datagram if frame was carrying one.
|
||||
optional<InternetDatagram> ip_dgram = _interface.recv_frame(frame);
|
||||
|
||||
// The incoming frame may have caused the NetworkInterface to send a frame.
|
||||
send_pending();
|
||||
|
||||
// Try to interpret IPv4 datagram as TCP
|
||||
if (ip_dgram) {
|
||||
return unwrap_tcp_in_ip(ip_dgram.value());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method
|
||||
void TCPOverIPv4OverEthernetAdapter::tick(const size_t ms_since_last_tick) {
|
||||
_interface.tick(ms_since_last_tick);
|
||||
send_pending();
|
||||
}
|
||||
|
||||
//! \param[in] seg the TCPSegment to send
|
||||
void TCPOverIPv4OverEthernetAdapter::write(TCPSegment &seg) {
|
||||
_interface.send_datagram(wrap_tcp_in_ip(seg), _next_hop);
|
||||
send_pending();
|
||||
}
|
||||
|
||||
void TCPOverIPv4OverEthernetAdapter::send_pending() {
|
||||
while (not _interface.frames_out().empty()) {
|
||||
_tap.write(_interface.frames_out().front().serialize());
|
||||
_interface.frames_out().pop();
|
||||
}
|
||||
}
|
||||
|
||||
//! Specialize LossyFdAdapter to TCPOverIPv4OverTunFdAdapter
|
||||
template class LossyFdAdapter<TCPOverIPv4OverTunFdAdapter>;
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
#ifndef SPONGE_LIBSPONGE_TUNFD_ADAPTER_HH
|
||||
#define SPONGE_LIBSPONGE_TUNFD_ADAPTER_HH
|
||||
|
||||
#include "tcp_over_ip.hh"
|
||||
#include "ethernet_header.hh"
|
||||
#include "network_interface.hh"
|
||||
#include "tun.hh"
|
||||
|
||||
#include <optional>
|
||||
@ -39,4 +40,37 @@ class TCPOverIPv4OverTunFdAdapter : public TCPOverIPv4Adapter {
|
||||
//! Typedef for TCPOverIPv4OverTunFdAdapter
|
||||
using LossyTCPOverIPv4OverTunFdAdapter = LossyFdAdapter<TCPOverIPv4OverTunFdAdapter>;
|
||||
|
||||
//! \brief A FD adapter for IPv4 datagrams read from and written to a TAP device
|
||||
class TCPOverIPv4OverEthernetAdapter : public TCPOverIPv4Adapter {
|
||||
private:
|
||||
TapFD _tap; //!< Raw Ethernet connection
|
||||
|
||||
NetworkInterface _interface; //!< NIC abstraction
|
||||
|
||||
Address _next_hop; //!< IP address of the next hop
|
||||
|
||||
void send_pending(); //!< Sends any pending Ethernet frames
|
||||
|
||||
public:
|
||||
//! Construct from a TapFD
|
||||
explicit TCPOverIPv4OverEthernetAdapter(TapFD &&tap,
|
||||
const EthernetAddress ð_address,
|
||||
const Address &ip_address,
|
||||
const Address &next_hop);
|
||||
//! Attempts to read and parse an Ethernet frame containing an IPv4 datagram that contains a TCP segment
|
||||
std::optional<TCPSegment> read();
|
||||
|
||||
//! Sends a TCP segment (in an IPv4 datagram, in an Ethernet frame).
|
||||
void write(TCPSegment &seg);
|
||||
|
||||
//! Called periodically when time elapses
|
||||
void tick(const size_t ms_since_last_tick);
|
||||
|
||||
//! Access the underlying raw Ethernet connection
|
||||
operator TapFD &() { return _tap; }
|
||||
|
||||
//! Access the underlying raw Ethernet connection
|
||||
operator const TapFD &() const { return _tap; }
|
||||
};
|
||||
|
||||
#endif // SPONGE_LIBSPONGE_TUNFD_ADAPTER_HH
|
||||
|
||||
113
tap.sh
Executable file
113
tap.sh
Executable file
@ -0,0 +1,113 @@
|
||||
#!/bin/bash
|
||||
|
||||
show_usage () {
|
||||
echo "Usage: $0 <start | stop | restart | check> [tapnum ...]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
start_tap () {
|
||||
local TAPNUM="$1" TAPDEV="tap$1" LLADDR="02:B0:1D:FA:CE:"`printf "%02x" $1`
|
||||
ip tuntap add mode tap user "${SUDO_USER}" name "${TAPDEV}"
|
||||
ip link set "${TAPDEV}" address "${LLADDR}"
|
||||
|
||||
ip addr add "${TUN_IP_PREFIX}.${TAPNUM}.1/24" dev "${TAPDEV}"
|
||||
ip link set dev "${TAPDEV}" up
|
||||
ip route change "${TUN_IP_PREFIX}.${TAPNUM}.0/24" dev "${TAPDEV}" rto_min 10ms
|
||||
|
||||
# Apply NAT (masquerading) only to traffic from CS144's network devices
|
||||
iptables -t nat -A PREROUTING -s ${TUN_IP_PREFIX}.${TAPNUM}.0/24 -j CONNMARK --set-mark ${TAPNUM}
|
||||
iptables -t nat -A POSTROUTING -j MASQUERADE -m connmark --mark ${TAPNUM}
|
||||
echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
}
|
||||
|
||||
stop_tap () {
|
||||
local TAPDEV="tap$1"
|
||||
iptables -t nat -D PREROUTING -s ${TUN_IP_PREFIX}.${1}.0/24 -j CONNMARK --set-mark ${1}
|
||||
iptables -t nat -D POSTROUTING -j MASQUERADE -m connmark --mark ${1}
|
||||
ip tuntap del mode tap name "$TAPDEV"
|
||||
}
|
||||
|
||||
start_all () {
|
||||
while [ ! -z "$1" ]; do
|
||||
local INTF="$1"; shift
|
||||
start_tap "$INTF"
|
||||
done
|
||||
}
|
||||
|
||||
stop_all () {
|
||||
while [ ! -z "$1" ]; do
|
||||
local INTF="$1"; shift
|
||||
stop_tap "$INTF"
|
||||
done
|
||||
}
|
||||
|
||||
restart_all() {
|
||||
stop_all "$@"
|
||||
start_all "$@"
|
||||
}
|
||||
|
||||
check_tap () {
|
||||
[ "$#" != 1 ] && { echo "bad params in check_tap"; exit 1; }
|
||||
local TAPDEV="tap${1}"
|
||||
# make sure tap is healthy: device is up, ip_forward is set, and iptables is configured
|
||||
ip link show ${TAPDEV} &>/dev/null || return 1
|
||||
[ "$(cat /proc/sys/net/ipv4/ip_forward)" = "1" ] || return 2
|
||||
}
|
||||
|
||||
check_sudo () {
|
||||
if [ "$SUDO_USER" = "root" ]; then
|
||||
echo "please execute this script as a regular user, not as root"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$SUDO_USER" ]; then
|
||||
# if the user didn't call us with sudo, re-execute
|
||||
exec sudo $0 "$MODE" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# check arguments
|
||||
if [ -z "$1" ] || ([ "$1" != "start" ] && [ "$1" != "stop" ] && [ "$1" != "restart" ] && [ "$1" != "check" ]); then
|
||||
show_usage
|
||||
fi
|
||||
MODE=$1; shift
|
||||
|
||||
# set default argument
|
||||
if [ "$#" = "0" ]; then
|
||||
set -- 10
|
||||
fi
|
||||
|
||||
# execute 'check' before trying to sudo
|
||||
# - like start, but exit successfully if everything is OK
|
||||
if [ "$MODE" = "check" ]; then
|
||||
declare -a INTFS
|
||||
MODE="start"
|
||||
while [ ! -z "$1" ]; do
|
||||
INTF="$1"; shift
|
||||
check_tap ${INTF}
|
||||
RET=$?
|
||||
if [ "$RET" = "0" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$((RET > 1))" = "1" ]; then
|
||||
MODE="restart"
|
||||
fi
|
||||
INTFS+=($INTF)
|
||||
done
|
||||
|
||||
# address only the interfaces that need it
|
||||
set -- "${INTFS[@]}"
|
||||
if [ "$#" = "0" ]; then
|
||||
exit 0
|
||||
fi
|
||||
echo -e "[$0] Bringing up tunnels ${INTFS[@]}:"
|
||||
fi
|
||||
|
||||
# sudo if necessary
|
||||
check_sudo "$@"
|
||||
|
||||
# get configuration
|
||||
. "$(dirname "$0")"/etc/tunconfig
|
||||
|
||||
# start, stop, or restart all intfs
|
||||
eval "${MODE}_all" "$@"
|
||||
@ -1,4 +1,4 @@
|
||||
add_library (spongechecks STATIC send_equivalence_checker.cc tcp_fsm_test_harness.cc byte_stream_test_harness.cc)
|
||||
add_library (spongechecks STATIC send_equivalence_checker.cc tcp_fsm_test_harness.cc byte_stream_test_harness.cc network_interface_test_harness.cc)
|
||||
|
||||
macro (add_test_exec exec_name)
|
||||
add_executable ("${exec_name}" "${exec_name}.cc")
|
||||
@ -50,3 +50,4 @@ add_test_exec (send_ack)
|
||||
add_test_exec (send_window)
|
||||
add_test_exec (send_close)
|
||||
add_test_exec (send_extra)
|
||||
add_test_exec (net_interface)
|
||||
|
||||
29
writeups/lab5.md
Normal file
29
writeups/lab5.md
Normal file
@ -0,0 +1,29 @@
|
||||
Lab 5 Writeup
|
||||
=============
|
||||
|
||||
My name: [your name here]
|
||||
|
||||
My SUNet ID: [your sunetid here]
|
||||
|
||||
I collaborated with: [list sunetids here]
|
||||
|
||||
I would like to thank/reward these classmates for their help: [list sunetids here]
|
||||
|
||||
This lab took me about [n] hours to do. I [did/did not] attend the lab session.
|
||||
|
||||
Program Structure and Design of the NetworkInterface:
|
||||
[]
|
||||
|
||||
Implementation Challenges:
|
||||
[]
|
||||
|
||||
Remaining Bugs:
|
||||
[]
|
||||
|
||||
- Optional: I had unexpected difficulty with: [describe]
|
||||
|
||||
- Optional: I think you could make this lab better by: [describe]
|
||||
|
||||
- Optional: I was surprised by: [describe]
|
||||
|
||||
- Optional: I'm not sure about: [describe]
|
||||
Loading…
Reference in New Issue
Block a user