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

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

Start adding tests for TCP conn module. Make sure all connections have been freed at the end of a test.

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