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
RevLine 
[c5808b41]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/**
[032bbe7]34 * @file TCP connection processing and state machine
[c5808b41]35 */
36
37#include <adt/list.h>
[8c7a054]38#include <bool.h>
[c5808b41]39#include <errno.h>
40#include <io/log.h>
[0093ab6]41#include <macros.h>
[c5808b41]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"
[762b48a]49#include "ucall.h"
[c5808b41]50
[a52de0e]51#define RCV_BUF_SIZE 4096/*2*/
[32105348]52#define SND_BUF_SIZE 4096
[0093ab6]53
[2a3214e]54#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
55#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
56
[c5808b41]57LIST_INITIALIZE(conn_list);
58
59static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
[2a3214e]60static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
[704586fb]61static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
[c5808b41]62
[7a8c1c4e]63/** Create new connection structure.
[032bbe7]64 *
65 * @param lsock Local socket (will be deeply copied)
66 * @param fsock Foreign socket (will be deeply copied)
[7a8c1c4e]67 * @return New connection or NULL
[032bbe7]68 */
[c5808b41]69tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
70{
[2a3214e]71 tcp_conn_t *conn = NULL;
[7cf7ded]72 bool tqueue_inited = false;
[c5808b41]73
[0093ab6]74 /* Allocate connection structure */
[c5808b41]75 conn = calloc(1, sizeof(tcp_conn_t));
76 if (conn == NULL)
[2a3214e]77 goto error;
78
79 conn->tw_timer = fibril_timer_create();
80 if (conn->tw_timer == NULL)
81 goto error;
[c5808b41]82
[0093ab6]83 /* Allocate receive buffer */
[d9ce049]84 fibril_mutex_initialize(&conn->rcv_buf_lock);
85 fibril_condvar_initialize(&conn->rcv_buf_cv);
[0093ab6]86 conn->rcv_buf_size = RCV_BUF_SIZE;
87 conn->rcv_buf_used = 0;
[8c7a054]88 conn->rcv_buf_fin = false;
[d9ce049]89
[0093ab6]90 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
[2a3214e]91 if (conn->rcv_buf == NULL)
92 goto error;
[0093ab6]93
[32105348]94 /** Allocate send buffer */
[bbf159a]95 fibril_mutex_initialize(&conn->snd_buf_lock);
96 fibril_condvar_initialize(&conn->snd_buf_cv);
[32105348]97 conn->snd_buf_size = SND_BUF_SIZE;
98 conn->snd_buf_used = 0;
[8c7a054]99 conn->snd_buf_fin = false;
[32105348]100 conn->snd_buf = calloc(1, conn->snd_buf_size);
[2a3214e]101 if (conn->snd_buf == NULL)
102 goto error;
[32105348]103
[0093ab6]104 /* Set up receive window. */
105 conn->rcv_wnd = conn->rcv_buf_size;
106
107 /* Initialize incoming segment queue */
[c5808b41]108 tcp_iqueue_init(&conn->incoming, conn);
109
[6df418c4]110 /* Initialize retransmission queue */
[7cf7ded]111 if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
112 goto error;
113
114 tqueue_inited = true;
[6df418c4]115
[004a5fe]116 /* Connection state change signalling */
117 fibril_mutex_initialize(&conn->cstate_lock);
118 fibril_condvar_initialize(&conn->cstate_cv);
119
[c5808b41]120 conn->cstate = st_listen;
[d9e14fa4]121 conn->reset = false;
[7a8c1c4e]122 conn->deleted = false;
[d9e14fa4]123 conn->ap = ap_passive;
[6df418c4]124 conn->fin_is_acked = false;
[c5808b41]125 conn->ident.local = *lsock;
126 if (fsock != NULL)
127 conn->ident.foreign = *fsock;
128
129 return conn;
[2a3214e]130
131error:
[7cf7ded]132 if (tqueue_inited)
133 tcp_tqueue_fini(&conn->retransmit);
[2a3214e]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;
[c5808b41]144}
145
[7a8c1c4e]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
[032bbe7]188/** Enlist connection.
189 *
190 * Add connection to the connection map.
191 */
[c5808b41]192void tcp_conn_add(tcp_conn_t *conn)
193{
194 list_append(&conn->link, &conn_list);
195}
196
[6e88fea]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
[004a5fe]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);
[7a8c1c4e]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 }
[004a5fe]218}
219
[032bbe7]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 */
[c5808b41]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;
[d9e14fa4]231 conn->ap = ap_active;
[c5808b41]232
233 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
[004a5fe]234 tcp_conn_state_set(conn, st_syn_sent);
[c5808b41]235}
236
[f343a16]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:
[6896409c]247 log_msg(LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
[004a5fe]248 tcp_conn_state_set(conn, st_fin_wait_1);
[f343a16]249 break;
250 case st_close_wait:
[6896409c]251 log_msg(LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
[004a5fe]252 tcp_conn_state_set(conn, st_last_ack);
[f343a16]253 break;
254 default:
[6896409c]255 log_msg(LVL_ERROR, "%s: Connection state %d", conn->name,
256 conn->cstate);
[f343a16]257 assert(false);
258 }
[6df418c4]259
260 conn->fin_is_acked = false;
[f343a16]261}
262
[032bbe7]263/** Compare two sockets.
264 *
265 * Two sockets are equal if the address is equal and the port number
266 * is equal.
267 */
[3aa2642a]268static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
[c5808b41]269{
[3aa2642a]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);
[c5808b41]272
[3aa2642a]273 if (patt->addr.ipv4 != TCP_IPV4_ANY &&
274 patt->addr.ipv4 != sock->addr.ipv4)
[c5808b41]275 return false;
276
[3aa2642a]277 if (patt->port != TCP_PORT_ANY &&
278 patt->port != sock->port)
[c5808b41]279 return false;
280
281 log_msg(LVL_DEBUG, " -> match");
282
283 return true;
284}
285
[032bbe7]286/** Match socket with pattern. */
[c5808b41]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
[3aa2642a]291 if (!tcp_socket_match(&sp->local, &pattern->local))
[c5808b41]292 return false;
293
[3aa2642a]294 if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
[c5808b41]295 return false;
296
297 return true;
298}
299
[032bbe7]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 */
[c5808b41]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);
[704586fb]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)) {
[c5808b41]319 return conn;
320 }
321 }
322
323 return NULL;
324}
325
[704586fb]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);
[d9e14fa4]334 conn->reset = true;
335
[704586fb]336 tcp_conn_tw_timer_clear(conn);
337 tcp_tqueue_clear(&conn->retransmit);
[d9e14fa4]338
339 fibril_condvar_broadcast(&conn->rcv_buf_cv);
[bbf159a]340 fibril_condvar_broadcast(&conn->snd_buf_cv);
[d9e14fa4]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);
[704586fb]351}
352
[32105348]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:
[7cf7ded]374 log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
[32105348]375 assert(false);
376 }
377
378 assert(false);
379}
380
[032bbe7]381/** Segment arrived in Listen state.
382 *
383 * @param conn Connection
384 * @param seg Segment
385 */
[c5808b41]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
[4c55a64]411
[c5808b41]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
[4c55a64]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
[004a5fe]431 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]432
433 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
[0093ab6]434
435 tcp_segment_delete(seg);
[c5808b41]436}
437
[032bbe7]438/** Segment arrived in Syn-Sent state.
439 *
440 * @param conn Connection
441 * @param seg Segment
442 */
[c5808b41]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) {
[6896409c]458 log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
459 conn->name);
[704586fb]460 /* Reset connection */
461 tcp_conn_reset(conn);
[c5808b41]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;
[d9ce049]478
479 /*
480 * Prune acked segments from retransmission queue and
481 * possibly transmit more data.
482 */
483 tcp_tqueue_ack_received(conn);
[c5808b41]484 }
485
486 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
487
[32105348]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
[c5808b41]499 if (seq_no_syn_acked(conn)) {
[6896409c]500 log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
[004a5fe]501 tcp_conn_state_set(conn, st_established);
[c5808b41]502 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
503 } else {
[6896409c]504 log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
505 conn->name);
[004a5fe]506 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]507 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
508 }
[0093ab6]509
510 tcp_segment_delete(seg);
[c5808b41]511}
512
[032bbe7]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 */
[c5808b41]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
[7cf7ded]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 }
[c5808b41]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
[032bbe7]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 */
[c5808b41]554static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
555{
[03be171]556 if ((seg->ctrl & CTL_RST) == 0)
557 return cp_continue;
558
[d9e14fa4]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;
[c5808b41]591}
592
[032bbe7]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 */
[c5808b41]600static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
601{
[4c55a64]602 /* TODO */
[c5808b41]603 return cp_continue;
604}
605
[032bbe7]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 */
[c5808b41]613static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
614{
[7cf7ded]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;
[c5808b41]634}
635
[032bbe7]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 */
[c5808b41]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);
[4c55a64]649 tcp_segment_delete(seg);
650 return cp_done;
[c5808b41]651 }
652
[6896409c]653 log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
[c5808b41]654
[004a5fe]655 tcp_conn_state_set(conn, st_established);
[c5808b41]656
657 /* XXX Not mentioned in spec?! */
658 conn->snd_una = seg->ack;
659
660 return cp_continue;
661}
662
[032bbe7]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 */
[c5808b41]670static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
671{
[0093ab6]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
[4c55a64]678 if (!seq_no_ack_acceptable(conn, seg->ack)) {
[0093ab6]679 log_msg(LVL_DEBUG, "ACK not acceptable.");
[4c55a64]680 if (!seq_no_ack_duplicate(conn, seg->ack)) {
[0093ab6]681 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
682 "Send ACK and drop.");
[4c55a64]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;
[0093ab6]687 } else {
688 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
[4c55a64]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;
[d9ce049]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);
[4c55a64]703 }
704
[d9ce049]705 /*
706 * Prune acked segments from retransmission queue and
707 * possibly transmit more data.
708 */
709 tcp_tqueue_ack_received(conn);
710
[c5808b41]711 return cp_continue;
712}
713
[032bbe7]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 */
[c5808b41]721static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
722{
[4c55a64]723 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
724 return cp_done;
725
[6df418c4]726 if (conn->fin_is_acked) {
[6896409c]727 log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
[004a5fe]728 tcp_conn_state_set(conn, st_fin_wait_2);
[6df418c4]729 }
730
[c5808b41]731 return cp_continue;
732}
733
[032bbe7]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 */
[c5808b41]741static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
742{
[4c55a64]743 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
744 return cp_done;
745
746 /* TODO */
[c5808b41]747 return cp_continue;
748}
749
[032bbe7]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 */
[c5808b41]757static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
758{
[4c55a64]759 /* The same processing as in Established state */
760 return tcp_conn_seg_proc_ack_est(conn, seg);
[c5808b41]761}
762
[032bbe7]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 */
[c5808b41]770static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
771{
[4c55a64]772 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
773 return cp_done;
774
775 /* TODO */
[c5808b41]776 return cp_continue;
777}
778
[032bbe7]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 */
[c5808b41]786static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
787{
[4c55a64]788 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
789 return cp_done;
790
[6e88fea]791 if (conn->fin_is_acked) {
[6896409c]792 log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
[6e88fea]793 tcp_conn_remove(conn);
[004a5fe]794 tcp_conn_state_set(conn, st_closed);
[6e88fea]795 return cp_done;
796 }
797
[c5808b41]798 return cp_continue;
799}
800
[032bbe7]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 */
[c5808b41]808static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
809{
[4c55a64]810 /* Nothing to do */
[c5808b41]811 return cp_continue;
812}
813
[032bbe7]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 */
[c5808b41]821static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
822{
[23fe06c]823 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
824 conn->name, conn, seg);
[c5808b41]825
826 if ((seg->ctrl & CTL_ACK) == 0) {
827 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
[4c55a64]828 tcp_segment_delete(seg);
[c5808b41]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
[032bbe7]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 */
[c5808b41]865static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
866{
867 return cp_continue;
868}
869
[032bbe7]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 */
[c5808b41]877static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
878{
[0093ab6]879 size_t text_size;
880 size_t xfer_size;
881
[23fe06c]882 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
883 conn->name, conn, seg);
[0093ab6]884
[4c55a64]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
[0093ab6]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
[d9ce049]912 fibril_mutex_lock(&conn->rcv_buf_lock);
913
[0093ab6]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 */
[d9ce049]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);
[0093ab6]926
[32105348]927 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
928
[0093ab6]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) {
[8c7a054]940 /* Trim part of segment which we just received */
[0093ab6]941 tcp_conn_trim_seg_to_wnd(conn, seg);
[8c7a054]942 } else {
[6896409c]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);
[8c7a054]946 /* Nothing left in segment */
947 tcp_segment_delete(seg);
[0093ab6]948 return cp_done;
949 }
950
[c5808b41]951 return cp_continue;
952}
953
[032bbe7]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 */
[c5808b41]961static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
962{
[23fe06c]963 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
964 conn->name, conn, seg);
[7cf7ded]965 log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
966 (unsigned) seg->ctrl);
[8c7a054]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
[6e88fea]972 /* Send ACK */
973 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
974
[8c7a054]975 conn->rcv_nxt++;
976 conn->rcv_wnd--;
977
[f343a16]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:
[6896409c]987 log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
988 conn->name);
[004a5fe]989 tcp_conn_state_set(conn, st_close_wait);
[f343a16]990 break;
991 case st_fin_wait_1:
[6896409c]992 log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
993 conn->name);
[004a5fe]994 tcp_conn_state_set(conn, st_closing);
[f343a16]995 break;
996 case st_fin_wait_2:
[6896409c]997 log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
998 conn->name);
[004a5fe]999 tcp_conn_state_set(conn, st_time_wait);
[2a3214e]1000 /* Start the Time-Wait timer */
1001 tcp_conn_tw_timer_set(conn);
[f343a16]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:
[2a3214e]1009 /* Restart the Time-Wait timer */
1010 tcp_conn_tw_timer_set(conn);
[f343a16]1011 break;
1012 }
[8c7a054]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
[c5808b41]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.
[032bbe7]1032 *
1033 * @param conn Connection
1034 * @param seg Segment
[c5808b41]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);
[6896409c]1039 tcp_segment_dump(seg);
[c5808b41]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;
[0093ab6]1072
[8c7a054]1073 /*
1074 * If anything is left from the segment, insert it back into the
1075 * incoming segments queue.
1076 */
[7cf7ded]1077 if (seg->len > 0) {
1078 log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
1079 seg, (size_t) seg->len);
[8c7a054]1080 tcp_iqueue_insert_seg(&conn->incoming, seg);
[7cf7ded]1081 } else {
[8c7a054]1082 tcp_segment_delete(seg);
[7cf7ded]1083 }
[c5808b41]1084}
1085
[032bbe7]1086/** Segment arrived on a connection.
1087 *
1088 * @param conn Connection
1089 * @param seg Segment
1090 */
[c5808b41]1091void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
1092{
[23fe06c]1093 log_msg(LVL_DEBUG, "%c: tcp_conn_segment_arrived(%p)",
1094 conn->name, seg);
[c5808b41]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:
[7cf7ded]1112 log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
[c5808b41]1113 assert(false);
1114 }
1115}
1116
[2a3214e]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
[704586fb]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);
[2a3214e]1133 tcp_conn_remove(conn);
[004a5fe]1134 tcp_conn_state_set(conn, st_closed);
[2a3214e]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
[704586fb]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
[032bbe7]1156/** Trim segment to the receive window.
1157 *
1158 * @param conn Connection
1159 * @param seg Segment
1160 */
[0093ab6]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
[032bbe7]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 */
[c5808b41]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
[032bbe7]1184/** Compute flipped socket pair for response.
1185 *
[32105348]1186 * Flipped socket pair has local and foreign sockets exchanged.
[032bbe7]1187 *
1188 * @param sp Socket pair
1189 * @param fsp Place to store flipped socket pair
1190 */
[c5808b41]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
[032bbe7]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 */
[c5808b41]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);
[704586fb]1209 tcp_transmit_segment(sp, rseg);
[c5808b41]1210}
1211
1212/**
1213 * @}
1214 */
Note: See TracBrowser for help on using the repository browser.