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

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

Fix off-by-one bug in BIT_V.
Fix FIN being sent too early.
Fix FIN not being sent.
Fix seg→len from decoded PDU.
Add segment dumps.

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