source: mainline/uspace/srv/net/tcp/conn.c@ 3bacee1

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

Make ccheck-fix again and commit more good files.

  • Property mode set to 100644
File size: 36.9 KB
RevLine 
[c5808b41]1/*
[2f19103]2 * Copyright (c) 2015 Jiri Svoboda
[c5808b41]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>
38#include <errno.h>
[2f19103]39#include <inet/endpoint.h>
[c5808b41]40#include <io/log.h>
[0093ab6]41#include <macros.h>
[2989c7e]42#include <nettl/amap.h>
43#include <stdbool.h>
[c5808b41]44#include <stdlib.h>
45#include "conn.h"
[975d528]46#include "inet.h"
[c5808b41]47#include "iqueue.h"
[975d528]48#include "pdu.h"
[9520af7]49#include "rqueue.h"
[c5808b41]50#include "segment.h"
51#include "seq_no.h"
52#include "tcp_type.h"
53#include "tqueue.h"
[762b48a]54#include "ucall.h"
[c5808b41]55
[a52de0e]56#define RCV_BUF_SIZE 4096/*2*/
[32105348]57#define SND_BUF_SIZE 4096
[0093ab6]58
[2a3214e]59#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
60#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
61
[e1b4ae0]62/** List of all allocated connections */
[2989c7e]63static LIST_INITIALIZE(conn_list);
[2c4bb828]64/** Taken after tcp_conn_t lock */
[2989c7e]65static FIBRIL_MUTEX_INITIALIZE(conn_list_lock);
[e1b4ae0]66/** Connection association map */
[2989c7e]67static amap_t *amap;
[9520af7]68/** Taken after tcp_conn_t lock */
[e1b4ae0]69static FIBRIL_MUTEX_INITIALIZE(amap_lock);
[c5808b41]70
[9520af7]71/** Internal loopback configuration */
72tcp_lb_t tcp_conn_lb = tcp_lb_none;
73
[975d528]74static void tcp_conn_seg_process(tcp_conn_t *, tcp_segment_t *);
75static void tcp_conn_tw_timer_set(tcp_conn_t *);
76static void tcp_conn_tw_timer_clear(tcp_conn_t *);
77static void tcp_transmit_segment(inet_ep2_t *, tcp_segment_t *);
[9520af7]78static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *, tcp_segment_t *);
79static void tcp_reply_rst(inet_ep2_t *, tcp_segment_t *);
[975d528]80
81static tcp_tqueue_cb_t tcp_conn_tqueue_cb = {
82 .transmit_seg = tcp_transmit_segment
83};
[c5808b41]84
[2989c7e]85/** Initialize connections. */
[b7fd2a0]86errno_t tcp_conns_init(void)
[2989c7e]87{
[b7fd2a0]88 errno_t rc;
[2989c7e]89
90 rc = amap_create(&amap);
91 if (rc != EOK) {
92 assert(rc == ENOMEM);
93 return ENOMEM;
94 }
95
96 return EOK;
97}
98
[e1b4ae0]99/** Finalize connections. */
100void tcp_conns_fini(void)
101{
[3e2291a9]102 assert(list_empty(&conn_list));
103
[e1b4ae0]104 amap_destroy(amap);
105 amap = NULL;
106}
107
[7a8c1c4e]108/** Create new connection structure.
[032bbe7]109 *
[2f19103]110 * @param epp Endpoint pair (will be deeply copied)
[7a8c1c4e]111 * @return New connection or NULL
[032bbe7]112 */
[2f19103]113tcp_conn_t *tcp_conn_new(inet_ep2_t *epp)
[c5808b41]114{
[2a3214e]115 tcp_conn_t *conn = NULL;
[7cf7ded]116 bool tqueue_inited = false;
[c5808b41]117
[0093ab6]118 /* Allocate connection structure */
[c5808b41]119 conn = calloc(1, sizeof(tcp_conn_t));
120 if (conn == NULL)
[2a3214e]121 goto error;
122
[78192cc7]123 fibril_mutex_initialize(&conn->lock);
124
125 conn->tw_timer = fibril_timer_create(&conn->lock);
[2a3214e]126 if (conn->tw_timer == NULL)
127 goto error;
[c5808b41]128
[0edaf0f6]129 /* One for the user, one for not being in closed state */
130 atomic_set(&conn->refcnt, 2);
131
[0093ab6]132 /* Allocate receive buffer */
[d9ce049]133 fibril_condvar_initialize(&conn->rcv_buf_cv);
[0093ab6]134 conn->rcv_buf_size = RCV_BUF_SIZE;
135 conn->rcv_buf_used = 0;
[8c7a054]136 conn->rcv_buf_fin = false;
[d9ce049]137
[0093ab6]138 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
[2a3214e]139 if (conn->rcv_buf == NULL)
140 goto error;
[0093ab6]141
[32105348]142 /** Allocate send buffer */
[bbf159a]143 fibril_condvar_initialize(&conn->snd_buf_cv);
[32105348]144 conn->snd_buf_size = SND_BUF_SIZE;
145 conn->snd_buf_used = 0;
[8c7a054]146 conn->snd_buf_fin = false;
[32105348]147 conn->snd_buf = calloc(1, conn->snd_buf_size);
[2a3214e]148 if (conn->snd_buf == NULL)
149 goto error;
[32105348]150
[0093ab6]151 /* Set up receive window. */
152 conn->rcv_wnd = conn->rcv_buf_size;
153
154 /* Initialize incoming segment queue */
[c5808b41]155 tcp_iqueue_init(&conn->incoming, conn);
156
[6df418c4]157 /* Initialize retransmission queue */
[3bacee1]158 if (tcp_tqueue_init(&conn->retransmit, conn, &tcp_conn_tqueue_cb) !=
159 EOK) {
[7cf7ded]160 goto error;
[975d528]161 }
[7cf7ded]162
163 tqueue_inited = true;
[6df418c4]164
[004a5fe]165 /* Connection state change signalling */
166 fibril_condvar_initialize(&conn->cstate_cv);
167
[779541b]168 conn->cb = NULL;
[ae481e0]169
[c5808b41]170 conn->cstate = st_listen;
[d9e14fa4]171 conn->reset = false;
[7a8c1c4e]172 conn->deleted = false;
[d9e14fa4]173 conn->ap = ap_passive;
[6df418c4]174 conn->fin_is_acked = false;
[2f19103]175 if (epp != NULL)
176 conn->ident = *epp;
[c5808b41]177
[e1b4ae0]178 fibril_mutex_lock(&conn_list_lock);
179 list_append(&conn->link, &conn_list);
180 fibril_mutex_unlock(&conn_list_lock);
181
[c5808b41]182 return conn;
[2a3214e]183
184error:
[7cf7ded]185 if (tqueue_inited)
186 tcp_tqueue_fini(&conn->retransmit);
[2a3214e]187 if (conn != NULL && conn->rcv_buf != NULL)
188 free(conn->rcv_buf);
189 if (conn != NULL && conn->snd_buf != NULL)
190 free(conn->snd_buf);
191 if (conn != NULL && conn->tw_timer != NULL)
192 fibril_timer_destroy(conn->tw_timer);
193 if (conn != NULL)
194 free(conn);
195
196 return NULL;
[c5808b41]197}
198
[7a8c1c4e]199/** Destroy connection structure.
200 *
[0edaf0f6]201 * Connection structure should be destroyed when the folowing condtitions
202 * are met:
203 * (1) user has deleted the connection
204 * (2) the connection has entered closed state
205 * (3) nobody is holding references to the connection
206 *
207 * This happens when @a conn->refcnt is zero as we count (1) and (2)
208 * as special references.
[7a8c1c4e]209 *
210 * @param conn Connection
211 */
212static void tcp_conn_free(tcp_conn_t *conn)
213{
[a1a101d]214 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_free(%p)", conn->name, conn);
[0a1e7e4]215
216 assert(conn->mapped == false);
[7a8c1c4e]217 tcp_tqueue_fini(&conn->retransmit);
218
[e1b4ae0]219 fibril_mutex_lock(&conn_list_lock);
220 list_remove(&conn->link);
221 fibril_mutex_unlock(&conn_list_lock);
222
[7a8c1c4e]223 if (conn->rcv_buf != NULL)
224 free(conn->rcv_buf);
225 if (conn->snd_buf != NULL)
226 free(conn->snd_buf);
227 if (conn->tw_timer != NULL)
228 fibril_timer_destroy(conn->tw_timer);
229 free(conn);
230}
231
[0edaf0f6]232/** Add reference to connection.
233 *
234 * Increase connection reference count by one.
235 *
236 * @param conn Connection
237 */
238void tcp_conn_addref(tcp_conn_t *conn)
239{
[8d48c7e]240 log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_addref(%p) before=%zu",
241 conn->name, conn, atomic_get(&conn->refcnt));
[0edaf0f6]242 atomic_inc(&conn->refcnt);
243}
244
245/** Remove reference from connection.
246 *
247 * Decrease connection reference count by one.
248 *
249 * @param conn Connection
250 */
251void tcp_conn_delref(tcp_conn_t *conn)
252{
[8d48c7e]253 log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_delref(%p) before=%zu",
254 conn->name, conn, atomic_get(&conn->refcnt));
[0edaf0f6]255
256 if (atomic_predec(&conn->refcnt) == 0)
257 tcp_conn_free(conn);
258}
259
[78192cc7]260/** Lock connection.
261 *
262 * Must be called before any other connection-manipulating function,
263 * except tcp_conn_{add|del}ref(). Locks the connection including
264 * its timers. Must not be called inside any of the connection
265 * timer handlers.
266 *
267 * @param conn Connection
268 */
269void tcp_conn_lock(tcp_conn_t *conn)
270{
271 fibril_mutex_lock(&conn->lock);
272}
273
274/** Unlock connection.
275 *
276 * @param conn Connection
277 */
278void tcp_conn_unlock(tcp_conn_t *conn)
279{
280 fibril_mutex_unlock(&conn->lock);
281}
282
[7a8c1c4e]283/** Delete connection.
284 *
285 * The caller promises not make no further references to @a conn.
286 * TCP will free @a conn eventually.
287 *
288 * @param conn Connection
289 */
290void tcp_conn_delete(tcp_conn_t *conn)
291{
[a1a101d]292 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_delete(%p)", conn->name, conn);
[7a8c1c4e]293
[0edaf0f6]294 assert(conn->deleted == false);
[bf7587b0]295 conn->deleted = true;
296 conn->cb = NULL;
297 conn->cb_arg = NULL;
[0edaf0f6]298 tcp_conn_delref(conn);
[7a8c1c4e]299}
300
[032bbe7]301/** Enlist connection.
302 *
303 * Add connection to the connection map.
304 */
[b7fd2a0]305errno_t tcp_conn_add(tcp_conn_t *conn)
[c5808b41]306{
[2989c7e]307 inet_ep2_t aepp;
[b7fd2a0]308 errno_t rc;
[2989c7e]309
[e1c6dde9]310 tcp_conn_addref(conn);
[e1b4ae0]311 fibril_mutex_lock(&amap_lock);
[2989c7e]312
[c3f7d37]313 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_add: conn=%p", conn);
[8d48c7e]314
[2989c7e]315 rc = amap_insert(amap, &conn->ident, conn, af_allow_system, &aepp);
316 if (rc != EOK) {
317 tcp_conn_delref(conn);
[e1b4ae0]318 fibril_mutex_unlock(&amap_lock);
[2989c7e]319 return rc;
320 }
321
322 conn->ident = aepp;
[e1b4ae0]323 conn->mapped = true;
324 fibril_mutex_unlock(&amap_lock);
[2989c7e]325
326 return EOK;
[c5808b41]327}
328
[6e88fea]329/** Delist connection.
330 *
331 * Remove connection from the connection map.
332 */
[9520af7]333static void tcp_conn_remove(tcp_conn_t *conn)
[6e88fea]334{
[e1b4ae0]335 if (!conn->mapped)
[975d528]336 return;
337
[e1b4ae0]338 fibril_mutex_lock(&amap_lock);
[2989c7e]339 amap_remove(amap, &conn->ident);
[e1b4ae0]340 conn->mapped = false;
341 fibril_mutex_unlock(&amap_lock);
[e1c6dde9]342 tcp_conn_delref(conn);
[6e88fea]343}
344
[004a5fe]345static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
346{
[0edaf0f6]347 tcp_cstate_t old_state;
348
[a1a101d]349 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set(%p)", conn);
[ae481e0]350
[0edaf0f6]351 old_state = conn->cstate;
[004a5fe]352 conn->cstate = nstate;
353 fibril_condvar_broadcast(&conn->cstate_cv);
[7a8c1c4e]354
[ae481e0]355 /* Run user callback function */
[779541b]356 if (conn->cb != NULL && conn->cb->cstate_change != NULL) {
[a1a101d]357 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - run user CB");
[1d4b815]358 conn->cb->cstate_change(conn, conn->cb_arg, old_state);
[ae481e0]359 } else {
[a1a101d]360 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - no user CB");
[ae481e0]361 }
362
[0edaf0f6]363 assert(old_state != st_closed);
364 if (nstate == st_closed) {
[8499160]365 tcp_conn_remove(conn);
[0edaf0f6]366 /* Drop one reference for now being in closed state */
367 tcp_conn_delref(conn);
[7a8c1c4e]368 }
[004a5fe]369}
370
[032bbe7]371/** Synchronize connection.
372 *
373 * This is the first step of an active connection attempt,
374 * sends out SYN and sets up ISS and SND.xxx.
375 */
[c5808b41]376void tcp_conn_sync(tcp_conn_t *conn)
377{
[9520af7]378 assert(fibril_mutex_is_locked(&conn->lock));
379
[c5808b41]380 /* XXX select ISS */
381 conn->iss = 1;
382 conn->snd_nxt = conn->iss;
383 conn->snd_una = conn->iss;
[d9e14fa4]384 conn->ap = ap_active;
[c5808b41]385
386 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
[004a5fe]387 tcp_conn_state_set(conn, st_syn_sent);
[c5808b41]388}
389
[f343a16]390/** FIN has been sent.
391 *
392 * This function should be called when FIN is sent over the connection,
393 * as a result the connection state is changed appropriately.
394 */
395void tcp_conn_fin_sent(tcp_conn_t *conn)
396{
397 switch (conn->cstate) {
398 case st_syn_received:
399 case st_established:
[a1a101d]400 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
[004a5fe]401 tcp_conn_state_set(conn, st_fin_wait_1);
[f343a16]402 break;
403 case st_close_wait:
[a1a101d]404 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
[004a5fe]405 tcp_conn_state_set(conn, st_last_ack);
[f343a16]406 break;
407 default:
[a1a101d]408 log_msg(LOG_DEFAULT, LVL_ERROR, "%s: Connection state %d", conn->name,
[6896409c]409 conn->cstate);
[f343a16]410 assert(false);
411 }
[6df418c4]412
413 conn->fin_is_acked = false;
[f343a16]414}
415
[2f19103]416/** Find connection structure for specified endpoint pair.
[032bbe7]417 *
[2f19103]418 * A connection is uniquely identified by a endpoint pair. Look up our
419 * connection map and return connection structure based on endpoint pair.
[0edaf0f6]420 * The connection reference count is bumped by one.
[032bbe7]421 *
[2f19103]422 * @param epp Endpoint pair
[032bbe7]423 * @return Connection structure or NULL if not found.
424 */
[2f19103]425tcp_conn_t *tcp_conn_find_ref(inet_ep2_t *epp)
[c5808b41]426{
[b7fd2a0]427 errno_t rc;
[8d48c7e]428 void *arg;
429 tcp_conn_t *conn;
[2f19103]430
[8d48c7e]431 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref(%p)", epp);
[2f19103]432
[e1b4ae0]433 fibril_mutex_lock(&amap_lock);
[2f19103]434
[8d48c7e]435 rc = amap_find_match(amap, epp, &arg);
436 if (rc != EOK) {
437 assert(rc == ENOENT);
[e1b4ae0]438 fibril_mutex_unlock(&amap_lock);
[8d48c7e]439 return NULL;
[c5808b41]440 }
[2f19103]441
[8d48c7e]442 conn = (tcp_conn_t *)arg;
443 tcp_conn_addref(conn);
444
[e1b4ae0]445 fibril_mutex_unlock(&amap_lock);
[c3f7d37]446 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref: got conn=%p",
[8d48c7e]447 conn);
448 return conn;
[c5808b41]449}
450
[704586fb]451/** Reset connection.
452 *
453 * @param conn Connection
454 */
[b243da3]455void tcp_conn_reset(tcp_conn_t *conn)
[704586fb]456{
[e1b4ae0]457 assert(fibril_mutex_is_locked(&conn->lock));
458
[a1a101d]459 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
[3e2291a9]460
461 if (conn->cstate == st_closed)
462 return;
463
[d9e14fa4]464 conn->reset = true;
[9362cc2]465 tcp_conn_state_set(conn, st_closed);
[d9e14fa4]466
[704586fb]467 tcp_conn_tw_timer_clear(conn);
468 tcp_tqueue_clear(&conn->retransmit);
[d9e14fa4]469
470 fibril_condvar_broadcast(&conn->rcv_buf_cv);
[bbf159a]471 fibril_condvar_broadcast(&conn->snd_buf_cv);
[d9e14fa4]472}
473
474/** Signal to the user that connection has been reset.
475 *
476 * Send an out-of-band signal to the user.
477 */
478static void tcp_reset_signal(tcp_conn_t *conn)
479{
480 /* TODO */
[a1a101d]481 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
[704586fb]482}
483
[32105348]484/** Determine if SYN has been received.
485 *
486 * @param conn Connection
487 * @return @c true if SYN has been received, @c false otherwise.
488 */
489bool tcp_conn_got_syn(tcp_conn_t *conn)
490{
491 switch (conn->cstate) {
492 case st_listen:
493 case st_syn_sent:
494 return false;
495 case st_syn_received:
496 case st_established:
497 case st_fin_wait_1:
498 case st_fin_wait_2:
499 case st_close_wait:
500 case st_closing:
501 case st_last_ack:
502 case st_time_wait:
503 return true;
504 case st_closed:
[a1a101d]505 log_msg(LOG_DEFAULT, LVL_WARN, "state=%d", (int) conn->cstate);
[32105348]506 assert(false);
507 }
508
509 assert(false);
510}
511
[032bbe7]512/** Segment arrived in Listen state.
513 *
514 * @param conn Connection
515 * @param seg Segment
516 */
[c5808b41]517static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
518{
[a1a101d]519 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
[c5808b41]520
521 if ((seg->ctrl & CTL_RST) != 0) {
[a1a101d]522 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring incoming RST.");
[c5808b41]523 return;
524 }
525
526 if ((seg->ctrl & CTL_ACK) != 0) {
[a1a101d]527 log_msg(LOG_DEFAULT, LVL_DEBUG, "Incoming ACK, send acceptable RST.");
[c5808b41]528 tcp_reply_rst(&conn->ident, seg);
529 return;
530 }
531
532 if ((seg->ctrl & CTL_SYN) == 0) {
[a1a101d]533 log_msg(LOG_DEFAULT, LVL_DEBUG, "SYN not present. Ignoring segment.");
[c5808b41]534 return;
535 }
536
[a1a101d]537 log_msg(LOG_DEFAULT, LVL_DEBUG, "Got SYN, sending SYN, ACK.");
[c5808b41]538
539 conn->rcv_nxt = seg->seq + 1;
540 conn->irs = seg->seq;
541
[4c55a64]542
[a1a101d]543 log_msg(LOG_DEFAULT, LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
[c5808b41]544
545 if (seg->len > 1)
[a1a101d]546 log_msg(LOG_DEFAULT, LVL_WARN, "SYN combined with data, ignoring data.");
[c5808b41]547
548 /* XXX select ISS */
549 conn->iss = 1;
550 conn->snd_nxt = conn->iss;
551 conn->snd_una = conn->iss;
552
[4c55a64]553 /*
554 * Surprisingly the spec does not deal with initial window setting.
555 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
556 * will always be accepted as new window setting.
557 */
558 conn->snd_wnd = seg->wnd;
559 conn->snd_wl1 = seg->seq;
560 conn->snd_wl2 = seg->seq;
561
[004a5fe]562 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]563
564 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
[0093ab6]565
566 tcp_segment_delete(seg);
[c5808b41]567}
568
[032bbe7]569/** Segment arrived in Syn-Sent state.
570 *
571 * @param conn Connection
572 * @param seg Segment
573 */
[c5808b41]574static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
575{
[a1a101d]576 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
[c5808b41]577
578 if ((seg->ctrl & CTL_ACK) != 0) {
[a1a101d]579 log_msg(LOG_DEFAULT, LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
[c5808b41]580 conn->snd_una, seg->ack, conn->snd_nxt);
581 if (!seq_no_ack_acceptable(conn, seg->ack)) {
[2f0dd2a]582 if ((seg->ctrl & CTL_RST) == 0) {
[a1a101d]583 log_msg(LOG_DEFAULT, LVL_WARN, "ACK not acceptable, send RST");
[2f0dd2a]584 tcp_reply_rst(&conn->ident, seg);
585 } else {
[a1a101d]586 log_msg(LOG_DEFAULT, LVL_WARN, "RST,ACK not acceptable, drop");
[2f0dd2a]587 }
[c5808b41]588 return;
589 }
590 }
591
592 if ((seg->ctrl & CTL_RST) != 0) {
[2f0dd2a]593 /* If we get here, we have either an acceptable ACK or no ACK */
594 if ((seg->ctrl & CTL_ACK) != 0) {
[a1a101d]595 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Connection reset. -> Closed",
[2f0dd2a]596 conn->name);
597 /* Reset connection */
598 tcp_conn_reset(conn);
599 return;
600 } else {
[a1a101d]601 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: RST without ACK, drop",
[2f0dd2a]602 conn->name);
603 return;
604 }
[c5808b41]605 }
606
607 /* XXX precedence */
608
609 if ((seg->ctrl & CTL_SYN) == 0) {
[a1a101d]610 log_msg(LOG_DEFAULT, LVL_DEBUG, "No SYN bit, ignoring segment.");
[c5808b41]611 return;
612 }
613
614 conn->rcv_nxt = seg->seq + 1;
615 conn->irs = seg->seq;
616
617 if ((seg->ctrl & CTL_ACK) != 0) {
618 conn->snd_una = seg->ack;
[d9ce049]619
620 /*
621 * Prune acked segments from retransmission queue and
622 * possibly transmit more data.
623 */
624 tcp_tqueue_ack_received(conn);
[c5808b41]625 }
626
[a1a101d]627 log_msg(LOG_DEFAULT, LVL_DEBUG, "Sent SYN, got SYN.");
[c5808b41]628
[32105348]629 /*
630 * Surprisingly the spec does not deal with initial window setting.
631 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
632 * will always be accepted as new window setting.
633 */
[a1a101d]634 log_msg(LOG_DEFAULT, LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
[32105348]635 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
636 conn->snd_wnd = seg->wnd;
637 conn->snd_wl1 = seg->seq;
638 conn->snd_wl2 = seg->seq;
639
[c5808b41]640 if (seq_no_syn_acked(conn)) {
[a1a101d]641 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn acked -> Established", conn->name);
[004a5fe]642 tcp_conn_state_set(conn, st_established);
[c5808b41]643 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
644 } else {
[a1a101d]645 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn not acked -> Syn-Received",
[6896409c]646 conn->name);
[004a5fe]647 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]648 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
649 }
[0093ab6]650
651 tcp_segment_delete(seg);
[c5808b41]652}
653
[032bbe7]654/** Segment arrived in state where segments are processed in sequence order.
655 *
656 * Queue segment in incoming segments queue for processing.
657 *
658 * @param conn Connection
659 * @param seg Segment
660 */
[c5808b41]661static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
662{
663 tcp_segment_t *pseg;
664
[a1a101d]665 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
[c5808b41]666
[7cf7ded]667 /* Discard unacceptable segments ("old duplicates") */
668 if (!seq_no_segment_acceptable(conn, seg)) {
[a1a101d]669 log_msg(LOG_DEFAULT, LVL_DEBUG, "Replying ACK to unacceptable segment.");
[7cf7ded]670 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
671 tcp_segment_delete(seg);
672 return;
673 }
[c5808b41]674
675 /* Queue for processing */
676 tcp_iqueue_insert_seg(&conn->incoming, seg);
677
678 /*
679 * Process all segments from incoming queue that are ready.
680 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
681 *
682 * XXX Need to return ACK for unacceptable segments
683 */
684 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
685 tcp_conn_seg_process(conn, pseg);
686}
687
[032bbe7]688/** Process segment RST field.
689 *
690 * @param conn Connection
691 * @param seg Segment
692 * @return cp_done if we are done with this segment, cp_continue
693 * if not
694 */
[c5808b41]695static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
696{
[03be171]697 if ((seg->ctrl & CTL_RST) == 0)
698 return cp_continue;
699
[d9e14fa4]700 switch (conn->cstate) {
701 case st_syn_received:
702 /* XXX In case of passive open, revert to Listen state */
703 if (conn->ap == ap_passive) {
704 tcp_conn_state_set(conn, st_listen);
705 /* XXX Revert conn->ident */
706 tcp_conn_tw_timer_clear(conn);
707 tcp_tqueue_clear(&conn->retransmit);
708 } else {
709 tcp_conn_reset(conn);
710 }
711 break;
712 case st_established:
713 case st_fin_wait_1:
714 case st_fin_wait_2:
715 case st_close_wait:
716 /* General "connection reset" signal */
717 tcp_reset_signal(conn);
718 tcp_conn_reset(conn);
719 break;
720 case st_closing:
721 case st_last_ack:
722 case st_time_wait:
723 tcp_conn_reset(conn);
724 break;
725 case st_listen:
726 case st_syn_sent:
727 case st_closed:
728 assert(false);
729 }
730
731 return cp_done;
[c5808b41]732}
733
[032bbe7]734/** Process segment security and precedence fields.
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_sp(tcp_conn_t *conn, tcp_segment_t *seg)
742{
[4c55a64]743 /* TODO */
[c5808b41]744 return cp_continue;
745}
746
[032bbe7]747/** Process segment SYN field.
748 *
749 * @param conn Connection
750 * @param seg Segment
751 * @return cp_done if we are done with this segment, cp_continue
752 * if not
753 */
[c5808b41]754static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
755{
[7cf7ded]756 if ((seg->ctrl & CTL_SYN) == 0)
757 return cp_continue;
758
759 /*
760 * Assert SYN is in receive window, otherwise this step should not
761 * be reached.
762 */
763 assert(seq_no_in_rcv_wnd(conn, seg->seq));
764
[a1a101d]765 log_msg(LOG_DEFAULT, LVL_WARN, "SYN is in receive window, should send reset. XXX");
[7cf7ded]766
767 /*
768 * TODO
769 *
770 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
771 * flush segment queues. Send unsolicited "connection reset" signal
772 * to user, connection -> closed state, delete TCB, return.
773 */
774 return cp_done;
[c5808b41]775}
776
[032bbe7]777/** Process segment ACK field in Syn-Received state.
778 *
779 * @param conn Connection
780 * @param seg Segment
781 * @return cp_done if we are done with this segment, cp_continue
782 * if not
783 */
[c5808b41]784static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
785{
786 if (!seq_no_ack_acceptable(conn, seg->ack)) {
787 /* ACK is not acceptable, send RST. */
[a1a101d]788 log_msg(LOG_DEFAULT, LVL_WARN, "Segment ACK not acceptable, sending RST.");
[c5808b41]789 tcp_reply_rst(&conn->ident, seg);
[4c55a64]790 tcp_segment_delete(seg);
791 return cp_done;
[c5808b41]792 }
793
[a1a101d]794 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
[c5808b41]795
[004a5fe]796 tcp_conn_state_set(conn, st_established);
[c5808b41]797
798 /* XXX Not mentioned in spec?! */
799 conn->snd_una = seg->ack;
800
801 return cp_continue;
802}
803
[032bbe7]804/** Process segment ACK field in Established state.
805 *
806 * @param conn Connection
807 * @param seg Segment
808 * @return cp_done if we are done with this segment, cp_continue
809 * if not
810 */
[c5808b41]811static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
812{
[a1a101d]813 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
[0093ab6]814
[a1a101d]815 log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
[0093ab6]816 (unsigned)seg->ack, (unsigned)conn->snd_una,
817 (unsigned)conn->snd_nxt);
818
[4c55a64]819 if (!seq_no_ack_acceptable(conn, seg->ack)) {
[a1a101d]820 log_msg(LOG_DEFAULT, LVL_DEBUG, "ACK not acceptable.");
[4c55a64]821 if (!seq_no_ack_duplicate(conn, seg->ack)) {
[a1a101d]822 log_msg(LOG_DEFAULT, LVL_WARN, "Not acceptable, not duplicate. "
[0093ab6]823 "Send ACK and drop.");
[4c55a64]824 /* Not acceptable, not duplicate. Send ACK and drop. */
825 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
826 tcp_segment_delete(seg);
827 return cp_done;
[0093ab6]828 } else {
[a1a101d]829 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring duplicate ACK.");
[4c55a64]830 }
831 } else {
832 /* Update SND.UNA */
833 conn->snd_una = seg->ack;
834 }
835
836 if (seq_no_new_wnd_update(conn, seg)) {
837 conn->snd_wnd = seg->wnd;
838 conn->snd_wl1 = seg->seq;
839 conn->snd_wl2 = seg->ack;
[d9ce049]840
[a1a101d]841 log_msg(LOG_DEFAULT, LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
[d9ce049]842 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
843 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
[4c55a64]844 }
845
[d9ce049]846 /*
847 * Prune acked segments from retransmission queue and
848 * possibly transmit more data.
849 */
850 tcp_tqueue_ack_received(conn);
851
[c5808b41]852 return cp_continue;
853}
854
[032bbe7]855/** Process segment ACK field in Fin-Wait-1 state.
856 *
857 * @param conn Connection
858 * @param seg Segment
859 * @return cp_done if we are done with this segment, cp_continue
860 * if not
861 */
[c5808b41]862static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
863{
[4c55a64]864 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
865 return cp_done;
866
[6df418c4]867 if (conn->fin_is_acked) {
[a1a101d]868 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
[004a5fe]869 tcp_conn_state_set(conn, st_fin_wait_2);
[6df418c4]870 }
871
[c5808b41]872 return cp_continue;
873}
874
[032bbe7]875/** Process segment ACK field in Fin-Wait-2 state.
876 *
877 * @param conn Connection
878 * @param seg Segment
879 * @return cp_done if we are done with this segment, cp_continue
880 * if not
881 */
[c5808b41]882static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
883{
[4c55a64]884 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
885 return cp_done;
886
887 /* TODO */
[c5808b41]888 return cp_continue;
889}
890
[032bbe7]891/** Process segment ACK field in Close-Wait state.
892 *
893 * @param conn Connection
894 * @param seg Segment
895 * @return cp_done if we are done with this segment, cp_continue
896 * if not
897 */
[c5808b41]898static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
899{
[4c55a64]900 /* The same processing as in Established state */
901 return tcp_conn_seg_proc_ack_est(conn, seg);
[c5808b41]902}
903
[032bbe7]904/** Process segment ACK field in Closing state.
905 *
906 * @param conn Connection
907 * @param seg Segment
908 * @return cp_done if we are done with this segment, cp_continue
909 * if not
910 */
[c5808b41]911static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
912{
[4c55a64]913 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
914 return cp_done;
915
[3e2291a9]916 if (conn->fin_is_acked) {
917 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Time-Wait",
918 conn->name);
919 tcp_conn_state_set(conn, st_time_wait);
920 }
921
[c5808b41]922 return cp_continue;
923}
924
[032bbe7]925/** Process segment ACK field in Last-Ack state.
926 *
927 * @param conn Connection
928 * @param seg Segment
929 * @return cp_done if we are done with this segment, cp_continue
930 * if not
931 */
[c5808b41]932static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
933{
[4c55a64]934 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
935 return cp_done;
936
[6e88fea]937 if (conn->fin_is_acked) {
[a1a101d]938 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
[004a5fe]939 tcp_conn_state_set(conn, st_closed);
[6e88fea]940 return cp_done;
941 }
942
[c5808b41]943 return cp_continue;
944}
945
[032bbe7]946/** Process segment ACK field in Time-Wait state.
947 *
948 * @param conn Connection
949 * @param seg Segment
950 * @return cp_done if we are done with this segment, cp_continue
951 * if not
952 */
[c5808b41]953static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
954{
[4c55a64]955 /* Nothing to do */
[c5808b41]956 return cp_continue;
957}
958
[032bbe7]959/** Process segment ACK field.
960 *
961 * @param conn Connection
962 * @param seg Segment
963 * @return cp_done if we are done with this segment, cp_continue
964 * if not
965 */
[c5808b41]966static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
967{
[a1a101d]968 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
[23fe06c]969 conn->name, conn, seg);
[c5808b41]970
971 if ((seg->ctrl & CTL_ACK) == 0) {
[a1a101d]972 log_msg(LOG_DEFAULT, LVL_WARN, "Segment has no ACK. Dropping.");
[4c55a64]973 tcp_segment_delete(seg);
[c5808b41]974 return cp_done;
975 }
976
977 switch (conn->cstate) {
978 case st_syn_received:
979 return tcp_conn_seg_proc_ack_sr(conn, seg);
980 case st_established:
981 return tcp_conn_seg_proc_ack_est(conn, seg);
982 case st_fin_wait_1:
983 return tcp_conn_seg_proc_ack_fw1(conn, seg);
984 case st_fin_wait_2:
985 return tcp_conn_seg_proc_ack_fw2(conn, seg);
986 case st_close_wait:
987 return tcp_conn_seg_proc_ack_cw(conn, seg);
988 case st_closing:
989 return tcp_conn_seg_proc_ack_cls(conn, seg);
990 case st_last_ack:
991 return tcp_conn_seg_proc_ack_la(conn, seg);
992 case st_time_wait:
993 return tcp_conn_seg_proc_ack_tw(conn, seg);
994 case st_listen:
995 case st_syn_sent:
996 case st_closed:
997 assert(false);
998 }
999
1000 assert(false);
1001}
1002
[032bbe7]1003/** Process segment URG field.
1004 *
1005 * @param conn Connection
1006 * @param seg Segment
1007 * @return cp_done if we are done with this segment, cp_continue
1008 * if not
1009 */
[c5808b41]1010static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
1011{
1012 return cp_continue;
1013}
1014
[032bbe7]1015/** Process segment text.
1016 *
1017 * @param conn Connection
1018 * @param seg Segment
1019 * @return cp_done if we are done with this segment, cp_continue
1020 * if not
1021 */
[c5808b41]1022static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
1023{
[0093ab6]1024 size_t text_size;
1025 size_t xfer_size;
1026
[a1a101d]1027 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
[23fe06c]1028 conn->name, conn, seg);
[0093ab6]1029
[4c55a64]1030 switch (conn->cstate) {
1031 case st_established:
1032 case st_fin_wait_1:
1033 case st_fin_wait_2:
1034 /* OK */
1035 break;
1036 case st_close_wait:
1037 case st_closing:
1038 case st_last_ack:
1039 case st_time_wait:
1040 /* Invalid since FIN has been received. Ignore text. */
1041 return cp_continue;
1042 case st_listen:
1043 case st_syn_sent:
1044 case st_syn_received:
1045 case st_closed:
1046 assert(false);
1047 }
1048
[0093ab6]1049 /*
1050 * Process segment text
1051 */
1052 assert(seq_no_segment_ready(conn, seg));
1053
1054 /* Trim anything outside our receive window */
1055 tcp_conn_trim_seg_to_wnd(conn, seg);
1056
1057 /* Determine how many bytes to copy */
1058 text_size = tcp_segment_text_size(seg);
1059 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
1060
1061 /* Copy data to receive buffer */
[d9ce049]1062 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
1063 xfer_size);
1064 conn->rcv_buf_used += xfer_size;
1065
1066 /* Signal to the receive function that new data has arrived */
[204ba47]1067 if (xfer_size > 0) {
1068 fibril_condvar_broadcast(&conn->rcv_buf_cv);
1069 if (conn->cb != NULL && conn->cb->recv_data != NULL)
1070 conn->cb->recv_data(conn, conn->cb_arg);
1071 }
[0093ab6]1072
[a1a101d]1073 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
[32105348]1074
[0093ab6]1075 /* Advance RCV.NXT */
1076 conn->rcv_nxt += xfer_size;
1077
1078 /* Update receive window. XXX Not an efficient strategy. */
1079 conn->rcv_wnd -= xfer_size;
1080
1081 /* Send ACK */
1082 if (xfer_size > 0)
1083 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1084
1085 if (xfer_size < seg->len) {
[8c7a054]1086 /* Trim part of segment which we just received */
[0093ab6]1087 tcp_conn_trim_seg_to_wnd(conn, seg);
[8c7a054]1088 } else {
[a1a101d]1089 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Nothing left in segment, dropping "
[f0a2720]1090 "(xfer_size=%zu, SEG.LEN=%" PRIu32 ", seg->ctrl=%u)",
1091 conn->name, xfer_size, seg->len, (unsigned int) seg->ctrl);
[8c7a054]1092 /* Nothing left in segment */
1093 tcp_segment_delete(seg);
[0093ab6]1094 return cp_done;
1095 }
1096
[c5808b41]1097 return cp_continue;
1098}
1099
[032bbe7]1100/** Process segment FIN field.
1101 *
1102 * @param conn Connection
1103 * @param seg Segment
1104 * @return cp_done if we are done with this segment, cp_continue
1105 * if not
1106 */
[c5808b41]1107static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
1108{
[a1a101d]1109 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
[23fe06c]1110 conn->name, conn, seg);
[a1a101d]1111 log_msg(LOG_DEFAULT, LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
[7cf7ded]1112 (unsigned) seg->ctrl);
[8c7a054]1113
1114 /* Only process FIN if no text is left in segment. */
1115 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
[a1a101d]1116 log_msg(LOG_DEFAULT, LVL_DEBUG, " - FIN found in segment.");
[8c7a054]1117
1118 conn->rcv_nxt++;
1119 conn->rcv_wnd--;
1120
[3e2291a9]1121 /* Send ACK */
1122 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1123
[f343a16]1124 /* Change connection state */
1125 switch (conn->cstate) {
1126 case st_listen:
1127 case st_syn_sent:
1128 case st_closed:
1129 /* Connection not synchronized */
1130 assert(false);
[dc12262]1131 /* Fallthrough */
[f343a16]1132 case st_syn_received:
1133 case st_established:
[a1a101d]1134 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Close-Wait",
[6896409c]1135 conn->name);
[004a5fe]1136 tcp_conn_state_set(conn, st_close_wait);
[f343a16]1137 break;
1138 case st_fin_wait_1:
[a1a101d]1139 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Closing",
[6896409c]1140 conn->name);
[004a5fe]1141 tcp_conn_state_set(conn, st_closing);
[f343a16]1142 break;
1143 case st_fin_wait_2:
[a1a101d]1144 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Time-Wait",
[6896409c]1145 conn->name);
[004a5fe]1146 tcp_conn_state_set(conn, st_time_wait);
[2a3214e]1147 /* Start the Time-Wait timer */
1148 tcp_conn_tw_timer_set(conn);
[f343a16]1149 break;
1150 case st_close_wait:
1151 case st_closing:
1152 case st_last_ack:
1153 /* Do nothing */
1154 break;
1155 case st_time_wait:
[2a3214e]1156 /* Restart the Time-Wait timer */
1157 tcp_conn_tw_timer_set(conn);
[f343a16]1158 break;
1159 }
[8c7a054]1160
1161 /* Add FIN to the receive buffer */
1162 conn->rcv_buf_fin = true;
1163 fibril_condvar_broadcast(&conn->rcv_buf_cv);
[779541b]1164 if (conn->cb != NULL && conn->cb->recv_data != NULL)
1165 conn->cb->recv_data(conn, conn->cb_arg);
[8c7a054]1166
1167 tcp_segment_delete(seg);
1168 return cp_done;
1169 }
1170
[c5808b41]1171 return cp_continue;
1172}
1173
1174/** Process incoming segment.
1175 *
1176 * We are in connection state where segments are processed in order
1177 * of sequence number. This processes one segment taken from the
1178 * connection incoming segments queue.
[032bbe7]1179 *
1180 * @param conn Connection
1181 * @param seg Segment
[c5808b41]1182 */
1183static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
1184{
[a1a101d]1185 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
[6896409c]1186 tcp_segment_dump(seg);
[c5808b41]1187
1188 /* Check whether segment is acceptable */
1189 /* XXX Permit valid ACKs, URGs and RSTs */
1190/* if (!seq_no_segment_acceptable(conn, seg)) {
[a1a101d]1191 log_msg(LOG_DEFAULT, LVL_WARN, "Segment not acceptable, dropping.");
[c5808b41]1192 if ((seg->ctrl & CTL_RST) == 0) {
1193 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1194 }
1195 return;
1196 }
1197*/
1198
1199 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
1200 return;
1201
1202 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
1203 return;
1204
1205 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
1206 return;
1207
1208 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
1209 return;
1210
1211 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
1212 return;
1213
1214 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
1215 return;
1216
1217 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
1218 return;
[0093ab6]1219
[8c7a054]1220 /*
1221 * If anything is left from the segment, insert it back into the
1222 * incoming segments queue.
1223 */
[7cf7ded]1224 if (seg->len > 0) {
[a1a101d]1225 log_msg(LOG_DEFAULT, LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
[7cf7ded]1226 seg, (size_t) seg->len);
[8c7a054]1227 tcp_iqueue_insert_seg(&conn->incoming, seg);
[7cf7ded]1228 } else {
[8c7a054]1229 tcp_segment_delete(seg);
[7cf7ded]1230 }
[c5808b41]1231}
1232
[032bbe7]1233/** Segment arrived on a connection.
1234 *
1235 * @param conn Connection
[2c4bb828]1236 * @param epp Endpoint pair on which segment was received
[032bbe7]1237 * @param seg Segment
1238 */
[2c4bb828]1239void tcp_conn_segment_arrived(tcp_conn_t *conn, inet_ep2_t *epp,
1240 tcp_segment_t *seg)
[c5808b41]1241{
[2c4bb828]1242 inet_ep2_t aepp;
1243 inet_ep2_t oldepp;
[b7fd2a0]1244 errno_t rc;
[2c4bb828]1245
[c3f7d37]1246 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_segment_arrived(%p)",
[23fe06c]1247 conn->name, seg);
[c5808b41]1248
[2c4bb828]1249 tcp_conn_lock(conn);
1250
1251 if (conn->cstate == st_closed) {
1252 log_msg(LOG_DEFAULT, LVL_WARN, "Connection is closed.");
1253 tcp_unexpected_segment(epp, seg);
1254 tcp_conn_unlock(conn);
1255 return;
1256 }
1257
1258 if (inet_addr_is_any(&conn->ident.remote.addr) ||
1259 conn->ident.remote.port == inet_port_any ||
1260 inet_addr_is_any(&conn->ident.local.addr)) {
1261
[c3f7d37]1262 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_segment_arrived: "
[2c4bb828]1263 "Changing connection ID, updating amap.");
1264 oldepp = conn->ident;
1265
1266 /* Need to remove and re-insert connection with new identity */
[e1b4ae0]1267 fibril_mutex_lock(&amap_lock);
[2c4bb828]1268
1269 if (inet_addr_is_any(&conn->ident.remote.addr))
1270 conn->ident.remote.addr = epp->remote.addr;
1271
1272 if (conn->ident.remote.port == inet_port_any)
1273 conn->ident.remote.port = epp->remote.port;
1274
1275 if (inet_addr_is_any(&conn->ident.local.addr))
1276 conn->ident.local.addr = epp->local.addr;
1277
1278 rc = amap_insert(amap, &conn->ident, conn, af_allow_system, &aepp);
1279 if (rc != EOK) {
[8a637a4]1280 assert(rc != EEXIST);
[2c4bb828]1281 assert(rc == ENOMEM);
1282 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
[e1b4ae0]1283 fibril_mutex_unlock(&amap_lock);
[2c4bb828]1284 tcp_conn_unlock(conn);
1285 return;
1286 }
1287
1288 amap_remove(amap, &oldepp);
[e1b4ae0]1289 fibril_mutex_unlock(&amap_lock);
[2c4bb828]1290
1291 conn->name = (char *) "a";
1292 }
1293
[c5808b41]1294 switch (conn->cstate) {
1295 case st_listen:
[2c4bb828]1296 tcp_conn_sa_listen(conn, seg);
1297 break;
[c5808b41]1298 case st_syn_sent:
[2c4bb828]1299 tcp_conn_sa_syn_sent(conn, seg);
1300 break;
[c5808b41]1301 case st_syn_received:
1302 case st_established:
1303 case st_fin_wait_1:
1304 case st_fin_wait_2:
1305 case st_close_wait:
1306 case st_closing:
1307 case st_last_ack:
1308 case st_time_wait:
1309 /* Process segments in order of sequence number */
[2c4bb828]1310 tcp_conn_sa_queue(conn, seg);
1311 break;
[c5808b41]1312 case st_closed:
[a1a101d]1313 log_msg(LOG_DEFAULT, LVL_DEBUG, "state=%d", (int) conn->cstate);
[c5808b41]1314 assert(false);
1315 }
[2c4bb828]1316
1317 tcp_conn_unlock(conn);
[c5808b41]1318}
1319
[2a3214e]1320/** Time-Wait timeout handler.
1321 *
1322 * @param arg Connection
1323 */
1324static void tw_timeout_func(void *arg)
1325{
1326 tcp_conn_t *conn = (tcp_conn_t *) arg;
1327
[a1a101d]1328 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p)", conn);
[2a3214e]1329
[78192cc7]1330 tcp_conn_lock(conn);
[0edaf0f6]1331
[704586fb]1332 if (conn->cstate == st_closed) {
[a1a101d]1333 log_msg(LOG_DEFAULT, LVL_DEBUG, "Connection already closed.");
[78192cc7]1334 tcp_conn_unlock(conn);
[0edaf0f6]1335 tcp_conn_delref(conn);
[704586fb]1336 return;
1337 }
1338
[a1a101d]1339 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
[004a5fe]1340 tcp_conn_state_set(conn, st_closed);
[0edaf0f6]1341
[78192cc7]1342 tcp_conn_unlock(conn);
[0edaf0f6]1343 tcp_conn_delref(conn);
[78192cc7]1344
1345 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p) end", conn);
[2a3214e]1346}
1347
1348/** Start or restart the Time-Wait timeout.
1349 *
1350 * @param conn Connection
1351 */
1352void tcp_conn_tw_timer_set(tcp_conn_t *conn)
1353{
[78192cc7]1354 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_set() begin");
[0edaf0f6]1355 tcp_conn_addref(conn);
[78192cc7]1356 fibril_timer_set_locked(conn->tw_timer, TIME_WAIT_TIMEOUT,
1357 tw_timeout_func, (void *)conn);
1358 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_set() end");
[2a3214e]1359}
1360
[704586fb]1361/** Clear the Time-Wait timeout.
1362 *
1363 * @param conn Connection
1364 */
1365void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1366{
[78192cc7]1367 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() begin");
1368 if (fibril_timer_clear_locked(conn->tw_timer) == fts_active)
[0edaf0f6]1369 tcp_conn_delref(conn);
[78192cc7]1370 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() end");
[704586fb]1371}
1372
[032bbe7]1373/** Trim segment to the receive window.
1374 *
1375 * @param conn Connection
1376 * @param seg Segment
1377 */
[9520af7]1378static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
[0093ab6]1379{
1380 uint32_t left, right;
1381
1382 seq_no_seg_trim_calc(conn, seg, &left, &right);
1383 tcp_segment_trim(seg, left, right);
1384}
1385
[2f19103]1386/** Handle unexpected segment received on an endpoint pair.
[032bbe7]1387 *
1388 * We reply with an RST unless the received segment has RST.
1389 *
[2f19103]1390 * @param sp Endpoint pair which received the segment
[032bbe7]1391 * @param seg Unexpected segment
1392 */
[2f19103]1393void tcp_unexpected_segment(inet_ep2_t *epp, tcp_segment_t *seg)
[c5808b41]1394{
[2f19103]1395 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", epp,
1396 seg);
[c5808b41]1397
1398 if ((seg->ctrl & CTL_RST) == 0)
[2f19103]1399 tcp_reply_rst(epp, seg);
[c5808b41]1400}
1401
[9520af7]1402/** Transmit segment over network.
1403 *
1404 * @param epp Endpoint pair with source and destination information
1405 * @param seg Segment (ownership retained by caller)
1406 */
[975d528]1407static void tcp_transmit_segment(inet_ep2_t *epp, tcp_segment_t *seg)
1408{
[9520af7]1409 tcp_pdu_t *pdu;
1410 tcp_segment_t *dseg;
1411 inet_ep2_t rident;
1412
[975d528]1413 log_msg(LOG_DEFAULT, LVL_DEBUG,
1414 "tcp_transmit_segment(l:(%u),f:(%u), %p)",
1415 epp->local.port, epp->remote.port, seg);
1416
1417 log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
1418 seg->seq, seg->wnd);
1419
1420 tcp_segment_dump(seg);
1421
[9520af7]1422 if (tcp_conn_lb == tcp_lb_segment) {
1423 /* Loop back segment */
1424// tcp_ncsim_bounce_seg(sp, seg);
[975d528]1425
[9520af7]1426 /* Reverse the identification */
1427 tcp_ep2_flipped(epp, &rident);
1428
1429 /* Insert segment back into rqueue */
1430 dseg = tcp_segment_dup(seg);
1431 tcp_rqueue_insert_seg(&rident, dseg);
1432 return;
1433 }
[975d528]1434
1435 if (tcp_pdu_encode(epp, seg, &pdu) != EOK) {
1436 log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
1437 return;
1438 }
1439
[9520af7]1440 if (tcp_conn_lb == tcp_lb_pdu) {
1441 /* Loop back PDU */
1442 if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
1443 log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
1444 tcp_pdu_delete(pdu);
1445 return;
1446 }
1447
1448 tcp_pdu_delete(pdu);
1449
1450 /* Insert decoded segment into rqueue */
1451 tcp_rqueue_insert_seg(&rident, dseg);
1452 return;
1453 }
1454
[975d528]1455 tcp_transmit_pdu(pdu);
1456 tcp_pdu_delete(pdu);
1457}
1458
[2f19103]1459/** Compute flipped endpoint pair for response.
[032bbe7]1460 *
[2f19103]1461 * Flipped endpoint pair has local and remote endpoints exchanged.
[032bbe7]1462 *
[2f19103]1463 * @param epp Endpoint pair
1464 * @param fepp Place to store flipped endpoint pair
[032bbe7]1465 */
[2f19103]1466void tcp_ep2_flipped(inet_ep2_t *epp, inet_ep2_t *fepp)
[c5808b41]1467{
[2f19103]1468 fepp->local = epp->remote;
1469 fepp->remote = epp->local;
[c5808b41]1470}
1471
[032bbe7]1472/** Send RST in response to an incoming segment.
1473 *
[2f19103]1474 * @param epp Endpoint pair which received the segment
[032bbe7]1475 * @param seg Incoming segment
1476 */
[9520af7]1477static void tcp_reply_rst(inet_ep2_t *epp, tcp_segment_t *seg)
[c5808b41]1478{
1479 tcp_segment_t *rseg;
1480
[2f19103]1481 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_reply_rst(%p, %p)", epp, seg);
[c5808b41]1482
1483 rseg = tcp_segment_make_rst(seg);
[2f19103]1484 tcp_transmit_segment(epp, rseg);
[c5808b41]1485}
1486
1487/**
1488 * @}
1489 */
Note: See TracBrowser for help on using the repository browser.