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

198 lines
6.6 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 <cstdlib>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>
using namespace std;
using State = TCPTestHarness::State;
int main() {
try {
TCPConfig cfg{};
// test #1: start in TIME_WAIT, timeout
{
TCPTestHarness test_1 = TCPTestHarness::in_time_wait(cfg);
test_1.execute(Tick(10 * cfg.rt_timeout - 1));
test_1.execute(ExpectState{State::TIME_WAIT});
test_1.execute(Tick(1));
test_1.execute(ExpectNotInState{State::TIME_WAIT});
test_1.execute(Tick(10 * cfg.rt_timeout));
test_1.execute(ExpectState{State::CLOSED});
}
// test #2: start in CLOSING, send ack, time out
{
TCPTestHarness test_2 = TCPTestHarness::in_closing(cfg);
test_2.execute(Tick(4 * cfg.rt_timeout));
test_2.execute(ExpectOneSegment{}.with_fin(true));
test_2.execute(ExpectState{State::CLOSING});
test_2.send_ack(WrappingInt32{2}, WrappingInt32{2});
test_2.execute(ExpectNoSegment{});
test_2.execute(ExpectState{State::TIME_WAIT});
test_2.execute(Tick(10 * cfg.rt_timeout - 1));
test_2.execute(ExpectState{State::TIME_WAIT});
test_2.execute(Tick(2));
test_2.execute(ExpectState{State::CLOSED});
}
// test #3: start in FIN_WAIT_2, send FIN, time out
{
TCPTestHarness test_3 = TCPTestHarness::in_fin_wait_2(cfg);
test_3.execute(Tick(4 * cfg.rt_timeout));
test_3.execute(ExpectState{State::FIN_WAIT_2});
const WrappingInt32 rx_seqno{1};
test_3.send_fin(rx_seqno, WrappingInt32{2});
const auto ack_expect = rx_seqno + 1;
test_3.execute(Tick(1));
test_3.execute(ExpectOneSegment{}.with_ack(true).with_ackno(ack_expect),
"test 3 failed: wrong ACK for FIN");
test_3.execute(ExpectState{State::TIME_WAIT});
test_3.execute(Tick(10 * cfg.rt_timeout));
test_3.execute(ExpectState{State::CLOSED});
}
// test #4: start in FIN_WAIT_1, ack, FIN, time out
{
TCPTestHarness test_4 = TCPTestHarness::in_fin_wait_1(cfg);
// Expect retransmission of FIN
test_4.execute(Tick(4 * cfg.rt_timeout));
test_4.execute(ExpectOneSegment{}.with_fin(true));
// ACK the FIN
const WrappingInt32 rx_seqno{1};
test_4.send_ack(rx_seqno, WrappingInt32{2});
test_4.execute(Tick(5));
// Send our own FIN
test_4.send_fin(rx_seqno, WrappingInt32{2});
const auto ack_expect = rx_seqno + 1;
test_4.execute(Tick(1));
test_4.execute(ExpectOneSegment{}.with_no_flags().with_ack(true).with_ackno(ack_expect));
test_4.execute(Tick(10 * cfg.rt_timeout));
test_4.execute(ExpectState{State::CLOSED});
}
// test 5: start in FIN_WAIT_1, ack, FIN, FIN again, time out
{
TCPTestHarness test_5 = TCPTestHarness::in_fin_wait_1(cfg);
// ACK the FIN
const WrappingInt32 rx_seqno{1};
test_5.send_ack(rx_seqno, WrappingInt32{2});
test_5.execute(ExpectState{State::FIN_WAIT_2});
test_5.execute(Tick(5));
test_5.send_fin(rx_seqno, WrappingInt32{2});
test_5.execute(ExpectState{State::TIME_WAIT});
test_5.execute(ExpectLingerTimer{0ul});
const auto ack_expect = rx_seqno + 1;
test_5.execute(Tick(1));
test_5.execute(ExpectLingerTimer{1ul});
test_5.execute(ExpectOneSegment{}.with_no_flags().with_ack(true).with_ackno(ack_expect));
test_5.execute(Tick(10 * cfg.rt_timeout - 10));
test_5.execute(ExpectLingerTimer{uint64_t(10 * cfg.rt_timeout - 9)});
test_5.send_fin(rx_seqno, WrappingInt32{2});
test_5.execute(ExpectLingerTimer{0ul});
test_5.execute(Tick(1));
test_5.execute(ExpectOneSegment{}.with_ack(true).with_ackno(ack_expect),
"test 5 failed: no ACK for 2nd FIN");
test_5.execute(ExpectState{State::TIME_WAIT});
// tick the timer and see what happens---a 2nd FIN in TIME_WAIT should reset the wait timer!
// (this is an edge case of "throw it away and send another ack" for out-of-window segs)
test_5.execute(Tick(10 * cfg.rt_timeout - 10));
test_5.execute(ExpectLingerTimer{uint64_t(10 * cfg.rt_timeout - 9)},
"test 5 failed: time_since_last_segment_received() should reset after 2nd FIN");
test_5.execute(ExpectNoSegment{});
test_5.execute(Tick(10));
test_5.execute(ExpectState{State::CLOSED});
}
// test 6: start in ESTABLISHED, get FIN, get FIN re-tx, send FIN, get ACK, send ACK, time out
{
TCPTestHarness test_6 = TCPTestHarness::in_established(cfg);
test_6.execute(Close{});
test_6.execute(Tick(1));
TCPSegment seg1 = test_6.expect_seg(ExpectOneSegment{}.with_fin(true),
"test 6 failed: bad FIN after close()");
auto &seg1_hdr = seg1.header();
test_6.execute(Tick(cfg.rt_timeout - 2));
test_6.execute(ExpectNoSegment{}, "test 6 failed: FIN re-tx was too fast");
test_6.execute(Tick(2));
TCPSegment seg2 = test_6.expect_seg(ExpectOneSegment{}.with_fin(true).with_seqno(seg1_hdr.seqno),
"test 6 failed: bad re-tx FIN");
auto &seg2_hdr = seg2.header();
const WrappingInt32 rx_seqno{1};
test_6.send_fin(rx_seqno, WrappingInt32{0});
const auto ack_expect = rx_seqno + 1;
test_6.execute(Tick(1));
test_6.execute(ExpectState{State::CLOSING});
test_6.execute(ExpectOneSegment{}.with_ack(true).with_ackno(ack_expect), "test 6 failed: bad ACK for FIN");
test_6.send_ack(ack_expect, seg2_hdr.seqno + 1);
test_6.execute(Tick(1));
test_6.execute(ExpectState{State::TIME_WAIT});
test_6.execute(Tick(10 * cfg.rt_timeout));
test_6.execute(ExpectState{State::CLOSED});
}
} catch (const exception &e) {
cerr << e.what() << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}