source: EcnlProtoTool/trunk/asp3_dcre/tinet/netinet/tcp_output.c@ 321

Last change on this file since 321 was 321, checked in by coas-nagasima, 7 years ago

文字コードを設定

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-csrc;charset=UTF-8
File size: 35.4 KB
Line 
1/*
2 * TINET (TCP/IP Protocol Stack)
3 *
4 * Copyright (C) 2001-2009 by Dep. of Computer Science and Engineering
5 * Tomakomai National College of Technology, JAPAN
6 *
7 * 上記著作権者は,以下の (1)~(4) の条件か,Free Software Foundation
8 * によって公表されている GNU General Public License の Version 2 に記
9 * 述されている条件を満たす場合に限り,本ソフトウェア(本ソフトウェア
10 * を改変したものを含む.以下同じ)を使用・複製・改変・再配布(以下,
11 * 利用と呼ぶ)することを無償で許諾する.
12 * (1) 本ソフトウェアをソースコードの形で利用する場合には,上記の著作
13 * 権表示,この利用条件および下記の無保証規定が,そのままの形でソー
14 * スコード中に含まれていること.
15 * (2) 本ソフトウェアを,ライブラリ形式など,他のソフトウェア開発に使
16 * 用できる形で再配布する場合には,再配布に伴うドキュメント(利用
17 * 者マニュアルなど)に,上記の著作権表示,この利用条件および下記
18 * の無保証規定を掲載すること.
19 * (3) 本ソフトウェアを,機器に組み込むなど,他のソフトウェア開発に使
20 * 用できない形で再配布する場合には,次の条件を満たすこと.
21 * (a) 再配布に伴うドキュメント(利用者マニュアルなど)に,上記の著
22 * 作権表示,この利用条件および下記の無保証規定を掲載すること.
23 * (4) 本ソフトウェアの利用により直接的または間接的に生じるいかなる損
24 * 害からも,上記著作権者およびTOPPERSプロジェクトを免責すること.
25 *
26 * 本ソフトウェアは,無保証で提供されているものである.上記著作権者お
27 * よびTOPPERSプロジェクトは,本ソフトウェアに関して,その適用可能性も
28 * 含めて,いかなる保証も行わない.また,本ソフトウェアの利用により直
29 * 接的または間接的に生じたいかなる損害に関しても,その責任を負わない.
30 *
31 * @(#) $Id$
32 */
33
34/*
35 * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
36 * The Regents of the University of California. All rights reserved.
37 *
38 * Redistribution and use in source and binary forms, with or without
39 * modification, are permitted provided that the following conditions
40 * are met:
41 * 1. Redistributions of source code must retain the above copyright
42 * notice, this list of conditions and the following disclaimer.
43 * 2. Redistributions in binary form must reproduce the above copyright
44 * notice, this list of conditions and the following disclaimer in the
45 * documentation and/or other materials provided with the distribution.
46 * 3. All advertising materials mentioning features or use of this software
47 * must display the following acknowledgement:
48 * This product includes software developed by the University of
49 * California, Berkeley and its contributors.
50 * 4. Neither the name of the University nor the names of its contributors
51 * may be used to endorse or promote products derived from this software
52 * without specific prior written permission.
53 *
54 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
55 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
56 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
57 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
58 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
59 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
60 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
61 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
62 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
63 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
64 * SUCH DAMAGE.
65 *
66 * @(#)tcp_output.c 8.4 (Berkeley) 5/24/95
67 * $FreeBSD: src/sys/netinet/tcp_output.c,v 1.32.2.2 1999/08/29 16:29:55 peter Exp $
68 */
69
70#ifdef TARGET_KERNEL_ASP
71
72#include <kernel.h>
73#include <sil.h>
74#include <t_syslog.h>
75#include "kernel_cfg.h"
76
77#endif /* of #ifdef TARGET_KERNEL_ASP */
78
79#ifdef TARGET_KERNEL_JSP
80
81#include <s_services.h>
82#include <t_services.h>
83#include "kernel_id.h"
84
85#endif /* of #ifdef TARGET_KERNEL_JSP */
86
87#include <tinet_defs.h>
88#include <tinet_config.h>
89
90#include <net/if.h>
91#include <net/if_ppp.h>
92#include <net/if_loop.h>
93#include <net/ethernet.h>
94#include <net/net.h>
95#include <net/net_buf.h>
96#include <net/net_timer.h>
97#include <net/net_count.h>
98
99#include <netinet/in.h>
100#include <netinet6/in6.h>
101#include <netinet/in_var.h>
102#include <netinet/in_itron.h>
103#include <netinet/ip.h>
104#include <netinet/ip_var.h>
105#include <netinet6/in6_var.h>
106#include <netinet/ip6.h>
107#include <netinet6/ip6_var.h>
108#include <netinet/tcp.h>
109#include <netinet/tcp_timer.h>
110#include <netinet/tcp_var.h>
111#include <netinet/tcp_fsm.h>
112#include <netinet/tcp_seq.h>
113
114#ifdef SUPPORT_TCP
115
116/*
117 * 関数
118 */
119
120static ER send_segment (bool_t *sendalot, T_TCP_CEP *cep, uint_t doff, uint_t win, uint_t len, uint8_t flags);
121static void tcp_output (T_TCP_CEP *cep);
122
123/*
124 * 変数
125 */
126
127/* 出力時のフラグを FSM 状態により選択するための表 */
128
129const static uint8_t tcp_outflags[] = {
130 TCP_FLG_RST | TCP_FLG_ACK, /* 0, クローズ */
131 0, /* 1, 受動オープン */
132 TCP_FLG_SYN, /* 2, 能動オープン、SYN 送信済み */
133 TCP_FLG_SYN | TCP_FLG_ACK, /* 3, SYM を受信し、SYN 送信済み */
134 TCP_FLG_ACK, /* 4, コネクション開設完了 */
135 TCP_FLG_ACK, /* 5, FIN 受信、クローズ待ち */
136 TCP_FLG_FIN | TCP_FLG_ACK, /* 6, 終了して、FIN 送信済み */
137 TCP_FLG_FIN | TCP_FLG_ACK, /* 7, 終了、FIN 交換済み、ACK 待ち */
138 TCP_FLG_FIN | TCP_FLG_ACK, /* 8, FIN 受信、終了、ACK 待ち */
139 TCP_FLG_ACK, /* 9, 終了、FIN 伝達確認受信、FIN待ち */
140 TCP_FLG_ACK, /* 10, 終了、時間待ち */
141 };
142
143/*
144 * send_segment -- TCP 出力処理
145 */
146
147static ER
148send_segment (bool_t *sendalot, T_TCP_CEP *cep, uint_t doff, uint_t win, uint_t len, uint8_t flags)
149{
150 T_NET_BUF *output;
151 T_TCP_HDR *tcph;
152 uint_t optlen;
153 ER error;
154
155#ifdef TCP_CFG_OPT_MSS
156
157 uint8_t *optp;
158
159 if (flags & TCP_FLG_SYN)
160 optlen = TCP_OPT_LEN_MAXSEG;
161 else
162 optlen = 0;
163
164#else/* of #ifdef TCP_CFG_OPT_MSS */
165
166 optlen = 0;
167
168#endif/* of #ifdef TCP_CFG_OPT_MSS */
169
170 NET_COUNT_TCP(net_count_tcp[NC_TCP_SEND_SEGS], 1);
171 NET_COUNT_MIB(tcp_stats.tcpOutSegs, 1);
172
173 /*
174 * セグメント長を、相手の最大受信セグメント長に調整する。
175 * もし、超えている場合は、超えた分を後で送信する。
176 * このため、FIN ビットをクリアする。
177 *
178 * オリジナルでは、t_maxopd を制限長にしているが、
179 * 本実装では、相手の最大受信セグメントにする。
180 */
181 if (len + optlen > cep->maxseg) {
182 flags &= ~TCP_FLG_FIN;
183 len = cep->maxseg - optlen;
184 *sendalot = true;
185 }
186
187 /*
188 * 送信バッファが空になるときは PUSH フラグを設定する。
189 */
190 if (len && doff + len >= cep->swbuf_count)
191 flags |= TCP_FLG_PUSH;
192
193#if defined(TCP_CFG_SWBUF_CSAVE_ONLY)
194
195 if (len > 0 && ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_SEND_READY ||
196 (cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_SENT)) {
197
198 /*
199 * 送信ウインドバッファが開放されないようにして、
200 * ネットワークバッファを出力に移す。
201 */
202 cep->swbufq->flags |= NB_FLG_NOREL_IFOUT;
203 output = cep->swbufq;
204 }
205 else {
206
207 /*
208 * ACK 完了状態で、この関数が呼び出されることもある。
209 * この時は、len を 0 にして、処理を継続する。
210 */
211 len = 0;
212 if ((error = tcp_get_segment(&output, cep, optlen,
213 len, (uint_t)(net_buf_max_siz() - IF_IP_TCP_HDR_SIZE),
214 NBA_SEARCH_ASCENT, TMO_TCP_GET_NET_BUF)) != E_OK) {
215 if (cep->timer[TCP_TIM_REXMT] == 0)
216 cep->timer[TCP_TIM_REXMT] = cep->rxtcur;
217 goto err_ret;
218 }
219 }
220
221#elif defined(TCP_CFG_SWBUF_CSAVE) /* of #if defined(TCP_CFG_SWBUF_CSAVE_ONLY) */
222
223 if (IS_PTR_DEFINED(cep->sbuf)) {
224 if ((error = tcp_get_segment(&output, cep, optlen,
225 len, (uint_t)(net_buf_max_siz() - IF_IP_TCP_HDR_SIZE),
226 NBA_SEARCH_ASCENT, TMO_TCP_GET_NET_BUF)) != E_OK) {
227 if (cep->timer[TCP_TIM_REXMT] == 0)
228 cep->timer[TCP_TIM_REXMT] = cep->rxtcur;
229 goto err_ret;
230 }
231 }
232 else if (len > 0 && ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_SEND_READY ||
233 (cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_SENT)) {
234
235 /*
236 * 送信ウインドバッファが開放されないようにして、
237 * ネットワークバッファを出力に移す。
238 */
239 cep->swbufq->flags |= NB_FLG_NOREL_IFOUT;
240 output = cep->swbufq;
241 }
242 else {
243
244 /*
245 * ACK 完了状態で、この関数が呼び出されることもある。
246 * この時は、len を 0 にして、処理を継続する。
247 */
248 len = 0;
249 if ((error = tcp_get_segment(&output, cep, optlen,
250 len, (uint_t)(net_buf_max_siz() - IF_IP_TCP_HDR_SIZE),
251 NBA_SEARCH_ASCENT, TMO_TCP_GET_NET_BUF)) != E_OK) {
252 if (cep->timer[TCP_TIM_REXMT] == 0)
253 cep->timer[TCP_TIM_REXMT] = cep->rxtcur;
254 goto err_ret;
255 }
256 }
257
258#else /* of #if defined(TCP_CFG_SWBUF_CSAVE_ONLY) */
259
260 if ((error = tcp_get_segment(&output, cep, optlen,
261 len, (uint_t)(net_buf_max_siz() - IF_IP_TCP_HDR_SIZE),
262 NBA_SEARCH_ASCENT, TMO_TCP_GET_NET_BUF)) != E_OK) {
263 if (cep->timer[TCP_TIM_REXMT] == 0)
264 cep->timer[TCP_TIM_REXMT] = cep->rxtcur;
265 goto err_ret;
266 }
267
268#endif /* of #if defined(TCP_CFG_SWBUF_CSAVE_ONLY) */
269
270 /*
271 * TCP オプションの設定を行う。
272 * 本実装では、最大セグメントサイズのみ設定する。
273 */
274 if (flags & TCP_FLG_SYN) {
275 cep->snd_nxt = cep->iss;
276
277#ifdef TCP_CFG_OPT_MSS
278
279 optp = GET_TCP_OPT(output, IF_IP_TCP_HDR_OFFSET);
280 *optp ++ = TCP_OPT_MAXSEG;
281 *optp ++ = TCP_OPT_LEN_MAXSEG;
282 *(uint16_t*)optp = htons(DEF_TCP_RCV_SEG);
283
284#endif/* of #ifdef TCP_CFG_OPT_MSS */
285
286 }
287
288 /* TCP SDU に送信データをコピーする。*/
289
290 if (len > 0) {
291 if (SEQ_LT(cep->snd_nxt, cep->snd_max)) {
292 NET_COUNT_TCP(net_count_tcp[NC_TCP_SEND_REXMIT_SEGS], 1);
293 NET_COUNT_MIB(tcp_stats.tcpRetransSegs, 1);
294 }
295 TCP_READ_SWBUF(cep, output, len, doff);
296 }
297 else {
298 if (cep->flags & TCP_CEP_FLG_ACK_NOW)
299 NET_COUNT_TCP(net_count_tcp[NC_TCP_SEND_ACKS], 1);
300 if (flags & (TCP_FLG_FIN | TCP_FLG_SYN | TCP_FLG_RST))
301 NET_COUNT_TCP(net_count_tcp[NC_TCP_SEND_CNTL_SEGS], 1);
302
303
304#ifdef TCP_CFG_EXTENTIONS
305
306 if (SEQ_LT(cep->snd_up, cep->snd_una))
307 NET_COUNT_TCP(net_count_tcp[NC_TCP_SEND_URG_SEGS], 1);
308
309#endif /* of #ifdef TCP_CFG_EXTENTIONS */
310
311 }
312
313 /*
314 * snd_max: 送信した最大 SEQ
315 * snd_nxt: 次に送信する SEQ
316 *
317 * 相手から FIN を受信し、まだ FIN を送信していないか、
318 * 送るデータがないときは、FIN を相手に届けるため、
319 * セグメントを送信するが、SEQ は進めない。
320 */
321 if ((flags & TCP_FLG_FIN) && (cep->flags & TCP_CEP_FLG_SENT_FIN) &&
322 cep->snd_nxt == cep->snd_max) {
323 cep->snd_nxt --;
324 }
325
326 tcph = GET_TCP_HDR(output, IF_IP_TCP_HDR_OFFSET);
327
328 /*
329 * SEQ、ACK、フラグの設定。
330 */
331 if (len > 0 || (flags & (TCP_FLG_SYN | TCP_FLG_FIN)) || cep->timer[TCP_TIM_PERSIST] != 0)
332 tcph->seq = htonl(cep->snd_nxt);
333 else
334 tcph->seq = htonl(cep->snd_max);
335
336 /*
337 * rcv_nxt: 受信を期待している最小の SEQ
338 */
339 tcph->ack = htonl(cep->rcv_nxt);
340 tcph->flags = flags;
341
342 /*
343 * 受信ウィンドの計算
344 *
345 * rbufsz: 受信用バッファサイズ
346 * maxseg: 相手の最大受信セグメントサイズ
347 */
348 if (win < (cep->rbufsz / 4) && win < cep->maxseg)
349 win = 0;
350
351 /*
352 * rcv_nxt: 受信を期待している最小の SEQ
353 * rcv_adv: 受信を期待している最大の SEQ
354 */
355 if ((int32_t)win < (int32_t)(cep->rcv_adv - cep->rcv_nxt))
356 win = (uint_t)(cep->rcv_adv - cep->rcv_nxt);
357
358 tcph->win = htons(win);
359
360#ifdef TCP_CFG_EXTENTIONS
361
362 /*
363 * 緊急ポインタの設定
364 */
365 if (SEQ_GT(cep->snd_up, cep->snd_nxt)) {
366 if (TCP_CFG_URG_OFFSET)
367 tcph->urp = htons((uint16_t)(cep->snd_up - cep->snd_nxt));
368 else
369 tcph->urp = htons((uint16_t)(cep->snd_up - cep->snd_nxt - 1));
370 tcph->flags |= TCP_FLG_URG;
371 }
372 else
373 cep->snd_up = cep->snd_una;
374
375#endif /* of #ifdef TCP_CFG_EXTENTIONS */
376
377 /*
378 * チェックサムを設定する。
379 */
380 tcph->sum = 0;
381 tcph->sum = IN_CKSUM(output, IPPROTO_TCP, (uint_t)GET_TCP_HDR_OFFSET(output),
382 GET_TCP_HDR_SIZE2(output, IF_IP_TCP_HDR_OFFSET) + len);
383
384 /* ネットワークバッファ長を調整する。*/
385 output->len = (uint16_t)(GET_IF_IP_TCP_HDR_SIZE2(output, IF_IP_TCP_HDR_OFFSET) + len);
386
387 /*
388 * タイマの調整
389 */
390 if ((cep->flags & TCP_CEP_FLG_FORCE) == 0 || cep->timer[TCP_TIM_PERSIST] == 0) {
391 T_TCP_SEQ startseq = cep->snd_nxt;
392
393 /*
394 * 次に送信する SEQ (snd_nxt) を今回送信するデータ数分進める。
395 */
396 if (flags & TCP_FLG_SYN)
397 cep->snd_nxt ++;
398 if (flags & TCP_FLG_FIN) {
399 cep->flags |= TCP_CEP_FLG_SENT_FIN;
400 cep->snd_nxt ++;
401 }
402
403 cep->snd_nxt += len;
404
405 /*
406 * 次に送信する SEQ (snd_nxt) が
407 * 送信した最大 SEQ (snd_max) より進んでいれば、
408 * 送信した最大 SEQ (snd_max) を更新する。
409 */
410 if (SEQ_GT(cep->snd_nxt, cep->snd_max)) {
411 cep->snd_max = cep->snd_nxt;
412 /*
413 * もし、往復時間計測を行っていなければ、
414 * この送信に時間を合わせる。
415 */
416 if (cep->rtt == 0) {
417 cep->rtt = 1;
418 cep->rtseq = startseq; /* 更新前の cep->snd_nxt */
419 }
420 }
421
422 /*
423 * もし設定されていないか、ACK または保留が発生していなければ、
424 * 再送タイマを設定する。設定する初期値は、
425 * 「滑らかな往復時間 + 2 × 往復時間変動」である。
426 * 再送時間のバックオフに使われるシフトカウントも初期化する。
427 */
428 if (cep->timer[TCP_TIM_REXMT] == 0 && cep->snd_nxt != cep->snd_una) {
429 cep->timer[TCP_TIM_REXMT] = cep->rxtcur;
430 if (cep->timer[TCP_TIM_PERSIST] != 0) {
431 cep->timer[TCP_TIM_PERSIST] = 0;
432 cep->rxtshift = 0;
433 }
434 }
435 }
436
437 /*
438 * 次に送信する SEQ (snd_nxt) + 今回送信するデータ数 (len) が
439 * 送信した最大 SEQ (snd_max) より進んでいれば、
440 * 送信した最大 SEQ (snd_max) を更新する。
441 */
442 else if (SEQ_GT(cep->snd_nxt + len, cep->snd_max))
443 cep->snd_max = cep->snd_nxt + len;
444
445#ifdef TCP_CFG_SWBUF_CSAVE
446
447 if ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_SEND_READY)
448 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_SENT;
449
450#endif /* of #ifdef TCP_CFG_SWBUF_CSAVE */
451
452#ifdef TCP_CFG_TRACE
453
454 tcp_output_trace(output, cep);
455
456#endif /* of #ifdef TCP_CFG_TRACE */
457
458 /* ネットワーク層 (IP) の出力関数を呼び出す。*/
459 if ((error = IP_OUTPUT(output, TMO_TCP_OUTPUT)) != E_OK)
460 goto err_ret;
461
462 /*
463 * 相手に伝えたウィンドウサイズ (win) が 0 以上で、
464 * 受信を期待している最小の SEQ (rcv_nxt) + win が
465 * 受信を期待している最大の SEQ (rcv_adv) より進んでいれば
466 * 受信を期待している最大の SEQ を更新する。
467 */
468 if (win > 0 && SEQ_GT(cep->rcv_nxt + win, cep->rcv_adv)) {
469 cep->rcv_adv = cep->rcv_nxt + win;
470 }
471
472 /*
473 * 最後に送信した ACK (last_ack_sent) を更新する。
474 */
475 cep->last_ack_sent = cep->rcv_nxt;
476
477 /*
478 * フラグの設定を行う。
479 */
480 cep->flags &= ~(TCP_CEP_FLG_ACK_NOW | TCP_CEP_FLG_DEL_ACK);
481 if (cep->flags & TCP_CEP_FLG_FORCE_CLEAR)
482 cep->flags &= ~(TCP_CEP_FLG_FORCE | TCP_CEP_FLG_FORCE_CLEAR);
483
484 return E_OK;
485
486err_ret:
487 /*
488 * 以下に関係しないフラグをクリアーする。
489 * ・送受信ウィンドバッファの省コピー機能
490 * ・動的な通信端点の生成・削除機能
491 */
492 cep->flags &= (TCP_CEP_FLG_WBCS_NBUF_REQ | TCP_CEP_FLG_WBCS_MASK |
493 TCP_CEP_FLG_DYNAMIC | TCP_CEP_FLG_VALID);
494
495 return error;
496 }
497
498/*
499 * tcp_output -- TCP 出力処理
500 */
501
502void
503tcp_output (T_TCP_CEP *cep)
504{
505 bool_t sendalot = true, idle;
506 ER error = E_OK;
507 int32_t len;
508 uint_t doff, win;
509 uint8_t flags;
510
511 /*
512 * snd_una: 未確認の最小送信 SEQ または、確認された最大送信 SEQ
513 * snd_max: 送信した最大 SEQ
514 */
515 idle = (cep->snd_max == cep->snd_una);
516
517 /*
518 * idle: アイドル時間
519 * rxtcur: 現在の再送タイムアウト
520 */
521 if (idle && cep->idle >= cep->rxtcur)
522
523 /*
524 * snd_cwnd: 輻輳ウィンドサイズ
525 * maxseg : 相手の最大受信セグメントサイズ
526 *
527 * 長時間アイドルだったのでスロースタート制御に設定する。
528 */
529 cep->snd_cwnd = cep->maxseg;
530
531 while (error == E_OK && sendalot) {
532 sendalot = false;
533
534 /*
535 * snd_nxt: 次に送信する SEQ、この時点では、前回送信した SEQ
536 * snd_una: 未確認の最小送信 SEQ、または確認された最大送信 SEQ
537 *
538 * doff: 送信を開始するオフセット。
539 * swbuf_count (送信バッファにあるオクテット数)
540 * 0 V
541 * +-------------------------------------------+
542 * | sbuf |
543 * +-------------------------------------------+
544 * ^ ^
545 * |<------------->snd_nxt (前回送信した SEQ)
546 * | doff
547 * snd_una (まだ確認されていない)
548 */
549 doff = (uint_t)(cep->snd_nxt - cep->snd_una);
550
551 /*
552 * snd_wnd: 相手の受信可能ウィンドサイズ
553 * snd_cwnd: 輻輳ウィンドサイズ
554 *
555 * win: どちらか小さいウィンドサイズに設定する。
556 */
557 win = cep->snd_wnd < cep->snd_cwnd ? cep->snd_wnd : cep->snd_cwnd;
558
559 /* 出力フラグの設定 */
560 flags = tcp_outflags[cep->fsm_state];
561 if (cep->flags & TCP_CEP_FLG_NEED_FIN)
562 flags |= TCP_FLG_FIN;
563 if (cep->flags & TCP_CEP_FLG_NEED_SYN)
564 flags |= TCP_FLG_SYN;
565 if (cep->flags & TCP_CEP_FLG_FORCE) {
566
567 /*
568 * もし、送信ウインドサイズ (win) が 0 なら 1 オクテット送信する。
569 * そうでなければ、持続タイムアウトをキャンセルし、
570 * 再送信回数 (rxtshift) を 0 にする。
571 */
572 if (win == 0) {
573
574 /*
575 * doff: 送信するオクテット数。
576 * swbuf_count: 送信バッファの使用中サイズ
577 *
578 * 送信バッファに残っているオクテットが、これから
579 * 送信しようとしているオクテット数より多ければ
580 * FIN フラグをクリアする。
581 */
582 if (doff < cep->swbuf_count)
583 flags &=~TCP_FLG_FIN;
584 win = 1;
585 }
586 else {
587 /*
588 * TCP_TIM_PERSIST: 持続タイマ
589 * rxtshift: 再送信回数の log(2)
590 */
591 cep->timer[TCP_TIM_PERSIST] = 0;
592 cep->rxtshift = 0;
593 }
594 }
595
596 /*
597 * len: 今回送信するオクテット数
598 * swbuf_count (送信バッファにあるオクテット数)
599 * |
600 * 0 V
601 * +-------------------------------------------+
602 * | sbuf | |
603 * +-------------------------------------------+
604 * ^ ^<------------->
605 * | | len
606 * |<------------->snd_nxt (前回送信した SEQ)
607 * | doff
608 * snd_una (まだ確認されていない)
609 */
610 if (cep->swbuf_count < win)
611 len = (int32_t)cep->swbuf_count - doff;
612 else
613 len = (int32_t)win - doff;
614
615 /*
616 * すでに送信されていれば、SYN ビットをオフする。
617 * しかし、以下の条件では送信を控える。
618 *
619 * ・状態が SYN 送信。
620 * ・セグメントがデータを含んでいる。
621 */
622 if ((flags & TCP_FLG_SYN) && SEQ_GT(cep->snd_nxt, cep->snd_una)) {
623 flags &= ~TCP_FLG_SYN;
624 doff --; /* -1 は SYN フラグ分 */
625 len ++; /* +1 は SYN フラグ分 */
626 if (len > 0 && cep->fsm_state == TCP_FSM_SYN_SENT)
627 break;
628 }
629
630 if (flags & TCP_FLG_SYN) {
631 len = 0;
632 flags &= ~TCP_FLG_FIN;
633 }
634
635 if (len < 0) {
636
637 /*
638 * len が 0 以下なら、0 に設定する。
639 * もし、送信ウィンドウサイズが 0 なら、
640 * 再送信タイマをキャンセルし、
641 * 前回送信した SEQ (snd_nxt) を
642 * 確認された最大送信 SEQ (snd_una) に戻す。
643 * そして、持続タイマーが止まっていれば、再設定する。
644 */
645 len = 0;
646 if (win == 0) {
647 cep->timer[TCP_TIM_REXMT] = 0;
648 cep->rxtshift = 0;
649 cep->snd_nxt = cep->snd_una;
650 if (cep->timer[TCP_TIM_PERSIST] == 0)
651 tcp_set_persist_timer(cep);
652 }
653 }
654
655
656 /*
657 * 今回送信するオクテット数 (len) は、
658 * 相手の最大受信セグメントサイズ (maxseg) を超えないようにする。
659 */
660 if (len > cep->maxseg) {
661 len = cep->maxseg;
662 sendalot = true;
663 }
664
665 /*
666 * swbuf_count (送信バッファにあるオクテット数)
667 * |
668 * 0 V
669 * +-------------------------------------------+
670 * | sbuf | |
671 * +-------------------------------------------+
672 * ^ ^<------------->
673 * | | len
674 * |<------------->snd_nxt (前回送信した SEQ)
675 * | doff
676 * snd_una (まだ確認されていない)
677 *
678 * 今回送信後も、送信バッファにデータが残っていれば
679 * FIN フラグをクリアする。
680 */
681 if (SEQ_LT(cep->snd_nxt + len, cep->snd_una + cep->swbuf_count))
682 flags &= ~TCP_FLG_FIN;
683
684 /*
685 * ここから win は、受信ウィンドウサイズ。
686 * 受信バッファの空き容量
687 */
688 win = cep->rbufsz - cep->rwbuf_count;
689
690 /*
691 * 愚かなウィンドウ・シンドロームの回避処理 (送信側)
692 *
693 * 以下の条件で、送信を行う。
694 *
695 * ・フルサイズ (maxseg) のセグメントを送ることができる。
696 * ・相手の最大の受信ウィンドウサイズの 1/2 のデータを
697 * 送ることができる。
698 * ・送信バッファを空にでき、アイドルか非遅延オプションが有効なとき。
699 */
700 if (len) {
701
702 /*
703 * 今回送信するオクテット数 (len) が
704 * 相手の最大受信セグメントサイズ (maxseg) に
705 * 一致するときは送信する。
706 */
707 if (len == cep->maxseg) {
708 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
709 continue;
710 }
711
712 /*
713 * 今回の送信で、送信バッファを空にでき、
714 * アイドルか非 PUSH オプションが有効なとき。
715 */
716 if ((idle || (cep->flags & TCP_CEP_FLG_NO_DELAY)) &&
717 (cep->flags & TCP_CEP_FLG_NO_PUSH) == 0 &&
718 len + doff >= cep->swbuf_count) {
719 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
720 continue;
721 }
722
723 /*
724 * max_sndwnd: 今までの最大送信ウィンドサイズ
725 * snd_nxt: 次に送信する SEQ
726 * snd_max: 送信した最大 SEQ
727 *
728 * 次の条件では送信を行う。
729 *
730 * ・強制送信フラグがセットされている。
731 * ・データ長が相手の最大の受信ウィンドウサイズの 1/2 以上で、
732 * 相手の最大の受信ウィンドウサイズが 0 より大きい。
733 * ・次に送信する SEQ が送信した最大 SEQ より小さい、
734 * つまり、再送するとき。
735 */
736 if ((cep->flags & TCP_CEP_FLG_FORCE) ||
737 (len >= cep->max_sndwnd / 2 && cep->max_sndwnd > 0) ||
738 SEQ_LT(cep->snd_nxt, cep->snd_max)) {
739 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
740 continue;
741 }
742 }
743
744
745 /*
746 * 愚かなウィンドウ・シンドロームの回避処理 (受信側)
747 *
748 * ウィンドウサイズがフルサイズの 2 倍のセグメント、あるいは
749 * 受信バッファ容量の 1/2 の、いずれか小さいほうの
750 * サイズで増加される場合は、ウィンドウサイズの更新を行う。
751 */
752 if (win > 0) {
753 long adv;
754
755 /*
756 * win: 受信バッファの空き容量
757 * MAX_TCP_WIN_SIZE: TCP ヘッダの win フィールドに設定できる最大値
758 * rcv_adv: 受信を期待している最大の SEQ
759 * rcv_nxt: 受信を期待している最小の SEQ
760 */
761 if (win < MAX_TCP_WIN_SIZE)
762 adv = win - (cep->rcv_adv - cep->rcv_nxt);
763 else
764 adv = MAX_TCP_WIN_SIZE - (cep->rcv_adv - cep->rcv_nxt);
765
766 if (adv >= (long)(cep->maxseg * 2) ||
767 adv * 2 >= (long) cep->rbufsz) {
768 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
769 continue;
770 }
771 }
772
773 /*
774 * ACK を送信する。
775 */
776 if (cep->flags & TCP_CEP_FLG_ACK_NOW) {
777 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
778 continue;
779 }
780
781 if ( (flags & TCP_FLG_RST) ||
782 ((flags & TCP_FLG_SYN) && (cep->flags & TCP_CEP_FLG_NEED_SYN) == 0)) {
783 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
784 continue;
785 }
786
787#ifdef TCP_CFG_EXTENTIONS
788
789 if (SEQ_GT(cep->snd_up, cep->snd_una)) {
790 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
791 continue;
792 }
793
794#endif /* of #ifdef TCP_CFG_EXTENTIONS */
795
796 /*
797 * snd_nxt: 次に送信する SEQ
798 * snd_una: 未確認の最小送信 SEQ、または確認された最大送信 SEQ
799 *
800 * 相手から FIN を受信し、まだ FIN を送信していないか、
801 * 送るデータがないときは、FIN を相手に届けるため、
802 * セグメントを送信する。
803 */
804 if ((flags & TCP_FLG_FIN) &&
805 ((cep->flags & TCP_CEP_FLG_SENT_FIN) == 0 || cep->snd_nxt == cep->snd_una)) {
806 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
807 continue;
808 }
809
810 /*
811 * 送信すべきデータがあり、再送タイマと持続タイマが切れているときは
812 * 持続タイマを設定する。
813 */
814 if (cep->swbuf_count && cep->timer[TCP_TIM_REXMT ] == 0 &&
815 cep->timer[TCP_TIM_PERSIST] == 0) {
816 cep->rxtshift = 0;
817 tcp_set_persist_timer(cep);
818 break;
819 }
820
821 }
822 }
823
824#ifdef TCP_CFG_SWBUF_CSAVE
825
826/*
827 * tcptsk_alloc_swbufq -- 送信ウィンドバッファ割り当て
828 */
829
830static void
831tcptsk_alloc_swbufq (T_TCP_CEP *cep)
832{
833 ER error;
834 uint_t win;
835
836 /*
837 * snd_wnd: 相手の受信可能ウィンドサイズ
838 * snd_cwnd: 輻輳ウィンドサイズ
839 *
840 * win: どちらか小さいウィンドサイズに設定する。
841 */
842 win = cep->snd_wnd < cep->snd_cwnd ? cep->snd_wnd : cep->snd_cwnd;
843
844 /*
845 * 相手の受信ウィンドが閉じている場合は、開くまで待機する。
846 */
847 if (win == 0) {
848 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_WOPEN_PEND;
849 }
850 else {
851
852#ifdef TCP_CFG_NON_BLOCKING
853
854 /* ノンブロッキングコール */
855 if (!IS_PTR_DEFINED(cep->callback)) {
856 syslog(LOG_WARNING, "[TCP] no call back, CEP: %d.", GET_TCP_CEPID(cep));
857
858 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
859 cep->snd_tskid = TA_NULL;
860 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
861 return;
862 }
863
864#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
865
866 if ((error = tcp_get_segment(&cep->swbufq, cep, 0,
867 (uint_t) TCP_CFG_SWBUF_CSAVE_MIN_SIZE,
868 (uint_t)(TCP_CFG_SWBUF_CSAVE_MAX_SIZE - IF_IP_TCP_HDR_SIZE),
869 (ATR)(NBA_SEARCH_DESCENT |
870 NBA_RESERVE_TCP |
871 (GET_TCP_CEPID(cep) & NBA_ID_MASK)), TMO_POL)) != E_OK) {
872
873 /* ネットワークバッファを予約する。*/
874 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_NBUF_PEND;
875 }
876 else {
877
878 /* 送信ウィンドバッファを初期化する。*/
879 tcp_init_swbuf(cep);
880
881#ifdef TCP_CFG_NON_BLOCKING
882
883 if (cep->snd_nblk_tfn == TFN_TCP_GET_BUF) {
884
885 uint_t len;
886
887 /* 送信ウィンドバッファの書き込みアドレスを設定する。*/
888 len = TCP_GET_SWBUF_ADDR(cep, cep->snd_p_buf);
889
890#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
891
892 /* コールバック関数を呼び出す。*/
893 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)(uint32_t)len);
894
895#else /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
896
897 /* コールバック関数を呼び出す。*/
898 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&len);
899
900#endif /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
901
902
903 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
904 cep->snd_tskid = TA_NULL;
905 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
906 }
907
908 else { /* cep->snd_nblk_tfn == TFN_TCP_SND_DAT || */
909 /* cep->snd_nblk_tfn == TFN_TCP_SND_OOB */
910
911 uint_t len;
912
913 /* 送信ウィンドバッファにデータを書き込む。*/
914 len = TCP_WRITE_SWBUF(cep, cep->snd_data, (uint_t)cep->snd_len);
915
916#ifdef TCP_CFG_EXTENTIONS
917
918 /* 送信緊急ポインタを設定する。*/
919 if (cep->snd_nblk_tfn == TFN_TCP_SND_OOB)
920 cep->snd_up = cep->snd_una + len;
921
922#endif /* of #ifdef TCP_CFG_EXTENTIONS */
923
924 /* フラグを、送信可能に設定し、強制的に送信する。*/
925 cep->flags |= TCP_CEP_FLG_FORCE | TCP_CEP_FLG_FORCE_CLEAR | TCP_CEP_FLG_POST_OUTPUT;
926#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
927
928 /* コールバック関数を呼び出す。*/
929 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)(uint32_t)len);
930
931#else /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
932
933 /* コールバック関数を呼び出す。*/
934 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&len);
935
936#endif /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
937
938
939 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
940 cep->snd_tskid = TA_NULL;
941 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
942 }
943
944#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
945
946 }
947 }
948 }
949
950/*
951 * tcptsk_free_swbufq -- 送信ウィンドバッファ開放
952 */
953
954static void
955tcptsk_free_swbufq (T_TCP_CEP *cep)
956{
957 /*
958 * 受信確認が完了し、ネットワークインタフェースからの
959 * 出力も完了したときは、送信ウィンドバッファキューを解放する。
960 */
961
962 /* 送信ウィンドバッファの使用中サイズをリセットする。*/
963 cep->swbuf_count = 0;
964
965 /* 送信ウィンドバッファキューのネットワークバッファを解放する。*/
966 syscall(rel_net_buf(cep->swbufq));
967 cep->swbufq = NULL;
968
969 /* フラグを空きに設定する。*/
970 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_FREE;
971
972 /* 送信ウィンドバッファに空きができたことを知らせる。*/
973 syscall(set_flg(cep->snd_flgid, TCP_CEP_EVT_SWBUF_READY));
974
975 /* 送信ウィンドバッファの空き待ちのときは、TCP 出力タスクを起動する。*/
976 if ((cep->flags & TCP_CEP_FLG_WBCS_NBUF_REQ) != 0) {
977 sig_sem(SEM_TCP_POST_OUTPUT);
978 }
979 }
980
981#endif /* of #ifdef TCP_CFG_SWBUF_CSAVE */
982
983/*
984 * TCP 出力タスク
985 */
986
987void
988tcp_output_task (intptr_t exinf)
989{
990 static int_t last_ix = 0;
991
992 T_TCP_CEP *cep;
993 ID tskid;
994 int_t ix, sel_ix;
995
996 get_tid(&tskid);
997 syslog(LOG_NOTICE, "[TCP OUTPUT:%d] started.", tskid);
998
999 tcp_init();
1000
1001#ifdef SUPPORT_INET6
1002
1003 /* IPv6 のステートレス・アドレス自動設定を実行する。*/
1004 in6_if_up(IF_GET_IFNET());
1005
1006#endif /* of #ifdef SUPPORT_INET6 */
1007
1008 while (true) {
1009
1010 /* 出力がポストされるまで待つ。*/
1011 syscall(wai_sem(SEM_TCP_POST_OUTPUT));
1012
1013 if (++ last_ix == tmax_tcp_cepid)
1014 last_ix = 0;
1015 sel_ix = ix = last_ix;
1016 do {
1017 cep = &tcp_cep[ix];
1018
1019#ifdef TCP_CFG_SWBUF_CSAVE
1020
1021 if ((cep->flags & TCP_CEP_FLG_WBCS_NBUF_REQ) != 0 &&
1022 ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_FREE ||
1023 (cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_NBUF_RSVD)) {
1024 tcptsk_alloc_swbufq(cep);
1025 sel_ix = ix;
1026 }
1027
1028 if ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_ACKED &&
1029 (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) == 0) {
1030 tcptsk_free_swbufq(cep);
1031 sel_ix = ix;
1032 }
1033
1034 /*
1035 * ネットワークインタフェースから送信が終わっていないときは、
1036 * 送信を予約する。
1037 */
1038 if (cep->flags & TCP_CEP_FLG_POST_OUTPUT &&
1039 (cep->flags & TCP_CEP_FLG_WBCS_MASK) >= TCP_CEP_FLG_WBCS_SENT) {
1040 syscall(wai_sem(cep->semid_lock));
1041 if (cep->swbufq == NULL)
1042 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1043 else if (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) {
1044 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1045 cep->flags |= TCP_CEP_FLG_RESERVE_OUTPUT;
1046 }
1047 syscall(sig_sem(cep->semid_lock));
1048 }
1049
1050 /*
1051 * 送信予約中に、ネットワークインタフェースから送信が終了したら、
1052 * 送信を開始する。ただし、完全に送信が終了したときは何もしない。
1053 */
1054 if (cep->flags & TCP_CEP_FLG_RESERVE_OUTPUT) {
1055 syscall(wai_sem(cep->semid_lock));
1056 if (cep->swbufq != NULL && (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) == 0) {
1057 cep->flags |= TCP_CEP_FLG_POST_OUTPUT;
1058 }
1059 syscall(sig_sem(cep->semid_lock));
1060 cep->flags &= ~TCP_CEP_FLG_RESERVE_OUTPUT;
1061 }
1062
1063#endif /* of #ifdef TCP_CFG_SWBUF_CSAVE */
1064
1065 if (cep->flags & TCP_CEP_FLG_POST_OUTPUT) {
1066
1067 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1068
1069#ifdef TCP_CFG_NON_BLOCKING
1070
1071 if (cep->snd_nblk_tfn == TFN_TCP_CON_CEP && cep->myaddr.portno == TCP_PORTANY) {
1072 ER error;
1073
1074 /*
1075 * tcp_con_cep のノンブロッキングコールで、
1076 * 未割当のの場合は、ポート番号を割り当てる。
1077 * p_myaddr が NADR (-1) か、
1078 * 自ポート番号が TCP_PORTANY なら、自動で割り当てる。
1079 */
1080 if (cep->p_myaddr == NADR || cep->p_myaddr->portno == TCP_PORTANY)
1081 tcp_alloc_auto_port(cep);
1082 else if ((error = tcp_alloc_port(cep, cep->p_myaddr->portno)) != E_OK) {
1083
1084 if (IS_PTR_DEFINED(cep->callback))
1085
1086#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
1087
1088 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)error);
1089
1090#else /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
1091
1092 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&error);
1093
1094#endif /* of #ifdef TCP_CFG_NON_BLOCKING_COMPAT14 */
1095
1096 else
1097 syslog(LOG_WARNING, "[TCP] no call back, CEP: %d.", GET_TCP_CEPID(cep));
1098
1099 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
1100 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
1101 cep->snd_tskid = TA_NULL;
1102 continue;
1103 }
1104 }
1105
1106#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
1107
1108 tcp_output(cep);
1109
1110 if (cep->flags & TCP_CEP_FLG_CLOSE_AFTER_OUTPUT) {
1111 /* コネクションを閉じる。*/
1112 tcp_close(cep);
1113 cep->flags &= ~TCP_CEP_FLG_CLOSE_AFTER_OUTPUT;
1114 }
1115
1116 if (cep->flags & TCP_CEP_FLG_RESTORE_NEXT_OUTPUT) {
1117 /* snd_nxt を元に戻す。*/
1118 if (SEQ_GT(cep->snd_old_nxt, cep->snd_nxt))
1119 cep->snd_nxt = cep->snd_old_nxt;
1120 cep->flags &= ~TCP_CEP_FLG_RESTORE_NEXT_OUTPUT;
1121 }
1122
1123 sel_ix = ix;
1124 }
1125
1126 if (++ ix == tmax_tcp_cepid)
1127 ix = 0;
1128 } while (ix != last_ix);
1129
1130 /* 次回は、処理した通信端点を後回しにする。*/
1131 last_ix = sel_ix;
1132 }
1133 }
1134
1135#endif /* of #ifdef SUPPORT_TCP */
Note: See TracBrowser for help on using the repository browser.