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

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

Eliminate busy waiting when TCP send buffer is full.

  • Property mode set to 100644
File size: 28.6 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 "tcp_type.h"
48#include "tqueue.h"
49#include "ucall.h"
50
51#define RCV_BUF_SIZE 4096/*2*/
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);
61static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
62
63/** Create new segment structure.
64 *
65 * @param lsock Local socket (will be deeply copied)
66 * @param fsock Foreign socket (will be deeply copied)
67 * @return New segment or NULL
68 */
69tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
70{
71 tcp_conn_t *conn = NULL;
72 bool tqueue_inited = false;
73
74 /* Allocate connection structure */
75 conn = calloc(1, sizeof(tcp_conn_t));
76 if (conn == NULL)
77 goto error;
78
79 conn->tw_timer = fibril_timer_create();
80 if (conn->tw_timer == NULL)
81 goto error;
82
83 /* Allocate receive buffer */
84 fibril_mutex_initialize(&conn->rcv_buf_lock);
85 fibril_condvar_initialize(&conn->rcv_buf_cv);
86 conn->rcv_buf_size = RCV_BUF_SIZE;
87 conn->rcv_buf_used = 0;
88 conn->rcv_buf_fin = false;
89
90 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
91 if (conn->rcv_buf == NULL)
92 goto error;
93
94 /** Allocate send buffer */
95 fibril_mutex_initialize(&conn->snd_buf_lock);
96 fibril_condvar_initialize(&conn->snd_buf_cv);
97 conn->snd_buf_size = SND_BUF_SIZE;
98 conn->snd_buf_used = 0;
99 conn->snd_buf_fin = false;
100 conn->snd_buf = calloc(1, conn->snd_buf_size);
101 if (conn->snd_buf == NULL)
102 goto error;
103
104 /* Set up receive window. */
105 conn->rcv_wnd = conn->rcv_buf_size;
106
107 /* Initialize incoming segment queue */
108 tcp_iqueue_init(&conn->incoming, conn);
109
110 /* Initialize retransmission queue */
111 if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
112 goto error;
113
114 tqueue_inited = true;
115
116 /* Connection state change signalling */
117 fibril_mutex_initialize(&conn->cstate_lock);
118 fibril_condvar_initialize(&conn->cstate_cv);
119
120 conn->cstate = st_listen;
121 conn->reset = false;
122 conn->ap = ap_passive;
123 conn->fin_is_acked = false;
124 conn->ident.local = *lsock;
125 if (fsock != NULL)
126 conn->ident.foreign = *fsock;
127
128 return conn;
129
130error:
131 if (tqueue_inited)
132 tcp_tqueue_fini(&conn->retransmit);
133 if (conn != NULL && conn->rcv_buf != NULL)
134 free(conn->rcv_buf);
135 if (conn != NULL && conn->snd_buf != NULL)
136 free(conn->snd_buf);
137 if (conn != NULL && conn->tw_timer != NULL)
138 fibril_timer_destroy(conn->tw_timer);
139 if (conn != NULL)
140 free(conn);
141
142 return NULL;
143}
144
145/** Enlist connection.
146 *
147 * Add connection to the connection map.
148 */
149void tcp_conn_add(tcp_conn_t *conn)
150{
151 list_append(&conn->link, &conn_list);
152}
153
154/** Delist connection.
155 *
156 * Remove connection from the connection map.
157 */
158void tcp_conn_remove(tcp_conn_t *conn)
159{
160 list_remove(&conn->link);
161}
162
163static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
164{
165 fibril_mutex_lock(&conn->cstate_lock);
166 conn->cstate = nstate;
167 fibril_condvar_broadcast(&conn->cstate_cv);
168 fibril_mutex_unlock(&conn->cstate_lock);
169}
170
171/** Synchronize connection.
172 *
173 * This is the first step of an active connection attempt,
174 * sends out SYN and sets up ISS and SND.xxx.
175 */
176void tcp_conn_sync(tcp_conn_t *conn)
177{
178 /* XXX select ISS */
179 conn->iss = 1;
180 conn->snd_nxt = conn->iss;
181 conn->snd_una = conn->iss;
182 conn->ap = ap_active;
183
184 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
185 tcp_conn_state_set(conn, st_syn_sent);
186}
187
188/** FIN has been sent.
189 *
190 * This function should be called when FIN is sent over the connection,
191 * as a result the connection state is changed appropriately.
192 */
193void tcp_conn_fin_sent(tcp_conn_t *conn)
194{
195 switch (conn->cstate) {
196 case st_syn_received:
197 case st_established:
198 log_msg(LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
199 tcp_conn_state_set(conn, st_fin_wait_1);
200 break;
201 case st_close_wait:
202 log_msg(LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
203 tcp_conn_state_set(conn, st_last_ack);
204 break;
205 default:
206 log_msg(LVL_ERROR, "%s: Connection state %d", conn->name,
207 conn->cstate);
208 assert(false);
209 }
210
211 conn->fin_is_acked = false;
212}
213
214/** Compare two sockets.
215 *
216 * Two sockets are equal if the address is equal and the port number
217 * is equal.
218 */
219static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
220{
221 log_msg(LVL_DEBUG, "tcp_socket_match(sock=(%x,%u), pat=(%x,%u))",
222 sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
223
224 if (patt->addr.ipv4 != TCP_IPV4_ANY &&
225 patt->addr.ipv4 != sock->addr.ipv4)
226 return false;
227
228 if (patt->port != TCP_PORT_ANY &&
229 patt->port != sock->port)
230 return false;
231
232 log_msg(LVL_DEBUG, " -> match");
233
234 return true;
235}
236
237/** Match socket with pattern. */
238static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
239{
240 log_msg(LVL_DEBUG, "tcp_sockpair_match(%p, %p)", sp, pattern);
241
242 if (!tcp_socket_match(&sp->local, &pattern->local))
243 return false;
244
245 if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
246 return false;
247
248 return true;
249}
250
251/** Find connection structure for specified socket pair.
252 *
253 * A connection is uniquely identified by a socket pair. Look up our
254 * connection map and return connection structure based on socket pair.
255 *
256 * @param sp Socket pair
257 * @return Connection structure or NULL if not found.
258 */
259tcp_conn_t *tcp_conn_find(tcp_sockpair_t *sp)
260{
261 log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
262
263 list_foreach(conn_list, link) {
264 tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
265 tcp_sockpair_t *csp = &conn->ident;
266 log_msg(LVL_DEBUG, "compare with conn (f:(%x,%u), l:(%x,%u))",
267 csp->foreign.addr.ipv4, csp->foreign.port,
268 csp->local.addr.ipv4, csp->local.port);
269 if (tcp_sockpair_match(sp, csp)) {
270 return conn;
271 }
272 }
273
274 return NULL;
275}
276
277/** Reset connection.
278 *
279 * @param conn Connection
280 */
281static void tcp_conn_reset(tcp_conn_t *conn)
282{
283 log_msg(LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
284 tcp_conn_state_set(conn, st_closed);
285 conn->reset = true;
286
287 tcp_conn_tw_timer_clear(conn);
288 tcp_tqueue_clear(&conn->retransmit);
289
290 fibril_condvar_broadcast(&conn->rcv_buf_cv);
291 fibril_condvar_broadcast(&conn->snd_buf_cv);
292}
293
294/** Signal to the user that connection has been reset.
295 *
296 * Send an out-of-band signal to the user.
297 */
298static void tcp_reset_signal(tcp_conn_t *conn)
299{
300 /* TODO */
301 log_msg(LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
302}
303
304/** Determine if SYN has been received.
305 *
306 * @param conn Connection
307 * @return @c true if SYN has been received, @c false otherwise.
308 */
309bool tcp_conn_got_syn(tcp_conn_t *conn)
310{
311 switch (conn->cstate) {
312 case st_listen:
313 case st_syn_sent:
314 return false;
315 case st_syn_received:
316 case st_established:
317 case st_fin_wait_1:
318 case st_fin_wait_2:
319 case st_close_wait:
320 case st_closing:
321 case st_last_ack:
322 case st_time_wait:
323 return true;
324 case st_closed:
325 log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
326 assert(false);
327 }
328
329 assert(false);
330}
331
332/** Segment arrived in Listen state.
333 *
334 * @param conn Connection
335 * @param seg Segment
336 */
337static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
338{
339 log_msg(LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
340
341 if ((seg->ctrl & CTL_RST) != 0) {
342 log_msg(LVL_DEBUG, "Ignoring incoming RST.");
343 return;
344 }
345
346 if ((seg->ctrl & CTL_ACK) != 0) {
347 log_msg(LVL_DEBUG, "Incoming ACK, send acceptable RST.");
348 tcp_reply_rst(&conn->ident, seg);
349 return;
350 }
351
352 if ((seg->ctrl & CTL_SYN) == 0) {
353 log_msg(LVL_DEBUG, "SYN not present. Ignoring segment.");
354 return;
355 }
356
357 log_msg(LVL_DEBUG, "Got SYN, sending SYN, ACK.");
358
359 conn->rcv_nxt = seg->seq + 1;
360 conn->irs = seg->seq;
361
362
363 log_msg(LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
364
365 if (seg->len > 1)
366 log_msg(LVL_WARN, "SYN combined with data, ignoring data.");
367
368 /* XXX select ISS */
369 conn->iss = 1;
370 conn->snd_nxt = conn->iss;
371 conn->snd_una = conn->iss;
372
373 /*
374 * Surprisingly the spec does not deal with initial window setting.
375 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
376 * will always be accepted as new window setting.
377 */
378 conn->snd_wnd = seg->wnd;
379 conn->snd_wl1 = seg->seq;
380 conn->snd_wl2 = seg->seq;
381
382 tcp_conn_state_set(conn, st_syn_received);
383
384 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
385
386 tcp_segment_delete(seg);
387}
388
389/** Segment arrived in Syn-Sent state.
390 *
391 * @param conn Connection
392 * @param seg Segment
393 */
394static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
395{
396 log_msg(LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
397
398 if ((seg->ctrl & CTL_ACK) != 0) {
399 log_msg(LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
400 conn->snd_una, seg->ack, conn->snd_nxt);
401 if (!seq_no_ack_acceptable(conn, seg->ack)) {
402 log_msg(LVL_WARN, "ACK not acceptable, send RST.");
403 tcp_reply_rst(&conn->ident, seg);
404 return;
405 }
406 }
407
408 if ((seg->ctrl & CTL_RST) != 0) {
409 log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
410 conn->name);
411 /* Reset connection */
412 tcp_conn_reset(conn);
413 /* XXX delete connection */
414 return;
415 }
416
417 /* XXX precedence */
418
419 if ((seg->ctrl & CTL_SYN) == 0) {
420 log_msg(LVL_DEBUG, "No SYN bit, ignoring segment.");
421 return;
422 }
423
424 conn->rcv_nxt = seg->seq + 1;
425 conn->irs = seg->seq;
426
427 if ((seg->ctrl & CTL_ACK) != 0) {
428 conn->snd_una = seg->ack;
429
430 /*
431 * Prune acked segments from retransmission queue and
432 * possibly transmit more data.
433 */
434 tcp_tqueue_ack_received(conn);
435 }
436
437 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
438
439 /*
440 * Surprisingly the spec does not deal with initial window setting.
441 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
442 * will always be accepted as new window setting.
443 */
444 log_msg(LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
445 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
446 conn->snd_wnd = seg->wnd;
447 conn->snd_wl1 = seg->seq;
448 conn->snd_wl2 = seg->seq;
449
450 if (seq_no_syn_acked(conn)) {
451 log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
452 tcp_conn_state_set(conn, st_established);
453 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
454 } else {
455 log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
456 conn->name);
457 tcp_conn_state_set(conn, st_syn_received);
458 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
459 }
460
461 tcp_segment_delete(seg);
462}
463
464/** Segment arrived in state where segments are processed in sequence order.
465 *
466 * Queue segment in incoming segments queue for processing.
467 *
468 * @param conn Connection
469 * @param seg Segment
470 */
471static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
472{
473 tcp_segment_t *pseg;
474
475 log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
476
477 /* Discard unacceptable segments ("old duplicates") */
478 if (!seq_no_segment_acceptable(conn, seg)) {
479 log_msg(LVL_DEBUG, "Replying ACK to unacceptable segment.");
480 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
481 tcp_segment_delete(seg);
482 return;
483 }
484
485 /* Queue for processing */
486 tcp_iqueue_insert_seg(&conn->incoming, seg);
487
488 /*
489 * Process all segments from incoming queue that are ready.
490 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
491 *
492 * XXX Need to return ACK for unacceptable segments
493 */
494 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
495 tcp_conn_seg_process(conn, pseg);
496}
497
498/** Process segment RST field.
499 *
500 * @param conn Connection
501 * @param seg Segment
502 * @return cp_done if we are done with this segment, cp_continue
503 * if not
504 */
505static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
506{
507 if ((seg->ctrl & CTL_RST) == 0)
508 return cp_continue;
509
510 switch (conn->cstate) {
511 case st_syn_received:
512 /* XXX In case of passive open, revert to Listen state */
513 if (conn->ap == ap_passive) {
514 tcp_conn_state_set(conn, st_listen);
515 /* XXX Revert conn->ident */
516 tcp_conn_tw_timer_clear(conn);
517 tcp_tqueue_clear(&conn->retransmit);
518 } else {
519 tcp_conn_reset(conn);
520 }
521 break;
522 case st_established:
523 case st_fin_wait_1:
524 case st_fin_wait_2:
525 case st_close_wait:
526 /* General "connection reset" signal */
527 tcp_reset_signal(conn);
528 tcp_conn_reset(conn);
529 break;
530 case st_closing:
531 case st_last_ack:
532 case st_time_wait:
533 tcp_conn_reset(conn);
534 break;
535 case st_listen:
536 case st_syn_sent:
537 case st_closed:
538 assert(false);
539 }
540
541 return cp_done;
542}
543
544/** Process segment security and precedence fields.
545 *
546 * @param conn Connection
547 * @param seg Segment
548 * @return cp_done if we are done with this segment, cp_continue
549 * if not
550 */
551static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
552{
553 /* TODO */
554 return cp_continue;
555}
556
557/** Process segment SYN field.
558 *
559 * @param conn Connection
560 * @param seg Segment
561 * @return cp_done if we are done with this segment, cp_continue
562 * if not
563 */
564static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
565{
566 if ((seg->ctrl & CTL_SYN) == 0)
567 return cp_continue;
568
569 /*
570 * Assert SYN is in receive window, otherwise this step should not
571 * be reached.
572 */
573 assert(seq_no_in_rcv_wnd(conn, seg->seq));
574
575 log_msg(LVL_WARN, "SYN is in receive window, should send reset. XXX");
576
577 /*
578 * TODO
579 *
580 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
581 * flush segment queues. Send unsolicited "connection reset" signal
582 * to user, connection -> closed state, delete TCB, return.
583 */
584 return cp_done;
585}
586
587/** Process segment ACK field in Syn-Received state.
588 *
589 * @param conn Connection
590 * @param seg Segment
591 * @return cp_done if we are done with this segment, cp_continue
592 * if not
593 */
594static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
595{
596 if (!seq_no_ack_acceptable(conn, seg->ack)) {
597 /* ACK is not acceptable, send RST. */
598 log_msg(LVL_WARN, "Segment ACK not acceptable, sending RST.");
599 tcp_reply_rst(&conn->ident, seg);
600 tcp_segment_delete(seg);
601 return cp_done;
602 }
603
604 log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
605
606 tcp_conn_state_set(conn, st_established);
607
608 /* XXX Not mentioned in spec?! */
609 conn->snd_una = seg->ack;
610
611 return cp_continue;
612}
613
614/** Process segment ACK field in Established state.
615 *
616 * @param conn Connection
617 * @param seg Segment
618 * @return cp_done if we are done with this segment, cp_continue
619 * if not
620 */
621static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
622{
623 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
624
625 log_msg(LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
626 (unsigned)seg->ack, (unsigned)conn->snd_una,
627 (unsigned)conn->snd_nxt);
628
629 if (!seq_no_ack_acceptable(conn, seg->ack)) {
630 log_msg(LVL_DEBUG, "ACK not acceptable.");
631 if (!seq_no_ack_duplicate(conn, seg->ack)) {
632 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
633 "Send ACK and drop.");
634 /* Not acceptable, not duplicate. Send ACK and drop. */
635 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
636 tcp_segment_delete(seg);
637 return cp_done;
638 } else {
639 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
640 }
641 } else {
642 /* Update SND.UNA */
643 conn->snd_una = seg->ack;
644 }
645
646 if (seq_no_new_wnd_update(conn, seg)) {
647 conn->snd_wnd = seg->wnd;
648 conn->snd_wl1 = seg->seq;
649 conn->snd_wl2 = seg->ack;
650
651 log_msg(LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
652 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
653 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
654 }
655
656 /*
657 * Prune acked segments from retransmission queue and
658 * possibly transmit more data.
659 */
660 tcp_tqueue_ack_received(conn);
661
662 return cp_continue;
663}
664
665/** Process segment ACK field in Fin-Wait-1 state.
666 *
667 * @param conn Connection
668 * @param seg Segment
669 * @return cp_done if we are done with this segment, cp_continue
670 * if not
671 */
672static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
673{
674 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
675 return cp_done;
676
677 if (conn->fin_is_acked) {
678 log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
679 tcp_conn_state_set(conn, st_fin_wait_2);
680 }
681
682 return cp_continue;
683}
684
685/** Process segment ACK field in Fin-Wait-2 state.
686 *
687 * @param conn Connection
688 * @param seg Segment
689 * @return cp_done if we are done with this segment, cp_continue
690 * if not
691 */
692static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
693{
694 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
695 return cp_done;
696
697 /* TODO */
698 return cp_continue;
699}
700
701/** Process segment ACK field in Close-Wait state.
702 *
703 * @param conn Connection
704 * @param seg Segment
705 * @return cp_done if we are done with this segment, cp_continue
706 * if not
707 */
708static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
709{
710 /* The same processing as in Established state */
711 return tcp_conn_seg_proc_ack_est(conn, seg);
712}
713
714/** Process segment ACK field in Closing state.
715 *
716 * @param conn Connection
717 * @param seg Segment
718 * @return cp_done if we are done with this segment, cp_continue
719 * if not
720 */
721static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
722{
723 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
724 return cp_done;
725
726 /* TODO */
727 return cp_continue;
728}
729
730/** Process segment ACK field in Last-Ack state.
731 *
732 * @param conn Connection
733 * @param seg Segment
734 * @return cp_done if we are done with this segment, cp_continue
735 * if not
736 */
737static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
738{
739 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
740 return cp_done;
741
742 if (conn->fin_is_acked) {
743 log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
744 tcp_conn_remove(conn);
745 tcp_conn_state_set(conn, st_closed);
746 return cp_done;
747 }
748
749 return cp_continue;
750}
751
752/** Process segment ACK field in Time-Wait state.
753 *
754 * @param conn Connection
755 * @param seg Segment
756 * @return cp_done if we are done with this segment, cp_continue
757 * if not
758 */
759static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
760{
761 /* Nothing to do */
762 return cp_continue;
763}
764
765/** Process segment ACK field.
766 *
767 * @param conn Connection
768 * @param seg Segment
769 * @return cp_done if we are done with this segment, cp_continue
770 * if not
771 */
772static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
773{
774 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
775 conn->name, conn, seg);
776
777 if ((seg->ctrl & CTL_ACK) == 0) {
778 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
779 tcp_segment_delete(seg);
780 return cp_done;
781 }
782
783 switch (conn->cstate) {
784 case st_syn_received:
785 return tcp_conn_seg_proc_ack_sr(conn, seg);
786 case st_established:
787 return tcp_conn_seg_proc_ack_est(conn, seg);
788 case st_fin_wait_1:
789 return tcp_conn_seg_proc_ack_fw1(conn, seg);
790 case st_fin_wait_2:
791 return tcp_conn_seg_proc_ack_fw2(conn, seg);
792 case st_close_wait:
793 return tcp_conn_seg_proc_ack_cw(conn, seg);
794 case st_closing:
795 return tcp_conn_seg_proc_ack_cls(conn, seg);
796 case st_last_ack:
797 return tcp_conn_seg_proc_ack_la(conn, seg);
798 case st_time_wait:
799 return tcp_conn_seg_proc_ack_tw(conn, seg);
800 case st_listen:
801 case st_syn_sent:
802 case st_closed:
803 assert(false);
804 }
805
806 assert(false);
807}
808
809/** Process segment URG field.
810 *
811 * @param conn Connection
812 * @param seg Segment
813 * @return cp_done if we are done with this segment, cp_continue
814 * if not
815 */
816static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
817{
818 return cp_continue;
819}
820
821/** Process segment text.
822 *
823 * @param conn Connection
824 * @param seg Segment
825 * @return cp_done if we are done with this segment, cp_continue
826 * if not
827 */
828static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
829{
830 size_t text_size;
831 size_t xfer_size;
832
833 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
834 conn->name, conn, seg);
835
836 switch (conn->cstate) {
837 case st_established:
838 case st_fin_wait_1:
839 case st_fin_wait_2:
840 /* OK */
841 break;
842 case st_close_wait:
843 case st_closing:
844 case st_last_ack:
845 case st_time_wait:
846 /* Invalid since FIN has been received. Ignore text. */
847 return cp_continue;
848 case st_listen:
849 case st_syn_sent:
850 case st_syn_received:
851 case st_closed:
852 assert(false);
853 }
854
855 /*
856 * Process segment text
857 */
858 assert(seq_no_segment_ready(conn, seg));
859
860 /* Trim anything outside our receive window */
861 tcp_conn_trim_seg_to_wnd(conn, seg);
862
863 fibril_mutex_lock(&conn->rcv_buf_lock);
864
865 /* Determine how many bytes to copy */
866 text_size = tcp_segment_text_size(seg);
867 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
868
869 /* Copy data to receive buffer */
870 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
871 xfer_size);
872 conn->rcv_buf_used += xfer_size;
873
874 /* Signal to the receive function that new data has arrived */
875 fibril_condvar_broadcast(&conn->rcv_buf_cv);
876 fibril_mutex_unlock(&conn->rcv_buf_lock);
877
878 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
879
880 /* Advance RCV.NXT */
881 conn->rcv_nxt += xfer_size;
882
883 /* Update receive window. XXX Not an efficient strategy. */
884 conn->rcv_wnd -= xfer_size;
885
886 /* Send ACK */
887 if (xfer_size > 0)
888 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
889
890 if (xfer_size < seg->len) {
891 /* Trim part of segment which we just received */
892 tcp_conn_trim_seg_to_wnd(conn, seg);
893 } else {
894 log_msg(LVL_DEBUG, "%s: Nothing left in segment, dropping "
895 "(xfer_size=%zu, SEG.LEN=%zu, seg->ctrl=%u)",
896 conn->name, xfer_size, seg->len, (unsigned)seg->ctrl);
897 /* Nothing left in segment */
898 tcp_segment_delete(seg);
899 return cp_done;
900 }
901
902 return cp_continue;
903}
904
905/** Process segment FIN field.
906 *
907 * @param conn Connection
908 * @param seg Segment
909 * @return cp_done if we are done with this segment, cp_continue
910 * if not
911 */
912static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
913{
914 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
915 conn->name, conn, seg);
916 log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
917 (unsigned) seg->ctrl);
918
919 /* Only process FIN if no text is left in segment. */
920 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
921 log_msg(LVL_DEBUG, " - FIN found in segment.");
922
923 /* Send ACK */
924 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
925
926 conn->rcv_nxt++;
927 conn->rcv_wnd--;
928
929 /* Change connection state */
930 switch (conn->cstate) {
931 case st_listen:
932 case st_syn_sent:
933 case st_closed:
934 /* Connection not synchronized */
935 assert(false);
936 case st_syn_received:
937 case st_established:
938 log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
939 conn->name);
940 tcp_conn_state_set(conn, st_close_wait);
941 break;
942 case st_fin_wait_1:
943 log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
944 conn->name);
945 tcp_conn_state_set(conn, st_closing);
946 break;
947 case st_fin_wait_2:
948 log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
949 conn->name);
950 tcp_conn_state_set(conn, st_time_wait);
951 /* Start the Time-Wait timer */
952 tcp_conn_tw_timer_set(conn);
953 break;
954 case st_close_wait:
955 case st_closing:
956 case st_last_ack:
957 /* Do nothing */
958 break;
959 case st_time_wait:
960 /* Restart the Time-Wait timer */
961 tcp_conn_tw_timer_set(conn);
962 break;
963 }
964
965 /* Add FIN to the receive buffer */
966 fibril_mutex_lock(&conn->rcv_buf_lock);
967 conn->rcv_buf_fin = true;
968 fibril_condvar_broadcast(&conn->rcv_buf_cv);
969 fibril_mutex_unlock(&conn->rcv_buf_lock);
970
971 tcp_segment_delete(seg);
972 return cp_done;
973 }
974
975 return cp_continue;
976}
977
978/** Process incoming segment.
979 *
980 * We are in connection state where segments are processed in order
981 * of sequence number. This processes one segment taken from the
982 * connection incoming segments queue.
983 *
984 * @param conn Connection
985 * @param seg Segment
986 */
987static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
988{
989 log_msg(LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
990 tcp_segment_dump(seg);
991
992 /* Check whether segment is acceptable */
993 /* XXX Permit valid ACKs, URGs and RSTs */
994/* if (!seq_no_segment_acceptable(conn, seg)) {
995 log_msg(LVL_WARN, "Segment not acceptable, dropping.");
996 if ((seg->ctrl & CTL_RST) == 0) {
997 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
998 }
999 return;
1000 }
1001*/
1002
1003 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
1004 return;
1005
1006 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
1007 return;
1008
1009 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
1010 return;
1011
1012 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
1013 return;
1014
1015 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
1016 return;
1017
1018 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
1019 return;
1020
1021 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
1022 return;
1023
1024 /*
1025 * If anything is left from the segment, insert it back into the
1026 * incoming segments queue.
1027 */
1028 if (seg->len > 0) {
1029 log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
1030 seg, (size_t) seg->len);
1031 tcp_iqueue_insert_seg(&conn->incoming, seg);
1032 } else {
1033 tcp_segment_delete(seg);
1034 }
1035}
1036
1037/** Segment arrived on a connection.
1038 *
1039 * @param conn Connection
1040 * @param seg Segment
1041 */
1042void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
1043{
1044 log_msg(LVL_DEBUG, "%c: tcp_conn_segment_arrived(%p)",
1045 conn->name, seg);
1046
1047 switch (conn->cstate) {
1048 case st_listen:
1049 tcp_conn_sa_listen(conn, seg); break;
1050 case st_syn_sent:
1051 tcp_conn_sa_syn_sent(conn, seg); break;
1052 case st_syn_received:
1053 case st_established:
1054 case st_fin_wait_1:
1055 case st_fin_wait_2:
1056 case st_close_wait:
1057 case st_closing:
1058 case st_last_ack:
1059 case st_time_wait:
1060 /* Process segments in order of sequence number */
1061 tcp_conn_sa_queue(conn, seg); break;
1062 case st_closed:
1063 log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
1064 assert(false);
1065 }
1066}
1067
1068/** Time-Wait timeout handler.
1069 *
1070 * @param arg Connection
1071 */
1072static void tw_timeout_func(void *arg)
1073{
1074 tcp_conn_t *conn = (tcp_conn_t *) arg;
1075
1076 log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
1077
1078 if (conn->cstate == st_closed) {
1079 log_msg(LVL_DEBUG, "Connection already closed.");
1080 return;
1081 }
1082
1083 log_msg(LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
1084 tcp_conn_remove(conn);
1085 tcp_conn_state_set(conn, st_closed);
1086}
1087
1088/** Start or restart the Time-Wait timeout.
1089 *
1090 * @param conn Connection
1091 */
1092void tcp_conn_tw_timer_set(tcp_conn_t *conn)
1093{
1094 fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
1095 (void *)conn);
1096}
1097
1098/** Clear the Time-Wait timeout.
1099 *
1100 * @param conn Connection
1101 */
1102void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1103{
1104 fibril_timer_clear(conn->tw_timer);
1105}
1106
1107/** Trim segment to the receive window.
1108 *
1109 * @param conn Connection
1110 * @param seg Segment
1111 */
1112void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
1113{
1114 uint32_t left, right;
1115
1116 seq_no_seg_trim_calc(conn, seg, &left, &right);
1117 tcp_segment_trim(seg, left, right);
1118}
1119
1120/** Handle unexpected segment received on a socket pair.
1121 *
1122 * We reply with an RST unless the received segment has RST.
1123 *
1124 * @param sp Socket pair which received the segment
1125 * @param seg Unexpected segment
1126 */
1127void tcp_unexpected_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
1128{
1129 log_msg(LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", sp, seg);
1130
1131 if ((seg->ctrl & CTL_RST) == 0)
1132 tcp_reply_rst(sp, seg);
1133}
1134
1135/** Compute flipped socket pair for response.
1136 *
1137 * Flipped socket pair has local and foreign sockets exchanged.
1138 *
1139 * @param sp Socket pair
1140 * @param fsp Place to store flipped socket pair
1141 */
1142void tcp_sockpair_flipped(tcp_sockpair_t *sp, tcp_sockpair_t *fsp)
1143{
1144 fsp->local = sp->foreign;
1145 fsp->foreign = sp->local;
1146}
1147
1148/** Send RST in response to an incoming segment.
1149 *
1150 * @param sp Socket pair which received the segment
1151 * @param seg Incoming segment
1152 */
1153void tcp_reply_rst(tcp_sockpair_t *sp, tcp_segment_t *seg)
1154{
1155 tcp_segment_t *rseg;
1156
1157 log_msg(LVL_DEBUG, "tcp_reply_rst(%p, %p)", sp, seg);
1158
1159 rseg = tcp_segment_make_rst(seg);
1160 tcp_transmit_segment(sp, rseg);
1161}
1162
1163/**
1164 * @}
1165 */
Note: See TracBrowser for help on using the repository browser.