Skip to content

Commit

Permalink
CS144 Lab 2 assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
keithw committed Sep 28, 2020
1 parent c1471b9 commit 81b077f
Show file tree
Hide file tree
Showing 27 changed files with 2,106 additions and 3 deletions.
8 changes: 5 additions & 3 deletions etc/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ enable_testing ()

set (LOSS_RATE 0.1)

add_test(NAME t_wrapping_ints_cmp COMMAND wrapping_integers_cmp)
add_test(NAME t_wrapping_ints_unwrap COMMAND wrapping_integers_unwrap)
add_test(NAME t_wrapping_ints_wrap COMMAND wrapping_integers_wrap)
add_test(NAME t_wrapping_ints_cmp COMMAND wrapping_integers_cmp)
add_test(NAME t_wrapping_ints_unwrap COMMAND wrapping_integers_unwrap)
add_test(NAME t_wrapping_ints_wrap COMMAND wrapping_integers_wrap)
add_test(NAME t_wrapping_ints_roundtrip COMMAND wrapping_integers_roundtrip)

add_test(NAME t_recv_connect COMMAND recv_connect)
add_test(NAME t_recv_transmit COMMAND recv_transmit)
add_test(NAME t_recv_window COMMAND recv_window)
add_test(NAME t_recv_reorder COMMAND recv_reorder)
add_test(NAME t_recv_close COMMAND recv_close)
add_test(NAME t_recv_special COMMAND recv_special)

add_test(NAME t_send_connect COMMAND send_connect)
add_test(NAME t_send_transmit COMMAND send_transmit)
Expand Down
108 changes: 108 additions & 0 deletions libsponge/tcp_helpers/tcp_header.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "tcp_header.hh"

#include <sstream>

using namespace std;

