CS144Lab/tests/fsm_ack_rst_relaxed.cc
2021-09-28 17:03:04 -07:00

151 lines
5.8 KiB
C++

#include "tcp_config.hh"
#include "tcp_expectation.hh"
#include "tcp_fsm_test_harness.hh"
#include "tcp_header.hh"
#include "tcp_segment.hh"
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <iostream>
using namespace std;
using State = TCPTestHarness::State;
// in LISTEN, send ACK
static void ack_listen_test(const TCPConfig &cfg,
const WrappingInt32 seqno,
const WrappingInt32 ackno,
const int lineno) {
try {
TCPTestHarness test = TCPTestHarness::in_listen(cfg);
// any ACK should result in a RST
test.send_ack(seqno, ackno);
test.execute(ExpectState{State::LISTEN});
test.execute(ExpectNoSegment{}, "test 3 failed: ACKs in LISTEN should be ignored");
} catch (const exception &e) {
throw runtime_error(string(e.what()) + " (ack_listen_test called from line " + to_string(lineno) + ")");
}
}
// in SYN_SENT, send ACK and maybe RST
static void ack_rst_syn_sent_test(const TCPConfig &cfg,
const WrappingInt32 base_seq,
const WrappingInt32 seqno,
const WrappingInt32 ackno,
const int lineno) {
try {
TCPTestHarness test = TCPTestHarness::in_syn_sent(cfg, base_seq);
// unacceptable ACKs should be ignored
test.send_ack(seqno, ackno);
test.execute(ExpectState{State::SYN_SENT});
test.execute(ExpectNoSegment{}, "test 3 failed: bad ACKs in SYN_SENT should be ignored");
} catch (const exception &e) {
throw runtime_error(string(e.what()) + " (ack_rst_syn_sent_test called from line " + to_string(lineno) + ")");
}
}
int main() {
try {
TCPConfig cfg{};
const WrappingInt32 base_seq(1 << 31);
// test #1: in ESTABLISHED, send unacceptable segments and ACKs
{
cerr << "Test 1" << endl;
TCPTestHarness test_1 = TCPTestHarness::in_established(cfg, base_seq - 1, base_seq - 1);
// acceptable ack---no response
test_1.send_ack(base_seq, base_seq);
test_1.execute(ExpectNoSegment{}, "test 1 failed: ACK after acceptable ACK");
// ack in the past---no response
test_1.send_ack(base_seq, base_seq - 1);
test_1.execute(ExpectNoSegment{}, "test 1 failed: ACK after past ACK");
/* remove requirement for corrective ACKs
// ack in the future---should get ACK back
test_1.send_ack(base_seq, base_seq + 1);
test_1.execute(ExpectOneSegment{}.with_ack(true).with_ackno(base_seq), "test 1 failed: bad ACK");
*/
// segment out of the window---should get an ACK
test_1.send_byte(base_seq - 1, base_seq, 1);
test_1.execute(ExpectUnassembledBytes{0}, "test 1 failed: seg queued on early seqno");
test_1.execute(ExpectOneSegment{}.with_ack(true).with_ackno(base_seq), "test 1 failed: bad ACK");
// segment out of the window---should get an ACK
test_1.send_byte(base_seq + cfg.recv_capacity, base_seq, 1);
test_1.execute(ExpectUnassembledBytes{0}, "test 1 failed: seg queued on late seqno");
test_1.execute(ExpectOneSegment{}.with_ack(true).with_ackno(base_seq),
"test 1 failed: bad ACK on late seqno");
// packet next byte in the window - ack should advance and data should be readable
test_1.send_byte(base_seq, base_seq, 1);
test_1.execute(ExpectData{}, "test 1 failed: pkt not processed on next seqno");
test_1.execute(ExpectOneSegment{}.with_ack(true).with_ackno(base_seq + 1), "test 1 failed: bad ACK");
test_1.send_rst(base_seq + 1);
test_1.execute(ExpectState{State::RESET});
}
// test #2: in LISTEN, send RSTs
{
cerr << "Test 2" << endl;
TCPTestHarness test_2 = TCPTestHarness::in_listen(cfg);
// all RSTs should be ignored in LISTEN
test_2.send_rst(base_seq);
test_2.send_rst(base_seq - 1);
test_2.send_rst(base_seq + cfg.recv_capacity);
test_2.execute(ExpectNoSegment{}, "test 2 failed: RST was not ignored in LISTEN");
}
// test 3: ACKs in LISTEN
cerr << "Test 3" << endl;
ack_listen_test(cfg, base_seq, base_seq, __LINE__);
ack_listen_test(cfg, base_seq - 1, base_seq, __LINE__);
ack_listen_test(cfg, base_seq, base_seq - 1, __LINE__);
ack_listen_test(cfg, base_seq - 1, base_seq, __LINE__);
ack_listen_test(cfg, base_seq - 1, base_seq - 1, __LINE__);
ack_listen_test(cfg, base_seq + cfg.recv_capacity, base_seq, __LINE__);
ack_listen_test(cfg, base_seq, base_seq + cfg.recv_capacity, __LINE__);
ack_listen_test(cfg, base_seq + cfg.recv_capacity, base_seq, __LINE__);
ack_listen_test(cfg, base_seq + cfg.recv_capacity, base_seq + cfg.recv_capacity, __LINE__);
// test 4: ACK and RST in SYN_SENT
{
cerr << "Test 4" << endl;
TCPTestHarness test_4 = TCPTestHarness::in_syn_sent(cfg, base_seq);
// good ACK with RST should result in a RESET but no RST segment sent
test_4.send_rst(base_seq, base_seq + 1);
test_4.execute(ExpectState{State::RESET});
test_4.execute(ExpectNoSegment{}, "test 4 failed: RST with good ackno should RESET the connection");
}
// test 5: ack/rst in SYN_SENT
cerr << "Test 5" << endl;
ack_rst_syn_sent_test(cfg, base_seq, base_seq, base_seq, __LINE__);
ack_rst_syn_sent_test(cfg, base_seq, base_seq, base_seq + 2, __LINE__);
} catch (const exception &e) {
cerr << e.what() << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}