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

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

Connection state change when FIN is sent.

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