//! \param[in,out] p is a NetParser from which the TCP fields will be extracted
//! \returns a ParseResult indicating success or the reason for failure
//! \details It is important to check for (at least) the following potential errors
//! (but note that NetParser inherently checks for certain errors;
//! use that fact to your advantage!):
//!
//! - data stream inside the NetParser is too short to contain a header
//! - the header's `doff` field is shorter than the minimum allowed
//! - there is less data in the header than the `doff` field claims
//! - the checksum is bad
ParseResult TCPHeader::parse(NetParser &p) {
sport = p.u16(); // source port
dport = p.u16(); // destination port
seqno = WrappingInt32{p.u32()}; // sequence number
ackno = WrappingInt32{p.u32()}; // ack number
doff = p.u8() >> 4; // data offset

const uint8_t fl_b = p.u8(); // byte including flags
urg = static_cast<bool>(fl_b & 0b0010'0000); // binary literals and ' digit separator since C++14!!!
ack = static_cast<bool>(fl_b & 0b0001'0000);
psh = static_cast<bool>(fl_b & 0b0000'1000);
rst = static_cast<bool>(fl_b & 0b0000'0100);
syn = static_cast<bool>(fl_b & 0b0000'0010);
fin = static_cast<bool>(fl_b & 0b0000'0001);

win = p.u16(); // window size
cksum = p.u16(); // checksum
uptr = p.u16(); // urgent pointer

if (doff < 5) {
return ParseResult::HeaderTooShort;
}

// skip any options or anything extra in the header
p.remove_prefix(doff * 4 - TCPHeader::LENGTH);

if (p.error()) {
return p.get_error();
}

return ParseResult::NoError;
}

//! Serialize the TCPHeader to a string (does not recompute the checksum)
string TCPHeader::serialize() const {
// sanity check
if (doff < 5) {
throw runtime_error("TCP header too short");
}

string ret;
ret.reserve(4 * doff);

NetUnparser::u16(ret, sport); // source port
NetUnparser::u16(ret, dport); // destination port
NetUnparser::u32(ret, seqno.raw_value()); // sequence number
NetUnparser::u32(ret, ackno.raw_value()); // ack number
NetUnparser::u8(ret, doff << 4); // data offset

const uint8_t fl_b = (urg ? 0b0010'0000 : 0) | (ack ? 0b0001'0000 : 0) | (psh ? 0b0000'1000 : 0) |
(rst ? 0b0000'0100 : 0) | (syn ? 0b0000'0010 : 0) | (fin ? 0b0000'0001 : 0);
NetUnparser::u8(ret, fl_b); // flags
NetUnparser::u16(ret, win); // window size

NetUnparser::u16(ret, cksum); // checksum

NetUnparser::u16(ret, uptr); // urgent pointer

ret.resize(4 * doff); // expand header to advertised size

return ret;
}

//! \returns A string with the header's contents
string TCPHeader::to_string() const {
stringstream ss{};
ss << hex << boolalpha << "TCP source port: " << +sport << '\n'
<< "TCP dest port: " << +dport << '\n'
<< "TCP seqno: " << seqno << '\n'
<< "TCP ackno: " << ackno << '\n'
<< "TCP doff: " << +doff << '\n'
<< "Flags: urg: " << urg << " ack: " << ack << " psh: " << psh << " rst: " << rst << " syn: " << syn
<< " fin: " << fin << '\n'
<< "TCP winsize: " << +win << '\n'
<< "TCP cksum: " << +cksum << '\n'
<< "TCP uptr: " << +uptr << '\n';
return ss.str();
}

string TCPHeader::summary() const {
stringstream ss{};
ss << "Header(flags=" << (syn ? "S" : "") << (ack ? "A" : "") << (rst ? "R" : "") << (fin ? "F" : "")
<< ",seqno=" << seqno << ",ack=" << ackno << ",win=" << win << ")";
return ss.str();
}

bool TCPHeader::operator==(const TCPHeader &other) const {
// TODO(aozdemir) more complete check (right now we omit cksum, src, dst
return seqno == other.seqno && ackno == other.ackno && doff == other.doff && urg == other.urg && ack == other.ack &&
psh == other.psh && rst == other.rst && syn == other.syn && fin == other.fin && win == other.win &&
uptr == other.uptr;
}
68 changes: 68 additions & 0 deletions libsponge/tcp_helpers/tcp_header.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef SPONGE_LIBSPONGE_TCP_HEADER_HH
#define SPONGE_LIBSPONGE_TCP_HEADER_HH

#include "parser.hh"
#include "wrapping_integers.hh"

//! \brief [TCP](\ref rfc::rfc793) segment header
//! \note TCP options are not supported
struct TCPHeader {
static constexpr size_t LENGTH = 20; //!< [TCP](\ref rfc::rfc793) header length, not including options

//! \struct TCPHeader
//! ~~~{.txt}
//! 0 1 2 3
//! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Source Port | Destination Port |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Sequence Number |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Acknowledgment Number |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Data | |U|A|P|R|S|F| |
//! | Offset| Reserved |R|C|S|S|Y|I| Window |
//! | | |G|K|H|T|N|N| |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Checksum | Urgent Pointer |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | Options | Padding |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! | data |
//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//! ~~~

//! \name TCP Header fields
//!@{
uint16_t sport = 0; //!< source port
uint16_t dport = 0; //!< destination port
WrappingInt32 seqno{0}; //!< sequence number
WrappingInt32 ackno{0}; //!< ack number
uint8_t doff = LENGTH / 4; //!< data offset
bool urg = false; //!< urgent flag
bool ack = false; //!< ack flag
bool psh = false; //!< push flag
bool rst = false; //!< rst flag
bool syn = false; //!< syn flag
bool fin = false; //!< fin flag
uint16_t win = 0; //!< window size
uint16_t cksum = 0; //!< checksum
uint16_t uptr = 0; //!< urgent pointer
//!@}

//! Parse the TCP fields from the provided NetParser
ParseResult parse(NetParser &p);

//! Serialize the TCP fields
std::string serialize() const;

//! Return a string containing a header in human-readable format
std::string to_string() const;

//! Return a string containing a human-readable summary of the header
std::string summary() const;

bool operator==(const TCPHeader &other) const;
};

#endif // SPONGE_LIBSPONGE_TCP_HEADER_HH
45 changes: 45 additions & 0 deletions libsponge/tcp_helpers/tcp_segment.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include "tcp_segment.hh"

#include "parser.hh"
#include "util.hh"

#include <variant>

using namespace std;

//! \param[in] buffer string/Buffer to be parsed
//! \param[in] datagram_layer_checksum pseudo-checksum from the lower-layer protocol
ParseResult TCPSegment::parse(const Buffer buffer, const uint32_t datagram_layer_checksum) {
InternetChecksum check(datagram_layer_checksum);
check.add(buffer);
if (check.value()) {
return ParseResult::BadChecksum;
}

NetParser p{buffer};
_header.parse(p);
_payload = p.buffer();
return p.get_error();
}

size_t TCPSegment::length_in_sequence_space() const {
return payload().str().size() + (header().syn ? 1 : 0) + (header().fin ? 1 : 0);
}

//! \param[in] datagram_layer_checksum pseudo-checksum from the lower-layer protocol
BufferList TCPSegment::serialize(const uint32_t datagram_layer_checksum) const {
TCPHeader header_out = _header;
header_out.cksum = 0;

// calculate checksum -- taken over entire segment
InternetChecksum check(datagram_layer_checksum);
check.add(header_out.serialize());
check.add(_payload);
header_out.cksum = check.value();

BufferList ret;
ret.append(header_out.serialize());
ret.append(_payload);

return ret;
}
36 changes: 36 additions & 0 deletions libsponge/tcp_helpers/tcp_segment.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#ifndef SPONGE_LIBSPONGE_TCP_SEGMENT_HH
#define SPONGE_LIBSPONGE_TCP_SEGMENT_HH

#include "buffer.hh"
#include "tcp_header.hh"

#include <cstdint>

//! \brief [TCP](\ref rfc::rfc793) segment
class TCPSegment {
private:
TCPHeader _header{};
Buffer _payload{};

public:
//! \brief Parse the segment from a string
ParseResult parse(const Buffer buffer, const uint32_t datagram_layer_checksum = 0);

//! \brief Serialize the segment to a string
BufferList serialize(const uint32_t datagram_layer_checksum = 0) const;

//! \name Accessors
//!@{
const TCPHeader &header() const { return _header; }
TCPHeader &header() { return _header; }

const Buffer &payload() const { return _payload; }
Buffer &payload() { return _payload; }
//!@}

//! \brief Segment's length in sequence space
//! \note Equal to payload length plus one byte if SYN is set, plus one byte if FIN is set
size_t length_in_sequence_space() const;
};

#endif // SPONGE_LIBSPONGE_TCP_SEGMENT_HH
15 changes: 15 additions & 0 deletions libsponge/tcp_helpers/tcp_state.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "tcp_state.hh"

using namespace std;

string TCPState::state_summary(const TCPReceiver &receiver) {
if (receiver.stream_out().error()) {
return TCPReceiverStateSummary::ERROR;
} else if (not receiver.ackno().has_value()) {
return TCPReceiverStateSummary::LISTEN;
} else if (receiver.stream_out().input_ended()) {
return TCPReceiverStateSummary::FIN_RECV;
} else {
return TCPReceiverStateSummary::SYN_RECV;
}
}
35 changes: 35 additions & 0 deletions libsponge/tcp_helpers/tcp_state.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#ifndef SPONGE_LIBSPONGE_TCP_STATE
#define SPONGE_LIBSPONGE_TCP_STATE

#include "tcp_receiver.hh"

#include <string>

//! \brief Summary of a TCPConnection's internal state
//!
//! Most TCP implementations have a global per-connection state
//! machine, as described in the [TCP](\ref rfc::rfc793)
//! specification. Sponge is a bit different: we have factored the
//! connection into two independent parts (the sender and the
//! receiver). The TCPSender and TCPReceiver maintain their interval
//! state variables independently (e.g. next_seqno, number of bytes in
//! flight, or whether each stream has ended). There is no notion of a
//! discrete state machine or much overarching state outside the
//! sender and receiver. To test that Sponge follows the TCP spec, we
//! use this class to compare the "official" states with Sponge's
//! sender/receiver states and two variables that belong to the
//! overarching TCPConnection object.
class TCPState {
public:
//! \brief Summarize the state of a TCPReceiver in a string
static std::string state_summary(const TCPReceiver &receiver);
};

namespace TCPReceiverStateSummary {
const std::string ERROR = "error (connection was reset)";
const std::string LISTEN = "waiting for SYN: ackno is empty";
const std::string SYN_RECV = "SYN received (ackno exists), and input to stream hasn't ended";
const std::string FIN_RECV = "input to stream has ended";
} // namespace TCPReceiverStateSummary

#endif // SPONGE_LIBSPONGE_TCP_STATE
19 changes: 19 additions & 0 deletions libsponge/tcp_receiver.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include "tcp_receiver.hh"

// Dummy implementation of a TCP receiver

// For Lab 2, please replace with a real implementation that passes the
// automated checks run by `make check_lab2`.

template <typename... Targs>
void DUMMY_CODE(Targs &&... /* unused */) {}

using namespace std;

void TCPReceiver::segment_received(const TCPSegment &seg) {
DUMMY_CODE(seg);
}

optional<WrappingInt32> TCPReceiver::ackno() const { return {}; }

size_t TCPReceiver::window_size() const { return {}; }
Loading

0 comments on commit 81b077f

Please sign in to comment.