source: mainline/uspace/srv/net/tcp/conn.c@ 98abd40

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 98abd40 was a2e3ee6, checked in by Martin Decky <martin@…>, 12 years ago

use new network address infrastructure (towards IPv6 support)

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