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

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

Allow TCP conn tests that involve transferring data by enabling an internal loopback. Add simple →SYN, ←RST test.

  • Property mode set to 100644
File size: 36.7 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. */
86int tcp_conns_init(void)
87{
88 int 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 amap_destroy(amap);
103 amap = NULL;
104
105 assert(list_empty(&conn_list));
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 */
305int tcp_conn_add(tcp_conn_t *conn)
306{
307 inet_ep2_t aepp;
308 int 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 int 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 conn->reset = true;
461 tcp_conn_state_set(conn, st_closed);
462
463 tcp_conn_tw_timer_clear(conn);
464 tcp_tqueue_clear(&conn->retransmit);
465
466 fibril_condvar_broadcast(&conn->rcv_buf_cv);
467 fibril_condvar_broadcast(&conn->snd_buf_cv);
468}
469
470/** Signal to the user that connection has been reset.
471 *
472 * Send an out-of-band signal to the user.
473 */
474static void tcp_reset_signal(tcp_conn_t *conn)
475{
476 /* TODO */
477 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_reset_signal()", conn->name);
478}
479
480/** Determine if SYN has been received.
481 *
482 * @param conn Connection
483 * @return @c true if SYN has been received, @c false otherwise.
484 */
485bool tcp_conn_got_syn(tcp_conn_t *conn)
486{
487 switch (conn->cstate) {
488 case st_listen:
489 case st_syn_sent:
490 return false;
491 case st_syn_received:
492 case st_established:
493 case st_fin_wait_1:
494 case st_fin_wait_2:
495 case st_close_wait:
496 case st_closing:
497 case st_last_ack:
498 case st_time_wait:
499 return true;
500 case st_closed:
501 log_msg(LOG_DEFAULT, LVL_WARN, "state=%d", (int) conn->cstate);
502 assert(false);
503 }
504
505 assert(false);
506}
507
508/** Segment arrived in Listen state.
509 *
510 * @param conn Connection
511 * @param seg Segment
512 */
513static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
514{
515 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
516
517 if ((seg->ctrl & CTL_RST) != 0) {
518 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring incoming RST.");
519 return;
520 }
521
522 if ((seg->ctrl & CTL_ACK) != 0) {
523 log_msg(LOG_DEFAULT, LVL_DEBUG, "Incoming ACK, send acceptable RST.");
524 tcp_reply_rst(&conn->ident, seg);
525 return;
526 }
527
528 if ((seg->ctrl & CTL_SYN) == 0) {
529 log_msg(LOG_DEFAULT, LVL_DEBUG, "SYN not present. Ignoring segment.");
530 return;
531 }
532
533 log_msg(LOG_DEFAULT, LVL_DEBUG, "Got SYN, sending SYN, ACK.");
534
535 conn->rcv_nxt = seg->seq + 1;
536 conn->irs = seg->seq;
537
538
539 log_msg(LOG_DEFAULT, LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
540
541 if (seg->len > 1)
542 log_msg(LOG_DEFAULT, LVL_WARN, "SYN combined with data, ignoring data.");
543
544 /* XXX select ISS */
545 conn->iss = 1;
546 conn->snd_nxt = conn->iss;
547 conn->snd_una = conn->iss;
548
549 /*
550 * Surprisingly the spec does not deal with initial window setting.
551 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
552 * will always be accepted as new window setting.
553 */
554 conn->snd_wnd = seg->wnd;
555 conn->snd_wl1 = seg->seq;
556 conn->snd_wl2 = seg->seq;
557
558 tcp_conn_state_set(conn, st_syn_received);
559
560 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
561
562 tcp_segment_delete(seg);
563}
564
565/** Segment arrived in Syn-Sent state.
566 *
567 * @param conn Connection
568 * @param seg Segment
569 */
570static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
571{
572 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
573
574 if ((seg->ctrl & CTL_ACK) != 0) {
575 log_msg(LOG_DEFAULT, LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
576 conn->snd_una, seg->ack, conn->snd_nxt);
577 if (!seq_no_ack_acceptable(conn, seg->ack)) {
578 if ((seg->ctrl & CTL_RST) == 0) {
579 log_msg(LOG_DEFAULT, LVL_WARN, "ACK not acceptable, send RST");
580 tcp_reply_rst(&conn->ident, seg);
581 } else {
582 log_msg(LOG_DEFAULT, LVL_WARN, "RST,ACK not acceptable, drop");
583 }
584 return;
585 }
586 }
587
588 if ((seg->ctrl & CTL_RST) != 0) {
589 /* If we get here, we have either an acceptable ACK or no ACK */
590 if ((seg->ctrl & CTL_ACK) != 0) {
591 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Connection reset. -> Closed",
592 conn->name);
593 /* Reset connection */
594 tcp_conn_reset(conn);
595 return;
596 } else {
597 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: RST without ACK, drop",
598 conn->name);
599 return;
600 }
601 }
602
603 /* XXX precedence */
604
605 if ((seg->ctrl & CTL_SYN) == 0) {
606 log_msg(LOG_DEFAULT, LVL_DEBUG, "No SYN bit, ignoring segment.");
607 return;
608 }
609
610 conn->rcv_nxt = seg->seq + 1;
611 conn->irs = seg->seq;
612
613 if ((seg->ctrl & CTL_ACK) != 0) {
614 conn->snd_una = seg->ack;
615
616 /*
617 * Prune acked segments from retransmission queue and
618 * possibly transmit more data.
619 */
620 tcp_tqueue_ack_received(conn);
621 }
622
623 log_msg(LOG_DEFAULT, LVL_DEBUG, "Sent SYN, got SYN.");
624
625 /*
626 * Surprisingly the spec does not deal with initial window setting.
627 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
628 * will always be accepted as new window setting.
629 */
630 log_msg(LOG_DEFAULT, LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
631 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
632 conn->snd_wnd = seg->wnd;
633 conn->snd_wl1 = seg->seq;
634 conn->snd_wl2 = seg->seq;
635
636 if (seq_no_syn_acked(conn)) {
637 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn acked -> Established", conn->name);
638 tcp_conn_state_set(conn, st_established);
639 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
640 } else {
641 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: syn not acked -> Syn-Received",
642 conn->name);
643 tcp_conn_state_set(conn, st_syn_received);
644 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
645 }
646
647 tcp_segment_delete(seg);
648}
649
650/** Segment arrived in state where segments are processed in sequence order.
651 *
652 * Queue segment in incoming segments queue for processing.
653 *
654 * @param conn Connection
655 * @param seg Segment
656 */
657static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
658{
659 tcp_segment_t *pseg;
660
661 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
662
663 /* Discard unacceptable segments ("old duplicates") */
664 if (!seq_no_segment_acceptable(conn, seg)) {
665 log_msg(LOG_DEFAULT, LVL_DEBUG, "Replying ACK to unacceptable segment.");
666 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
667 tcp_segment_delete(seg);
668 return;
669 }
670
671 /* Queue for processing */
672 tcp_iqueue_insert_seg(&conn->incoming, seg);
673
674 /*
675 * Process all segments from incoming queue that are ready.
676 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
677 *
678 * XXX Need to return ACK for unacceptable segments
679 */
680 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
681 tcp_conn_seg_process(conn, pseg);
682}
683
684/** Process segment RST field.
685 *
686 * @param conn Connection
687 * @param seg Segment
688 * @return cp_done if we are done with this segment, cp_continue
689 * if not
690 */
691static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
692{
693 if ((seg->ctrl & CTL_RST) == 0)
694 return cp_continue;
695
696 switch (conn->cstate) {
697 case st_syn_received:
698 /* XXX In case of passive open, revert to Listen state */
699 if (conn->ap == ap_passive) {
700 tcp_conn_state_set(conn, st_listen);
701 /* XXX Revert conn->ident */
702 tcp_conn_tw_timer_clear(conn);
703 tcp_tqueue_clear(&conn->retransmit);
704 } else {
705 tcp_conn_reset(conn);
706 }
707 break;
708 case st_established:
709 case st_fin_wait_1:
710 case st_fin_wait_2:
711 case st_close_wait:
712 /* General "connection reset" signal */
713 tcp_reset_signal(conn);
714 tcp_conn_reset(conn);
715 break;
716 case st_closing:
717 case st_last_ack:
718 case st_time_wait:
719 tcp_conn_reset(conn);
720 break;
721 case st_listen:
722 case st_syn_sent:
723 case st_closed:
724 assert(false);
725 }
726
727 return cp_done;
728}
729
730/** Process segment security and precedence fields.
731 *
732 * @param conn Connection
733 * @param seg Segment
734 * @return cp_done if we are done with this segment, cp_continue
735 * if not
736 */
737static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
738{
739 /* TODO */
740 return cp_continue;
741}
742
743/** Process segment SYN field.
744 *
745 * @param conn Connection
746 * @param seg Segment
747 * @return cp_done if we are done with this segment, cp_continue
748 * if not
749 */
750static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
751{
752 if ((seg->ctrl & CTL_SYN) == 0)
753 return cp_continue;
754
755 /*
756 * Assert SYN is in receive window, otherwise this step should not
757 * be reached.
758 */
759 assert(seq_no_in_rcv_wnd(conn, seg->seq));
760
761 log_msg(LOG_DEFAULT, LVL_WARN, "SYN is in receive window, should send reset. XXX");
762
763 /*
764 * TODO
765 *
766 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
767 * flush segment queues. Send unsolicited "connection reset" signal
768 * to user, connection -> closed state, delete TCB, return.
769 */
770 return cp_done;
771}
772
773/** Process segment ACK field in Syn-Received state.
774 *
775 * @param conn Connection
776 * @param seg Segment
777 * @return cp_done if we are done with this segment, cp_continue
778 * if not
779 */
780static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
781{
782 if (!seq_no_ack_acceptable(conn, seg->ack)) {
783 /* ACK is not acceptable, send RST. */
784 log_msg(LOG_DEFAULT, LVL_WARN, "Segment ACK not acceptable, sending RST.");
785 tcp_reply_rst(&conn->ident, seg);
786 tcp_segment_delete(seg);
787 return cp_done;
788 }
789
790 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
791
792 tcp_conn_state_set(conn, st_established);
793
794 /* XXX Not mentioned in spec?! */
795 conn->snd_una = seg->ack;
796
797 return cp_continue;
798}
799
800/** Process segment ACK field in Established state.
801 *
802 * @param conn Connection
803 * @param seg Segment
804 * @return cp_done if we are done with this segment, cp_continue
805 * if not
806 */
807static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
808{
809 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
810
811 log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
812 (unsigned)seg->ack, (unsigned)conn->snd_una,
813 (unsigned)conn->snd_nxt);
814
815 if (!seq_no_ack_acceptable(conn, seg->ack)) {
816 log_msg(LOG_DEFAULT, LVL_DEBUG, "ACK not acceptable.");
817 if (!seq_no_ack_duplicate(conn, seg->ack)) {
818 log_msg(LOG_DEFAULT, LVL_WARN, "Not acceptable, not duplicate. "
819 "Send ACK and drop.");
820 /* Not acceptable, not duplicate. Send ACK and drop. */
821 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
822 tcp_segment_delete(seg);
823 return cp_done;
824 } else {
825 log_msg(LOG_DEFAULT, LVL_DEBUG, "Ignoring duplicate ACK.");
826 }
827 } else {
828 /* Update SND.UNA */
829 conn->snd_una = seg->ack;
830 }
831
832 if (seq_no_new_wnd_update(conn, seg)) {
833 conn->snd_wnd = seg->wnd;
834 conn->snd_wl1 = seg->seq;
835 conn->snd_wl2 = seg->ack;
836
837 log_msg(LOG_DEFAULT, LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
838 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
839 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
840 }
841
842 /*
843 * Prune acked segments from retransmission queue and
844 * possibly transmit more data.
845 */
846 tcp_tqueue_ack_received(conn);
847
848 return cp_continue;
849}
850
851/** Process segment ACK field in Fin-Wait-1 state.
852 *
853 * @param conn Connection
854 * @param seg Segment
855 * @return cp_done if we are done with this segment, cp_continue
856 * if not
857 */
858static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
859{
860 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
861 return cp_done;
862
863 if (conn->fin_is_acked) {
864 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
865 tcp_conn_state_set(conn, st_fin_wait_2);
866 }
867
868 return cp_continue;
869}
870
871/** Process segment ACK field in Fin-Wait-2 state.
872 *
873 * @param conn Connection
874 * @param seg Segment
875 * @return cp_done if we are done with this segment, cp_continue
876 * if not
877 */
878static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
879{
880 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
881 return cp_done;
882
883 /* TODO */
884 return cp_continue;
885}
886
887/** Process segment ACK field in Close-Wait state.
888 *
889 * @param conn Connection
890 * @param seg Segment
891 * @return cp_done if we are done with this segment, cp_continue
892 * if not
893 */
894static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
895{
896 /* The same processing as in Established state */
897 return tcp_conn_seg_proc_ack_est(conn, seg);
898}
899
900/** Process segment ACK field in Closing state.
901 *
902 * @param conn Connection
903 * @param seg Segment
904 * @return cp_done if we are done with this segment, cp_continue
905 * if not
906 */
907static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
908{
909 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
910 return cp_done;
911
912 /* TODO */
913 return cp_continue;
914}
915
916/** Process segment ACK field in Last-Ack state.
917 *
918 * @param conn Connection
919 * @param seg Segment
920 * @return cp_done if we are done with this segment, cp_continue
921 * if not
922 */
923static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
924{
925 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
926 return cp_done;
927
928 if (conn->fin_is_acked) {
929 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
930 tcp_conn_state_set(conn, st_closed);
931 return cp_done;
932 }
933
934 return cp_continue;
935}
936
937/** Process segment ACK field in Time-Wait state.
938 *
939 * @param conn Connection
940 * @param seg Segment
941 * @return cp_done if we are done with this segment, cp_continue
942 * if not
943 */
944static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
945{
946 /* Nothing to do */
947 return cp_continue;
948}
949
950/** Process segment ACK field.
951 *
952 * @param conn Connection
953 * @param seg Segment
954 * @return cp_done if we are done with this segment, cp_continue
955 * if not
956 */
957static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
958{
959 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
960 conn->name, conn, seg);
961
962 if ((seg->ctrl & CTL_ACK) == 0) {
963 log_msg(LOG_DEFAULT, LVL_WARN, "Segment has no ACK. Dropping.");
964 tcp_segment_delete(seg);
965 return cp_done;
966 }
967
968 switch (conn->cstate) {
969 case st_syn_received:
970 return tcp_conn_seg_proc_ack_sr(conn, seg);
971 case st_established:
972 return tcp_conn_seg_proc_ack_est(conn, seg);
973 case st_fin_wait_1:
974 return tcp_conn_seg_proc_ack_fw1(conn, seg);
975 case st_fin_wait_2:
976 return tcp_conn_seg_proc_ack_fw2(conn, seg);
977 case st_close_wait:
978 return tcp_conn_seg_proc_ack_cw(conn, seg);
979 case st_closing:
980 return tcp_conn_seg_proc_ack_cls(conn, seg);
981 case st_last_ack:
982 return tcp_conn_seg_proc_ack_la(conn, seg);
983 case st_time_wait:
984 return tcp_conn_seg_proc_ack_tw(conn, seg);
985 case st_listen:
986 case st_syn_sent:
987 case st_closed:
988 assert(false);
989 }
990
991 assert(false);
992}
993
994/** Process segment URG field.
995 *
996 * @param conn Connection
997 * @param seg Segment
998 * @return cp_done if we are done with this segment, cp_continue
999 * if not
1000 */
1001static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
1002{
1003 return cp_continue;
1004}
1005
1006/** Process segment text.
1007 *
1008 * @param conn Connection
1009 * @param seg Segment
1010 * @return cp_done if we are done with this segment, cp_continue
1011 * if not
1012 */
1013static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
1014{
1015 size_t text_size;
1016 size_t xfer_size;
1017
1018 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
1019 conn->name, conn, seg);
1020
1021 switch (conn->cstate) {
1022 case st_established:
1023 case st_fin_wait_1:
1024 case st_fin_wait_2:
1025 /* OK */
1026 break;
1027 case st_close_wait:
1028 case st_closing:
1029 case st_last_ack:
1030 case st_time_wait:
1031 /* Invalid since FIN has been received. Ignore text. */
1032 return cp_continue;
1033 case st_listen:
1034 case st_syn_sent:
1035 case st_syn_received:
1036 case st_closed:
1037 assert(false);
1038 }
1039
1040 /*
1041 * Process segment text
1042 */
1043 assert(seq_no_segment_ready(conn, seg));
1044
1045 /* Trim anything outside our receive window */
1046 tcp_conn_trim_seg_to_wnd(conn, seg);
1047
1048 /* Determine how many bytes to copy */
1049 text_size = tcp_segment_text_size(seg);
1050 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
1051
1052 /* Copy data to receive buffer */
1053 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
1054 xfer_size);
1055 conn->rcv_buf_used += xfer_size;
1056
1057 /* Signal to the receive function that new data has arrived */
1058 if (xfer_size > 0) {
1059 fibril_condvar_broadcast(&conn->rcv_buf_cv);
1060 if (conn->cb != NULL && conn->cb->recv_data != NULL)
1061 conn->cb->recv_data(conn, conn->cb_arg);
1062 }
1063
1064 log_msg(LOG_DEFAULT, LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
1065
1066 /* Advance RCV.NXT */
1067 conn->rcv_nxt += xfer_size;
1068
1069 /* Update receive window. XXX Not an efficient strategy. */
1070 conn->rcv_wnd -= xfer_size;
1071
1072 /* Send ACK */
1073 if (xfer_size > 0)
1074 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1075
1076 if (xfer_size < seg->len) {
1077 /* Trim part of segment which we just received */
1078 tcp_conn_trim_seg_to_wnd(conn, seg);
1079 } else {
1080 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: Nothing left in segment, dropping "
1081 "(xfer_size=%zu, SEG.LEN=%" PRIu32 ", seg->ctrl=%u)",
1082 conn->name, xfer_size, seg->len, (unsigned int) seg->ctrl);
1083 /* Nothing left in segment */
1084 tcp_segment_delete(seg);
1085 return cp_done;
1086 }
1087
1088 return cp_continue;
1089}
1090
1091/** Process segment FIN field.
1092 *
1093 * @param conn Connection
1094 * @param seg Segment
1095 * @return cp_done if we are done with this segment, cp_continue
1096 * if not
1097 */
1098static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
1099{
1100 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
1101 conn->name, conn, seg);
1102 log_msg(LOG_DEFAULT, LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
1103 (unsigned) seg->ctrl);
1104
1105 /* Only process FIN if no text is left in segment. */
1106 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
1107 log_msg(LOG_DEFAULT, LVL_DEBUG, " - FIN found in segment.");
1108
1109 /* Send ACK */
1110 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1111
1112 conn->rcv_nxt++;
1113 conn->rcv_wnd--;
1114
1115 /* Change connection state */
1116 switch (conn->cstate) {
1117 case st_listen:
1118 case st_syn_sent:
1119 case st_closed:
1120 /* Connection not synchronized */
1121 assert(false);
1122 /* Fallthrough */
1123 case st_syn_received:
1124 case st_established:
1125 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Close-Wait",
1126 conn->name);
1127 tcp_conn_state_set(conn, st_close_wait);
1128 break;
1129 case st_fin_wait_1:
1130 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Closing",
1131 conn->name);
1132 tcp_conn_state_set(conn, st_closing);
1133 break;
1134 case st_fin_wait_2:
1135 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN received -> Time-Wait",
1136 conn->name);
1137 tcp_conn_state_set(conn, st_time_wait);
1138 /* Start the Time-Wait timer */
1139 tcp_conn_tw_timer_set(conn);
1140 break;
1141 case st_close_wait:
1142 case st_closing:
1143 case st_last_ack:
1144 /* Do nothing */
1145 break;
1146 case st_time_wait:
1147 /* Restart the Time-Wait timer */
1148 tcp_conn_tw_timer_set(conn);
1149 break;
1150 }
1151
1152 /* Add FIN to the receive buffer */
1153 conn->rcv_buf_fin = true;
1154 fibril_condvar_broadcast(&conn->rcv_buf_cv);
1155 if (conn->cb != NULL && conn->cb->recv_data != NULL)
1156 conn->cb->recv_data(conn, conn->cb_arg);
1157
1158 tcp_segment_delete(seg);
1159 return cp_done;
1160 }
1161
1162 return cp_continue;
1163}
1164
1165/** Process incoming segment.
1166 *
1167 * We are in connection state where segments are processed in order
1168 * of sequence number. This processes one segment taken from the
1169 * connection incoming segments queue.
1170 *
1171 * @param conn Connection
1172 * @param seg Segment
1173 */
1174static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
1175{
1176 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
1177 tcp_segment_dump(seg);
1178
1179 /* Check whether segment is acceptable */
1180 /* XXX Permit valid ACKs, URGs and RSTs */
1181/* if (!seq_no_segment_acceptable(conn, seg)) {
1182 log_msg(LOG_DEFAULT, LVL_WARN, "Segment not acceptable, dropping.");
1183 if ((seg->ctrl & CTL_RST) == 0) {
1184 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
1185 }
1186 return;
1187 }
1188*/
1189
1190 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
1191 return;
1192
1193 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
1194 return;
1195
1196 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
1197 return;
1198
1199 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
1200 return;
1201
1202 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
1203 return;
1204
1205 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
1206 return;
1207
1208 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
1209 return;
1210
1211 /*
1212 * If anything is left from the segment, insert it back into the
1213 * incoming segments queue.
1214 */
1215 if (seg->len > 0) {
1216 log_msg(LOG_DEFAULT, LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
1217 seg, (size_t) seg->len);
1218 tcp_iqueue_insert_seg(&conn->incoming, seg);
1219 } else {
1220 tcp_segment_delete(seg);
1221 }
1222}
1223
1224/** Segment arrived on a connection.
1225 *
1226 * @param conn Connection
1227 * @param epp Endpoint pair on which segment was received
1228 * @param seg Segment
1229 */
1230void tcp_conn_segment_arrived(tcp_conn_t *conn, inet_ep2_t *epp,
1231 tcp_segment_t *seg)
1232{
1233 inet_ep2_t aepp;
1234 inet_ep2_t oldepp;
1235 int rc;
1236
1237 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_segment_arrived(%p)",
1238 conn->name, seg);
1239
1240 tcp_conn_lock(conn);
1241
1242 if (conn->cstate == st_closed) {
1243 log_msg(LOG_DEFAULT, LVL_WARN, "Connection is closed.");
1244 tcp_unexpected_segment(epp, seg);
1245 tcp_conn_unlock(conn);
1246 return;
1247 }
1248
1249 if (inet_addr_is_any(&conn->ident.remote.addr) ||
1250 conn->ident.remote.port == inet_port_any ||
1251 inet_addr_is_any(&conn->ident.local.addr)) {
1252
1253 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_segment_arrived: "
1254 "Changing connection ID, updating amap.");
1255 oldepp = conn->ident;
1256
1257 /* Need to remove and re-insert connection with new identity */
1258 fibril_mutex_lock(&amap_lock);
1259
1260 if (inet_addr_is_any(&conn->ident.remote.addr))
1261 conn->ident.remote.addr = epp->remote.addr;
1262
1263 if (conn->ident.remote.port == inet_port_any)
1264 conn->ident.remote.port = epp->remote.port;
1265
1266 if (inet_addr_is_any(&conn->ident.local.addr))
1267 conn->ident.local.addr = epp->local.addr;
1268
1269 rc = amap_insert(amap, &conn->ident, conn, af_allow_system, &aepp);
1270 if (rc != EOK) {
1271 assert(rc != EEXIST);
1272 assert(rc == ENOMEM);
1273 log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
1274 fibril_mutex_unlock(&amap_lock);
1275 tcp_conn_unlock(conn);
1276 return;
1277 }
1278
1279 amap_remove(amap, &oldepp);
1280 fibril_mutex_unlock(&amap_lock);
1281
1282 conn->name = (char *) "a";
1283 }
1284
1285 switch (conn->cstate) {
1286 case st_listen:
1287 tcp_conn_sa_listen(conn, seg);
1288 break;
1289 case st_syn_sent:
1290 tcp_conn_sa_syn_sent(conn, seg);
1291 break;
1292 case st_syn_received:
1293 case st_established:
1294 case st_fin_wait_1:
1295 case st_fin_wait_2:
1296 case st_close_wait:
1297 case st_closing:
1298 case st_last_ack:
1299 case st_time_wait:
1300 /* Process segments in order of sequence number */
1301 tcp_conn_sa_queue(conn, seg);
1302 break;
1303 case st_closed:
1304 log_msg(LOG_DEFAULT, LVL_DEBUG, "state=%d", (int) conn->cstate);
1305 assert(false);
1306 }
1307
1308 tcp_conn_unlock(conn);
1309}
1310
1311/** Time-Wait timeout handler.
1312 *
1313 * @param arg Connection
1314 */
1315static void tw_timeout_func(void *arg)
1316{
1317 tcp_conn_t *conn = (tcp_conn_t *) arg;
1318
1319 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p)", conn);
1320
1321 tcp_conn_lock(conn);
1322
1323 if (conn->cstate == st_closed) {
1324 log_msg(LOG_DEFAULT, LVL_DEBUG, "Connection already closed.");
1325 tcp_conn_unlock(conn);
1326 tcp_conn_delref(conn);
1327 return;
1328 }
1329
1330 log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
1331 tcp_conn_state_set(conn, st_closed);
1332
1333 tcp_conn_unlock(conn);
1334 tcp_conn_delref(conn);
1335
1336 log_msg(LOG_DEFAULT, LVL_DEBUG, "tw_timeout_func(%p) end", conn);
1337}
1338
1339/** Start or restart the Time-Wait timeout.
1340 *
1341 * @param conn Connection
1342 */
1343void tcp_conn_tw_timer_set(tcp_conn_t *conn)
1344{
1345 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_set() begin");
1346 tcp_conn_addref(conn);
1347 fibril_timer_set_locked(conn->tw_timer, TIME_WAIT_TIMEOUT,
1348 tw_timeout_func, (void *)conn);
1349 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_set() end");
1350}
1351
1352/** Clear the Time-Wait timeout.
1353 *
1354 * @param conn Connection
1355 */
1356void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1357{
1358 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() begin");
1359 if (fibril_timer_clear_locked(conn->tw_timer) == fts_active)
1360 tcp_conn_delref(conn);
1361 log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_conn_tw_timer_clear() end");
1362}
1363
1364/** Trim segment to the receive window.
1365 *
1366 * @param conn Connection
1367 * @param seg Segment
1368 */
1369static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
1370{
1371 uint32_t left, right;
1372
1373 seq_no_seg_trim_calc(conn, seg, &left, &right);
1374 tcp_segment_trim(seg, left, right);
1375}
1376
1377/** Handle unexpected segment received on an endpoint pair.
1378 *
1379 * We reply with an RST unless the received segment has RST.
1380 *
1381 * @param sp Endpoint pair which received the segment
1382 * @param seg Unexpected segment
1383 */
1384void tcp_unexpected_segment(inet_ep2_t *epp, tcp_segment_t *seg)
1385{
1386 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", epp,
1387 seg);
1388
1389 if ((seg->ctrl & CTL_RST) == 0)
1390 tcp_reply_rst(epp, seg);
1391}
1392
1393/** Transmit segment over network.
1394 *
1395 * @param epp Endpoint pair with source and destination information
1396 * @param seg Segment (ownership retained by caller)
1397 */
1398static void tcp_transmit_segment(inet_ep2_t *epp, tcp_segment_t *seg)
1399{
1400 tcp_pdu_t *pdu;
1401 tcp_segment_t *dseg;
1402 inet_ep2_t rident;
1403
1404 log_msg(LOG_DEFAULT, LVL_DEBUG,
1405 "tcp_transmit_segment(l:(%u),f:(%u), %p)",
1406 epp->local.port, epp->remote.port, seg);
1407
1408 log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
1409 seg->seq, seg->wnd);
1410
1411 tcp_segment_dump(seg);
1412
1413 if (tcp_conn_lb == tcp_lb_segment) {
1414 /* Loop back segment */
1415// tcp_ncsim_bounce_seg(sp, seg);
1416
1417 /* Reverse the identification */
1418 tcp_ep2_flipped(epp, &rident);
1419
1420 /* Insert segment back into rqueue */
1421 dseg = tcp_segment_dup(seg);
1422 tcp_rqueue_insert_seg(&rident, dseg);
1423 return;
1424 }
1425
1426 if (tcp_pdu_encode(epp, seg, &pdu) != EOK) {
1427 log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
1428 return;
1429 }
1430
1431 if (tcp_conn_lb == tcp_lb_pdu) {
1432 /* Loop back PDU */
1433 if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
1434 log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
1435 tcp_pdu_delete(pdu);
1436 return;
1437 }
1438
1439 tcp_pdu_delete(pdu);
1440
1441 /* Insert decoded segment into rqueue */
1442 tcp_rqueue_insert_seg(&rident, dseg);
1443 return;
1444 }
1445
1446 tcp_transmit_pdu(pdu);
1447 tcp_pdu_delete(pdu);
1448}
1449
1450/** Compute flipped endpoint pair for response.
1451 *
1452 * Flipped endpoint pair has local and remote endpoints exchanged.
1453 *
1454 * @param epp Endpoint pair
1455 * @param fepp Place to store flipped endpoint pair
1456 */
1457void tcp_ep2_flipped(inet_ep2_t *epp, inet_ep2_t *fepp)
1458{
1459 fepp->local = epp->remote;
1460 fepp->remote = epp->local;
1461}
1462
1463/** Send RST in response to an incoming segment.
1464 *
1465 * @param epp Endpoint pair which received the segment
1466 * @param seg Incoming segment
1467 */
1468static void tcp_reply_rst(inet_ep2_t *epp, tcp_segment_t *seg)
1469{
1470 tcp_segment_t *rseg;
1471
1472 log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_reply_rst(%p, %p)", epp, seg);
1473
1474 rseg = tcp_segment_make_rst(seg);
1475 tcp_transmit_segment(epp, rseg);
1476}
1477
1478/**
1479 * @}
1480 */
Note: See TracBrowser for help on using the repository browser.