source: mainline/uspace/srv/net/tl/tcp/conn.c@ 7cf7ded

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

TCP retransmission (WIP). Allow setting timer in timer handler.
Simulate packet drop. Fixes.

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