source: mainline/uspace/srv/net/tl/tcp/conn.c@ 2a3214e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2a3214e was 2a3214e, checked in by Jiri Svoboda <jiri@…>, 14 years ago

Fibril timer primitive.
TCP Time-Wait timeout.

  • Property mode set to 100644
File size: 24.4 KB
RevLine 
[c5808b41]1/*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup tcp
30 * @{
31 */
32
33/**
[032bbe7]34 * @file TCP connection processing and state machine
[c5808b41]35 */
36
37#include <adt/list.h>
[8c7a054]38#include <bool.h>
[c5808b41]39#include <errno.h>
40#include <io/log.h>
[0093ab6]41#include <macros.h>
[c5808b41]42#include <stdlib.h>
43#include "conn.h"
44#include "iqueue.h"
45#include "segment.h"
46#include "seq_no.h"
[2a3214e]47#include "state.h"
[c5808b41]48#include "tcp_type.h"
49#include "tqueue.h"
50
[0093ab6]51#define RCV_BUF_SIZE 4096
[32105348]52#define SND_BUF_SIZE 4096
[0093ab6]53
[2a3214e]54#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
55#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
56
[c5808b41]57LIST_INITIALIZE(conn_list);
58
59static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
[2a3214e]60static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
[c5808b41]61
[032bbe7]62/** Create new segment structure.
63 *
64 * @param lsock Local socket (will be deeply copied)
65 * @param fsock Foreign socket (will be deeply copied)
66 * @return New segment or NULL
67 */
[c5808b41]68tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
69{
[2a3214e]70 tcp_conn_t *conn = NULL;
[c5808b41]71
[0093ab6]72 /* Allocate connection structure */
[c5808b41]73 conn = calloc(1, sizeof(tcp_conn_t));
74 if (conn == NULL)
[2a3214e]75 goto error;
76
77 conn->tw_timer = fibril_timer_create();
78 if (conn->tw_timer == NULL)
79 goto error;
[c5808b41]80
[0093ab6]81 /* Allocate receive buffer */
[d9ce049]82 fibril_mutex_initialize(&conn->rcv_buf_lock);
83 fibril_condvar_initialize(&conn->rcv_buf_cv);
[0093ab6]84 conn->rcv_buf_size = RCV_BUF_SIZE;
85 conn->rcv_buf_used = 0;
[8c7a054]86 conn->rcv_buf_fin = false;
[d9ce049]87
[0093ab6]88 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
[2a3214e]89 if (conn->rcv_buf == NULL)
90 goto error;
[0093ab6]91
[32105348]92 /** Allocate send buffer */
93 conn->snd_buf_size = SND_BUF_SIZE;
94 conn->snd_buf_used = 0;
[8c7a054]95 conn->snd_buf_fin = false;
[32105348]96 conn->snd_buf = calloc(1, conn->snd_buf_size);
[2a3214e]97 if (conn->snd_buf == NULL)
98 goto error;
[32105348]99
[0093ab6]100 /* Set up receive window. */
101 conn->rcv_wnd = conn->rcv_buf_size;
102
103 /* Initialize incoming segment queue */
[c5808b41]104 tcp_iqueue_init(&conn->incoming, conn);
105
[6df418c4]106 /* Initialize retransmission queue */
107 tcp_tqueue_init(&conn->retransmit, conn);
108
[c5808b41]109 conn->cstate = st_listen;
[6df418c4]110 conn->fin_is_acked = false;
[c5808b41]111 conn->ident.local = *lsock;
112 if (fsock != NULL)
113 conn->ident.foreign = *fsock;
114
115 return conn;
[2a3214e]116
117error:
118 if (conn != NULL && conn->rcv_buf != NULL)
119 free(conn->rcv_buf);
120 if (conn != NULL && conn->snd_buf != NULL)
121 free(conn->snd_buf);
122 if (conn != NULL && conn->tw_timer != NULL)
123 fibril_timer_destroy(conn->tw_timer);
124 if (conn != NULL)
125 free(conn);
126
127 return NULL;
[c5808b41]128}
129
[032bbe7]130/** Enlist connection.
131 *
132 * Add connection to the connection map.
133 */
[c5808b41]134void tcp_conn_add(tcp_conn_t *conn)
135{
136 list_append(&conn->link, &conn_list);
137}
138
[6e88fea]139/** Delist connection.
140 *
141 * Remove connection from the connection map.
142 */
143void tcp_conn_remove(tcp_conn_t *conn)
144{
145 list_remove(&conn->link);
146}
147
[032bbe7]148/** Synchronize connection.
149 *
150 * This is the first step of an active connection attempt,
151 * sends out SYN and sets up ISS and SND.xxx.
152 */
[c5808b41]153void tcp_conn_sync(tcp_conn_t *conn)
154{
155 /* XXX select ISS */
156 conn->iss = 1;
157 conn->snd_nxt = conn->iss;
158 conn->snd_una = conn->iss;
159
160 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
161 conn->cstate = st_syn_sent;
162}
163
[f343a16]164/** FIN has been sent.
165 *
166 * This function should be called when FIN is sent over the connection,
167 * as a result the connection state is changed appropriately.
168 */
169void tcp_conn_fin_sent(tcp_conn_t *conn)
170{
171 switch (conn->cstate) {
172 case st_syn_received:
173 case st_established:
174 log_msg(LVL_DEBUG, "FIN sent -> Fin-Wait-1");
175 conn->cstate = st_fin_wait_1;
176 break;
177 case st_close_wait:
[6e88fea]178 log_msg(LVL_DEBUG, "FIN sent -> Last-Ack");
[f343a16]179 conn->cstate = st_last_ack;
180 break;
181 default:
182 assert(false);
183 }
[6df418c4]184
185 conn->fin_is_acked = false;
[f343a16]186}
187
[032bbe7]188/** Compare two sockets.
189 *
190 * Two sockets are equal if the address is equal and the port number
191 * is equal.
192 */
[c5808b41]193static bool tcp_socket_equal(tcp_sock_t *a, tcp_sock_t *b)
194{
195 log_msg(LVL_DEBUG, "tcp_socket_equal((%x,%u), (%x,%u))",
196 a->addr.ipv4, a->port, b->addr.ipv4, b->port);
197
198 if (a->addr.ipv4 != b->addr.ipv4)
199 return false;
200
201 if (a->port != b->port)
202 return false;
203
204 log_msg(LVL_DEBUG, " -> match");
205
206 return true;
207}
208
[032bbe7]209/** Match socket with pattern. */
[c5808b41]210static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
211{
212 log_msg(LVL_DEBUG, "tcp_sockpair_match(%p, %p)", sp, pattern);
213
214 if (!tcp_socket_equal(&sp->local, &pattern->local))
215 return false;
216
217 if (!tcp_socket_equal(&sp->foreign, &pattern->foreign))
218 return false;
219
220 return true;
221}
222
[032bbe7]223/** Find connection structure for specified socket pair.
224 *
225 * A connection is uniquely identified by a socket pair. Look up our
226 * connection map and return connection structure based on socket pair.
227 *
228 * @param sp Socket pair
229 * @return Connection structure or NULL if not found.
230 */
[c5808b41]231tcp_conn_t *tcp_conn_find(tcp_sockpair_t *sp)
232{
233 log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
234
235 list_foreach(conn_list, link) {
236 tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
237 if (tcp_sockpair_match(sp, &conn->ident)) {
238 return conn;
239 }
240 }
241
242 return NULL;
243}
244
[32105348]245/** Determine if SYN has been received.
246 *
247 * @param conn Connection
248 * @return @c true if SYN has been received, @c false otherwise.
249 */
250bool tcp_conn_got_syn(tcp_conn_t *conn)
251{
252 switch (conn->cstate) {
253 case st_listen:
254 case st_syn_sent:
255 return false;
256 case st_syn_received:
257 case st_established:
258 case st_fin_wait_1:
259 case st_fin_wait_2:
260 case st_close_wait:
261 case st_closing:
262 case st_last_ack:
263 case st_time_wait:
264 return true;
265 case st_closed:
266 assert(false);
267 }
268
269 assert(false);
270}
271
[032bbe7]272/** Segment arrived in Listen state.
273 *
274 * @param conn Connection
275 * @param seg Segment
276 */
[c5808b41]277static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
278{
279 log_msg(LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
280
281 if ((seg->ctrl & CTL_RST) != 0) {
282 log_msg(LVL_DEBUG, "Ignoring incoming RST.");
283 return;
284 }
285
286 if ((seg->ctrl & CTL_ACK) != 0) {
287 log_msg(LVL_DEBUG, "Incoming ACK, send acceptable RST.");
288 tcp_reply_rst(&conn->ident, seg);
289 return;
290 }
291
292 if ((seg->ctrl & CTL_SYN) == 0) {
293 log_msg(LVL_DEBUG, "SYN not present. Ignoring segment.");
294 return;
295 }
296
297 log_msg(LVL_DEBUG, "Got SYN, sending SYN, ACK.");
298
299 conn->rcv_nxt = seg->seq + 1;
300 conn->irs = seg->seq;
301
[4c55a64]302
[c5808b41]303 log_msg(LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
304
305 if (seg->len > 1)
306 log_msg(LVL_WARN, "SYN combined with data, ignoring data.");
307
308 /* XXX select ISS */
309 conn->iss = 1;
310 conn->snd_nxt = conn->iss;
311 conn->snd_una = conn->iss;
312
[4c55a64]313 /*
314 * Surprisingly the spec does not deal with initial window setting.
315 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
316 * will always be accepted as new window setting.
317 */
318 conn->snd_wnd = seg->wnd;
319 conn->snd_wl1 = seg->seq;
320 conn->snd_wl2 = seg->seq;
321
[c5808b41]322 conn->cstate = st_syn_received;
323
324 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
[0093ab6]325
326 tcp_segment_delete(seg);
[c5808b41]327}
328
[032bbe7]329/** Segment arrived in Syn-Sent state.
330 *
331 * @param conn Connection
332 * @param seg Segment
333 */
[c5808b41]334static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
335{
336 log_msg(LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
337
338 if ((seg->ctrl & CTL_ACK) != 0) {
339 log_msg(LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
340 conn->snd_una, seg->ack, conn->snd_nxt);
341 if (!seq_no_ack_acceptable(conn, seg->ack)) {
342 log_msg(LVL_WARN, "ACK not acceptable, send RST.");
343 tcp_reply_rst(&conn->ident, seg);
344 return;
345 }
346 }
347
348 if ((seg->ctrl & CTL_RST) != 0) {
349 log_msg(LVL_DEBUG, "Connection reset.");
350 /* XXX Signal user error */
351 conn->cstate = st_closed;
352 /* XXX delete connection */
353 return;
354 }
355
356 /* XXX precedence */
357
358 if ((seg->ctrl & CTL_SYN) == 0) {
359 log_msg(LVL_DEBUG, "No SYN bit, ignoring segment.");
360 return;
361 }
362
363 conn->rcv_nxt = seg->seq + 1;
364 conn->irs = seg->seq;
365
366 if ((seg->ctrl & CTL_ACK) != 0) {
367 conn->snd_una = seg->ack;
[d9ce049]368
369 /*
370 * Prune acked segments from retransmission queue and
371 * possibly transmit more data.
372 */
373 tcp_tqueue_ack_received(conn);
[c5808b41]374 }
375
376 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
377
[32105348]378 /*
379 * Surprisingly the spec does not deal with initial window setting.
380 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
381 * will always be accepted as new window setting.
382 */
383 log_msg(LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
384 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
385 conn->snd_wnd = seg->wnd;
386 conn->snd_wl1 = seg->seq;
387 conn->snd_wl2 = seg->seq;
388
[c5808b41]389 if (seq_no_syn_acked(conn)) {
390 log_msg(LVL_DEBUG, " syn acked -> Established");
391 conn->cstate = st_established;
392 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
393 } else {
394 log_msg(LVL_DEBUG, " syn not acked -> Syn-Received");
395 conn->cstate = st_syn_received;
396 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
397 }
[0093ab6]398
399 tcp_segment_delete(seg);
[c5808b41]400}
401
[032bbe7]402/** Segment arrived in state where segments are processed in sequence order.
403 *
404 * Queue segment in incoming segments queue for processing.
405 *
406 * @param conn Connection
407 * @param seg Segment
408 */
[c5808b41]409static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
410{
411 tcp_segment_t *pseg;
412
413 log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
414
415 /* XXX Discard old duplicates */
416
417 /* Queue for processing */
418 tcp_iqueue_insert_seg(&conn->incoming, seg);
419
420 /*
421 * Process all segments from incoming queue that are ready.
422 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
423 *
424 * XXX Need to return ACK for unacceptable segments
425 */
426 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
427 tcp_conn_seg_process(conn, pseg);
428}
429
[032bbe7]430/** Process segment RST field.
431 *
432 * @param conn Connection
433 * @param seg Segment
434 * @return cp_done if we are done with this segment, cp_continue
435 * if not
436 */
[c5808b41]437static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
438{
[4c55a64]439 /* TODO */
[c5808b41]440 return cp_continue;
441}
442
[032bbe7]443/** Process segment security and precedence fields.
444 *
445 * @param conn Connection
446 * @param seg Segment
447 * @return cp_done if we are done with this segment, cp_continue
448 * if not
449 */
[c5808b41]450static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
451{
[4c55a64]452 /* TODO */
[c5808b41]453 return cp_continue;
454}
455
[032bbe7]456/** Process segment SYN field.
457 *
458 * @param conn Connection
459 * @param seg Segment
460 * @return cp_done if we are done with this segment, cp_continue
461 * if not
462 */
[c5808b41]463static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
464{
[4c55a64]465 /* TODO */
[c5808b41]466 return cp_continue;
467}
468
[032bbe7]469/** Process segment ACK field in Syn-Received state.
470 *
471 * @param conn Connection
472 * @param seg Segment
473 * @return cp_done if we are done with this segment, cp_continue
474 * if not
475 */
[c5808b41]476static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
477{
478 if (!seq_no_ack_acceptable(conn, seg->ack)) {
479 /* ACK is not acceptable, send RST. */
480 log_msg(LVL_WARN, "Segment ACK not acceptable, sending RST.");
481 tcp_reply_rst(&conn->ident, seg);
[4c55a64]482 tcp_segment_delete(seg);
483 return cp_done;
[c5808b41]484 }
485
486 log_msg(LVL_DEBUG, "SYN ACKed -> Established");
487
488 conn->cstate = st_established;
489
490 /* XXX Not mentioned in spec?! */
491 conn->snd_una = seg->ack;
492
493 return cp_continue;
494}
495
[032bbe7]496/** Process segment ACK field in Established state.
497 *
498 * @param conn Connection
499 * @param seg Segment
500 * @return cp_done if we are done with this segment, cp_continue
501 * if not
502 */
[c5808b41]503static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
504{
[0093ab6]505 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
506
507 log_msg(LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
508 (unsigned)seg->ack, (unsigned)conn->snd_una,
509 (unsigned)conn->snd_nxt);
510
[4c55a64]511 if (!seq_no_ack_acceptable(conn, seg->ack)) {
[0093ab6]512 log_msg(LVL_DEBUG, "ACK not acceptable.");
[4c55a64]513 if (!seq_no_ack_duplicate(conn, seg->ack)) {
[0093ab6]514 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
515 "Send ACK and drop.");
[4c55a64]516 /* Not acceptable, not duplicate. Send ACK and drop. */
517 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
518 tcp_segment_delete(seg);
519 return cp_done;
[0093ab6]520 } else {
521 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
[4c55a64]522 }
523 } else {
524 /* Update SND.UNA */
525 conn->snd_una = seg->ack;
526 }
527
528 if (seq_no_new_wnd_update(conn, seg)) {
529 conn->snd_wnd = seg->wnd;
530 conn->snd_wl1 = seg->seq;
531 conn->snd_wl2 = seg->ack;
[d9ce049]532
533 log_msg(LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
534 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
535 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
[4c55a64]536 }
537
[d9ce049]538 /*
539 * Prune acked segments from retransmission queue and
540 * possibly transmit more data.
541 */
542 tcp_tqueue_ack_received(conn);
543
[c5808b41]544 return cp_continue;
545}
546
[032bbe7]547/** Process segment ACK field in Fin-Wait-1 state.
548 *
549 * @param conn Connection
550 * @param seg Segment
551 * @return cp_done if we are done with this segment, cp_continue
552 * if not
553 */
[c5808b41]554static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
555{
[4c55a64]556 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
557 return cp_done;
558
[6df418c4]559 if (conn->fin_is_acked) {
560 log_msg(LVL_DEBUG, " FIN acked -> Fin-Wait-2");
561 conn->cstate = st_fin_wait_2;
562 }
563
[c5808b41]564 return cp_continue;
565}
566
[032bbe7]567/** Process segment ACK field in Fin-Wait-2 state.
568 *
569 * @param conn Connection
570 * @param seg Segment
571 * @return cp_done if we are done with this segment, cp_continue
572 * if not
573 */
[c5808b41]574static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
575{
[4c55a64]576 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
577 return cp_done;
578
579 /* TODO */
[c5808b41]580 return cp_continue;
581}
582
[032bbe7]583/** Process segment ACK field in Close-Wait state.
584 *
585 * @param conn Connection
586 * @param seg Segment
587 * @return cp_done if we are done with this segment, cp_continue
588 * if not
589 */
[c5808b41]590static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
591{
[4c55a64]592 /* The same processing as in Established state */
593 return tcp_conn_seg_proc_ack_est(conn, seg);
[c5808b41]594}
595
[032bbe7]596/** Process segment ACK field in Closing state.
597 *
598 * @param conn Connection
599 * @param seg Segment
600 * @return cp_done if we are done with this segment, cp_continue
601 * if not
602 */
[c5808b41]603static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
604{
[4c55a64]605 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
606 return cp_done;
607
608 /* TODO */
[c5808b41]609 return cp_continue;
610}
611
[032bbe7]612/** Process segment ACK field in Last-Ack state.
613 *
614 * @param conn Connection
615 * @param seg Segment
616 * @return cp_done if we are done with this segment, cp_continue
617 * if not
618 */
[c5808b41]619static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
620{
[4c55a64]621 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
622 return cp_done;
623
[6e88fea]624 if (conn->fin_is_acked) {
625 log_msg(LVL_DEBUG, " FIN acked -> Closed");
626 tcp_conn_remove(conn);
627 conn->cstate = st_closed;
628 return cp_done;
629 }
630
[c5808b41]631 return cp_continue;
632}
633
[032bbe7]634/** Process segment ACK field in Time-Wait state.
635 *
636 * @param conn Connection
637 * @param seg Segment
638 * @return cp_done if we are done with this segment, cp_continue
639 * if not
640 */
[c5808b41]641static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
642{
[4c55a64]643 /* Nothing to do */
[c5808b41]644 return cp_continue;
645}
646
[032bbe7]647/** Process segment ACK field.
648 *
649 * @param conn Connection
650 * @param seg Segment
651 * @return cp_done if we are done with this segment, cp_continue
652 * if not
653 */
[c5808b41]654static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
655{
656 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack(%p, %p)", conn, seg);
657
658 if ((seg->ctrl & CTL_ACK) == 0) {
659 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
[4c55a64]660 tcp_segment_delete(seg);
[c5808b41]661 return cp_done;
662 }
663
664 switch (conn->cstate) {
665 case st_syn_received:
666 return tcp_conn_seg_proc_ack_sr(conn, seg);
667 case st_established:
668 return tcp_conn_seg_proc_ack_est(conn, seg);
669 case st_fin_wait_1:
670 return tcp_conn_seg_proc_ack_fw1(conn, seg);
671 case st_fin_wait_2:
672 return tcp_conn_seg_proc_ack_fw2(conn, seg);
673 case st_close_wait:
674 return tcp_conn_seg_proc_ack_cw(conn, seg);
675 case st_closing:
676 return tcp_conn_seg_proc_ack_cls(conn, seg);
677 case st_last_ack:
678 return tcp_conn_seg_proc_ack_la(conn, seg);
679 case st_time_wait:
680 return tcp_conn_seg_proc_ack_tw(conn, seg);
681 case st_listen:
682 case st_syn_sent:
683 case st_closed:
684 assert(false);
685 }
686
687 assert(false);
688}
689
[032bbe7]690/** Process segment URG field.
691 *
692 * @param conn Connection
693 * @param seg Segment
694 * @return cp_done if we are done with this segment, cp_continue
695 * if not
696 */
[c5808b41]697static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
698{
699 return cp_continue;
700}
701
[032bbe7]702/** Process segment text.
703 *
704 * @param conn Connection
705 * @param seg Segment
706 * @return cp_done if we are done with this segment, cp_continue
707 * if not
708 */
[c5808b41]709static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
710{
[0093ab6]711 size_t text_size;
712 size_t xfer_size;
713
714 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_text(%p, %p)", conn, seg);
715
[4c55a64]716 switch (conn->cstate) {
717 case st_established:
718 case st_fin_wait_1:
719 case st_fin_wait_2:
720 /* OK */
721 break;
722 case st_close_wait:
723 case st_closing:
724 case st_last_ack:
725 case st_time_wait:
726 /* Invalid since FIN has been received. Ignore text. */
727 return cp_continue;
728 case st_listen:
729 case st_syn_sent:
730 case st_syn_received:
731 case st_closed:
732 assert(false);
733 }
734
[0093ab6]735 /*
736 * Process segment text
737 */
738 assert(seq_no_segment_ready(conn, seg));
739
740 /* Trim anything outside our receive window */
741 tcp_conn_trim_seg_to_wnd(conn, seg);
742
[d9ce049]743 fibril_mutex_lock(&conn->rcv_buf_lock);
744
[0093ab6]745 /* Determine how many bytes to copy */
746 text_size = tcp_segment_text_size(seg);
747 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
748
749 /* Copy data to receive buffer */
[d9ce049]750 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
751 xfer_size);
752 conn->rcv_buf_used += xfer_size;
753
754 /* Signal to the receive function that new data has arrived */
755 fibril_condvar_broadcast(&conn->rcv_buf_cv);
756 fibril_mutex_unlock(&conn->rcv_buf_lock);
[0093ab6]757
[32105348]758 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
759
[0093ab6]760 /* Advance RCV.NXT */
761 conn->rcv_nxt += xfer_size;
762
763 /* Update receive window. XXX Not an efficient strategy. */
764 conn->rcv_wnd -= xfer_size;
765
766 /* Send ACK */
767 if (xfer_size > 0)
768 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
769
770 if (xfer_size < seg->len) {
[8c7a054]771 /* Trim part of segment which we just received */
[0093ab6]772 tcp_conn_trim_seg_to_wnd(conn, seg);
[8c7a054]773 } else {
774 /* Nothing left in segment */
775 tcp_segment_delete(seg);
[0093ab6]776 return cp_done;
777 }
778
[c5808b41]779 return cp_continue;
780}
781
[032bbe7]782/** Process segment FIN field.
783 *
784 * @param conn Connection
785 * @param seg Segment
786 * @return cp_done if we are done with this segment, cp_continue
787 * if not
788 */
[c5808b41]789static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
790{
[8c7a054]791 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_fin(%p, %p)", conn, seg);
792
793 /* Only process FIN if no text is left in segment. */
794 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
795 log_msg(LVL_DEBUG, " - FIN found in segment.");
796
[6e88fea]797 /* Send ACK */
798 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
799
[8c7a054]800 conn->rcv_nxt++;
801 conn->rcv_wnd--;
802
[f343a16]803 /* Change connection state */
804 switch (conn->cstate) {
805 case st_listen:
806 case st_syn_sent:
807 case st_closed:
808 /* Connection not synchronized */
809 assert(false);
810 case st_syn_received:
811 case st_established:
812 log_msg(LVL_DEBUG, "FIN received -> Close-Wait");
813 conn->cstate = st_close_wait;
814 break;
815 case st_fin_wait_1:
816 log_msg(LVL_DEBUG, "FIN received -> Closing");
817 conn->cstate = st_closing;
818 break;
819 case st_fin_wait_2:
820 log_msg(LVL_DEBUG, "FIN received -> Time-Wait");
821 conn->cstate = st_time_wait;
[2a3214e]822 /* Start the Time-Wait timer */
823 tcp_conn_tw_timer_set(conn);
[f343a16]824 break;
825 case st_close_wait:
826 case st_closing:
827 case st_last_ack:
828 /* Do nothing */
829 break;
830 case st_time_wait:
[2a3214e]831 /* Restart the Time-Wait timer */
832 tcp_conn_tw_timer_set(conn);
[f343a16]833 break;
834 }
[8c7a054]835
836 /* Add FIN to the receive buffer */
837 fibril_mutex_lock(&conn->rcv_buf_lock);
838 conn->rcv_buf_fin = true;
839 fibril_condvar_broadcast(&conn->rcv_buf_cv);
840 fibril_mutex_unlock(&conn->rcv_buf_lock);
841
842 tcp_segment_delete(seg);
843 return cp_done;
844 }
845
[c5808b41]846 return cp_continue;
847}
848
849/** Process incoming segment.
850 *
851 * We are in connection state where segments are processed in order
852 * of sequence number. This processes one segment taken from the
853 * connection incoming segments queue.
[032bbe7]854 *
855 * @param conn Connection
856 * @param seg Segment
[c5808b41]857 */
858static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
859{
860 log_msg(LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
861
862 /* Check whether segment is acceptable */
863 /* XXX Permit valid ACKs, URGs and RSTs */
864/* if (!seq_no_segment_acceptable(conn, seg)) {
865 log_msg(LVL_WARN, "Segment not acceptable, dropping.");
866 if ((seg->ctrl & CTL_RST) == 0) {
867 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
868 }
869 return;
870 }
871*/
872
873 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
874 return;
875
876 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
877 return;
878
879 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
880 return;
881
882 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
883 return;
884
885 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
886 return;
887
888 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
889 return;
890
891 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
892 return;
[0093ab6]893
[8c7a054]894 /*
895 * If anything is left from the segment, insert it back into the
896 * incoming segments queue.
897 */
898 if (seg->len > 0)
899 tcp_iqueue_insert_seg(&conn->incoming, seg);
900 else
901 tcp_segment_delete(seg);
[c5808b41]902}
903
[032bbe7]904/** Segment arrived on a connection.
905 *
906 * @param conn Connection
907 * @param seg Segment
908 */
[c5808b41]909void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
910{
911 log_msg(LVL_DEBUG, "tcp_conn_segment_arrived(%p, %p)", conn, seg);
912
913 switch (conn->cstate) {
914 case st_listen:
915 tcp_conn_sa_listen(conn, seg); break;
916 case st_syn_sent:
917 tcp_conn_sa_syn_sent(conn, seg); break;
918 case st_syn_received:
919 case st_established:
920 case st_fin_wait_1:
921 case st_fin_wait_2:
922 case st_close_wait:
923 case st_closing:
924 case st_last_ack:
925 case st_time_wait:
926 /* Process segments in order of sequence number */
927 tcp_conn_sa_queue(conn, seg); break;
928 case st_closed:
929 assert(false);
930 }
931}
932
[2a3214e]933/** Time-Wait timeout handler.
934 *
935 * @param arg Connection
936 */
937static void tw_timeout_func(void *arg)
938{
939 tcp_conn_t *conn = (tcp_conn_t *) arg;
940
941 log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
942 log_msg(LVL_DEBUG, " TW Timeout -> Closed");
943
944 tcp_conn_remove(conn);
945 conn->cstate = st_closed;
946}
947
948/** Start or restart the Time-Wait timeout.
949 *
950 * @param conn Connection
951 */
952void tcp_conn_tw_timer_set(tcp_conn_t *conn)
953{
954 fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
955 (void *)conn);
956}
957
[032bbe7]958/** Trim segment to the receive window.
959 *
960 * @param conn Connection
961 * @param seg Segment
962 */
[0093ab6]963void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
964{
965 uint32_t left, right;
966
967 seq_no_seg_trim_calc(conn, seg, &left, &right);
968 tcp_segment_trim(seg, left, right);
969}
970
[032bbe7]971/** Handle unexpected segment received on a socket pair.
972 *
973 * We reply with an RST unless the received segment has RST.
974 *
975 * @param sp Socket pair which received the segment
976 * @param seg Unexpected segment
977 */
[c5808b41]978void tcp_unexpected_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
979{
980 log_msg(LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", sp, seg);
981
982 if ((seg->ctrl & CTL_RST) == 0)
983 tcp_reply_rst(sp, seg);
984}
985
[032bbe7]986/** Compute flipped socket pair for response.
987 *
[32105348]988 * Flipped socket pair has local and foreign sockets exchanged.
[032bbe7]989 *
990 * @param sp Socket pair
991 * @param fsp Place to store flipped socket pair
992 */
[c5808b41]993void tcp_sockpair_flipped(tcp_sockpair_t *sp, tcp_sockpair_t *fsp)
994{
995 fsp->local = sp->foreign;
996 fsp->foreign = sp->local;
997}
998
[032bbe7]999/** Send RST in response to an incoming segment.
1000 *
1001 * @param sp Socket pair which received the segment
1002 * @param seg Incoming segment
1003 */
[c5808b41]1004void tcp_reply_rst(tcp_sockpair_t *sp, tcp_segment_t *seg)
1005{
1006 tcp_sockpair_t rsp;
1007 tcp_segment_t *rseg;
1008
1009 log_msg(LVL_DEBUG, "tcp_reply_rst(%p, %p)", sp, seg);
1010
1011 tcp_sockpair_flipped(sp, &rsp);
1012 rseg = tcp_segment_make_rst(seg);
1013 tcp_transmit_segment(&rsp, rseg);
1014}
1015
1016/**
1017 * @}
1018 */
Note: See TracBrowser for help on using the repository browser.