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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a2eb85d 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
Line 
1/*
2 * Copyright (c) 2015 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 <errno.h>
39#include <inet/endpoint.h>
40#include <io/log.h>
41#include <macros.h>
42#include <nettl/amap.h>
43#include <stdbool.h>
44#include <stdlib.h>
45#include "conn.h"
46#include "inet.h"
47#include "iqueue.h"
48#include "pdu.h"
49#include "rqueue.h"
50#include "segment.h"
51#include "seq_no.h"
52#include "tcp_type.h"
53#include "tqueue.h"
54#include "ucall.h"
55
56#define RCV_BUF_SIZE 4096/*2*/
57#define SND_BUF_SIZE 4096
58
59#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
60#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
61
62/** List of all allocated connections */
63static LIST_INITIALIZE(conn_list);
64/** Taken after tcp_conn_t lock */
65static FIBRIL_MUTEX_INITIALIZE(conn_list_lock);
66/** Connection association map */
67static amap_t *amap;
68/** Taken after tcp_conn_t lock */
69static FIBRIL_MUTEX_INITIALIZE(amap_lock);
70
71/** Internal loopback configuration */
72tcp_lb_t tcp_conn_lb = tcp_lb_none;
73
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 *);
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 *);
80
81static tcp_tqueue_cb_t tcp_conn_tqueue_cb = {
82 .transmit_seg = tcp_transmit_segment
83};
84
85/** Initialize connections. */
86errno_t tcp_conns_init(void)
87{
88 errno_t rc;
89
90 rc = amap_create(&amap);
91 if (rc != EOK) {
92 assert(rc == ENOMEM);
93 return ENOMEM;
94 }
95
96 return EOK;
97}
98
99/** Finalize connections. */
100void tcp_conns_fini(void)
101{
102 assert(list_empty(&conn_list));
103
104 amap_destroy(amap);
105 amap = NULL;
106}
107
108/** Create new connection structure.
109 *
110 * @param epp Endpoint pair (will be deeply copied)
111 * @return New connection or NULL
112 */
113tcp_conn_t *tcp_conn_new(inet_ep2_t *epp)
114{
115 tcp_conn_t *conn = NULL;
116 bool tqueue_inited = false;
117
118 /* Allocate connection structure */
119 conn = calloc(1, sizeof(tcp_conn_t));
120 if (conn == NULL)
121 goto error;
122
123 fibril_mutex_initialize(&conn->lock);
124
125 conn->tw_timer = fibril_timer_create(&conn->lock);
126 if (conn->tw_timer == NULL)
127 goto error;
128
129 /* One for the user, one for not being in closed state */
130 atomic_set(&conn->refcnt, 2);
131
132 /* Allocate receive buffer */
133 fibril_condvar_initialize(&conn->rcv_buf_cv);
134 conn->rcv_buf_size = RCV_BUF_SIZE;
135 conn->rcv_buf_used = 0;
136 conn->rcv_buf_fin = false;
137
138 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
139 if (conn->rcv_buf == NULL)
140 goto error;
141
142 /** Allocate send buffer */
143 fibril_condvar_initialize(&conn->snd_buf_cv);
144 conn->snd_buf_size = SND_BUF_SIZE;
145 conn->snd_buf_used = 0;
146 conn->snd_buf_fin = false;
147 conn->snd_buf = calloc(1, conn->snd_buf_size);
148 if (conn->snd_buf == NULL)
149 goto error;
150
151 /* Set up receive window. */
152 conn->rcv_wnd = conn->rcv_buf_size;
153
154 /* Initialize incoming segment queue */
155 tcp_iqueue_init(&conn->incoming, conn);
156
157 /* Initialize retransmission queue */
158 if (tcp_tqueue_init(&conn->retransmit, conn, &tcp_conn_tqueue_cb) !=
159 EOK) {
160 goto error;
161 }
162
163 tqueue_inited = true;
164
165 /* Connection state change signalling */
166 fibril_condvar_initialize(&conn->cstate_cv);
167
168 conn->cb = NULL;
169
170 conn->cstate = st_listen;
171 conn->reset = false;
172 conn->deleted = false;
173 conn->ap = ap_passive;
174 conn->fin_is_acked = false;
175 if (epp != NULL)
176 conn->ident = *epp;
177
178 fibril_mutex_lock(&conn_list_lock);
179 list_append(&conn->link, &conn_list);
180 fibril_mutex_unlock(&conn_list_lock);
181
182 return conn;
183
184error:
185 if (tqueue_inited)
186 tcp_tqueue_fini(&conn->retransmit);
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;
197}
198
199/** Destroy connection structure.
200 *
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.
209 *
210 * @param conn Connection
211 */
212static void tcp_conn_free(tcp_conn_t *conn)
213{
214 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_free(%p)", conn->name, conn);
215
216 assert(conn->mapped == false);
217 tcp_tqueue_fini(&conn->retransmit);
218
219 fibril_mutex_lock(&conn_list_lock);
220 list_remove(&conn->link);
221 fibril_mutex_unlock(&conn_list_lock);
222
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
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{
240 log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_addref(%p) before=%zu",
241 conn->name, conn, atomic_get(&conn->refcnt));
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{
253 log_msg(LOG_DEFAULT, LVL_DEBUG2, "%s: tcp_conn_delref(%p) before=%zu",
254 conn->name, conn, atomic_get(&conn->refcnt));
255
256 if (atomic_predec(&conn->refcnt) == 0)
257 tcp_conn_free(conn);
258}
259
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
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{
292 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_delete(%p)", conn->name, conn);
293
294 assert(conn->deleted == false);
295 conn->deleted = true;
296 conn->cb = NULL;
297 conn->cb_arg = NULL;
298 tcp_conn_delref(conn);
299}
300
301/** Enlist connection.
302 *
303 * Add connection to the connection map.
304 */
305errno_t tcp_conn_add(tcp_conn_t *conn)
306{
307 inet_ep2_t aepp;
308 errno_t rc;
309
310 tcp_conn_addref(conn);
311 fibril_mutex_lock(&amap_lock);
312
313 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_add: conn=%p", conn);
314
315 rc = amap_insert(amap, &conn->ident, conn, af_allow_system, &aepp);
316 if (rc != EOK) {
317 tcp_conn_delref(conn);
318 fibril_mutex_unlock(&amap_lock);
319 return rc;
320 }
321
322 conn->ident = aepp;
323 conn->mapped = true;
324 fibril_mutex_unlock(&amap_lock);
325
326 return EOK;
327}
328
329/** Delist connection.
330 *
331 * Remove connection from the connection map.
332 */
333static void tcp_conn_remove(tcp_conn_t *conn)
334{
335 if (!conn->mapped)
336 return;
337
338 fibril_mutex_lock(&amap_lock);
339 amap_remove(amap, &conn->ident);
340 conn->mapped = false;
341 fibril_mutex_unlock(&amap_lock);
342 tcp_conn_delref(conn);
343}
344
345static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
346{
347 tcp_cstate_t old_state;
348
349 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set(%p)", conn);
350
351 old_state = conn->cstate;
352 conn->cstate = nstate;
353 fibril_condvar_broadcast(&conn->cstate_cv);
354
355 /* Run user callback function */
356 if (conn->cb != NULL && conn->cb->cstate_change != NULL) {
357 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - run user CB");
358 conn->cb->cstate_change(conn, conn->cb_arg, old_state);
359 } else {
360 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_state_set() - no user CB");
361 }
362
363 assert(old_state != st_closed);
364 if (nstate == st_closed) {
365 tcp_conn_remove(conn);
366 /* Drop one reference for now being in closed state */
367 tcp_conn_delref(conn);
368 }
369}
370
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 */
376void tcp_conn_sync(tcp_conn_t *conn)
377{
378 assert(fibril_mutex_is_locked(&conn->lock));
379
380 /* XXX select ISS */
381 conn->iss = 1;
382 conn->snd_nxt = conn->iss;
383 conn->snd_una = conn->iss;
384 conn->ap = ap_active;
385
386 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
387 tcp_conn_state_set(conn, st_syn_sent);
388}
389
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:
400 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
401 tcp_conn_state_set(conn, st_fin_wait_1);
402 break;
403 case st_close_wait:
404 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
405 tcp_conn_state_set(conn, st_last_ack);
406 break;
407 default:
408 log_msg(LOG_DEFAULT, LVL_ERROR, "%s: Connection state %d", conn->name,
409 conn->cstate);
410 assert(false);
411 }
412
413 conn->fin_is_acked = false;
414}
415
416/** Find connection structure for specified endpoint pair.
417 *
418 * A connection is uniquely identified by a endpoint pair. Look up our
419 * connection map and return connection structure based on endpoint pair.
420 * The connection reference count is bumped by one.
421 *
422 * @param epp Endpoint pair
423 * @return Connection structure or NULL if not found.
424 */
425tcp_conn_t *tcp_conn_find_ref(inet_ep2_t *epp)
426{
427 errno_t rc;
428 void *arg;
429 tcp_conn_t *conn;
430
431 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref(%p)", epp);
432
433 fibril_mutex_lock(&amap_lock);
434
435 rc = amap_find_match(amap, epp, &arg);
436 if (rc != EOK) {
437 assert(rc == ENOENT);
438 fibril_mutex_unlock(&amap_lock);
439 return NULL;
440 }
441
442 conn = (tcp_conn_t *)arg;
443 tcp_conn_addref(conn);
444
445 fibril_mutex_unlock(&amap_lock);
446 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref: got conn=%p",
447 conn);
448 return conn;
449}
450
451/** Reset connection.
452 *
453 * @param conn Connection
454 */
455void tcp_conn_reset(tcp_conn_t *conn)
456{
457 assert(fibril_mutex_is_locked(&conn->lock));
458
459 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
460
461 if (conn->cstate == st_closed)
462 return;
463
464 conn->reset = true;
465 tcp_conn_state_set(conn, st_closed);
466
467 tcp_conn_tw_timer_clear(conn);
468 tcp_tqueue_clear(&conn->retransmit);
469
470 fibril_condvar_broadcast(&conn->rcv_buf_cv);
471 fibril_condvar_broadcast(&conn->snd_buf_cv);
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 */
481 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
482}
483
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:
505 log_msg(LOG_DEFAULT, LVL_WARN, "state=%d", (int) conn->cstate);
506 assert(false);
507 }
508
509 assert(false);
510}
511
512/** Segment arrived in Listen state.
513 *
514 * @param conn Connection
515 * @param seg Segment
516 */
517static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
518{
519 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
520
521 if ((seg->ctrl & CTL_RST) != 0) {
522 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring incoming RST.");
523 return;
524 }
525
526 if ((seg->ctrl & CTL_ACK) != 0) {
527 log_msg(LOG_DEFAULT, LVL_DEBUG, "Incoming ACK, send acceptable RST.");
528 tcp_reply_rst(&conn->ident, seg);
529 return;
530 }
531
532 if ((seg->ctrl & CTL_SYN) == 0) {
533 log_msg(LOG_DEFAULT, LVL_DEBUG, "SYN not present. Ignoring segment.");
534 return;
535 }
536
537 log_msg(LOG_DEFAULT, LVL_DEBUG, "Got SYN, sending SYN, ACK.");
538
539 conn->rcv_nxt = seg->seq + 1;
540 conn->irs = seg->seq;
541
542
543 log_msg(LOG_DEFAULT, LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
544
545 if (seg->len > 1)
546 log_msg(LOG_DEFAULT, LVL_WARN, "SYN combined with data, ignoring data.");
547
548 /* XXX select ISS */
549 conn->iss = 1;
550 conn->snd_nxt = conn->iss;
551 conn->snd_una = conn->iss;
552
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
562 tcp_conn_state_set(conn, st_syn_received);
563
564 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
565
566 tcp_segment_delete(seg);
567}
568
569/** Segment arrived in Syn-Sent state.
570 *
571 * @param conn Connection
572 * @param seg Segment
573 */
574static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
575{
576 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
577
578 if ((seg->ctrl & CTL_ACK) != 0) {
579 log_msg(LOG_DEFAULT, LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
580 conn->snd_una, seg->ack, conn->snd_nxt);
581 if (!seq_no_ack_acceptable(conn, seg->ack)) {
582 if ((seg->ctrl & CTL_RST) == 0) {
583 log_msg(LOG_DEFAULT, LVL_WARN, "ACK not acceptable, send RST");
584 tcp_reply_rst(&conn->ident, seg);
585 } else {
586 log_msg(LOG_DEFAULT, LVL_WARN, "RST,ACK not acceptable, drop");
587 }
588 return;
589 }
590 }
591
592 if ((seg->ctrl & CTL_RST) != 0) {
593 /* If we get here, we have either an acceptable ACK or no ACK */
594 if ((seg->ctrl & CTL_ACK) != 0) {
595 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Connection reset. -> Closed",
596 conn->name);
597 /* Reset connection */
598 tcp_conn_reset(conn);
599 return;
600 } else {
601 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: RST without ACK, drop",
602 conn->name);
603 return;
604 }
605 }
606
607 /* XXX precedence */
608
609 if ((seg->ctrl & CTL_SYN) == 0) {
610 log_msg(LOG_DEFAULT, LVL_DEBUG, "No SYN bit, ignoring segment.");
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;
619
620 /*
621 * Prune acked segments from retransmission queue and
622 * possibly transmit more data.
623 */
624 tcp_tqueue_ack_received(conn);
625 }
626
627 log_msg(LOG_DEFAULT, LVL_DEBUG, "Sent SYN, got SYN.");
628
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 */
634 log_msg(LOG_DEFAULT, LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
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
640 if (seq_no_syn_acked(conn)) {
641 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn acked -> Established", conn->name);
642 tcp_conn_state_set(conn, st_established);
643 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
644 } else {
645 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn not acked -> Syn-Received",
646 conn->name);
647 tcp_conn_state_set(conn, st_syn_received);
648 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
649 }
650
651 tcp_segment_delete(seg);
652}
653
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 */
661static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
662{
663 tcp_segment_t *pseg;
664
665 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
666
667 /* Discard unacceptable segments ("old duplicates") */
668 if (!seq_no_segment_acceptable(conn, seg)) {
669 log_msg(LOG_DEFAULT, LVL_DEBUG, "Replying ACK to unacceptable segment.");
670 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
671 tcp_segment_delete(seg);
672 return;
673 }
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
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 */
695static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
696{
697 if ((seg->ctrl & CTL_RST) == 0)
698 return cp_continue;
699
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;
732}
733
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 */
741static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
742{
743 /* TODO */
744 return cp_continue;
745}
746
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 */
754static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
755{
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
765 log_msg(LOG_DEFAULT, LVL_WARN, "SYN is in receive window, should send reset. XXX");
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;
775}
776
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 */
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. */
788 log_msg(LOG_DEFAULT, LVL_WARN, "Segment ACK not acceptable, sending RST.");
789 tcp_reply_rst(&conn->ident, seg);
790 tcp_segment_delete(seg);
791 return cp_done;
792 }
793
794 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
795
796 tcp_conn_state_set(conn, st_established);
797
798 /* XXX Not mentioned in spec?! */
799 conn->snd_una = seg->ack;
800
801 return cp_continue;
802}
803
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 */
811static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
812{
813 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
814
815 log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
816 (unsigned)seg->ack, (unsigned)conn->snd_una,
817 (unsigned)conn->snd_nxt);
818
819 if (!seq_no_ack_acceptable(conn, seg->ack)) {
820 log_msg(LOG_DEFAULT, LVL_DEBUG, "ACK not acceptable.");
821 if (!seq_no_ack_duplicate(conn, seg->ack)) {
822 log_msg(LOG_DEFAULT, LVL_WARN, "Not acceptable, not duplicate. "
823 "Send ACK and drop.");
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;
828 } else {
829 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring duplicate ACK.");
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;
840
841 log_msg(LOG_DEFAULT, LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
842 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
843 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
844 }
845
846 /*
847 * Prune acked segments from retransmission queue and
848 * possibly transmit more data.
849 */
850 tcp_tqueue_ack_received(conn);
851
852 return cp_continue;
853}
854
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 */
862static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
863{
864 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
865 return cp_done;
866
867 if (conn->fin_is_acked) {
868 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
869 tcp_conn_state_set(conn, st_fin_wait_2);
870 }
871
872 return cp_continue;
873}
874
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 */
882static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
883{
884 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
885 return cp_done;
886
887 /* TODO */
888 return cp_continue;
889}
890
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 */
898static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
899{
900 /* The same processing as in Established state */
901 return tcp_conn_seg_proc_ack_est(conn, seg);
902}
903
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 */
911static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
912{
913 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
914 return cp_done;
915
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
922 return cp_continue;
923}
924
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 */
932static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
933{
934 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
935 return cp_done;
936
937 if (conn->fin_is_acked) {
938 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
939 tcp_conn_state_set(conn, st_closed);
940 return cp_done;
941 }
942
943 return cp_continue;
944}
945
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 */
953static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
954{
955 /* Nothing to do */
956 return cp_continue;
957}
958
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 */
966static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
967{
968 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
969 conn->name, conn, seg);
970
971 if ((seg->ctrl & CTL_ACK) == 0) {
972 log_msg(LOG_DEFAULT, LVL_WARN, "Segment has no ACK. Dropping.");
973 tcp_segment_delete(seg);
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
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 */
1010static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
1011{
1012 return cp_continue;
1013}
1014
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 */
1022static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
1023{
1024 size_t text_size;
1025 size_t xfer_size;
1026
1027 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
1028 conn->name, conn, seg);
1029
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
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 */
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 */
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 }
1072
1073 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
1074
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) {
1086 /* Trim part of segment which we just received */
1087 tcp_conn_trim_seg_to_wnd(conn, seg);
1088 } else {
1089 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Nothing left in segment, dropping "
1090 "(xfer_size=%zu, SEG.LEN=%" PRIu32 ", seg->ctrl=%u)",
1091 conn->name, xfer_size, seg->len, (unsigned int) seg->ctrl);
1092 /* Nothing left in segment */
1093 tcp_segment_delete(seg);
1094 return cp_done;
1095 }
1096
1097 return cp_continue;
1098}
1099
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 */
1107static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
1108{
1109 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
1110 conn->name, conn, seg);
1111 log_msg(LOG_DEFAULT, LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
1112 (unsigned) seg->ctrl);
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) {
1116 log_msg(LOG_DEFAULT, LVL_DEBUG, " - FIN found in segment.");
1117
1118 conn->rcv_nxt++;
1119 conn->rcv_wnd--;
1120
1121 /* Send ACK */
1122 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1123
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);
1131 /* Fallthrough */
1132 case st_syn_received:
1133 case st_established:
1134 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Close-Wait",
1135 conn->name);
1136 tcp_conn_state_set(conn, st_close_wait);
1137 break;
1138 case st_fin_wait_1:
1139 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Closing",
1140 conn->name);
1141 tcp_conn_state_set(conn, st_closing);
1142 break;
1143 case st_fin_wait_2:
1144 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Time-Wait",
1145 conn->name);
1146 tcp_conn_state_set(conn, st_time_wait);
1147 /* Start the Time-Wait timer */
1148 tcp_conn_tw_timer_set(conn);
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:
1156 /* Restart the Time-Wait timer */
1157 tcp_conn_tw_timer_set(conn);
1158 break;
1159 }
1160
1161 /* Add FIN to the receive buffer */
1162 conn->rcv_buf_fin = true;
1163 fibril_condvar_broadcast(&conn->rcv_buf_cv);
1164 if (conn->cb != NULL && conn->cb->recv_data != NULL)
1165 conn->cb->recv_data(conn, conn->cb_arg);
1166
1167 tcp_segment_delete(seg);
1168 return cp_done;
1169 }
1170
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.
1179 *
1180 * @param conn Connection
1181 * @param seg Segment
1182 */
1183static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
1184{
1185 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
1186 tcp_segment_dump(seg);
1187
1188 /* Check whether segment is acceptable */
1189 /* XXX Permit valid ACKs, URGs and RSTs */
1190/* if (!seq_no_segment_acceptable(conn, seg)) {
1191 log_msg(LOG_DEFAULT, LVL_WARN, "Segment not acceptable, dropping.");
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;
1219
1220 /*
1221 * If anything is left from the segment, insert it back into the
1222 * incoming segments queue.
1223 */
1224 if (seg->len > 0) {
1225 log_msg(LOG_DEFAULT, LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
1226 seg, (size_t) seg->len);
1227 tcp_iqueue_insert_seg(&conn->incoming, seg);
1228 } else {
1229 tcp_segment_delete(seg);
1230 }
1231}
1232
1233/** Segment arrived on a connection.
1234 *
1235 * @param conn Connection
1236 * @param epp Endpoint pair on which segment was received
1237 * @param seg Segment
1238 */
1239void tcp_conn_segment_arrived(tcp_conn_t *conn, inet_ep2_t *epp,
1240 tcp_segment_t *seg)
1241{
1242 inet_ep2_t aepp;
1243 inet_ep2_t oldepp;
1244 errno_t rc;
1245
1246 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_segment_arrived(%p)",
1247 conn->name, seg);
1248
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
1262 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_segment_arrived: "
1263 "Changing connection ID, updating amap.");
1264 oldepp = conn->ident;
1265
1266 /* Need to remove and re-insert connection with new identity */
1267 fibril_mutex_lock(&amap_lock);
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) {
1280 assert(rc != EEXIST);
1281 assert(rc == ENOMEM);
1282 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
1283 fibril_mutex_unlock(&amap_lock);
1284 tcp_conn_unlock(conn);
1285 return;
1286 }
1287
1288 amap_remove(amap, &oldepp);
1289 fibril_mutex_unlock(&amap_lock);
1290
1291 conn->name = (char *) "a";
1292 }
1293
1294 switch (conn->cstate) {
1295 case st_listen:
1296 tcp_conn_sa_listen(conn, seg);
1297 break;
1298 case st_syn_sent:
1299 tcp_conn_sa_syn_sent(conn, seg);
1300 break;
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 */
1310 tcp_conn_sa_queue(conn, seg);
1311 break;
1312 case st_closed:
1313 log_msg(LOG_DEFAULT, LVL_DEBUG, "state=%d", (int) conn->cstate);
1314 assert(false);
1315 }
1316
1317 tcp_conn_unlock(conn);
1318}
1319
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
1328 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p)", conn);
1329
1330 tcp_conn_lock(conn);
1331
1332 if (conn->cstate == st_closed) {
1333 log_msg(LOG_DEFAULT, LVL_DEBUG, "Connection already closed.");
1334 tcp_conn_unlock(conn);
1335 tcp_conn_delref(conn);
1336 return;
1337 }
1338
1339 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
1340 tcp_conn_state_set(conn, st_closed);
1341
1342 tcp_conn_unlock(conn);
1343 tcp_conn_delref(conn);
1344
1345 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p) end", conn);
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{
1354 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_set() begin");
1355 tcp_conn_addref(conn);
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");
1359}
1360
1361/** Clear the Time-Wait timeout.
1362 *
1363 * @param conn Connection
1364 */
1365void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1366{
1367 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() begin");
1368 if (fibril_timer_clear_locked(conn->tw_timer) == fts_active)
1369 tcp_conn_delref(conn);
1370 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() end");
1371}
1372
1373/** Trim segment to the receive window.
1374 *
1375 * @param conn Connection
1376 * @param seg Segment
1377 */
1378static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
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
1386/** Handle unexpected segment received on an endpoint pair.
1387 *
1388 * We reply with an RST unless the received segment has RST.
1389 *
1390 * @param sp Endpoint pair which received the segment
1391 * @param seg Unexpected segment
1392 */
1393void tcp_unexpected_segment(inet_ep2_t *epp, tcp_segment_t *seg)
1394{
1395 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", epp,
1396 seg);
1397
1398 if ((seg->ctrl & CTL_RST) == 0)
1399 tcp_reply_rst(epp, seg);
1400}
1401
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 */
1407static void tcp_transmit_segment(inet_ep2_t *epp, tcp_segment_t *seg)
1408{
1409 tcp_pdu_t *pdu;
1410 tcp_segment_t *dseg;
1411 inet_ep2_t rident;
1412
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
1422 if (tcp_conn_lb == tcp_lb_segment) {
1423 /* Loop back segment */
1424// tcp_ncsim_bounce_seg(sp, seg);
1425
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 }
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
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
1455 tcp_transmit_pdu(pdu);
1456 tcp_pdu_delete(pdu);
1457}
1458
1459/** Compute flipped endpoint pair for response.
1460 *
1461 * Flipped endpoint pair has local and remote endpoints exchanged.
1462 *
1463 * @param epp Endpoint pair
1464 * @param fepp Place to store flipped endpoint pair
1465 */
1466void tcp_ep2_flipped(inet_ep2_t *epp, inet_ep2_t *fepp)
1467{
1468 fepp->local = epp->remote;
1469 fepp->remote = epp->local;
1470}
1471
1472/** Send RST in response to an incoming segment.
1473 *
1474 * @param epp Endpoint pair which received the segment
1475 * @param seg Incoming segment
1476 */
1477static void tcp_reply_rst(inet_ep2_t *epp, tcp_segment_t *seg)
1478{
1479 tcp_segment_t *rseg;
1480
1481 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_reply_rst(%p, %p)", epp, seg);
1482
1483 rseg = tcp_segment_make_rst(seg);
1484 tcp_transmit_segment(epp, rseg);
1485}
1486
1487/**
1488 * @}
1489 */
Note: See TracBrowser for help on using the repository browser.