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
Line 
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/**
34 * @file TCP connection processing and state machine
35 */
36
37#include <adt/list.h>
38#include <bool.h>
39#include <errno.h>
40#include <io/log.h>
41#include <macros.h>
42#include <stdlib.h>
43#include "conn.h"
44#include "iqueue.h"
45#include "segment.h"
46#include "seq_no.h"
47#include "state.h"
48#include "tcp_type.h"
49#include "tqueue.h"
50
51#define RCV_BUF_SIZE 4096
52#define SND_BUF_SIZE 4096
53
54#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
55#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
56
57LIST_INITIALIZE(conn_list);
58
59static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
60static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
61
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 */
68tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
69{
70 tcp_conn_t *conn = NULL;
71
72 /* Allocate connection structure */
73 conn = calloc(1, sizeof(tcp_conn_t));
74 if (conn == NULL)
75 goto error;
76
77 conn->tw_timer = fibril_timer_create();
78 if (conn->tw_timer == NULL)
79 goto error;
80
81 /* Allocate receive buffer */
82 fibril_mutex_initialize(&conn->rcv_buf_lock);
83 fibril_condvar_initialize(&conn->rcv_buf_cv);
84 conn->rcv_buf_size = RCV_BUF_SIZE;
85 conn->rcv_buf_used = 0;
86 conn->rcv_buf_fin = false;
87
88 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
89 if (conn->rcv_buf == NULL)
90 goto error;
91
92 /** Allocate send buffer */
93 conn->snd_buf_size = SND_BUF_SIZE;
94 conn->snd_buf_used = 0;
95 conn->snd_buf_fin = false;
96 conn->snd_buf = calloc(1, conn->snd_buf_size);
97 if (conn->snd_buf == NULL)
98 goto error;
99
100 /* Set up receive window. */
101 conn->rcv_wnd = conn->rcv_buf_size;
102
103 /* Initialize incoming segment queue */
104 tcp_iqueue_init(&conn->incoming, conn);
105
106 /* Initialize retransmission queue */
107 tcp_tqueue_init(&conn->retransmit, conn);
108
109 conn->cstate = st_listen;
110 conn->fin_is_acked = false;
111 conn->ident.local = *lsock;
112 if (fsock != NULL)
113 conn->ident.foreign = *fsock;
114
115 return conn;
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;
128}
129
130/** Enlist connection.
131 *
132 * Add connection to the connection map.
133 */
134void tcp_conn_add(tcp_conn_t *conn)
135{
136 list_append(&conn->link, &conn_list);
137}
138
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
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 */
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
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:
178 log_msg(LVL_DEBUG, "FIN sent -> Last-Ack");
179 conn->cstate = st_last_ack;
180 break;
181 default:
182 assert(false);
183 }
184
185 conn->fin_is_acked = false;
186}
187
188/** Compare two sockets.
189 *
190 * Two sockets are equal if the address is equal and the port number
191 * is equal.
192 */
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
209/** Match socket with pattern. */
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
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 */
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
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
272/** Segment arrived in Listen state.
273 *
274 * @param conn Connection
275 * @param seg Segment
276 */
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
302
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
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
322 conn->cstate = st_syn_received;
323
324 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
325
326 tcp_segment_delete(seg);
327}
328
329/** Segment arrived in Syn-Sent state.
330 *
331 * @param conn Connection
332 * @param seg Segment
333 */
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;
368
369 /*
370 * Prune acked segments from retransmission queue and
371 * possibly transmit more data.
372 */
373 tcp_tqueue_ack_received(conn);
374 }
375
376 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
377
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
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 }
398
399 tcp_segment_delete(seg);
400}
401
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 */
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
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 */
437static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
438{
439 /* TODO */
440 return cp_continue;
441}
442
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 */
450static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
451{
452 /* TODO */
453 return cp_continue;
454}
455
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 */
463static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
464{
465 /* TODO */
466 return cp_continue;
467}
468
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 */
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);
482 tcp_segment_delete(seg);
483 return cp_done;
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
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 */
503static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
504{
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
511 if (!seq_no_ack_acceptable(conn, seg->ack)) {
512 log_msg(LVL_DEBUG, "ACK not acceptable.");
513 if (!seq_no_ack_duplicate(conn, seg->ack)) {
514 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
515 "Send ACK and drop.");
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;
520 } else {
521 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
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;
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);
536 }
537
538 /*
539 * Prune acked segments from retransmission queue and
540 * possibly transmit more data.
541 */
542 tcp_tqueue_ack_received(conn);
543
544 return cp_continue;
545}
546
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 */
554static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
555{
556 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
557 return cp_done;
558
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
564 return cp_continue;
565}
566
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 */
574static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
575{
576 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
577 return cp_done;
578
579 /* TODO */
580 return cp_continue;
581}
582
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 */
590static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
591{
592 /* The same processing as in Established state */
593 return tcp_conn_seg_proc_ack_est(conn, seg);
594}
595
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 */
603static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
604{
605 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
606 return cp_done;
607
608 /* TODO */
609 return cp_continue;
610}
611
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 */
619static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
620{
621 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
622 return cp_done;
623
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
631 return cp_continue;
632}
633
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 */
641static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
642{
643 /* Nothing to do */
644 return cp_continue;
645}
646
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 */
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.");
660 tcp_segment_delete(seg);
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
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 */
697static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
698{
699 return cp_continue;
700}
701
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 */
709static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
710{
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
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
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
743 fibril_mutex_lock(&conn->rcv_buf_lock);
744
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 */
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);
757
758 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
759
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) {
771 /* Trim part of segment which we just received */
772 tcp_conn_trim_seg_to_wnd(conn, seg);
773 } else {
774 /* Nothing left in segment */
775 tcp_segment_delete(seg);
776 return cp_done;
777 }
778
779 return cp_continue;
780}
781
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 */
789static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
790{
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
797 /* Send ACK */
798 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
799
800 conn->rcv_nxt++;
801 conn->rcv_wnd--;
802
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;
822 /* Start the Time-Wait timer */
823 tcp_conn_tw_timer_set(conn);
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:
831 /* Restart the Time-Wait timer */
832 tcp_conn_tw_timer_set(conn);
833 break;
834 }
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
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.
854 *
855 * @param conn Connection
856 * @param seg Segment
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;
893
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);
902}
903
904/** Segment arrived on a connection.
905 *
906 * @param conn Connection
907 * @param seg Segment
908 */
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
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
958/** Trim segment to the receive window.
959 *
960 * @param conn Connection
961 * @param seg Segment
962 */
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
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 */
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
986/** Compute flipped socket pair for response.
987 *
988 * Flipped socket pair has local and foreign sockets exchanged.
989 *
990 * @param sp Socket pair
991 * @param fsp Place to store flipped socket pair
992 */
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
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 */
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.