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

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

Retransmission queue, detect ACK of FIN.

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