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
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"
47#include "tcp_type.h"
48#include "tqueue.h"
[762b48a]49#include "ucall.h"
[c5808b41]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);
[704586fb]61static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
[c5808b41]62
[032bbe7]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 */
[c5808b41]69tcp_conn_t *tcp_conn_new(tcp_sock_t *lsock, tcp_sock_t *fsock)
70{
[2a3214e]71 tcp_conn_t *conn = NULL;
[7cf7ded]72 bool tqueue_inited = false;
[c5808b41]73
[0093ab6]74 /* Allocate connection structure */
[c5808b41]75 conn = calloc(1, sizeof(tcp_conn_t));
76 if (conn == NULL)
[2a3214e]77 goto error;
78
79 conn->tw_timer = fibril_timer_create();
80 if (conn->tw_timer == NULL)
81 goto error;
[c5808b41]82
[0093ab6]83 /* Allocate receive buffer */
[d9ce049]84 fibril_mutex_initialize(&conn->rcv_buf_lock);
85 fibril_condvar_initialize(&conn->rcv_buf_cv);
[0093ab6]86 conn->rcv_buf_size = RCV_BUF_SIZE;
87 conn->rcv_buf_used = 0;
[8c7a054]88 conn->rcv_buf_fin = false;
[d9ce049]89
[0093ab6]90 conn->rcv_buf = calloc(1, conn->rcv_buf_size);
[2a3214e]91 if (conn->rcv_buf == NULL)
92 goto error;
[0093ab6]93
[32105348]94 /** Allocate send buffer */
95 conn->snd_buf_size = SND_BUF_SIZE;
96 conn->snd_buf_used = 0;
[8c7a054]97 conn->snd_buf_fin = false;
[32105348]98 conn->snd_buf = calloc(1, conn->snd_buf_size);
[2a3214e]99 if (conn->snd_buf == NULL)
100 goto error;
[32105348]101
[0093ab6]102 /* Set up receive window. */
103 conn->rcv_wnd = conn->rcv_buf_size;
104
105 /* Initialize incoming segment queue */
[c5808b41]106 tcp_iqueue_init(&conn->incoming, conn);
107
[6df418c4]108 /* Initialize retransmission queue */
[7cf7ded]109 if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
110 goto error;
111
112 tqueue_inited = true;
[6df418c4]113
[004a5fe]114 /* Connection state change signalling */
115 fibril_mutex_initialize(&conn->cstate_lock);
116 fibril_condvar_initialize(&conn->cstate_cv);
117
[c5808b41]118 conn->cstate = st_listen;
[6df418c4]119 conn->fin_is_acked = false;
[c5808b41]120 conn->ident.local = *lsock;
121 if (fsock != NULL)
122 conn->ident.foreign = *fsock;
123
124 return conn;
[2a3214e]125
126error:
[7cf7ded]127 if (tqueue_inited)
128 tcp_tqueue_fini(&conn->retransmit);
[2a3214e]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;
[c5808b41]139}
140
[032bbe7]141/** Enlist connection.
142 *
143 * Add connection to the connection map.
144 */
[c5808b41]145void tcp_conn_add(tcp_conn_t *conn)
146{
147 list_append(&conn->link, &conn_list);
148}
149
[6e88fea]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
[004a5fe]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
[032bbe7]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 */
[c5808b41]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);
[004a5fe]180 tcp_conn_state_set(conn, st_syn_sent);
[c5808b41]181}
182
[f343a16]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:
[6896409c]193 log_msg(LVL_DEBUG, "%s: FIN sent -> Fin-Wait-1", conn->name);
[004a5fe]194 tcp_conn_state_set(conn, st_fin_wait_1);
[f343a16]195 break;
196 case st_close_wait:
[6896409c]197 log_msg(LVL_DEBUG, "%s: FIN sent -> Last-Ack", conn->name);
[004a5fe]198 tcp_conn_state_set(conn, st_last_ack);
[f343a16]199 break;
200 default:
[6896409c]201 log_msg(LVL_ERROR, "%s: Connection state %d", conn->name,
202 conn->cstate);
[f343a16]203 assert(false);
204 }
[6df418c4]205
206 conn->fin_is_acked = false;
[f343a16]207}
208
[032bbe7]209/** Compare two sockets.
210 *
211 * Two sockets are equal if the address is equal and the port number
212 * is equal.
213 */
[3aa2642a]214static bool tcp_socket_match(tcp_sock_t *sock, tcp_sock_t *patt)
[c5808b41]215{
[3aa2642a]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);
[c5808b41]218
[3aa2642a]219 if (patt->addr.ipv4 != TCP_IPV4_ANY &&
220 patt->addr.ipv4 != sock->addr.ipv4)
[c5808b41]221 return false;
222
[3aa2642a]223 if (patt->port != TCP_PORT_ANY &&
224 patt->port != sock->port)
[c5808b41]225 return false;
226
227 log_msg(LVL_DEBUG, " -> match");
228
229 return true;
230}
231
[032bbe7]232/** Match socket with pattern. */
[c5808b41]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
[3aa2642a]237 if (!tcp_socket_match(&sp->local, &pattern->local))
[c5808b41]238 return false;
239
[3aa2642a]240 if (!tcp_socket_match(&sp->foreign, &pattern->foreign))
[c5808b41]241 return false;
242
243 return true;
244}
245
[032bbe7]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 */
[c5808b41]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);
[704586fb]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)) {
[c5808b41]265 return conn;
266 }
267 }
268
269 return NULL;
270}
271
[704586fb]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
[32105348]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:
[7cf7ded]305 log_msg(LVL_WARN, "state=%d", (int) conn->cstate);
[32105348]306 assert(false);
307 }
308
309 assert(false);
310}
311
[032bbe7]312/** Segment arrived in Listen state.
313 *
314 * @param conn Connection
315 * @param seg Segment
316 */
[c5808b41]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
[4c55a64]342
[c5808b41]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
[4c55a64]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
[004a5fe]362 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]363
364 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
[0093ab6]365
366 tcp_segment_delete(seg);
[c5808b41]367}
368
[032bbe7]369/** Segment arrived in Syn-Sent state.
370 *
371 * @param conn Connection
372 * @param seg Segment
373 */
[c5808b41]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) {
[6896409c]389 log_msg(LVL_DEBUG, "%s: Connection reset. -> Closed",
390 conn->name);
[704586fb]391 /* Reset connection */
392 tcp_conn_reset(conn);
[c5808b41]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;
[d9ce049]409
410 /*
411 * Prune acked segments from retransmission queue and
412 * possibly transmit more data.
413 */
414 tcp_tqueue_ack_received(conn);
[c5808b41]415 }
416
417 log_msg(LVL_DEBUG, "Sent SYN, got SYN.");
418
[32105348]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
[c5808b41]430 if (seq_no_syn_acked(conn)) {
[6896409c]431 log_msg(LVL_DEBUG, "%s: syn acked -> Established", conn->name);
[004a5fe]432 tcp_conn_state_set(conn, st_established);
[c5808b41]433 tcp_tqueue_ctrl_seg(conn, CTL_ACK /* XXX */);
434 } else {
[6896409c]435 log_msg(LVL_DEBUG, "%s: syn not acked -> Syn-Received",
436 conn->name);
[004a5fe]437 tcp_conn_state_set(conn, st_syn_received);
[c5808b41]438 tcp_tqueue_ctrl_seg(conn, CTL_SYN | CTL_ACK /* XXX */);
439 }
[0093ab6]440
441 tcp_segment_delete(seg);
[c5808b41]442}
443
[032bbe7]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 */
[c5808b41]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
[7cf7ded]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 }
[c5808b41]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
[032bbe7]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 */
[c5808b41]485static cproc_t tcp_conn_seg_proc_rst(tcp_conn_t *conn, tcp_segment_t *seg)
486{
[4c55a64]487 /* TODO */
[c5808b41]488 return cp_continue;
489}
490
[032bbe7]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 */
[c5808b41]498static cproc_t tcp_conn_seg_proc_sp(tcp_conn_t *conn, tcp_segment_t *seg)
499{
[4c55a64]500 /* TODO */
[c5808b41]501 return cp_continue;
502}
503
[032bbe7]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 */
[c5808b41]511static cproc_t tcp_conn_seg_proc_syn(tcp_conn_t *conn, tcp_segment_t *seg)
512{
[7cf7ded]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;
[c5808b41]532}
533
[032bbe7]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 */
[c5808b41]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);
[4c55a64]547 tcp_segment_delete(seg);
548 return cp_done;
[c5808b41]549 }
550
[6896409c]551 log_msg(LVL_DEBUG, "%s: SYN ACKed -> Established", conn->name);
[c5808b41]552
[004a5fe]553 tcp_conn_state_set(conn, st_established);
[c5808b41]554
555 /* XXX Not mentioned in spec?! */
556 conn->snd_una = seg->ack;
557
558 return cp_continue;
559}
560
[032bbe7]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 */
[c5808b41]568static cproc_t tcp_conn_seg_proc_ack_est(tcp_conn_t *conn, tcp_segment_t *seg)
569{
[0093ab6]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
[4c55a64]576 if (!seq_no_ack_acceptable(conn, seg->ack)) {
[0093ab6]577 log_msg(LVL_DEBUG, "ACK not acceptable.");
[4c55a64]578 if (!seq_no_ack_duplicate(conn, seg->ack)) {
[0093ab6]579 log_msg(LVL_WARN, "Not acceptable, not duplicate. "
580 "Send ACK and drop.");
[4c55a64]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;
[0093ab6]585 } else {
586 log_msg(LVL_DEBUG, "Ignoring duplicate ACK.");
[4c55a64]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;
[d9ce049]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);
[4c55a64]601 }
602
[d9ce049]603 /*
604 * Prune acked segments from retransmission queue and
605 * possibly transmit more data.
606 */
607 tcp_tqueue_ack_received(conn);
608
[c5808b41]609 return cp_continue;
610}
611
[032bbe7]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 */
[c5808b41]619static cproc_t tcp_conn_seg_proc_ack_fw1(tcp_conn_t *conn, tcp_segment_t *seg)
620{
[4c55a64]621 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
622 return cp_done;
623
[6df418c4]624 if (conn->fin_is_acked) {
[6896409c]625 log_msg(LVL_DEBUG, "%s: FIN acked -> Fin-Wait-2", conn->name);
[004a5fe]626 tcp_conn_state_set(conn, st_fin_wait_2);
[6df418c4]627 }
628
[c5808b41]629 return cp_continue;
630}
631
[032bbe7]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 */
[c5808b41]639static cproc_t tcp_conn_seg_proc_ack_fw2(tcp_conn_t *conn, tcp_segment_t *seg)
640{
[4c55a64]641 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
642 return cp_done;
643
644 /* TODO */
[c5808b41]645 return cp_continue;
646}
647
[032bbe7]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 */
[c5808b41]655static cproc_t tcp_conn_seg_proc_ack_cw(tcp_conn_t *conn, tcp_segment_t *seg)
656{
[4c55a64]657 /* The same processing as in Established state */
658 return tcp_conn_seg_proc_ack_est(conn, seg);
[c5808b41]659}
660
[032bbe7]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 */
[c5808b41]668static cproc_t tcp_conn_seg_proc_ack_cls(tcp_conn_t *conn, tcp_segment_t *seg)
669{
[4c55a64]670 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
671 return cp_done;
672
673 /* TODO */
[c5808b41]674 return cp_continue;
675}
676
[032bbe7]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 */
[c5808b41]684static cproc_t tcp_conn_seg_proc_ack_la(tcp_conn_t *conn, tcp_segment_t *seg)
685{
[4c55a64]686 if (tcp_conn_seg_proc_ack_est(conn, seg) == cp_done)
687 return cp_done;
688
[6e88fea]689 if (conn->fin_is_acked) {
[6896409c]690 log_msg(LVL_DEBUG, "%s: FIN acked -> Closed", conn->name);
[6e88fea]691 tcp_conn_remove(conn);
[004a5fe]692 tcp_conn_state_set(conn, st_closed);
[6e88fea]693 return cp_done;
694 }
695
[c5808b41]696 return cp_continue;
697}
698
[032bbe7]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 */
[c5808b41]706static cproc_t tcp_conn_seg_proc_ack_tw(tcp_conn_t *conn, tcp_segment_t *seg)
707{
[4c55a64]708 /* Nothing to do */
[c5808b41]709 return cp_continue;
710}
711
[032bbe7]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 */
[c5808b41]719static cproc_t tcp_conn_seg_proc_ack(tcp_conn_t *conn, tcp_segment_t *seg)
720{
[23fe06c]721 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_ack(%p, %p)",
722 conn->name, conn, seg);
[c5808b41]723
724 if ((seg->ctrl & CTL_ACK) == 0) {
725 log_msg(LVL_WARN, "Segment has no ACK. Dropping.");
[4c55a64]726 tcp_segment_delete(seg);
[c5808b41]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
[032bbe7]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 */
[c5808b41]763static cproc_t tcp_conn_seg_proc_urg(tcp_conn_t *conn, tcp_segment_t *seg)
764{
765 return cp_continue;
766}
767
[032bbe7]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 */
[c5808b41]775static cproc_t tcp_conn_seg_proc_text(tcp_conn_t *conn, tcp_segment_t *seg)
776{
[0093ab6]777 size_t text_size;
778 size_t xfer_size;
779
[23fe06c]780 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_text(%p, %p)",
781 conn->name, conn, seg);
[0093ab6]782
[4c55a64]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
[0093ab6]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
[d9ce049]810 fibril_mutex_lock(&conn->rcv_buf_lock);
811
[0093ab6]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 */
[d9ce049]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);
[0093ab6]824
[32105348]825 log_msg(LVL_DEBUG, "Received %zu bytes of data.", xfer_size);
826
[0093ab6]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) {
[8c7a054]838 /* Trim part of segment which we just received */
[0093ab6]839 tcp_conn_trim_seg_to_wnd(conn, seg);
[8c7a054]840 } else {
[6896409c]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);
[8c7a054]844 /* Nothing left in segment */
845 tcp_segment_delete(seg);
[0093ab6]846 return cp_done;
847 }
848
[c5808b41]849 return cp_continue;
850}
851
[032bbe7]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 */
[c5808b41]859static cproc_t tcp_conn_seg_proc_fin(tcp_conn_t *conn, tcp_segment_t *seg)
860{
[23fe06c]861 log_msg(LVL_DEBUG, "%s: tcp_conn_seg_proc_fin(%p, %p)",
862 conn->name, conn, seg);
[7cf7ded]863 log_msg(LVL_DEBUG, " seg->len=%zu, seg->ctl=%u", (size_t) seg->len,
864 (unsigned) seg->ctrl);
[8c7a054]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
[6e88fea]870 /* Send ACK */
871 tcp_tqueue_ctrl_seg(conn, CTL_ACK);
872
[8c7a054]873 conn->rcv_nxt++;
874 conn->rcv_wnd--;
875
[f343a16]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:
[6896409c]885 log_msg(LVL_DEBUG, "%s: FIN received -> Close-Wait",
886 conn->name);
[004a5fe]887 tcp_conn_state_set(conn, st_close_wait);
[f343a16]888 break;
889 case st_fin_wait_1:
[6896409c]890 log_msg(LVL_DEBUG, "%s: FIN received -> Closing",
891 conn->name);
[004a5fe]892 tcp_conn_state_set(conn, st_closing);
[f343a16]893 break;
894 case st_fin_wait_2:
[6896409c]895 log_msg(LVL_DEBUG, "%s: FIN received -> Time-Wait",
896 conn->name);
[004a5fe]897 tcp_conn_state_set(conn, st_time_wait);
[2a3214e]898 /* Start the Time-Wait timer */
899 tcp_conn_tw_timer_set(conn);
[f343a16]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:
[2a3214e]907 /* Restart the Time-Wait timer */
908 tcp_conn_tw_timer_set(conn);
[f343a16]909 break;
910 }
[8c7a054]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
[c5808b41]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.
[032bbe7]930 *
931 * @param conn Connection
932 * @param seg Segment
[c5808b41]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);
[6896409c]937 tcp_segment_dump(seg);
[c5808b41]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;
[0093ab6]970
[8c7a054]971 /*
972 * If anything is left from the segment, insert it back into the
973 * incoming segments queue.
974 */
[7cf7ded]975 if (seg->len > 0) {
976 log_msg(LVL_DEBUG, "Re-insert segment %p. seg->len=%zu",
977 seg, (size_t) seg->len);
[8c7a054]978 tcp_iqueue_insert_seg(&conn->incoming, seg);
[7cf7ded]979 } else {
[8c7a054]980 tcp_segment_delete(seg);
[7cf7ded]981 }
[c5808b41]982}
983
[032bbe7]984/** Segment arrived on a connection.
985 *
986 * @param conn Connection
987 * @param seg Segment
988 */
[c5808b41]989void tcp_conn_segment_arrived(tcp_conn_t *conn, tcp_segment_t *seg)
990{
[23fe06c]991 log_msg(LVL_DEBUG, "%c: tcp_conn_segment_arrived(%p)",
992 conn->name, seg);
[c5808b41]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:
[7cf7ded]1010 log_msg(LVL_DEBUG, "state=%d", (int) conn->cstate);
[c5808b41]1011 assert(false);
1012 }
1013}
1014
[2a3214e]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
[704586fb]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);
[2a3214e]1031 tcp_conn_remove(conn);
[004a5fe]1032 tcp_conn_state_set(conn, st_closed);
[2a3214e]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
[704586fb]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
[032bbe7]1054/** Trim segment to the receive window.
1055 *
1056 * @param conn Connection
1057 * @param seg Segment
1058 */
[0093ab6]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
[032bbe7]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 */
[c5808b41]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
[032bbe7]1082/** Compute flipped socket pair for response.
1083 *
[32105348]1084 * Flipped socket pair has local and foreign sockets exchanged.
[032bbe7]1085 *
1086 * @param sp Socket pair
1087 * @param fsp Place to store flipped socket pair
1088 */
[c5808b41]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
[032bbe7]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 */
[c5808b41]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);
[704586fb]1107 tcp_transmit_segment(sp, rseg);
[c5808b41]1108}
1109
1110/**
1111 * @}
1112 */
Note: See TracBrowser for help on using the repository browser.