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

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

fix formatting arguments

  • Property mode set to 100644
File size: 31.3 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup tcp
30 * @{
31 */
32
33/**
34 * @file TCP connection processing and state machine
35 */
36
37#include <adt/list.h>
38#include <bool.h>
39#include <errno.h>
40#include <io/log.h>
41#include <macros.h>
42#include <stdlib.h>
43#include "conn.h"
44#include "iqueue.h"
45#include "segment.h"
46#include "seq_no.h"
47#include "tcp_type.h"
48#include "tqueue.h"
49#include "ucall.h"
50
51#define RCV_BUF_SIZE 4096/*2*/
52#define SND_BUF_SIZE 4096
53
54#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
55#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
56
57LIST_INITIALIZE(conn_list);
58FIBRIL_MUTEX_INITIALIZE(conn_list_lock);
59
60static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
61static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
62static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
63
64/** Create new connection structure.
65 *
66 * @param lsock Local socket (will be deeply copied)
67 * @param fsock Foreign socket (will be deeply copied)
68 * @return New connection or NULL
69 */
70tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
71{
72 tcp_conn_t *conn = NULL;
73 bool tqueue_inited = false;
74
75 /* Allocate connection structure */
76 conn = calloc(1, sizeof(tcp_conn_t));
77 if (conn == NULL)
78 goto error;
79
80 conn->tw_timer = fibril_timer_create();
81 if (conn->tw_timer == NULL)
82 goto error;
83
84 fibril_mutex_initialize(&conn->lock);
85
86 /* One for the user, one for not being in closed state */
87 atomic_set(&conn->refcnt, 2);
88
89 /* Allocate receive buffer */
90 fibril_condvar_initialize(&conn->rcv_buf_cv);
91 conn->rcv_buf_size = RCV_BUF_SIZE;
92 conn->rcv_buf_used = 0;
93 conn->rcv_buf_fin = false;
94
95 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
96 if (conn->rcv_buf == NULL)
97 goto error;
98
99 /** Allocate send buffer */
100 fibril_condvar_initialize(&conn->snd_buf_cv);
101 conn->snd_buf_size = SND_BUF_SIZE;
102 conn->snd_buf_used = 0;
103 conn->snd_buf_fin = false;
104 conn->snd_buf = calloc(1, conn->snd_buf_size);
105 if (conn->snd_buf == NULL)
106 goto error;
107
108 /* Set up receive window. */
109 conn->rcv_wnd = conn->rcv_buf_size;
110
111 /* Initialize incoming segment queue */
112 tcp_iqueue_init(&conn->incoming, conn);
113
114 /* Initialize retransmission queue */
115 if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
116 goto error;
117
118 tqueue_inited = true;
119
120 /* Connection state change signalling */
121 fibril_condvar_initialize(&conn->cstate_cv);
122
123 conn->cstate_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(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(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(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(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(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(LVL_DEBUG, "tcp_conn_state_set() - run user CB");
256 conn->cstate_cb(conn, conn->cstate_cb_arg);
257 } else {
258 log_msg(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(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(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(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 log_msg(LVL_DEBUG2, "tcp_socket_match(sock=(%x,%u), pat=(%x,%u))",
315 sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
316
317 if (patt->addr.ipv4 != TCP_IPV4_ANY &&
318 patt->addr.ipv4 != sock->addr.ipv4)
319 return false;
320
321 if (patt->port != TCP_PORT_ANY &&
322 patt->port != sock->port)
323 return false;
324
325 log_msg(LVL_DEBUG2, " -> match");
326
327 return true;
328}
329
330/** Match socket pair with pattern. */
331static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
332{
333 log_msg(LVL_DEBUG2, "tcp_sockpair_match(%p, %p)", sp, pattern);
334
335 if (!tcp_socket_match(&sp->local, &pattern->local))
336 return false;
337
338 if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
339 return false;
340
341 return true;
342}
343
344/** Find connection structure for specified socket pair.
345 *
346 * A connection is uniquely identified by a socket pair. Look up our
347 * connection map and return connection structure based on socket pair.
348 * The connection reference count is bumped by one.
349 *
350 * @param sp Socket pair
351 * @return Connection structure or NULL if not found.
352 */
353tcp_conn_t *tcp_conn_find_ref(tcp_sockpair_t *sp)
354{
355 log_msg(LVL_DEBUG, "tcp_conn_find_ref(%p)", sp);
356
357 fibril_mutex_lock(&conn_list_lock);
358
359 list_foreach(conn_list, link) {
360 tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
361 tcp_sockpair_t *csp = &conn->ident;
362 log_msg(LVL_DEBUG2, "compare with conn (f:(%x,%u), l:(%x,%u))",
363 csp->foreign.addr.ipv4, csp->foreign.port,
364 csp->local.addr.ipv4, csp->local.port);
365 if (tcp_sockpair_match(sp, csp)) {
366 tcp_conn_addref(conn);
367 fibril_mutex_unlock(&conn_list_lock);
368 return conn;
369 }
370 }
371
372 fibril_mutex_unlock(&conn_list_lock);
373 return NULL;
374}
375
376/** Reset connection.
377 *
378 * @param conn Connection
379 */
380static void tcp_conn_reset(tcp_conn_t *conn)
381{
382 log_msg(LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
383 tcp_conn_state_set(conn, st_closed);
384 conn->reset = true;
385
386 tcp_conn_tw_timer_clear(conn);
387 tcp_tqueue_clear(&conn->retransmit);
388
389 fibril_condvar_broadcast(&conn->rcv_buf_cv);
390 fibril_condvar_broadcast(&conn->snd_buf_cv);
391}
392
393/** Signal to the user that connection has been reset.
394 *
395 * Send an out-of-band signal to the user.
396 */
397static void tcp_reset_signal(tcp_conn_t *conn)
398{
399 /* TODO */
400 log_msg(LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
401}
402
403/** Determine if SYN has been received.
404 *
405 * @param conn Connection
406 * @return @c true if SYN has been received, @c false otherwise.
407 */
408bool tcp_conn_got_syn(tcp_conn_t *conn)
409{
410 switch (conn->cstate) {
411 case st_listen:
412 case st_syn_sent:
413 return false;
414 case st_syn_received:
415 case st_established:
416 case st_fin_wait_1:
417 case st_fin_wait_2:
418 case st_close_wait:
419 case st_closing:
420 case st_last_ack:
421 case st_time_wait:
422 return true;
423 case st_closed:
424 log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
425 assert(false);
426 }
427
428 assert(false);
429}
430
431/** Segment arrived in Listen state.
432 *
433 * @param conn Connection
434 * @param seg Segment
435 */
436static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
437{
438 log_msg(LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
439
440 if ((seg->ctrl & CTL_RST) != 0) {
441 log_msg(LVL_DEBUG, "Ignoring incoming RST.");
442 return;
443 }
444
445 if ((seg->ctrl & CTL_ACK) != 0) {
446 log_msg(LVL_DEBUG, "Incoming ACK, send acceptable RST.");
447 tcp_reply_rst(&conn->ident, seg);
448 return;
449 }
450
451 if ((seg->ctrl & CTL_SYN) == 0) {
452 log_msg(LVL_DEBUG, "SYN not present. Ignoring segment.");
453 return;
454 }
455
456 log_msg(LVL_DEBUG, "Got SYN, sending SYN, ACK.");
457
458 conn->rcv_nxt = seg->seq + 1;
459 conn->irs = seg->seq;
460
461
462 log_msg(LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
463
464 if (seg->len > 1)
465 log_msg(LVL_WARN, "SYN combined with data, ignoring data.");
466
467 /* XXX select ISS */
468 conn->iss = 1;
469 conn->snd_nxt = conn->iss;
470 conn->snd_una = conn->iss;
471
472 /*
473 * Surprisingly the spec does not deal with initial window setting.
474 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
475 * will always be accepted as new window setting.
476 */
477 conn->snd_wnd = seg->wnd;
478 conn->snd_wl1 = seg->seq;
479 conn->snd_wl2 = seg->seq;
480
481 tcp_conn_state_set(conn, st_syn_received);
482
483 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
484
485 tcp_segment_delete(seg);
486}
487
488/** Segment arrived in Syn-Sent state.
489 *
490 * @param conn Connection
491 * @param seg Segment
492 */
493static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
494{
495 log_msg(LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
496
497 if ((seg->ctrl & CTL_ACK) != 0) {
498 log_msg(LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
499 conn->snd_una, seg->ack, conn->snd_nxt);
500 if (!seq_no_ack_acceptable(conn, seg->ack)) {
501 if ((seg->ctrl & CTL_RST) == 0) {
502 log_msg(LVL_WARN, "ACK not acceptable, send RST");
503 tcp_reply_rst(&conn->ident, seg);
504 } else {
505 log_msg(LVL_WARN, "RST,ACK not acceptable, drop");
506 }
507 return;
508 }
509 }
510
511 if ((seg->ctrl & CTL_RST) != 0) {
512 /* If we get here, we have either an acceptable ACK or no ACK */
513 if ((seg->ctrl & CTL_ACK) != 0) {
514 log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
515 conn->name);
516 /* Reset connection */
517 tcp_conn_reset(conn);
518 return;
519 } else {
520 log_msg(LVL_DEBUG, "%s: RST without ACK, drop",
521 conn->name);
522 return;
523 }
524 }
525
526 /* XXX precedence */
527
528 if ((seg->ctrl & CTL_SYN) == 0) {
529 log_msg(LVL_DEBUG, "No SYN bit, ignoring segment.");
530 return;
531 }
532
533 conn->rcv_nxt = seg->seq + 1;
534 conn->irs = seg->seq;
535
536 if ((seg->ctrl & CTL_ACK) != 0) {
537 conn->snd_una = seg->ack;
538
539 /*
540 * Prune acked segments from retransmission queue and
541 * possibly transmit more data.
542 */
543 tcp_tqueue_ack_received(conn);
544 }
545
546 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
547
548 /*
549 * Surprisingly the spec does not deal with initial window setting.
550 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
551 * will always be accepted as new window setting.
552 */
553 log_msg(LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
554 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
555 conn->snd_wnd = seg->wnd;
556 conn->snd_wl1 = seg->seq;
557 conn->snd_wl2 = seg->seq;
558
559 if (seq_no_syn_acked(conn)) {
560 log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
561 tcp_conn_state_set(conn, st_established);
562 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
563 } else {
564 log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
565 conn->name);
566 tcp_conn_state_set(conn, st_syn_received);
567 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
568 }
569
570 tcp_segment_delete(seg);
571}
572
573/** Segment arrived in state where segments are processed in sequence order.
574 *
575 * Queue segment in incoming segments queue for processing.
576 *
577 * @param conn Connection
578 * @param seg Segment
579 */
580static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
581{
582 tcp_segment_t *pseg;
583
584 log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
585
586 /* Discard unacceptable segments ("old duplicates") */
587 if (!seq_no_segment_acceptable(conn, seg)) {
588 log_msg(LVL_DEBUG, "Replying ACK to unacceptable segment.");
589 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
590 tcp_segment_delete(seg);
591 return;
592 }
593
594 /* Queue for processing */
595 tcp_iqueue_insert_seg(&conn->incoming, seg);
596
597 /*
598 * Process all segments from incoming queue that are ready.
599 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
600 *
601 * XXX Need to return ACK for unacceptable segments
602 */
603 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
604 tcp_conn_seg_process(conn, pseg);
605}
606
607/** Process segment RST field.
608 *
609 * @param conn Connection
610 * @param seg Segment
611 * @return cp_done if we are done with this segment, cp_continue
612 * if not
613 */
614static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
615{
616 if ((seg->ctrl & CTL_RST) == 0)
617 return cp_continue;
618
619 switch (conn->cstate) {
620 case st_syn_received:
621 /* XXX In case of passive open, revert to Listen state */
622 if (conn->ap == ap_passive) {
623 tcp_conn_state_set(conn, st_listen);
624 /* XXX Revert conn->ident */
625 tcp_conn_tw_timer_clear(conn);
626 tcp_tqueue_clear(&conn->retransmit);
627 } else {
628 tcp_conn_reset(conn);
629 }
630 break;
631 case st_established:
632 case st_fin_wait_1:
633 case st_fin_wait_2:
634 case st_close_wait:
635 /* General "connection reset" signal */
636 tcp_reset_signal(conn);
637 tcp_conn_reset(conn);
638 break;
639 case st_closing:
640 case st_last_ack:
641 case st_time_wait:
642 tcp_conn_reset(conn);
643 break;
644 case st_listen:
645 case st_syn_sent:
646 case st_closed:
647 assert(false);
648 }
649
650 return cp_done;
651}
652
653/** Process segment security and precedence fields.
654 *
655 * @param conn Connection
656 * @param seg Segment
657 * @return cp_done if we are done with this segment, cp_continue
658 * if not
659 */
660static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
661{
662 /* TODO */
663 return cp_continue;
664}
665
666/** Process segment SYN field.
667 *
668 * @param conn Connection
669 * @param seg Segment
670 * @return cp_done if we are done with this segment, cp_continue
671 * if not
672 */
673static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
674{
675 if ((seg->ctrl & CTL_SYN) == 0)
676 return cp_continue;
677
678 /*
679 * Assert SYN is in receive window, otherwise this step should not
680 * be reached.
681 */
682 assert(seq_no_in_rcv_wnd(conn, seg->seq));
683
684 log_msg(LVL_WARN, "SYN is in receive window, should send reset. XXX");
685
686 /*
687 * TODO
688 *
689 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
690 * flush segment queues. Send unsolicited "connection reset" signal
691 * to user, connection -> closed state, delete TCB, return.
692 */
693 return cp_done;
694}
695
696/** Process segment ACK field in Syn-Received state.
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_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
704{
705 if (!seq_no_ack_acceptable(conn, seg->ack)) {
706 /* ACK is not acceptable, send RST. */
707 log_msg(LVL_WARN, "Segment ACK not acceptable, sending RST.");
708 tcp_reply_rst(&conn->ident, seg);
709 tcp_segment_delete(seg);
710 return cp_done;
711 }
712
713 log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
714
715 tcp_conn_state_set(conn, st_established);
716
717 /* XXX Not mentioned in spec?! */
718 conn->snd_una = seg->ack;
719
720 return cp_continue;
721}
722
723/** Process segment ACK field in Established state.
724 *
725 * @param conn Connection
726 * @param seg Segment
727 * @return cp_done if we are done with this segment, cp_continue
728 * if not
729 */
730static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
731{
732 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
733
734 log_msg(LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
735 (unsigned)seg->ack, (unsigned)conn->snd_una,
736 (unsigned)conn->snd_nxt);
737
738 if (!seq_no_ack_acceptable(conn, seg->ack)) {
739 log_msg(LVL_DEBUG, "ACK not acceptable.");
740 if (!seq_no_ack_duplicate(conn, seg->ack)) {
741 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
742 "Send ACK and drop.");
743 /* Not acceptable, not duplicate. Send ACK and drop. */
744 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
745 tcp_segment_delete(seg);
746 return cp_done;
747 } else {
748 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
749 }
750 } else {
751 /* Update SND.UNA */
752 conn->snd_una = seg->ack;
753 }
754
755 if (seq_no_new_wnd_update(conn, seg)) {
756 conn->snd_wnd = seg->wnd;
757 conn->snd_wl1 = seg->seq;
758 conn->snd_wl2 = seg->ack;
759
760 log_msg(LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
761 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
762 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
763 }
764
765 /*
766 * Prune acked segments from retransmission queue and
767 * possibly transmit more data.
768 */
769 tcp_tqueue_ack_received(conn);
770
771 return cp_continue;
772}
773
774/** Process segment ACK field in Fin-Wait-1 state.
775 *
776 * @param conn Connection
777 * @param seg Segment
778 * @return cp_done if we are done with this segment, cp_continue
779 * if not
780 */
781static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
782{
783 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
784 return cp_done;
785
786 if (conn->fin_is_acked) {
787 log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
788 tcp_conn_state_set(conn, st_fin_wait_2);
789 }
790
791 return cp_continue;
792}
793
794/** Process segment ACK field in Fin-Wait-2 state.
795 *
796 * @param conn Connection
797 * @param seg Segment
798 * @return cp_done if we are done with this segment, cp_continue
799 * if not
800 */
801static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
802{
803 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
804 return cp_done;
805
806 /* TODO */
807 return cp_continue;
808}
809
810/** Process segment ACK field in Close-Wait state.
811 *
812 * @param conn Connection
813 * @param seg Segment
814 * @return cp_done if we are done with this segment, cp_continue
815 * if not
816 */
817static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
818{
819 /* The same processing as in Established state */
820 return tcp_conn_seg_proc_ack_est(conn, seg);
821}
822
823/** Process segment ACK field in Closing state.
824 *
825 * @param conn Connection
826 * @param seg Segment
827 * @return cp_done if we are done with this segment, cp_continue
828 * if not
829 */
830static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
831{
832 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
833 return cp_done;
834
835 /* TODO */
836 return cp_continue;
837}
838
839/** Process segment ACK field in Last-Ack state.
840 *
841 * @param conn Connection
842 * @param seg Segment
843 * @return cp_done if we are done with this segment, cp_continue
844 * if not
845 */
846static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
847{
848 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
849 return cp_done;
850
851 if (conn->fin_is_acked) {
852 log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
853 tcp_conn_remove(conn);
854 tcp_conn_state_set(conn, st_closed);
855 return cp_done;
856 }
857
858 return cp_continue;
859}
860
861/** Process segment ACK field in Time-Wait state.
862 *
863 * @param conn Connection
864 * @param seg Segment
865 * @return cp_done if we are done with this segment, cp_continue
866 * if not
867 */
868static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
869{
870 /* Nothing to do */
871 return cp_continue;
872}
873
874/** Process segment ACK field.
875 *
876 * @param conn Connection
877 * @param seg Segment
878 * @return cp_done if we are done with this segment, cp_continue
879 * if not
880 */
881static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
882{
883 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
884 conn->name, conn, seg);
885
886 if ((seg->ctrl & CTL_ACK) == 0) {
887 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
888 tcp_segment_delete(seg);
889 return cp_done;
890 }
891
892 switch (conn->cstate) {
893 case st_syn_received:
894 return tcp_conn_seg_proc_ack_sr(conn, seg);
895 case st_established:
896 return tcp_conn_seg_proc_ack_est(conn, seg);
897 case st_fin_wait_1:
898 return tcp_conn_seg_proc_ack_fw1(conn, seg);
899 case st_fin_wait_2:
900 return tcp_conn_seg_proc_ack_fw2(conn, seg);
901 case st_close_wait:
902 return tcp_conn_seg_proc_ack_cw(conn, seg);
903 case st_closing:
904 return tcp_conn_seg_proc_ack_cls(conn, seg);
905 case st_last_ack:
906 return tcp_conn_seg_proc_ack_la(conn, seg);
907 case st_time_wait:
908 return tcp_conn_seg_proc_ack_tw(conn, seg);
909 case st_listen:
910 case st_syn_sent:
911 case st_closed:
912 assert(false);
913 }
914
915 assert(false);
916}
917
918/** Process segment URG field.
919 *
920 * @param conn Connection
921 * @param seg Segment
922 * @return cp_done if we are done with this segment, cp_continue
923 * if not
924 */
925static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
926{
927 return cp_continue;
928}
929
930/** Process segment text.
931 *
932 * @param conn Connection
933 * @param seg Segment
934 * @return cp_done if we are done with this segment, cp_continue
935 * if not
936 */
937static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
938{
939 size_t text_size;
940 size_t xfer_size;
941
942 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
943 conn->name, conn, seg);
944
945 switch (conn->cstate) {
946 case st_established:
947 case st_fin_wait_1:
948 case st_fin_wait_2:
949 /* OK */
950 break;
951 case st_close_wait:
952 case st_closing:
953 case st_last_ack:
954 case st_time_wait:
955 /* Invalid since FIN has been received. Ignore text. */
956 return cp_continue;
957 case st_listen:
958 case st_syn_sent:
959 case st_syn_received:
960 case st_closed:
961 assert(false);
962 }
963
964 /*
965 * Process segment text
966 */
967 assert(seq_no_segment_ready(conn, seg));
968
969 /* Trim anything outside our receive window */
970 tcp_conn_trim_seg_to_wnd(conn, seg);
971
972 /* Determine how many bytes to copy */
973 text_size = tcp_segment_text_size(seg);
974 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
975
976 /* Copy data to receive buffer */
977 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
978 xfer_size);
979 conn->rcv_buf_used += xfer_size;
980
981 /* Signal to the receive function that new data has arrived */
982 fibril_condvar_broadcast(&conn->rcv_buf_cv);
983
984 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
985
986 /* Advance RCV.NXT */
987 conn->rcv_nxt += xfer_size;
988
989 /* Update receive window. XXX Not an efficient strategy. */
990 conn->rcv_wnd -= xfer_size;
991
992 /* Send ACK */
993 if (xfer_size > 0)
994 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
995
996 if (xfer_size < seg->len) {
997 /* Trim part of segment which we just received */
998 tcp_conn_trim_seg_to_wnd(conn, seg);
999 } else {
1000 log_msg(LVL_DEBUG, "%s: Nothing left in segment, dropping "
1001 "(xfer_size=%zu, SEG.LEN=%" PRIu32 ", seg->ctrl=%u)",
1002 conn->name, xfer_size, seg->len, (unsigned int) seg->ctrl);
1003 /* Nothing left in segment */
1004 tcp_segment_delete(seg);
1005 return cp_done;
1006 }
1007
1008 return cp_continue;
1009}
1010
1011/** Process segment FIN field.
1012 *
1013 * @param conn Connection
1014 * @param seg Segment
1015 * @return cp_done if we are done with this segment, cp_continue
1016 * if not
1017 */
1018static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
1019{
1020 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
1021 conn->name, conn, seg);
1022 log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
1023 (unsigned) seg->ctrl);
1024
1025 /* Only process FIN if no text is left in segment. */
1026 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
1027 log_msg(LVL_DEBUG, " - FIN found in segment.");
1028
1029 /* Send ACK */
1030 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1031
1032 conn->rcv_nxt++;
1033 conn->rcv_wnd--;
1034
1035 /* Change connection state */
1036 switch (conn->cstate) {
1037 case st_listen:
1038 case st_syn_sent:
1039 case st_closed:
1040 /* Connection not synchronized */
1041 assert(false);
1042 case st_syn_received:
1043 case st_established:
1044 log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
1045 conn->name);
1046 tcp_conn_state_set(conn, st_close_wait);
1047 break;
1048 case st_fin_wait_1:
1049 log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
1050 conn->name);
1051 tcp_conn_state_set(conn, st_closing);
1052 break;
1053 case st_fin_wait_2:
1054 log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
1055 conn->name);
1056 tcp_conn_state_set(conn, st_time_wait);
1057 /* Start the Time-Wait timer */
1058 tcp_conn_tw_timer_set(conn);
1059 break;
1060 case st_close_wait:
1061 case st_closing:
1062 case st_last_ack:
1063 /* Do nothing */
1064 break;
1065 case st_time_wait:
1066 /* Restart the Time-Wait timer */
1067 tcp_conn_tw_timer_set(conn);
1068 break;
1069 }
1070
1071 /* Add FIN to the receive buffer */
1072 conn->rcv_buf_fin = true;
1073 fibril_condvar_broadcast(&conn->rcv_buf_cv);
1074
1075 tcp_segment_delete(seg);
1076 return cp_done;
1077 }
1078
1079 return cp_continue;
1080}
1081
1082/** Process incoming segment.
1083 *
1084 * We are in connection state where segments are processed in order
1085 * of sequence number. This processes one segment taken from the
1086 * connection incoming segments queue.
1087 *
1088 * @param conn Connection
1089 * @param seg Segment
1090 */
1091static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
1092{
1093 log_msg(LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
1094 tcp_segment_dump(seg);
1095
1096 /* Check whether segment is acceptable */
1097 /* XXX Permit valid ACKs, URGs and RSTs */
1098/* if (!seq_no_segment_acceptable(conn, seg)) {
1099 log_msg(LVL_WARN, "Segment not acceptable, dropping.");
1100 if ((seg->ctrl & CTL_RST) == 0) {
1101 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1102 }
1103 return;
1104 }
1105*/
1106
1107 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
1108 return;
1109
1110 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
1111 return;
1112
1113 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
1114 return;
1115
1116 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
1117 return;
1118
1119 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
1120 return;
1121
1122 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
1123 return;
1124
1125 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
1126 return;
1127
1128 /*
1129 * If anything is left from the segment, insert it back into the
1130 * incoming segments queue.
1131 */
1132 if (seg->len > 0) {
1133 log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
1134 seg, (size_t) seg->len);
1135 tcp_iqueue_insert_seg(&conn->incoming, seg);
1136 } else {
1137 tcp_segment_delete(seg);
1138 }
1139}
1140
1141/** Segment arrived on a connection.
1142 *
1143 * @param conn Connection
1144 * @param seg Segment
1145 */
1146void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
1147{
1148 log_msg(LVL_DEBUG, "%s: tcp_conn_segment_arrived(%p)",
1149 conn->name, seg);
1150
1151 switch (conn->cstate) {
1152 case st_listen:
1153 tcp_conn_sa_listen(conn, seg); break;
1154 case st_syn_sent:
1155 tcp_conn_sa_syn_sent(conn, seg); break;
1156 case st_syn_received:
1157 case st_established:
1158 case st_fin_wait_1:
1159 case st_fin_wait_2:
1160 case st_close_wait:
1161 case st_closing:
1162 case st_last_ack:
1163 case st_time_wait:
1164 /* Process segments in order of sequence number */
1165 tcp_conn_sa_queue(conn, seg); break;
1166 case st_closed:
1167 log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
1168 assert(false);
1169 }
1170}
1171
1172/** Time-Wait timeout handler.
1173 *
1174 * @param arg Connection
1175 */
1176static void tw_timeout_func(void *arg)
1177{
1178 tcp_conn_t *conn = (tcp_conn_t *) arg;
1179
1180 log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
1181
1182 fibril_mutex_lock(&conn->lock);
1183
1184 if (conn->cstate == st_closed) {
1185 log_msg(LVL_DEBUG, "Connection already closed.");
1186 fibril_mutex_unlock(&conn->lock);
1187 tcp_conn_delref(conn);
1188 return;
1189 }
1190
1191 log_msg(LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
1192 tcp_conn_remove(conn);
1193 tcp_conn_state_set(conn, st_closed);
1194
1195 fibril_mutex_unlock(&conn->lock);
1196 tcp_conn_delref(conn);
1197}
1198
1199/** Start or restart the Time-Wait timeout.
1200 *
1201 * @param conn Connection
1202 */
1203void tcp_conn_tw_timer_set(tcp_conn_t *conn)
1204{
1205 tcp_conn_addref(conn);
1206 fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
1207 (void *)conn);
1208}
1209
1210/** Clear the Time-Wait timeout.
1211 *
1212 * @param conn Connection
1213 */
1214void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1215{
1216 if (fibril_timer_clear(conn->tw_timer) == fts_active)
1217 tcp_conn_delref(conn);
1218}
1219
1220/** Trim segment to the receive window.
1221 *
1222 * @param conn Connection
1223 * @param seg Segment
1224 */
1225void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
1226{
1227 uint32_t left, right;
1228
1229 seq_no_seg_trim_calc(conn, seg, &left, &right);
1230 tcp_segment_trim(seg, left, right);
1231}
1232
1233/** Handle unexpected segment received on a socket pair.
1234 *
1235 * We reply with an RST unless the received segment has RST.
1236 *
1237 * @param sp Socket pair which received the segment
1238 * @param seg Unexpected segment
1239 */
1240void tcp_unexpected_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
1241{
1242 log_msg(LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", sp, seg);
1243
1244 if ((seg->ctrl & CTL_RST) == 0)
1245 tcp_reply_rst(sp, seg);
1246}
1247
1248/** Compute flipped socket pair for response.
1249 *
1250 * Flipped socket pair has local and foreign sockets exchanged.
1251 *
1252 * @param sp Socket pair
1253 * @param fsp Place to store flipped socket pair
1254 */
1255void tcp_sockpair_flipped(tcp_sockpair_t *sp, tcp_sockpair_t *fsp)
1256{
1257 fsp->local = sp->foreign;
1258 fsp->foreign = sp->local;
1259}
1260
1261/** Send RST in response to an incoming segment.
1262 *
1263 * @param sp Socket pair which received the segment
1264 * @param seg Incoming segment
1265 */
1266void tcp_reply_rst(tcp_sockpair_t *sp, tcp_segment_t *seg)
1267{
1268 tcp_segment_t *rseg;
1269
1270 log_msg(LVL_DEBUG, "tcp_reply_rst(%p, %p)", sp, seg);
1271
1272 rseg = tcp_segment_make_rst(seg);
1273 tcp_transmit_segment(sp, rseg);
1274}
1275
1276/**
1277 * @}
1278 */
Note: See TracBrowser for help on using the repository browser.