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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 12dcd5f was dc12262, checked in by Martin Decky <martin@…>, 8 years ago

add standardized case fallthrough comment annotations, add actual missing breaks

GCC 7.1's attribute((fallthrough)) would be more elegant, but unfortunatelly this annotation is incompatible with previous versions of GCC (it generates an empty declaration error)

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