source: mainline/uspace/srv/net/tcp/conn.c@ 58e9dec

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

Definitions for RFC 6335 port number ranges.

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