use crate::api::ErrorKind;
use crate::api::SocketEvent;
use crate::api::SocketTime;
use crate::packet::abort_chunk::AbortChunk;
use crate::packet::chunk::Chunk;
use crate::packet::error_causes::ErrorCause;
use crate::packet::sctp_packet::CommonHeader;
use crate::packet::sctp_packet::SctpPacketBuilder;
use crate::packet::shutdown_ack_chunk::ShutdownAckChunk;
use crate::packet::shutdown_chunk::ShutdownChunk;
use crate::packet::shutdown_complete_chunk::ShutdownCompleteChunk;
use crate::packet::user_initiated_abort_error_cause::UserInitiatedAbortErrorCause;
use crate::socket::context::Context;
use crate::socket::state::CookieEchoState;
use crate::socket::state::ShutdownState;
use crate::socket::state::State;
use crate::socket::transmission_control_block::TransmissionControlBlock;
use crate::timer::BackoffAlgorithm;
use crate::timer::Timer;
use crate::transition_between;
pub(crate) fn do_shutdown(state: &mut State, ctx: &mut Context, now: SocketTime) {
match state {
State::Closed
| State::ShutdownPending(_)
| State::ShutdownSent(_)
| State::ShutdownAckSent(_)
| State::ShutdownReceived(_) => {
}
State::CookieWait(_) => {
ctx.internal_close(state, ErrorKind::NoError, "".to_string());
}
State::CookieEchoed(_) | State::Established(_) => {
transition_between!(*state,
State::CookieEchoed(CookieEchoState { tcb, .. }) | State::Established(tcb) =>
State::ShutdownPending(tcb)
);
maybe_send_shutdown(state, ctx, now);
}
}
}
pub(crate) fn handle_shutdown(state: &mut State, ctx: &mut Context, now: SocketTime) {
match state {
State::Closed
| State::ShutdownReceived(_)
| State::CookieWait(_)
| State::CookieEchoed(_) => {
}
State::ShutdownAckSent(s) => {
s.t2_shutdown.set_duration(s.tcb.rto.rto());
s.t2_shutdown.start(now);
send_shutdown_ack(&s.tcb, ctx);
}
State::Established(_) | State::ShutdownPending(_) => {
transition_between!(*state,
State::Established(tcb), State::ShutdownPending(tcb) =>
State::ShutdownReceived(tcb)
);
maybe_send_shutdown_ack(state, ctx, now);
}
State::ShutdownSent(s) => {
let rto = s.tcb.rto.rto();
let mut t2_shutdown = Timer::new(
rto,
BackoffAlgorithm::Exponential,
ctx.options.max_retransmissions,
None,
);
t2_shutdown.start(now);
transition_between!(*state,
State::ShutdownSent(ShutdownState { tcb, .. }) =>
State::ShutdownAckSent(ShutdownState { tcb, t2_shutdown })
);
let State::ShutdownAckSent(s) = state else { unreachable!() };
send_shutdown_ack(&s.tcb, ctx);
}
}
}
pub(crate) fn handle_shutdown_ack(state: &mut State, ctx: &mut Context, header: &CommonHeader) {
match &state {
State::ShutdownSent(ShutdownState { tcb, .. })
| State::ShutdownAckSent(ShutdownState { tcb, .. }) => {
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet()
.add(&Chunk::ShutdownComplete(ShutdownCompleteChunk { tag_reflected: false }))
.build(),
));
ctx.tx_packets_count += 1;
ctx.internal_close(state, ErrorKind::NoError, "".to_string());
}
_ => {
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
SctpPacketBuilder::new(
header.verification_tag,
ctx.options.local_port,
ctx.options.remote_port,
ctx.options.mtu,
)
.add(&Chunk::ShutdownComplete(ShutdownCompleteChunk { tag_reflected: true }))
.build(),
));
ctx.tx_packets_count += 1;
}
}
}
pub(crate) fn handle_shutdown_complete(
state: &mut State,
ctx: &mut Context,
_chunk: ShutdownCompleteChunk,
) {
if let State::ShutdownAckSent(_) = state {
ctx.internal_close(state, ErrorKind::NoError, "".to_string());
}
}
pub(crate) fn handle_t2_shutdown_timeout(
state: &mut State,
ctx: &mut Context,
now: SocketTime,
) -> bool {
let (expired, running) = match state {
State::ShutdownSent(s) | State::ShutdownAckSent(s) => {
(s.t2_shutdown.expire(now), s.t2_shutdown.is_running())
}
_ => return false,
};
if !expired {
return false;
}
if running {
match state {
State::ShutdownSent(s) => send_shutdown(&s.tcb, ctx),
State::ShutdownAckSent(s) => send_shutdown_ack(&s.tcb, ctx),
_ => unreachable!(),
}
return true;
}
let tcb = match state {
State::ShutdownSent(s) | State::ShutdownAckSent(s) => &s.tcb,
_ => unreachable!(),
};
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet()
.add(&Chunk::Abort(AbortChunk {
error_causes: vec![ErrorCause::UserInitiatedAbort(UserInitiatedAbortErrorCause {
reason: "Too many retransmissions".into(),
})],
}))
.build(),
));
ctx.tx_packets_count += 1;
ctx.internal_close(state, ErrorKind::TooManyRetries, "Too many retransmissions".into());
true
}
pub(crate) fn maybe_send_shutdown_on_packet_received(
state: &mut State,
ctx: &mut Context,
now: SocketTime,
chunks: &[Chunk],
) {
if let State::ShutdownSent(s) = state {
if chunks.iter().any(|c| matches!(c, Chunk::Data(_))) {
s.t2_shutdown.set_duration(s.tcb.rto.rto());
s.t2_shutdown.start(now);
send_shutdown(&s.tcb, ctx);
}
}
}
pub(crate) fn maybe_send_shutdown(state: &mut State, ctx: &mut Context, now: SocketTime) {
let State::ShutdownPending(tcb) = state else { unreachable!() };
if tcb.retransmission_queue.unacked_bytes() != 0 {
return;
}
let mut t2_shutdown = Timer::new(
tcb.rto.rto(),
BackoffAlgorithm::Exponential,
ctx.options.max_retransmissions,
None,
);
t2_shutdown.start(now);
transition_between!(*state,
State::ShutdownPending(tcb) =>
State::ShutdownSent(ShutdownState { tcb, t2_shutdown })
);
let State::ShutdownSent(s) = state else { unreachable!() };
send_shutdown(&s.tcb, ctx);
}
pub(crate) fn maybe_send_shutdown_ack(state: &mut State, ctx: &mut Context, now: SocketTime) {
let State::ShutdownReceived(tcb) = state else { unreachable!() };
if tcb.retransmission_queue.unacked_bytes() != 0 {
return;
}
let mut t2_shutdown = Timer::new(
tcb.rto.rto(),
BackoffAlgorithm::Exponential,
ctx.options.max_retransmissions,
None,
);
t2_shutdown.start(now);
transition_between!(*state,
State::ShutdownReceived(tcb) => State::ShutdownAckSent(ShutdownState { tcb, t2_shutdown })
);
let State::ShutdownAckSent(s) = state else { unreachable!() };
send_shutdown_ack(&s.tcb, ctx);
}
pub(crate) fn send_shutdown(tcb: &TransmissionControlBlock, ctx: &mut Context) {
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet()
.add(&Chunk::Shutdown(ShutdownChunk {
cumulative_tsn_ack: tcb.data_tracker.last_cumulative_acked_tsn(),
}))
.build(),
));
ctx.tx_packets_count += 1;
}
pub(crate) fn send_shutdown_ack(tcb: &TransmissionControlBlock, ctx: &mut Context) {
ctx.events.borrow_mut().add(SocketEvent::SendPacket(
tcb.new_packet().add(&Chunk::ShutdownAck(ShutdownAckChunk {})).build(),
));
ctx.tx_packets_count += 1;
}