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

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

Make active OPEN wait for connection establishment (or reset).

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