CS144 Lab checkpoint 2 starter code
This commit is contained in:
parent
e36cd24d34
commit
87babd9d62
108
libsponge/tcp_helpers/tcp_header.cc
Normal file
108
libsponge/tcp_helpers/tcp_header.cc
Normal file
@ -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
libsponge/tcp_helpers/tcp_header.hh
Normal file
68
libsponge/tcp_helpers/tcp_header.hh
Normal file
@ -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
libsponge/tcp_helpers/tcp_segment.cc
Normal file
45
libsponge/tcp_helpers/tcp_segment.cc
Normal file
@ -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
libsponge/tcp_helpers/tcp_segment.hh
Normal file
36
libsponge/tcp_helpers/tcp_segment.hh
Normal file
@ -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
libsponge/tcp_helpers/tcp_state.cc
Normal file
15
libsponge/tcp_helpers/tcp_state.cc
Normal file
@ -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
libsponge/tcp_helpers/tcp_state.hh
Normal file
35
libsponge/tcp_helpers/tcp_state.hh
Normal file
@ -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
libsponge/tcp_receiver.cc
Normal file
19
libsponge/tcp_receiver.cc
Normal file
@ -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 {}; }
|
||||||
66
libsponge/tcp_receiver.hh
Normal file
66
libsponge/tcp_receiver.hh
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#ifndef SPONGE_LIBSPONGE_TCP_RECEIVER_HH
|
||||||
|
#define SPONGE_LIBSPONGE_TCP_RECEIVER_HH
|
||||||
|
|
||||||
|
#include "byte_stream.hh"
|
||||||
|
#include "stream_reassembler.hh"
|
||||||
|
#include "tcp_segment.hh"
|
||||||
|
#include "wrapping_integers.hh"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
//! \brief The "receiver" part of a TCP implementation.
|
||||||
|
|
||||||
|
//! Receives and reassembles segments into a ByteStream, and computes
|
||||||
|
//! the acknowledgment number and window size to advertise back to the
|
||||||
|
//! remote TCPSender.
|
||||||
|
class TCPReceiver {
|
||||||
|
//! Our data structure for re-assembling bytes.
|
||||||
|
StreamReassembler _reassembler;
|
||||||
|
|
||||||
|
//! The maximum number of bytes we'll store.
|
||||||
|
size_t _capacity;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! \brief Construct a TCP receiver
|
||||||
|
//!
|
||||||
|
//! \param capacity the maximum number of bytes that the receiver will
|
||||||
|
//! store in its buffers at any give time.
|
||||||
|
TCPReceiver(const size_t capacity) : _reassembler(capacity), _capacity(capacity) {}
|
||||||
|
|
||||||
|
//! \name Accessors to provide feedback to the remote TCPSender
|
||||||
|
//!@{
|
||||||
|
|
||||||
|
//! \brief The ackno that should be sent to the peer
|
||||||
|
//! \returns empty if no SYN has been received
|
||||||
|
//!
|
||||||
|
//! This is the beginning of the receiver's window, or in other words, the sequence number
|
||||||
|
//! of the first byte in the stream that the receiver hasn't received.
|
||||||
|
std::optional<WrappingInt32> ackno() const;
|
||||||
|
|
||||||
|
//! \brief The window size that should be sent to the peer
|
||||||
|
//!
|
||||||
|
//! Operationally: the capacity minus the number of bytes that the
|
||||||
|
//! TCPReceiver is holding in its byte stream (those that have been
|
||||||
|
//! reassembled, but not consumed).
|
||||||
|
//!
|
||||||
|
//! Formally: the difference between (a) the sequence number of
|
||||||
|
//! the first byte that falls after the window (and will not be
|
||||||
|
//! accepted by the receiver) and (b) the sequence number of the
|
||||||
|
//! beginning of the window (the ackno).
|
||||||
|
size_t window_size() const;
|
||||||
|
//!@}
|
||||||
|
|
||||||
|
//! \brief number of bytes stored but not yet reassembled
|
||||||
|
size_t unassembled_bytes() const { return _reassembler.unassembled_bytes(); }
|
||||||
|
|
||||||
|
//! \brief handle an inbound segment
|
||||||
|
void segment_received(const TCPSegment &seg);
|
||||||
|
|
||||||
|
//! \name "Output" interface for the reader
|
||||||
|
//!@{
|
||||||
|
ByteStream &stream_out() { return _reassembler.stream_out(); }
|
||||||
|
const ByteStream &stream_out() const { return _reassembler.stream_out(); }
|
||||||
|
//!@}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SPONGE_LIBSPONGE_TCP_RECEIVER_HH
|
||||||
34
libsponge/wrapping_integers.cc
Normal file
34
libsponge/wrapping_integers.cc
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "wrapping_integers.hh"
|
||||||
|
|
||||||
|
// Dummy implementation of a 32-bit wrapping integer
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
//! Transform an "absolute" 64-bit sequence number (zero-indexed) into a WrappingInt32
|
||||||
|
//! \param n The input absolute 64-bit sequence number
|
||||||
|
//! \param isn The initial sequence number
|
||||||
|
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn) {
|
||||||
|
DUMMY_CODE(n, isn);
|
||||||
|
return WrappingInt32{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Transform a WrappingInt32 into an "absolute" 64-bit sequence number (zero-indexed)
|
||||||
|
//! \param n The relative sequence number
|
||||||
|
//! \param isn The initial sequence number
|
||||||
|
//! \param checkpoint A recent absolute 64-bit sequence number
|
||||||
|
//! \returns the 64-bit sequence number that wraps to `n` and is closest to `checkpoint`
|
||||||
|
//!
|
||||||
|
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
|
||||||
|
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
|
||||||
|
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
|
||||||
|
//! has a different ISN.
|
||||||
|
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint) {
|
||||||
|
DUMMY_CODE(n, isn, checkpoint);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
65
libsponge/wrapping_integers.hh
Normal file
65
libsponge/wrapping_integers.hh
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH
|
||||||
|
#define SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
//! \brief A 32-bit integer, expressed relative to an arbitrary initial sequence number (ISN)
|
||||||
|
//! \note This is used to express TCP sequence numbers (seqno) and acknowledgment numbers (ackno)
|
||||||
|
class WrappingInt32 {
|
||||||
|
private:
|
||||||
|
uint32_t _raw_value; //!< The raw 32-bit stored integer
|
||||||
|
|
||||||
|
public:
|
||||||
|
//! Construct from a raw 32-bit unsigned integer
|
||||||
|
explicit WrappingInt32(uint32_t raw_value) : _raw_value(raw_value) {}
|
||||||
|
|
||||||
|
uint32_t raw_value() const { return _raw_value; } //!< Access raw stored value
|
||||||
|
};
|
||||||
|
|
||||||
|
//! Transform a 64-bit absolute sequence number (zero-indexed) into a 32-bit relative sequence number
|
||||||
|
//! \param n the absolute sequence number
|
||||||
|
//! \param isn the initial sequence number
|
||||||
|
//! \returns the relative sequence number
|
||||||
|
WrappingInt32 wrap(uint64_t n, WrappingInt32 isn);
|
||||||
|
|
||||||
|
//! Transform a 32-bit relative sequence number into a 64-bit absolute sequence number (zero-indexed)
|
||||||
|
//! \param n The relative sequence number
|
||||||
|
//! \param isn The initial sequence number
|
||||||
|
//! \param checkpoint A recent absolute sequence number
|
||||||
|
//! \returns the absolute sequence number that wraps to `n` and is closest to `checkpoint`
|
||||||
|
//!
|
||||||
|
//! \note Each of the two streams of the TCP connection has its own ISN. One stream
|
||||||
|
//! runs from the local TCPSender to the remote TCPReceiver and has one ISN,
|
||||||
|
//! and the other stream runs from the remote TCPSender to the local TCPReceiver and
|
||||||
|
//! has a different ISN.
|
||||||
|
uint64_t unwrap(WrappingInt32 n, WrappingInt32 isn, uint64_t checkpoint);
|
||||||
|
|
||||||
|
//! \name Helper functions
|
||||||
|
//!@{
|
||||||
|
|
||||||
|
//! \brief The offset of `a` relative to `b`
|
||||||
|
//! \param b the starting point
|
||||||
|
//! \param a the ending point
|
||||||
|
//! \returns the number of increments needed to get from `b` to `a`,
|
||||||
|
//! negative if the number of decrements needed is less than or equal to
|
||||||
|
//! the number of increments
|
||||||
|
inline int32_t operator-(WrappingInt32 a, WrappingInt32 b) { return a.raw_value() - b.raw_value(); }
|
||||||
|
|
||||||
|
//! \brief Whether the two integers are equal.
|
||||||
|
inline bool operator==(WrappingInt32 a, WrappingInt32 b) { return a.raw_value() == b.raw_value(); }
|
||||||
|
|
||||||
|
//! \brief Whether the two integers are not equal.
|
||||||
|
inline bool operator!=(WrappingInt32 a, WrappingInt32 b) { return !(a == b); }
|
||||||
|
|
||||||
|
//! \brief Serializes the wrapping integer, `a`.
|
||||||
|
inline std::ostream &operator<<(std::ostream &os, WrappingInt32 a) { return os << a.raw_value(); }
|
||||||
|
|
||||||
|
//! \brief The point `b` steps past `a`.
|
||||||
|
inline WrappingInt32 operator+(WrappingInt32 a, uint32_t b) { return WrappingInt32{a.raw_value() + b}; }
|
||||||
|
|
||||||
|
//! \brief The point `b` steps before `a`.
|
||||||
|
inline WrappingInt32 operator-(WrappingInt32 a, uint32_t b) { return a + -b; }
|
||||||
|
//!@}
|
||||||
|
|
||||||
|
#endif // SPONGE_LIBSPONGE_WRAPPING_INTEGERS_HH
|
||||||
@ -6,6 +6,7 @@ macro (add_test_exec exec_name)
|
|||||||
target_link_libraries ("${exec_name}" sponge ${ARGN})
|
target_link_libraries ("${exec_name}" sponge ${ARGN})
|
||||||
endmacro (add_test_exec)
|
endmacro (add_test_exec)
|
||||||
|
|
||||||
|
add_test_exec (tcp_parser ${LIBPCAP})
|
||||||
add_test_exec (fsm_stream_reassembler_single)
|
add_test_exec (fsm_stream_reassembler_single)
|
||||||
add_test_exec (fsm_stream_reassembler_seq)
|
add_test_exec (fsm_stream_reassembler_seq)
|
||||||
add_test_exec (fsm_stream_reassembler_dup)
|
add_test_exec (fsm_stream_reassembler_dup)
|
||||||
@ -19,3 +20,13 @@ add_test_exec (byte_stream_one_write)
|
|||||||
add_test_exec (byte_stream_two_writes)
|
add_test_exec (byte_stream_two_writes)
|
||||||
add_test_exec (byte_stream_capacity)
|
add_test_exec (byte_stream_capacity)
|
||||||
add_test_exec (byte_stream_many_writes)
|
add_test_exec (byte_stream_many_writes)
|
||||||
|
add_test_exec (wrapping_integers_cmp)
|
||||||
|
add_test_exec (wrapping_integers_unwrap)
|
||||||
|
add_test_exec (wrapping_integers_wrap)
|
||||||
|
add_test_exec (wrapping_integers_roundtrip)
|
||||||
|
add_test_exec (recv_connect)
|
||||||
|
add_test_exec (recv_transmit)
|
||||||
|
add_test_exec (recv_window)
|
||||||
|
add_test_exec (recv_reorder)
|
||||||
|
add_test_exec (recv_close)
|
||||||
|
add_test_exec (recv_special)
|
||||||
|
|||||||
29
writeups/lab2.md
Normal file
29
writeups/lab2.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Lab 2 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 TCPReceiver and wrap/unwrap routines:
|
||||||
|
[]
|
||||||
|
|
||||||
|
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