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

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

Stricter RST processing in Syn-sent state.

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