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

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

Deallocate connection structures.

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