# Transmission Control Protocol

NOTE: This unit is a WIP, this page doesn't describe what the unit actually
      does yet.

## Transmission Control Block

TCP connections need an elaborate structure to manage incoming and outgoing
data. The central concept of this structure is that of the sliding window. TCP
specs call this structure the TCB, Transmission Control Block.

TCB are covered in a separate unit, [com/tcb].

## Incoming ACK timers

One of the efficiencies built into TCP is this idea that ACKs can be merged.
It's not every little segment that needs a receipt. Moreover, because a reply
often follows a request directly, it's even possible that an ACK be made by that
reply.

We implement this with an ACK timer. First, we define a threshold with
MAXINACKMS. Then, we have a background job [lib/coop] that, on IDLE, goes
through all TCBs needing ACK and check if the threshold has been reached. If
yes, craft a ACK and send it.

In parallel, whenever we send a packet for whatever purpose (for example
outgoing contents), we check if there's a need for a ACK. If there is, we
include that ACK right here, which means that the IDLE timer won't ever have a
chance to reach the threshold. We saved a packet!

## Outgoing ACK timers

When sending contents, we need to set a threshold at which point we resend the
contents. This threshold is determined by MAXOUTACKMS.

Moreover, the contents we have to send might exceed MSS (Maximum Segment Size,
which is smaller than the window size), which means we have to send our contents
in chunks.

So, first of all, writing to a TCB writes to the outgoing buffer but doesn't
actually send anything. The TCP background job does it.

The background job goes through all TCBs that have outgoing contents to send.
For each of them, if there's contents to send and that the window allows it,
send a packet. If the timer isn't already running, set it, but otherwise, leave
it untouched.

In parallel of that, whenever we receive an "effective" ACK (that is, a ACK that
is for a range that actually makes our sliding window move), we reset the timer
if there's further outgoing contents to ACK, or disable it if we're 100% synced.

In parallel of all that, we check for outgoing timeouts. Whenever the threshold
is reached for a TCB, we consider all outgoing packets lost and start sending
again, resetting the timer.

TCP specs speak of multiple timers, but I think it's not worth bothering. This
single timer implementation will, I think, do an optimal job in a good majority
of the cases.

## PSH behavior

In the design described above, we already push received data as soon as it's
acknowledged. We can therefore ignore the PSH flag on the incoming side.

On the outgoing side, TCBs have a TFlags field which is ORed into outgoing
packets, so you can set the PSH bit in there if needed.

## URG behavior

Urgent data is out-of-band data. When an URG packet is received, a TCPURGENT
event is triggered and whoever listens to it can act on it.

To send an urgent packet, one needs to call "sendurgentpacket". Its contents
can't be larger than MSS.

TODO: theoretically, we should also be implementing a logic where urgent data
is mixed in regular in outgoing packets for efficiency, but is it really worth
the trouble? What is URG actually used for?

## Behavior at limits

There are four tricky limits with our design:

1. Outgoing buffer is full
2. Incoming buffer is full
3. There are no more available buffers for either outgoing or incoming data
4. We're out of connections

When writing outgoing data to a TCB that already has an outgoing buffer, it's
possible that this buffer is full of unacknowledged data. In this case, writing
to it will yield "0 bytes written". This means that whenever writing to a TCP
connection, one has to provide logic for the possibility that not all data has
been written.

When writing outgoing data to a TCB that doesn't have a buffer yet, and if there
is no available buffer, the result is similar: 0 bytes written.

When there's incoming data to a TCB that already has an incoming buffer, as long
as the system consumes the incoming data fast enough, the window system makes it
impossible not to find a place for it. However, if for whatever reason the
incoming data isn't consumed fast enough, we need to drop incoming data.

Incoming window size *can* overlap unconsumed data in the rolling buffer, but
when writing incoming packets, we only write and acknowledge what fits from the
incoming packet, dropping the rest.

However, when there's incoming data for a TCB that doesn't have a buffer, then
we need to drop that data. We never acknowledge it, so it's going to be resent
at some point, hopefully at a moment when we have available buffers.

Lastly, there's the case of a new incoming SYN packet at a time where all TCBs
are used up, we refuse the connection and answer with a RST packet.

## Connections

This unit maintains an array of TCPCon structures which are basically a pair of
incoming and outgoing TCBs along with a few supporting fields. There's a fixed
number of them and when we're out of them, we can't open a new connection unless
we close one.

Connections are Streams [io/stream]. Writing to it writes to the outgoing TCB,
and reading from it reads from the incoming TCB. The sending and receiving of
the packets that fill/consume those TCBs happen asynchronously.

For the outgoing side, there's a background task that takes care of emptying
the outgoing TCBs onto the network on idling.

For the incoming side, this unit it hooked into [com/ip4]'s incoming handlers
and that's where incoming packets are mapped to open connections and that from
there, incoming TCBs are filled.

Calling "close" on a TCPCon initialize the TCP closing sequence and then frees
the structure for a new connection.

## API

### Structures

struct TCPMessage {
  beshort SourcePort DestPort ;
  beint SeqNum AckNum ;
  beshort Flags Window Checksum UrgentPtr ;
  [void,0] Options ;
}
ip4payload TCPMessage bindstruct

Fields above exactly map to the TCP message structure from the standard.

extends Stream struct TCPCon {
  uint  addr port ints outts ;
  TCBIn in ;
  TCBOut out ;
}

addr  Remote address of the connection. 0 means that the structure is free.
port  Remote port of the connection.
ints  Timestamp of the oldest unacknowledged piece of data we've received.
      0 means that everything we have has been acknowledged.
outts Timestamp of the oldest outgoing packet we're waiting an ACK for.
      0 means we're not waiting on anything.
in    Incoming TCB (inlined struct, not a pointer)
out   Outgoing TCB (inlined struct, not a pointer)

### Words

getcon ( addr port -- con-or-0 )
  Yield the connection corresponding to the specified remote addr/port pair or
  0 if not found.

acceptcon ( -- con-or-0 )
  Open a new TCPCon using the current TCPMessage structure. If successful, yield
  a pointer to that connection. If there is no available connection, yield 0.

  It's not possible to open the same addr:port twice. If you call acceptcon for
  a pair that already has an open connection, that connection will be yielded,
  but that won't be a freshly opened connection.
