source: mainline/uspace/srv/net/tl/tcp/tqueue.c@ 1812a0d

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

Hook TCP into network stack IP layer.

  • Property mode set to 100644
File size: 8.3 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 transmission queue
35 */
36
37#include <adt/list.h>
38#include <errno.h>
39#include <fibril_synch.h>
40#include <byteorder.h>
41#include <io/log.h>
42#include <macros.h>
43#include <mem.h>
44#include <stdlib.h>
45#include "conn.h"
46#include "header.h"
47#include "ncsim.h"
48#include "rqueue.h"
49#include "segment.h"
50#include "seq_no.h"
51#include "tqueue.h"
52#include "tcp.h"
53#include "tcp_type.h"
54
55#define RETRANSMIT_TIMEOUT (2*1000*1000)
56
57static void retransmit_timeout_func(void *arg);
58static void tcp_tqueue_timer_set(tcp_conn_t *conn);
59static void tcp_tqueue_timer_clear(tcp_conn_t *conn);
60
61int tcp_tqueue_init(tcp_tqueue_t *tqueue, tcp_conn_t *conn)
62{
63 tqueue->conn = conn;
64 tqueue->timer = fibril_timer_create();
65 if (tqueue->timer == NULL)
66 return ENOMEM;
67
68 list_initialize(&tqueue->list);
69
70 return EOK;
71}
72
73void tcp_tqueue_fini(tcp_tqueue_t *tqueue)
74{
75 if (tqueue->timer != NULL) {
76 fibril_timer_destroy(tqueue->timer);
77 tqueue->timer = NULL;
78 }
79}
80
81void tcp_tqueue_ctrl_seg(tcp_conn_t *conn, tcp_control_t ctrl)
82{
83 tcp_segment_t *seg;
84
85 log_msg(LVL_DEBUG, "tcp_tqueue_ctrl_seg(%p, %u)", conn, ctrl);
86
87 seg = tcp_segment_make_ctrl(ctrl);
88 tcp_tqueue_seg(conn, seg);
89}
90
91void tcp_tqueue_seg(tcp_conn_t *conn, tcp_segment_t *seg)
92{
93 tcp_segment_t *rt_seg;
94 tcp_tqueue_entry_t *tqe;
95
96 log_msg(LVL_DEBUG, "tcp_tqueue_seg(%p, %p)", conn, seg);
97
98 /*
99 * Add segment to retransmission queue
100 */
101
102 if (seg->len > 0) {
103 rt_seg = tcp_segment_dup(seg);
104 if (rt_seg == NULL) {
105 log_msg(LVL_ERROR, "Memory allocation failed.");
106 /* XXX Handle properly */
107 return;
108 }
109
110 tqe = calloc(1, sizeof(tcp_tqueue_entry_t));
111 if (tqe == NULL) {
112 log_msg(LVL_ERROR, "Memory allocation failed.");
113 /* XXX Handle properly */
114 return;
115 }
116
117 tqe->conn = conn;
118 tqe->seg = rt_seg;
119 list_append(&tqe->link, &conn->retransmit.list);
120
121 /* Set retransmission timer */
122 tcp_tqueue_timer_set(conn);
123 }
124
125 tcp_prepare_transmit_segment(conn, seg);
126}
127
128void tcp_prepare_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
129{
130 /*
131 * Always send ACK once we have received SYN, except for RST segments.
132 * (Spec says we should always send ACK once connection has been
133 * established.)
134 */
135 if (tcp_conn_got_syn(conn) && (seg->ctrl & CTL_RST) == 0)
136 seg->ctrl |= CTL_ACK;
137
138 seg->seq = conn->snd_nxt;
139 conn->snd_nxt += seg->len;
140
141 tcp_conn_transmit_segment(conn, seg);
142}
143
144/** Transmit data from the send buffer.
145 *
146 * @param conn Connection
147 */
148void tcp_tqueue_new_data(tcp_conn_t *conn)
149{
150 size_t avail_wnd;
151 size_t xfer_seqlen;
152 size_t snd_buf_seqlen;
153 size_t data_size;
154 tcp_control_t ctrl;
155 bool send_fin;
156
157 tcp_segment_t *seg;
158
159 log_msg(LVL_DEBUG, "%s: tcp_tqueue_new_data()", conn->name);
160
161 /* Number of free sequence numbers in send window */
162 avail_wnd = (conn->snd_una + conn->snd_wnd) - conn->snd_nxt;
163 snd_buf_seqlen = conn->snd_buf_used + (conn->snd_buf_fin ? 1 : 0);
164
165 xfer_seqlen = min(snd_buf_seqlen, avail_wnd);
166 log_msg(LVL_DEBUG, "%s: snd_buf_seqlen = %zu, SND.WND = %zu, "
167 "xfer_seqlen = %zu", conn->name, snd_buf_seqlen, conn->snd_wnd,
168 xfer_seqlen);
169
170 if (xfer_seqlen == 0)
171 return;
172
173 /* XXX Do not always send immediately */
174
175 send_fin = conn->snd_buf_fin && xfer_seqlen == snd_buf_seqlen;
176 data_size = xfer_seqlen - (send_fin ? 1 : 0);
177
178 if (send_fin) {
179 log_msg(LVL_DEBUG, "%s: Sending out FIN.", conn->name);
180 /* We are sending out FIN */
181 ctrl = CTL_FIN;
182 tcp_conn_fin_sent(conn);
183 } else {
184 ctrl = 0;
185 }
186
187 seg = tcp_segment_make_data(ctrl, conn->snd_buf, data_size);
188 if (seg == NULL) {
189 log_msg(LVL_ERROR, "Memory allocation failure.");
190 return;
191 }
192
193 /* Remove data from send buffer */
194 memmove(conn->snd_buf, conn->snd_buf + data_size,
195 conn->snd_buf_used - data_size);
196 conn->snd_buf_used -= data_size;
197
198 if (send_fin)
199 conn->snd_buf_fin = false;
200
201 tcp_tqueue_seg(conn, seg);
202}
203
204/** Remove ACKed segments from retransmission queue and possibly transmit
205 * more data.
206 *
207 * This should be called when SND.UNA is updated due to incoming ACK.
208 */
209void tcp_tqueue_ack_received(tcp_conn_t *conn)
210{
211 link_t *cur, *next;
212
213 log_msg(LVL_DEBUG, "tcp_tqueue_ack_received(%p)", conn);
214
215 cur = conn->retransmit.list.head.next;
216
217 while (cur != &conn->retransmit.list.head) {
218 next = cur->next;
219
220 tcp_tqueue_entry_t *tqe = list_get_instance(cur,
221 tcp_tqueue_entry_t, link);
222
223 if (seq_no_segment_acked(conn, tqe->seg, conn->snd_una)) {
224 /* Remove acknowledged segment */
225 list_remove(cur);
226
227 if ((tqe->seg->ctrl & CTL_FIN) != 0) {
228 /* Our FIN has been acked */
229 conn->fin_is_acked = true;
230 }
231
232 tcp_segment_delete(tqe->seg);
233 free(tqe);
234
235 /* Reset retransmission timer */
236 tcp_tqueue_timer_set(conn);
237 }
238
239 cur = next;
240 }
241
242 /* Clear retransmission timer if the queue is empty. */
243 if (list_empty(&conn->retransmit.list))
244 tcp_tqueue_timer_clear(conn);
245
246 /* Possibly transmit more data */
247 tcp_tqueue_new_data(conn);
248}
249
250void tcp_conn_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
251{
252 log_msg(LVL_DEBUG, "tcp_conn_transmit_segment(%p, %p)", conn, seg);
253
254 seg->wnd = conn->rcv_wnd;
255
256 if ((seg->ctrl & CTL_ACK) != 0)
257 seg->ack = conn->rcv_nxt;
258 else
259 seg->ack = 0;
260
261 tcp_transmit_segment(&conn->ident, seg);
262}
263
264void tcp_transmit_segment(tcp_sockpair_t *sp, tcp_segment_t *seg)
265{
266 log_msg(LVL_DEBUG, "tcp_transmit_segment(%p, %p)", sp, seg);
267
268 log_msg(LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
269 seg->seq, seg->wnd);
270
271 tcp_segment_dump(seg);
272/*
273 tcp_pdu_prepare(conn, seg, &data, &len);
274 tcp_pdu_transmit(data, len);
275*/
276// tcp_rqueue_bounce_seg(sp, seg);
277// tcp_ncsim_bounce_seg(sp, seg);
278
279 tcp_pdu_t *pdu;
280
281 if (tcp_pdu_encode(sp, seg, &pdu) != EOK) {
282 log_msg(LVL_WARN, "Not enough memory. Segment dropped.");
283 return;
284 }
285
286 tcp_transmit_pdu(pdu);
287 tcp_pdu_delete(pdu);
288}
289
290static void retransmit_timeout_func(void *arg)
291{
292 tcp_conn_t *conn = (tcp_conn_t *) arg;
293 tcp_tqueue_entry_t *tqe;
294 tcp_segment_t *rt_seg;
295 link_t *link;
296
297 log_msg(LVL_DEBUG, "### %s: retransmit_timeout_func(%p)", conn->name, conn);
298 link = list_first(&conn->retransmit.list);
299 if (link == NULL) {
300 log_msg(LVL_DEBUG, "Nothing to retransmit");
301 return;
302 }
303
304 tqe = list_get_instance(link, tcp_tqueue_entry_t, link);
305
306 rt_seg = tcp_segment_dup(tqe->seg);
307 if (rt_seg == NULL) {
308 log_msg(LVL_ERROR, "Memory allocation failed.");
309 /* XXX Handle properly */
310 return;
311 }
312
313 log_msg(LVL_DEBUG, "### %s: retransmitting segment", conn->name);
314 tcp_conn_transmit_segment(tqe->conn, rt_seg);
315
316 /* Reset retransmission timer */
317 tcp_tqueue_timer_set(tqe->conn);
318}
319
320/** Set or re-set retransmission timer */
321static void tcp_tqueue_timer_set(tcp_conn_t *conn)
322{
323 log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_set()", conn->name);
324
325 (void) retransmit_timeout_func;
326 fibril_timer_set(conn->retransmit.timer, RETRANSMIT_TIMEOUT,
327 retransmit_timeout_func, (void *) conn);
328}
329
330/** Clear retransmission timer */
331static void tcp_tqueue_timer_clear(tcp_conn_t *conn)
332{
333 log_msg(LVL_DEBUG, "### %s: tcp_tqueue_timer_clear()", conn->name);
334
335 fibril_timer_clear(conn->retransmit.timer);
336}
337
338/**
339 * @}
340 */
Note: See TracBrowser for help on using the repository browser.