source: asp3_tinet_ecnl_rx/trunk/asp3_dcre/tinet/netinet/tcp_output.c@ 337

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

ASP3版ECNLを追加

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