source: mainline/uspace/srv/net/tl/tcp/conn.c@ 0ac2158

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

Allow accepting connection from arbitray foreign address, port.

  • Property mode set to 100644
File size: 27.4 KB
Line 
1/*
2 * Copyright (c) 2011 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 <bool.h>
39#include <errno.h>
40#include <io/log.h>
41#include <macros.h>
42#include <stdlib.h>
43#include "conn.h"
44#include "iqueue.h"
45#include "segment.h"
46#include "seq_no.h"
47#include "tcp_type.h"
48#include "tqueue.h"
49#include "ucall.h"
50
51#define RCV_BUF_SIZE 2/*4096*/
52#define SND_BUF_SIZE 4096
53
54#define MAX_SEGMENT_LIFETIME (15*1000*1000) //(2*60*1000*1000)
55#define TIME_WAIT_TIMEOUT (2*MAX_SEGMENT_LIFETIME)
56
57LIST_INITIALIZE(conn_list);
58
59static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
60static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
61static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
62
63/** Create new segment structure.
64 *
65 * @param lsock Local socket (will be deeply copied)
66 * @param fsock Foreign socket (will be deeply copied)
67 * @return New segment or NULL
68 */
69tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
70{
71 tcp_conn_t *conn = NULL;
72 bool tqueue_inited = false;
73
74 /* Allocate connection structure */
75 conn = calloc(1, sizeof(tcp_conn_t));
76 if (conn == NULL)
77 goto error;
78
79 conn->tw_timer = fibril_timer_create();
80 if (conn->tw_timer == NULL)
81 goto error;
82
83 /* Allocate receive buffer */
84 fibril_mutex_initialize(&conn->rcv_buf_lock);
85 fibril_condvar_initialize(&conn->rcv_buf_cv);
86 conn->rcv_buf_size = RCV_BUF_SIZE;
87 conn->rcv_buf_used = 0;
88 conn->rcv_buf_fin = false;
89
90 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
91 if (conn->rcv_buf == NULL)
92 goto error;
93
94 /** Allocate send buffer */
95 conn->snd_buf_size = SND_BUF_SIZE;
96 conn->snd_buf_used = 0;
97 conn->snd_buf_fin = false;
98 conn->snd_buf = calloc(1, conn->snd_buf_size);
99 if (conn->snd_buf == NULL)
100 goto error;
101
102 /* Set up receive window. */
103 conn->rcv_wnd = conn->rcv_buf_size;
104
105 /* Initialize incoming segment queue */
106 tcp_iqueue_init(&conn->incoming, conn);
107
108 /* Initialize retransmission queue */
109 if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
110 goto error;
111
112 tqueue_inited = true;
113
114 /* Connection state change signalling */
115 fibril_mutex_initialize(&conn->cstate_lock);
116 fibril_condvar_initialize(&conn->cstate_cv);
117
118 conn->cstate = st_listen;
119 conn->fin_is_acked = false;
120 conn->ident.local = *lsock;
121 if (fsock != NULL)
122 conn->ident.foreign = *fsock;
123
124 return conn;
125
126error:
127 if (tqueue_inited)
128 tcp_tqueue_fini(&conn->retransmit);
129 if (conn != NULL && conn->rcv_buf != NULL)
130 free(conn->rcv_buf);
131 if (conn != NULL && conn->snd_buf != NULL)
132 free(conn->snd_buf);
133 if (conn != NULL && conn->tw_timer != NULL)
134 fibril_timer_destroy(conn->tw_timer);
135 if (conn != NULL)
136 free(conn);
137
138 return NULL;
139}
140
141/** Enlist connection.
142 *
143 * Add connection to the connection map.
144 */
145void tcp_conn_add(tcp_conn_t *conn)
146{
147 list_append(&conn->link, &conn_list);
148}
149
150/** Delist connection.
151 *
152 * Remove connection from the connection map.
153 */
154void tcp_conn_remove(tcp_conn_t *conn)
155{
156 list_remove(&conn->link);
157}
158
159static void tcp_conn_state_set(tcp_conn_t *conn, tcp_cstate_t nstate)
160{
161 fibril_mutex_lock(&conn->cstate_lock);
162 conn->cstate = nstate;
163 fibril_condvar_broadcast(&conn->cstate_cv);
164 fibril_mutex_unlock(&conn->cstate_lock);
165}
166
167/** Synchronize connection.
168 *
169 * This is the first step of an active connection attempt,
170 * sends out SYN and sets up ISS and SND.xxx.
171 */
172void tcp_conn_sync(tcp_conn_t *conn)
173{
174 /* XXX select ISS */
175 conn->iss = 1;
176 conn->snd_nxt = conn->iss;
177 conn->snd_una = conn->iss;
178
179 tcp_tqueue_ctrl_seg(conn, CTL_SYN);
180 tcp_conn_state_set(conn, st_syn_sent);
181}
182
183/** FIN has been sent.
184 *
185 * This function should be called when FIN is sent over the connection,
186 * as a result the connection state is changed appropriately.
187 */
188void tcp_conn_fin_sent(tcp_conn_t *conn)
189{
190 switch (conn->cstate) {
191 case st_syn_received:
192 case st_established:
193 log_msg(LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
194 tcp_conn_state_set(conn, st_fin_wait_1);
195 break;
196 case st_close_wait:
197 log_msg(LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
198 tcp_conn_state_set(conn, st_last_ack);
199 break;
200 default:
201 log_msg(LVL_ERROR, "%s: Connection state %d", conn->name,
202 conn->cstate);
203 assert(false);
204 }
205
206 conn->fin_is_acked = false;
207}
208
209/** Compare two sockets.
210 *
211 * Two sockets are equal if the address is equal and the port number
212 * is equal.
213 */
214static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
215{
216 log_msg(LVL_DEBUG, "tcp_socket_match(sock=(%x,%u), pat=(%x,%u))",
217 sock->addr.ipv4, sock->port, patt->addr.ipv4, patt->port);
218
219 if (patt->addr.ipv4 != TCP_IPV4_ANY &&
220 patt->addr.ipv4 != sock->addr.ipv4)
221 return false;
222
223 if (patt->port != TCP_PORT_ANY &&
224 patt->port != sock->port)
225 return false;
226
227 log_msg(LVL_DEBUG, " -> match");
228
229 return true;
230}
231
232/** Match socket with pattern. */
233static bool tcp_sockpair_match(tcp_sockpair_t *sp, tcp_sockpair_t *pattern)
234{
235 log_msg(LVL_DEBUG, "tcp_sockpair_match(%p, %p)", sp, pattern);
236
237 if (!tcp_socket_match(&sp->local, &pattern->local))
238 return false;
239
240 if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
241 return false;
242
243 return true;
244}
245
246/** Find connection structure for specified socket pair.
247 *
248 * A connection is uniquely identified by a socket pair. Look up our
249 * connection map and return connection structure based on socket pair.
250 *
251 * @param sp Socket pair
252 * @return Connection structure or NULL if not found.
253 */
254tcp_conn_t *tcp_conn_find(tcp_sockpair_t *sp)
255{
256 log_msg(LVL_DEBUG, "tcp_conn_find(%p)", sp);
257
258 list_foreach(conn_list, link) {
259 tcp_conn_t *conn = list_get_instance(link, tcp_conn_t, link);
260 tcp_sockpair_t *csp = &conn->ident;
261 log_msg(LVL_DEBUG, "compare with conn (f:(%x,%u), l:(%x,%u))",
262 csp->foreign.addr.ipv4, csp->foreign.port,
263 csp->local.addr.ipv4, csp->local.port);
264 if (tcp_sockpair_match(sp, csp)) {
265 return conn;
266 }
267 }
268
269 return NULL;
270}
271
272/** Reset connection.
273 *
274 * @param conn Connection
275 */
276static void tcp_conn_reset(tcp_conn_t *conn)
277{
278 log_msg(LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
279 tcp_conn_state_set(conn, st_closed);
280 tcp_conn_tw_timer_clear(conn);
281 tcp_tqueue_clear(&conn->retransmit);
282}
283
284/** Determine if SYN has been received.
285 *
286 * @param conn Connection
287 * @return @c true if SYN has been received, @c false otherwise.
288 */
289bool tcp_conn_got_syn(tcp_conn_t *conn)
290{
291 switch (conn->cstate) {
292 case st_listen:
293 case st_syn_sent:
294 return false;
295 case st_syn_received:
296 case st_established:
297 case st_fin_wait_1:
298 case st_fin_wait_2:
299 case st_close_wait:
300 case st_closing:
301 case st_last_ack:
302 case st_time_wait:
303 return true;
304 case st_closed:
305 log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
306 assert(false);
307 }
308
309 assert(false);
310}
311
312/** Segment arrived in Listen state.
313 *
314 * @param conn Connection
315 * @param seg Segment
316 */
317static void tcp_conn_sa_listen(tcp_conn_t *conn, tcp_segment_t *seg)
318{
319 log_msg(LVL_DEBUG, "tcp_conn_sa_listen(%p, %p)", conn, seg);
320
321 if ((seg->ctrl & CTL_RST) != 0) {
322 log_msg(LVL_DEBUG, "Ignoring incoming RST.");
323 return;
324 }
325
326 if ((seg->ctrl & CTL_ACK) != 0) {
327 log_msg(LVL_DEBUG, "Incoming ACK, send acceptable RST.");
328 tcp_reply_rst(&conn->ident, seg);
329 return;
330 }
331
332 if ((seg->ctrl & CTL_SYN) == 0) {
333 log_msg(LVL_DEBUG, "SYN not present. Ignoring segment.");
334 return;
335 }
336
337 log_msg(LVL_DEBUG, "Got SYN, sending SYN, ACK.");
338
339 conn->rcv_nxt = seg->seq + 1;
340 conn->irs = seg->seq;
341
342
343 log_msg(LVL_DEBUG, "rcv_nxt=%u", conn->rcv_nxt);
344
345 if (seg->len > 1)
346 log_msg(LVL_WARN, "SYN combined with data, ignoring data.");
347
348 /* XXX select ISS */
349 conn->iss = 1;
350 conn->snd_nxt = conn->iss;
351 conn->snd_una = conn->iss;
352
353 /*
354 * Surprisingly the spec does not deal with initial window setting.
355 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
356 * will always be accepted as new window setting.
357 */
358 conn->snd_wnd = seg->wnd;
359 conn->snd_wl1 = seg->seq;
360 conn->snd_wl2 = seg->seq;
361
362 tcp_conn_state_set(conn, st_syn_received);
363
364 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
365
366 tcp_segment_delete(seg);
367}
368
369/** Segment arrived in Syn-Sent state.
370 *
371 * @param conn Connection
372 * @param seg Segment
373 */
374static void tcp_conn_sa_syn_sent(tcp_conn_t *conn, tcp_segment_t *seg)
375{
376 log_msg(LVL_DEBUG, "tcp_conn_sa_syn_sent(%p, %p)", conn, seg);
377
378 if ((seg->ctrl & CTL_ACK) != 0) {
379 log_msg(LVL_DEBUG, "snd_una=%u, seg.ack=%u, snd_nxt=%u",
380 conn->snd_una, seg->ack, conn->snd_nxt);
381 if (!seq_no_ack_acceptable(conn, seg->ack)) {
382 log_msg(LVL_WARN, "ACK not acceptable, send RST.");
383 tcp_reply_rst(&conn->ident, seg);
384 return;
385 }
386 }
387
388 if ((seg->ctrl & CTL_RST) != 0) {
389 log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
390 conn->name);
391 /* Reset connection */
392 tcp_conn_reset(conn);
393 /* XXX delete connection */
394 return;
395 }
396
397 /* XXX precedence */
398
399 if ((seg->ctrl & CTL_SYN) == 0) {
400 log_msg(LVL_DEBUG, "No SYN bit, ignoring segment.");
401 return;
402 }
403
404 conn->rcv_nxt = seg->seq + 1;
405 conn->irs = seg->seq;
406
407 if ((seg->ctrl & CTL_ACK) != 0) {
408 conn->snd_una = seg->ack;
409
410 /*
411 * Prune acked segments from retransmission queue and
412 * possibly transmit more data.
413 */
414 tcp_tqueue_ack_received(conn);
415 }
416
417 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
418
419 /*
420 * Surprisingly the spec does not deal with initial window setting.
421 * Set SND.WND = SEG.WND and set SND.WL1 so that next segment
422 * will always be accepted as new window setting.
423 */
424 log_msg(LVL_DEBUG, "SND.WND := %" PRIu32 ", SND.WL1 := %" PRIu32 ", "
425 "SND.WL2 = %" PRIu32, seg->wnd, seg->seq, seg->seq);
426 conn->snd_wnd = seg->wnd;
427 conn->snd_wl1 = seg->seq;
428 conn->snd_wl2 = seg->seq;
429
430 if (seq_no_syn_acked(conn)) {
431 log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
432 tcp_conn_state_set(conn, st_established);
433 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
434 } else {
435 log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
436 conn->name);
437 tcp_conn_state_set(conn, st_syn_received);
438 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
439 }
440
441 tcp_segment_delete(seg);
442}
443
444/** Segment arrived in state where segments are processed in sequence order.
445 *
446 * Queue segment in incoming segments queue for processing.
447 *
448 * @param conn Connection
449 * @param seg Segment
450 */
451static void tcp_conn_sa_queue(tcp_conn_t *conn, tcp_segment_t *seg)
452{
453 tcp_segment_t *pseg;
454
455 log_msg(LVL_DEBUG, "tcp_conn_sa_seq(%p, %p)", conn, seg);
456
457 /* Discard unacceptable segments ("old duplicates") */
458 if (!seq_no_segment_acceptable(conn, seg)) {
459 log_msg(LVL_DEBUG, "Replying ACK to unacceptable segment.");
460 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
461 tcp_segment_delete(seg);
462 return;
463 }
464
465 /* Queue for processing */
466 tcp_iqueue_insert_seg(&conn->incoming, seg);
467
468 /*
469 * Process all segments from incoming queue that are ready.
470 * Unacceptable segments are discarded by tcp_iqueue_get_ready_seg().
471 *
472 * XXX Need to return ACK for unacceptable segments
473 */
474 while (tcp_iqueue_get_ready_seg(&conn->incoming, &pseg) == EOK)
475 tcp_conn_seg_process(conn, pseg);
476}
477
478/** Process segment RST field.
479 *
480 * @param conn Connection
481 * @param seg Segment
482 * @return cp_done if we are done with this segment, cp_continue
483 * if not
484 */
485static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
486{
487 /* TODO */
488 return cp_continue;
489}
490
491/** Process segment security and precedence fields.
492 *
493 * @param conn Connection
494 * @param seg Segment
495 * @return cp_done if we are done with this segment, cp_continue
496 * if not
497 */
498static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
499{
500 /* TODO */
501 return cp_continue;
502}
503
504/** Process segment SYN field.
505 *
506 * @param conn Connection
507 * @param seg Segment
508 * @return cp_done if we are done with this segment, cp_continue
509 * if not
510 */
511static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
512{
513 if ((seg->ctrl & CTL_SYN) == 0)
514 return cp_continue;
515
516 /*
517 * Assert SYN is in receive window, otherwise this step should not
518 * be reached.
519 */
520 assert(seq_no_in_rcv_wnd(conn, seg->seq));
521
522 log_msg(LVL_WARN, "SYN is in receive window, should send reset. XXX");
523
524 /*
525 * TODO
526 *
527 * Send a reset, resond "reset" to all outstanding RECEIVEs and SEND,
528 * flush segment queues. Send unsolicited "connection reset" signal
529 * to user, connection -> closed state, delete TCB, return.
530 */
531 return cp_done;
532}
533
534/** Process segment ACK field in Syn-Received state.
535 *
536 * @param conn Connection
537 * @param seg Segment
538 * @return cp_done if we are done with this segment, cp_continue
539 * if not
540 */
541static cproc_t tcp_conn_seg_proc_ack_sr(tcp_conn_t *conn, tcp_segment_t *seg)
542{
543 if (!seq_no_ack_acceptable(conn, seg->ack)) {
544 /* ACK is not acceptable, send RST. */
545 log_msg(LVL_WARN, "Segment ACK not acceptable, sending RST.");
546 tcp_reply_rst(&conn->ident, seg);
547 tcp_segment_delete(seg);
548 return cp_done;
549 }
550
551 log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
552
553 tcp_conn_state_set(conn, st_established);
554
555 /* XXX Not mentioned in spec?! */
556 conn->snd_una = seg->ack;
557
558 return cp_continue;
559}
560
561/** Process segment ACK field in Established state.
562 *
563 * @param conn Connection
564 * @param seg Segment
565 * @return cp_done if we are done with this segment, cp_continue
566 * if not
567 */
568static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
569{
570 log_msg(LVL_DEBUG, "tcp_conn_seg_proc_ack_est(%p, %p)", conn, seg);
571
572 log_msg(LVL_DEBUG, "SEG.ACK=%u, SND.UNA=%u, SND.NXT=%u",
573 (unsigned)seg->ack, (unsigned)conn->snd_una,
574 (unsigned)conn->snd_nxt);
575
576 if (!seq_no_ack_acceptable(conn, seg->ack)) {
577 log_msg(LVL_DEBUG, "ACK not acceptable.");
578 if (!seq_no_ack_duplicate(conn, seg->ack)) {
579 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
580 "Send ACK and drop.");
581 /* Not acceptable, not duplicate. Send ACK and drop. */
582 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
583 tcp_segment_delete(seg);
584 return cp_done;
585 } else {
586 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
587 }
588 } else {
589 /* Update SND.UNA */
590 conn->snd_una = seg->ack;
591 }
592
593 if (seq_no_new_wnd_update(conn, seg)) {
594 conn->snd_wnd = seg->wnd;
595 conn->snd_wl1 = seg->seq;
596 conn->snd_wl2 = seg->ack;
597
598 log_msg(LVL_DEBUG, "Updating send window, SND.WND=%" PRIu32
599 ", SND.WL1=%" PRIu32 ", SND.WL2=%" PRIu32,
600 conn->snd_wnd, conn->snd_wl1, conn->snd_wl2);
601 }
602
603 /*
604 * Prune acked segments from retransmission queue and
605 * possibly transmit more data.
606 */
607 tcp_tqueue_ack_received(conn);
608
609 return cp_continue;
610}
611
612/** Process segment ACK field in Fin-Wait-1 state.
613 *
614 * @param conn Connection
615 * @param seg Segment
616 * @return cp_done if we are done with this segment, cp_continue
617 * if not
618 */
619static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
620{
621 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
622 return cp_done;
623
624 if (conn->fin_is_acked) {
625 log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
626 tcp_conn_state_set(conn, st_fin_wait_2);
627 }
628
629 return cp_continue;
630}
631
632/** Process segment ACK field in Fin-Wait-2 state.
633 *
634 * @param conn Connection
635 * @param seg Segment
636 * @return cp_done if we are done with this segment, cp_continue
637 * if not
638 */
639static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
640{
641 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
642 return cp_done;
643
644 /* TODO */
645 return cp_continue;
646}
647
648/** Process segment ACK field in Close-Wait state.
649 *
650 * @param conn Connection
651 * @param seg Segment
652 * @return cp_done if we are done with this segment, cp_continue
653 * if not
654 */
655static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
656{
657 /* The same processing as in Established state */
658 return tcp_conn_seg_proc_ack_est(conn, seg);
659}
660
661/** Process segment ACK field in Closing state.
662 *
663 * @param conn Connection
664 * @param seg Segment
665 * @return cp_done if we are done with this segment, cp_continue
666 * if not
667 */
668static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
669{
670 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
671 return cp_done;
672
673 /* TODO */
674 return cp_continue;
675}
676
677/** Process segment ACK field in Last-Ack state.
678 *
679 * @param conn Connection
680 * @param seg Segment
681 * @return cp_done if we are done with this segment, cp_continue
682 * if not
683 */
684static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
685{
686 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
687 return cp_done;
688
689 if (conn->fin_is_acked) {
690 log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
691 tcp_conn_remove(conn);
692 tcp_conn_state_set(conn, st_closed);
693 return cp_done;
694 }
695
696 return cp_continue;
697}
698
699/** Process segment ACK field in Time-Wait state.
700 *
701 * @param conn Connection
702 * @param seg Segment
703 * @return cp_done if we are done with this segment, cp_continue
704 * if not
705 */
706static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
707{
708 /* Nothing to do */
709 return cp_continue;
710}
711
712/** Process segment ACK field.
713 *
714 * @param conn Connection
715 * @param seg Segment
716 * @return cp_done if we are done with this segment, cp_continue
717 * if not
718 */
719static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
720{
721 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
722 conn->name, conn, seg);
723
724 if ((seg->ctrl & CTL_ACK) == 0) {
725 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
726 tcp_segment_delete(seg);
727 return cp_done;
728 }
729
730 switch (conn->cstate) {
731 case st_syn_received:
732 return tcp_conn_seg_proc_ack_sr(conn, seg);
733 case st_established:
734 return tcp_conn_seg_proc_ack_est(conn, seg);
735 case st_fin_wait_1:
736 return tcp_conn_seg_proc_ack_fw1(conn, seg);
737 case st_fin_wait_2:
738 return tcp_conn_seg_proc_ack_fw2(conn, seg);
739 case st_close_wait:
740 return tcp_conn_seg_proc_ack_cw(conn, seg);
741 case st_closing:
742 return tcp_conn_seg_proc_ack_cls(conn, seg);
743 case st_last_ack:
744 return tcp_conn_seg_proc_ack_la(conn, seg);
745 case st_time_wait:
746 return tcp_conn_seg_proc_ack_tw(conn, seg);
747 case st_listen:
748 case st_syn_sent:
749 case st_closed:
750 assert(false);
751 }
752
753 assert(false);
754}
755
756/** Process segment URG field.
757 *
758 * @param conn Connection
759 * @param seg Segment
760 * @return cp_done if we are done with this segment, cp_continue
761 * if not
762 */
763static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
764{
765 return cp_continue;
766}
767
768/** Process segment text.
769 *
770 * @param conn Connection
771 * @param seg Segment
772 * @return cp_done if we are done with this segment, cp_continue
773 * if not
774 */
775static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
776{
777 size_t text_size;
778 size_t xfer_size;
779
780 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
781 conn->name, conn, seg);
782
783 switch (conn->cstate) {
784 case st_established:
785 case st_fin_wait_1:
786 case st_fin_wait_2:
787 /* OK */
788 break;
789 case st_close_wait:
790 case st_closing:
791 case st_last_ack:
792 case st_time_wait:
793 /* Invalid since FIN has been received. Ignore text. */
794 return cp_continue;
795 case st_listen:
796 case st_syn_sent:
797 case st_syn_received:
798 case st_closed:
799 assert(false);
800 }
801
802 /*
803 * Process segment text
804 */
805 assert(seq_no_segment_ready(conn, seg));
806
807 /* Trim anything outside our receive window */
808 tcp_conn_trim_seg_to_wnd(conn, seg);
809
810 fibril_mutex_lock(&conn->rcv_buf_lock);
811
812 /* Determine how many bytes to copy */
813 text_size = tcp_segment_text_size(seg);
814 xfer_size = min(text_size, conn->rcv_buf_size - conn->rcv_buf_used);
815
816 /* Copy data to receive buffer */
817 tcp_segment_text_copy(seg, conn->rcv_buf + conn->rcv_buf_used,
818 xfer_size);
819 conn->rcv_buf_used += xfer_size;
820
821 /* Signal to the receive function that new data has arrived */
822 fibril_condvar_broadcast(&conn->rcv_buf_cv);
823 fibril_mutex_unlock(&conn->rcv_buf_lock);
824
825 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
826
827 /* Advance RCV.NXT */
828 conn->rcv_nxt += xfer_size;
829
830 /* Update receive window. XXX Not an efficient strategy. */
831 conn->rcv_wnd -= xfer_size;
832
833 /* Send ACK */
834 if (xfer_size > 0)
835 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
836
837 if (xfer_size < seg->len) {
838 /* Trim part of segment which we just received */
839 tcp_conn_trim_seg_to_wnd(conn, seg);
840 } else {
841 log_msg(LVL_DEBUG, "%s: Nothing left in segment, dropping "
842 "(xfer_size=%zu, SEG.LEN=%zu, seg->ctrl=%u)",
843 conn->name, xfer_size, seg->len, (unsigned)seg->ctrl);
844 /* Nothing left in segment */
845 tcp_segment_delete(seg);
846 return cp_done;
847 }
848
849 return cp_continue;
850}
851
852/** Process segment FIN field.
853 *
854 * @param conn Connection
855 * @param seg Segment
856 * @return cp_done if we are done with this segment, cp_continue
857 * if not
858 */
859static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
860{
861 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
862 conn->name, conn, seg);
863 log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
864 (unsigned) seg->ctrl);
865
866 /* Only process FIN if no text is left in segment. */
867 if (tcp_segment_text_size(seg) == 0 && (seg->ctrl & CTL_FIN) != 0) {
868 log_msg(LVL_DEBUG, " - FIN found in segment.");
869
870 /* Send ACK */
871 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
872
873 conn->rcv_nxt++;
874 conn->rcv_wnd--;
875
876 /* Change connection state */
877 switch (conn->cstate) {
878 case st_listen:
879 case st_syn_sent:
880 case st_closed:
881 /* Connection not synchronized */
882 assert(false);
883 case st_syn_received:
884 case st_established:
885 log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
886 conn->name);
887 tcp_conn_state_set(conn, st_close_wait);
888 break;
889 case st_fin_wait_1:
890 log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
891 conn->name);
892 tcp_conn_state_set(conn, st_closing);
893 break;
894 case st_fin_wait_2:
895 log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
896 conn->name);
897 tcp_conn_state_set(conn, st_time_wait);
898 /* Start the Time-Wait timer */
899 tcp_conn_tw_timer_set(conn);
900 break;
901 case st_close_wait:
902 case st_closing:
903 case st_last_ack:
904 /* Do nothing */
905 break;
906 case st_time_wait:
907 /* Restart the Time-Wait timer */
908 tcp_conn_tw_timer_set(conn);
909 break;
910 }
911
912 /* Add FIN to the receive buffer */
913 fibril_mutex_lock(&conn->rcv_buf_lock);
914 conn->rcv_buf_fin = true;
915 fibril_condvar_broadcast(&conn->rcv_buf_cv);
916 fibril_mutex_unlock(&conn->rcv_buf_lock);
917
918 tcp_segment_delete(seg);
919 return cp_done;
920 }
921
922 return cp_continue;
923}
924
925/** Process incoming segment.
926 *
927 * We are in connection state where segments are processed in order
928 * of sequence number. This processes one segment taken from the
929 * connection incoming segments queue.
930 *
931 * @param conn Connection
932 * @param seg Segment
933 */
934static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg)
935{
936 log_msg(LVL_DEBUG, "tcp_conn_seg_process(%p, %p)", conn, seg);
937 tcp_segment_dump(seg);
938
939 /* Check whether segment is acceptable */
940 /* XXX Permit valid ACKs, URGs and RSTs */
941/* if (!seq_no_segment_acceptable(conn, seg)) {
942 log_msg(LVL_WARN, "Segment not acceptable, dropping.");
943 if ((seg->ctrl & CTL_RST) == 0) {
944 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
945 }
946 return;
947 }
948*/
949
950 if (tcp_conn_seg_proc_rst(conn, seg) == cp_done)
951 return;
952
953 if (tcp_conn_seg_proc_sp(conn, seg) == cp_done)
954 return;
955
956 if (tcp_conn_seg_proc_syn(conn, seg) == cp_done)
957 return;
958
959 if (tcp_conn_seg_proc_ack(conn, seg) == cp_done)
960 return;
961
962 if (tcp_conn_seg_proc_urg(conn, seg) == cp_done)
963 return;
964
965 if (tcp_conn_seg_proc_text(conn, seg) == cp_done)
966 return;
967
968 if (tcp_conn_seg_proc_fin(conn, seg) == cp_done)
969 return;
970
971 /*
972 * If anything is left from the segment, insert it back into the
973 * incoming segments queue.
974 */
975 if (seg->len > 0) {
976 log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
977 seg, (size_t) seg->len);
978 tcp_iqueue_insert_seg(&conn->incoming, seg);
979 } else {
980 tcp_segment_delete(seg);
981 }
982}
983
984/** Segment arrived on a connection.
985 *
986 * @param conn Connection
987 * @param seg Segment
988 */
989void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
990{
991 log_msg(LVL_DEBUG, "%c: tcp_conn_segment_arrived(%p)",
992 conn->name, seg);
993
994 switch (conn->cstate) {
995 case st_listen:
996 tcp_conn_sa_listen(conn, seg); break;
997 case st_syn_sent:
998 tcp_conn_sa_syn_sent(conn, seg); break;
999 case st_syn_received:
1000 case st_established:
1001 case st_fin_wait_1:
1002 case st_fin_wait_2:
1003 case st_close_wait:
1004 case st_closing:
1005 case st_last_ack:
1006 case st_time_wait:
1007 /* Process segments in order of sequence number */
1008 tcp_conn_sa_queue(conn, seg); break;
1009 case st_closed:
1010 log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
1011 assert(false);
1012 }
1013}
1014
1015/** Time-Wait timeout handler.
1016 *
1017 * @param arg Connection
1018 */
1019static void tw_timeout_func(void *arg)
1020{
1021 tcp_conn_t *conn = (tcp_conn_t *) arg;
1022
1023 log_msg(LVL_DEBUG, "tw_timeout_func(%p)", conn);
1024
1025 if (conn->cstate == st_closed) {
1026 log_msg(LVL_DEBUG, "Connection already closed.");
1027 return;
1028 }
1029
1030 log_msg(LVL_DEBUG, "%s: TW Timeout -> Closed", conn->name);
1031 tcp_conn_remove(conn);
1032 tcp_conn_state_set(conn, st_closed);
1033}
1034
1035/** Start or restart the Time-Wait timeout.
1036 *
1037 * @param conn Connection
1038 */
1039void tcp_conn_tw_timer_set(tcp_conn_t *conn)
1040{
1041 fibril_timer_set(conn->tw_timer, TIME_WAIT_TIMEOUT, tw_timeout_func,
1042 (void *)conn);
1043}
1044
1045/** Clear the Time-Wait timeout.
1046 *
1047 * @param conn Connection
1048 */
1049void tcp_conn_tw_timer_clear(tcp_conn_t *conn)
1050{
1051 fibril_timer_clear(conn->tw_timer);
1052}
1053
1054/** Trim segment to the receive window.
1055 *
1056 * @param conn Connection
1057 * @param seg Segment
1058 */
1059void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
1060{
1061 uint32_t left, right;
1062
1063 seq_no_seg_trim_calc(conn, seg, &left, &right);
1064 tcp_segment_trim(seg, left, right);
1065}
1066
1067/** Handle unexpected segment received on a socket pair.
1068 *
1069 * We reply with an RST unless the received segment has RST.
1070 *
1071 * @param sp Socket pair which received the segment
1072 * @param seg Unexpected segment
1073 */
1074void tcp_unexpected_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
1075{
1076 log_msg(LVL_DEBUG, "tcp_unexpected_segment(%p, %p)", sp, seg);
1077
1078 if ((seg->ctrl & CTL_RST) == 0)
1079 tcp_reply_rst(sp, seg);
1080}
1081
1082/** Compute flipped socket pair for response.
1083 *
1084 * Flipped socket pair has local and foreign sockets exchanged.
1085 *
1086 * @param sp Socket pair
1087 * @param fsp Place to store flipped socket pair
1088 */
1089void tcp_sockpair_flipped(tcp_sockpair_t *sp, tcp_sockpair_t *fsp)
1090{
1091 fsp->local = sp->foreign;
1092 fsp->foreign = sp->local;
1093}
1094
1095/** Send RST in response to an incoming segment.
1096 *
1097 * @param sp Socket pair which received the segment
1098 * @param seg Incoming segment
1099 */
1100void tcp_reply_rst(tcp_sockpair_t *sp, tcp_segment_t *seg)
1101{
1102 tcp_segment_t *rseg;
1103
1104 log_msg(LVL_DEBUG, "tcp_reply_rst(%p, %p)", sp, seg);
1105
1106 rseg = tcp_segment_make_rst(seg);
1107 tcp_transmit_segment(sp, rseg);
1108}
1109
1110/**
1111 * @}
1112 */
Note: See TracBrowser for help on using the repository browser.