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

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

Implement RST processing.

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