source: mainline/uspace/srv/net/tcp/conn.c@ 8d48c7e

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

Find the association to deliver the datagram using amap.

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