AquilaOS documentation
How it works, and its code
Loading...
Searching...
No Matches
tcp_output.c
Go to the documentation of this file.
1
10
11#include <core/system.h>
12#include <core/arch.h>
13#include <core/string.h>
14#include <net/byteorder.h>
15#include <net/inet.h>
16#include <net/netif.h>
17#include <net/tcp.h>
18
19#include <aquila/errno.h>
20
21#define RTO_MIN 1000000000ULL /* RFC 6298: 1 s */
22#define RTO_MAX 60000000000ULL
23#define RTT_GRANULARITY 2000000ULL /* the clock's granularity: a tick */
24#define DACK_DELAY 100000000ULL /* 100 ms */
25
26#define SYN_RETRIES 5 /* 1 + 2 + 4 + 8 + 16 + 32 s: 63 s in all */
27#define DATA_RETRIES 8 /* about 2 minutes (RTO_MAX caps the backoff) */
28
29static void rexmt_timeout(void *arg);
30
33static uint16_t our_mss(struct tcp_pcb *pcb)
34{
35 struct netif *netif = ipv4_route(pcb->raddr);
36 return netif ? netif->mtu - sizeof(struct ipv4_hdr) - sizeof(struct tcp_hdr) : TCP_MSS_DEFAULT;
37}
38
49uint16_t tcp_rcv_window(struct tcp_pcb *pcb)
50{
51 uint32_t room = TCP_BUFSIZE - 1 - ringbuf_available(pcb->rcvbuf);
52 uint32_t cur = SEQ_GT(pcb->rcv_adv, pcb->rcv_nxt) ? pcb->rcv_adv - pcb->rcv_nxt : 0;
53 uint32_t step = MIN(TCP_BUFSIZE / 2, pcb->mss);
54
55 if (room < cur || (room - cur) < step)
56 room = cur;
57
58 return room > 0xffff ? 0xffff : room;
59}
60
65static int segment(struct tcp_pcb *pcb, uint32_t seq, int flags, size_t off, size_t len)
66{
67 struct netif *netif = ipv4_route(pcb->raddr);
68
69 if (!netif)
70 return -ENETUNREACH;
71
72 struct netbuf *nb = netbuf_alloc();
73
74 if (!nb)
75 return -ENOBUFS;
76
77 if (len)
78 ringbuf_read_noconsume(pcb->sndbuf, off, len, netbuf_put(nb, len));
79
80 /* a SYN carries the MSS option */
81 size_t hlen = sizeof(struct tcp_hdr) + (flags & TH_SYN ? 4 : 0);
82 struct tcp_hdr *th = netbuf_push(nb, hlen);
84
85 th->src = pcb->lport;
86 th->dst = pcb->rport;
87 th->seq = htonl(seq);
88 th->ack = flags & TH_ACK ? htonl(pcb->rcv_nxt) : 0;
89 th->off = (hlen / 4) << 4;
90 th->flags = flags;
91 th->win = htons(win);
92 th->checksum = 0;
93 th->urg = 0;
94
95 if (flags & TH_SYN) {
96 uint8_t *opt = (uint8_t *) (th + 1);
97 uint16_t mss = our_mss(pcb);
98
99 opt[0] = 2; /* MSS */
100 opt[1] = 4;
101 opt[2] = mss >> 8;
102 opt[3] = mss & 0xff;
103 }
104
105 th->checksum = inet_checksum_pseudo(pcb->laddr, pcb->raddr, IP_PROTO_TCP, th, nb->len);
106
107 /* this ACK covers what was waiting to be acknowledged */
108 if (flags & TH_ACK) {
109 pcb->rcv_adv = pcb->rcv_nxt + win;
110 pcb->ack_pending = 0;
111 callout_stop(&pcb->dack);
112 }
113
114 return ipv4_output(netif, pcb->laddr, pcb->raddr, IP_PROTO_TCP, nb);
115}
116
122void tcp_rexmt_start(struct tcp_pcb *pcb)
123{
124 if (!pcb->rexmt.pending)
125 net_callout_set(&pcb->rexmt, pcb->rto, rexmt_timeout, pcb);
126}
127
135void tcp_send_syn(struct tcp_pcb *pcb)
136{
137 int flags = TH_SYN | (pcb->state == TCP_SYN_RECEIVED ? TH_ACK : 0);
138
139 /* only the first one is timed */
140 if (!pcb->retries) {
141 pcb->rtt_timing = 1;
142 pcb->rtt_seq = pcb->iss;
143 pcb->rtt_start = arch_rtime_ns();
144 }
145
146 segment(pcb, pcb->iss, flags, 0, 0);
147
148 pcb->snd_nxt = pcb->snd_max = pcb->iss + 1;
149 tcp_rexmt_start(pcb);
150}
151
157void tcp_send_ack(struct tcp_pcb *pcb)
158{
159 segment(pcb, pcb->snd_nxt, TH_ACK, 0, 0);
160}
161
169{
170 segment(pcb, pcb->snd_una - 1, TH_ACK, 0, 0);
171}
172
178void tcp_send_rst(struct tcp_pcb *pcb)
179{
180 segment(pcb, pcb->snd_nxt, TH_RST | TH_ACK, 0, 0);
181}
182
192void tcp_reply_rst(struct netif *netif, const struct ipv4_hdr *ih, const struct tcp_hdr *th, size_t len)
193{
194 if (th->flags & TH_RST)
195 return;
196
197 struct netbuf *nb = netbuf_alloc();
198
199 if (!nb)
200 return;
201
202 struct tcp_hdr *rh = netbuf_push(nb, sizeof(*rh));
203
204 memset(rh, 0, sizeof(*rh));
205 rh->src = th->dst;
206 rh->dst = th->src;
207 rh->off = (sizeof(*rh) / 4) << 4;
208
209 if (th->flags & TH_ACK) {
210 rh->seq = th->ack;
211 rh->flags = TH_RST;
212 } else {
213 uint32_t seglen = len + !!(th->flags & TH_SYN) + !!(th->flags & TH_FIN);
214
215 rh->ack = htonl(ntohl(th->seq) + seglen);
216 rh->flags = TH_RST | TH_ACK;
217 }
218
219 rh->checksum = inet_checksum_pseudo(ih->dst, ih->src, IP_PROTO_TCP, rh, nb->len);
220 ipv4_output(netif, ih->dst, ih->src, IP_PROTO_TCP, nb);
221}
222
225static int sending(struct tcp_pcb *pcb)
226{
227 switch (pcb->state) {
228 case TCP_ESTABLISHED:
229 case TCP_CLOSE_WAIT:
230 case TCP_FIN_WAIT_1:
231 case TCP_CLOSING:
232 case TCP_LAST_ACK:
233 return 1;
234 }
235
236 return 0;
237}
238
249void tcp_output(struct tcp_pcb *pcb)
250{
251 if (!sending(pcb))
252 return;
253
254 for (;;) {
255 uint32_t buffered = ringbuf_available(pcb->sndbuf);
256 uint32_t flight = pcb->snd_nxt - pcb->snd_una;
257 uint32_t unsent = buffered > flight ? buffered - flight : 0;
258 uint32_t wnd = MIN(pcb->snd_wnd, pcb->cwnd);
259 uint32_t usable = wnd > flight ? wnd - flight : 0;
260 uint32_t len = MIN(unsent, MIN(usable, pcb->mss));
261 int fin = pcb->fin_queued && len == unsent &&
262 pcb->snd_nxt + len == pcb->snd_una + buffered;
263
264 if (!len && !fin)
265 break;
266
267 if (len < pcb->mss && !fin) {
268 if (len == unsent && flight && !pcb->nodelay)
269 break;
270
271 if (len < unsent && len < pcb->snd_wnd / 2)
272 break;
273 }
274
275 uint32_t seq = pcb->snd_nxt;
276 int flags = TH_ACK | (fin ? TH_FIN : 0) | (len && len == unsent ? TH_PSH : 0);
277
278 if (segment(pcb, seq, flags, seq - pcb->snd_una, len))
279 break;
280
281 /* time new data, one segment at a time; never time a
282 * retransmission (Karn's algorithm) */
283 if (!pcb->rtt_timing && len && seq == pcb->snd_max) {
284 pcb->rtt_timing = 1;
285 pcb->rtt_seq = seq;
286 pcb->rtt_start = arch_rtime_ns();
287 }
288
289 pcb->snd_nxt += len + fin;
290
291 if (SEQ_GT(pcb->snd_nxt, pcb->snd_max))
292 pcb->snd_max = pcb->snd_nxt;
293
294 tcp_rexmt_start(pcb);
295
296 if (fin)
297 break;
298 }
299
300 /* the window is zero and nothing is in flight: probe it (persist) */
301 uint32_t buffered = ringbuf_available(pcb->sndbuf);
302
303 if (!pcb->snd_wnd && buffered && pcb->snd_nxt == pcb->snd_una && !pcb->rexmt.pending) {
304 pcb->persist = 1;
305 net_callout_set(&pcb->rexmt, pcb->rto, rexmt_timeout, pcb);
306 }
307}
308
316void tcp_rtt_update(struct tcp_pcb *pcb, uint64_t rtt)
317{
318 if (!pcb->srtt) {
319 pcb->srtt = rtt;
320 pcb->rttvar = rtt / 2;
321 } else {
322 uint64_t delta = pcb->srtt > rtt ? pcb->srtt - rtt : rtt - pcb->srtt;
323
324 pcb->rttvar = (3 * pcb->rttvar + delta) / 4;
325 pcb->srtt = (7 * pcb->srtt + rtt) / 8;
326 }
327
328 pcb->rto = pcb->srtt + MAX(RTT_GRANULARITY, 4 * pcb->rttvar);
329 pcb->rto = MIN(MAX(pcb->rto, RTO_MIN), RTO_MAX);
330}
331
333static void dack_timeout(void *arg)
334{
335 tcp_send_ack(arg);
336}
337
344void tcp_dack(struct tcp_pcb *pcb)
345{
346 if (++pcb->ack_pending >= 2)
347 tcp_send_ack(pcb);
348 else if (!pcb->dack.pending)
350}
351
358static void rexmt_timeout(void *arg)
359{
360 struct tcp_pcb *pcb = arg;
361
362 /* a zero window: send a byte past it, to see if it opened, backing off
363 * for as long as the peer answers */
364 if (pcb->persist) {
365 if (pcb->snd_wnd || !ringbuf_available(pcb->sndbuf)) {
366 pcb->persist = 0;
367 tcp_output(pcb);
368 return;
369 }
370
371 pcb->snd_nxt = pcb->snd_una;
372 segment(pcb, pcb->snd_una, TH_ACK, 0, 1);
373 pcb->snd_nxt = pcb->snd_una + 1;
374
375 if (SEQ_GT(pcb->snd_nxt, pcb->snd_max))
376 pcb->snd_max = pcb->snd_nxt;
377
378 pcb->rto = MIN(pcb->rto * 2, RTO_MAX);
379 net_callout_set(&pcb->rexmt, pcb->rto, rexmt_timeout, pcb);
380 return;
381 }
382
383 int syn = pcb->state == TCP_SYN_SENT || pcb->state == TCP_SYN_RECEIVED;
384
385 /* everything was acknowledged */
386 if (!syn && pcb->snd_una == pcb->snd_max)
387 return;
388
389 if (++pcb->retries > (syn ? SYN_RETRIES : DATA_RETRIES)) {
390 tcp_drop(pcb, ETIMEDOUT);
391 return;
392 }
393
394 /* back off; a loss also starts the congestion window over from one
395 * segment (RFC 5681) */
396 uint32_t flight = pcb->snd_max - pcb->snd_una;
397
398 pcb->rto = MIN(pcb->rto * 2, RTO_MAX);
399 pcb->ssthresh = MAX(flight / 2, 2U * pcb->mss);
400 pcb->cwnd = pcb->mss;
401 pcb->dupacks = 0;
402 pcb->in_recovery = 0;
403 pcb->rtt_timing = 0;
404
405 if (syn) {
406 tcp_send_syn(pcb);
407 return;
408 }
409
410 pcb->snd_nxt = pcb->snd_una;
411 tcp_output(pcb);
412}
#define htons(x)
Definition byteorder.h:10
#define ntohl(x)
Definition byteorder.h:18
#define htonl(x)
Definition byteorder.h:11
void callout_stop(struct callout *c)
Cancels a callout, if it is pending.
Definition callout.c:23
#define MIN(a, b)
Definition system.h:11
#define MAX(a, b)
Definition system.h:10
struct netif * ipv4_route(uint32_t dst)
Chooses the interface to send a packet to dst through: the one whose link it is on,...
Definition ipv4.c:142
#define IP_PROTO_TCP
Definition inet.h:42
uint16_t inet_checksum_pseudo(uint32_t src, uint32_t dst, uint8_t proto, const void *data, size_t len)
Computes the Internet checksum of data with the IPv4 pseudo-header before it (the addresses,...
Definition ether.c:106
int ipv4_output(struct netif *netif, uint32_t src, uint32_t dst, uint8_t proto, struct netbuf *nb)
Sends a packet through an interface: adds its IPv4 header and sends it to dst, or to the gateway if d...
Definition ipv4.c:190
void * memset(void *dst, int c, size_t n)
Sets n bytes to c.
Definition libc.c:23
static void * netbuf_push(struct netbuf *nb, size_t size)
Makes room for a header of size bytes before the data, and returns it.
Definition netbuf.h:52
struct netbuf * netbuf_alloc(void)
Allocates a netbuf, empty, with room for the headers that will be put in front of its data.
Definition netbuf.c:58
void net_callout_set(struct callout *c, uint64_t ns, void(*fn)(void *), void *arg)
Sets a callout that the network's thread runs: fn(arg) is called once ns ns have passed.
Definition netif.c:137
unsigned int uint32_t
Definition stddef.h:22
unsigned char uint8_t
Definition stddef.h:20
unsigned long long int uint64_t
Definition stddef.h:23
unsigned short int uint16_t
Definition stddef.h:21
int pending
Definition callout.h:19
An IPv4 packet's header.
Definition inet.h:46
uint32_t dst
Definition inet.h:56
uint32_t src
Definition inet.h:55
A packet (net/netbuf.c).
Definition netbuf.h:16
size_t len
Definition netbuf.h:20
A network interface (net/netif.c): a device that sends and receives Ethernet frames,...
Definition netif.h:16
uint16_t mtu
Definition netif.h:19
A TCP segment's header.
Definition tcp.h:16
uint16_t urg
Definition tcp.h:23
uint32_t seq
Definition tcp.h:18
uint16_t dst
Definition tcp.h:17
uint16_t src
Definition tcp.h:17
uint16_t checksum
Definition tcp.h:22
uint16_t win
Definition tcp.h:21
uint8_t flags
Definition tcp.h:20
uint32_t ack
Definition tcp.h:18
uint8_t off
Definition tcp.h:19
A connection (its control block, or PCB).
Definition tcp.h:66
int nodelay
Definition tcp.h:106
uint32_t laddr
Definition tcp.h:70
uint16_t mss
Definition tcp.h:80
int in_recovery
Definition tcp.h:102
struct callout rexmt
Definition tcp.h:111
uint32_t cwnd
Definition tcp.h:100
uint64_t rttvar
Definition tcp.h:92
uint64_t rto
Definition tcp.h:92
uint32_t snd_una
Definition tcp.h:78
uint32_t iss
Definition tcp.h:78
int retries
Definition tcp.h:96
uint64_t rtt_start
Definition tcp.h:95
uint32_t snd_nxt
Definition tcp.h:78
uint32_t rcv_adv
Definition tcp.h:86
uint16_t rport
Definition tcp.h:71
uint16_t lport
Definition tcp.h:71
uint32_t snd_wnd
Definition tcp.h:79
uint64_t srtt
Definition tcp.h:92
int rtt_timing
Definition tcp.h:93
uint32_t ssthresh
Definition tcp.h:100
int ack_pending
Definition tcp.h:105
struct ringbuf * rcvbuf
Definition tcp.h:87
int state
Definition tcp.h:67
uint32_t rtt_seq
Definition tcp.h:94
int dupacks
Definition tcp.h:101
int fin_queued
Definition tcp.h:82
uint32_t rcv_nxt
Definition tcp.h:86
int persist
Definition tcp.h:97
struct ringbuf * sndbuf
Definition tcp.h:81
uint32_t raddr
Definition tcp.h:70
struct callout dack
Definition tcp.h:112
uint32_t snd_max
Definition tcp.h:78
#define TH_PSH
Definition tcp.h:30
#define TH_FIN
Definition tcp.h:27
void tcp_send_syn(struct tcp_pcb *pcb)
Sends the SYN (or, in SYN_RECEIVED, the SYN and ACK), the first time or again, and starts the retrans...
Definition tcp_output.c:135
#define SEQ_GT(a, b)
Definition tcp.h:52
void tcp_dack(struct tcp_pcb *pcb)
Acknowledges a received segment: at once if it's the second one not acknowledged yet,...
Definition tcp_output.c:344
#define TCP_BUFSIZE
Definition tcp.h:56
void tcp_reply_rst(struct netif *netif, const struct ipv4_hdr *ih, const struct tcp_hdr *th, size_t len)
Answers a segment that is for no connection with a reset (RFC 9293 3.10.7.1).
Definition tcp_output.c:192
#define TH_ACK
Definition tcp.h:31
void tcp_output(struct tcp_pcb *pcb)
Sends as much of the send buffer as the windows allow, in segments of at most mss bytes.
Definition tcp_output.c:249
#define TCP_MSS_DEFAULT
Definition tcp.h:59
void tcp_send_rst(struct tcp_pcb *pcb)
Sends a reset on a connection.
Definition tcp_output.c:178
void tcp_send_ack(struct tcp_pcb *pcb)
Sends an ACK, now.
Definition tcp_output.c:157
#define TH_RST
Definition tcp.h:29
#define TH_SYN
Definition tcp.h:28
void tcp_rtt_update(struct tcp_pcb *pcb, uint64_t rtt)
Updates the round trip estimates with a new measurement, and the retransmission timeout from them (RF...
Definition tcp_output.c:316
@ TCP_ESTABLISHED
Definition tcp.h:40
@ TCP_LAST_ACK
Definition tcp.h:45
@ TCP_FIN_WAIT_1
Definition tcp.h:41
@ TCP_CLOSE_WAIT
Definition tcp.h:43
@ TCP_CLOSING
Definition tcp.h:44
@ TCP_SYN_SENT
Definition tcp.h:38
@ TCP_SYN_RECEIVED
Definition tcp.h:39
uint16_t tcp_rcv_window(struct tcp_pcb *pcb)
Computes the window to advertise: the room in the receive buffer.
Definition tcp_output.c:49
void tcp_rexmt_start(struct tcp_pcb *pcb)
Starts the retransmission timer, if it isn't running.
Definition tcp_output.c:122
void tcp_drop(struct tcp_pcb *pcb, int err)
Drops a connection that is lost, giving its socket an error.
Definition tcp_socket.c:323
void tcp_send_keepalive(struct tcp_pcb *pcb)
Sends a keepalive probe: a segment one byte before what the peer has acknowledged (with no data),...
Definition tcp_output.c:168
#define DACK_DELAY
Definition tcp_output.c:24
#define RTO_MAX
Definition tcp_output.c:22
static uint16_t our_mss(struct tcp_pcb *pcb)
Returns the most data a segment we send may carry: the interface's MTU, less the headers.
Definition tcp_output.c:33
static int sending(struct tcp_pcb *pcb)
Returns whether data and the FIN may be sent in the connection's state.
Definition tcp_output.c:225
#define RTO_MIN
Definition tcp_output.c:21
static void rexmt_timeout(void *arg)
Handles the retransmission timer.
Definition tcp_output.c:358
static int segment(struct tcp_pcb *pcb, uint32_t seq, int flags, size_t off, size_t len)
Sends a segment with sequence number seq, carrying len bytes of the send buffer from offset off,...
Definition tcp_output.c:65
static void dack_timeout(void *arg)
Sends the delayed ACK, when its delay is over.
Definition tcp_output.c:333
#define DATA_RETRIES
Definition tcp_output.c:27
#define SYN_RETRIES
Definition tcp_output.c:26
#define RTT_GRANULARITY
Definition tcp_output.c:23
uint64_t arch_rtime_ns(void)
Reads the monotonic clock, which starts at boot.
Definition timer.c:56