source: mainline/uspace/srv/net/tl/tcp/conn.c@ 6e88fea

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

Transition to CLOSED state.

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