Close without losing the final acknowledgment
Implement FIN sequence ownership, simultaneous close, and TIME_WAIT.
Jump to code and terminal ↓End of stream is sequenced
A FIN consumes one sequence position just like a SYN. Only initiate close from ESTABLISHED once pending data is acknowledged. Save a FIN|ACK in the retry buffer, emit it at snd_nxt, increment snd_nxt, and enter FIN_WAIT1.
Two acknowledgments, either order
An ACK of your FIN clears its timer and moves FIN_WAIT1 to FIN_WAIT2. A peer FIN is acceptable only with no payload and seq==rcv_nxt; advance rcv_nxt and ACK it. If your FIN is already acknowledged, enter TIME_WAIT; otherwise enter CLOSING until its ACK arrives. Process an ACK before a FIN when both flags arrive together.
FIN_WAIT1 -- our FIN ACKed --> FIN_WAIT2 FIN_WAIT1 -- peer FIN --> CLOSING -- our FIN ACKed --> TIME_WAIT FIN_WAIT2 -- peer FIN --> TIME_WAIT -- 200 virtual ms --> CLOSED
Be available to answer a duplicate
In TIME_WAIT, another FIN at rcv_nxt-1 must receive the same final ACK and restart the 200 ms timer. Do not consume sequence space twice. tcp_tick closes at time_wait_until, using wrap-safe signed subtraction. The short interval is an explicit teaching value, not production TCP's 2MSL. Passive open and CLOSE_WAIT/LAST_ACK are outside this active-close exercise.
The boundary you own
Your callbacks run as a real C program in RV64 Linux. /course/netlab.h supplies bounded Ethernet/IP/TCP encoding, checksum validation, a virtual clock, and packet capture. You own the transport state and sequence numbers. emit_tcp(c, flags, seq, ack, bytes, length) emits actual packet bytes; the browser's program trace decodes them. Test injects packets and timer events rather than inspecting source text. This is a deliberately bounded active-open, stop-and-wait TCP implementation, not a replacement for the kernel TCP stack.
snd_una: first unacknowledged byte snd_nxt: next unsent byte rcv_nxt: next peer byte expected peer_window: advertised capacity pending_seq/flags/len/pending[]: one segment awaiting ACK sent_at, rto, retries, timer_active: retransmission ownership delivered[] / delivered_len: bytes accepted by the application lab_now: deterministic clock in milliseconds
Your goal
Add active close to tcp_close, tcp_receive, and tcp_tick, including retransmitted FINs, simultaneous close, and a bounded teaching TIME_WAIT interval.
What the tests observe
- FIN consumes one sequence number
- Drain before close
- Simultaneous close
- Duplicate FIN and TIME_WAIT
Need a nudge?
Hint 1
Reuse pending_seq/flags/len for FIN, with zero payload. Preserve the retransmission timer until its ACK.
Hint 2
A valid ACK may cause FIN_WAIT1 → FIN_WAIT2 or CLOSING → TIME_WAIT. Then process a valid peer FIN at rcv_nxt.
Hint 3
In TIME_WAIT, recognize retransmitted FIN seq == rcv_nxt - 1, emit ACK again, and set time_wait_until = lab_now + 200. Expire when (int32_t)(now - time_wait_until) >= 0.
What happens inside Linux
A closed application and a forgotten connection are different events. Retaining a small amount of protocol state lets the endpoint repair a lost final ACK without delivering a second end-of-stream event.
Optional account sync. Exercises work without an account.