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

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

TINETとSocket APIなどを更新

  • 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 tcph->urp = 0;
373 cep->snd_up = cep->snd_una;
374 }
375
376#endif /* of #ifdef TCP_CFG_EXTENTIONS */
377
378 /*
379 * チェックサムを設定する。
380 */
381 tcph->sum = 0;
382 tcph->sum = IN_CKSUM(output, IPPROTO_TCP, hdr_offset, GET_TCP_HDR_SIZE(output, hdr_offset) + len);
383
384 /* ネットワークバッファ長を調整する。*/
385 output->len = (uint16_t)(GET_IF_IP_TCP_HDR_SIZE(output, 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_NOT_CLEAR;
493
494 return error;
495 }
496
497/*
498 * tcp_output -- TCP 出力処理
499 */
500
501void
502tcp_output (T_TCP_CEP *cep)
503{
504 bool_t sendalot = true, idle;
505 ER error = E_OK;
506 int32_t len;
507 uint_t doff, win;
508 uint8_t flags;
509
510 /*
511 * snd_una: 未確認の最小送信 SEQ または、確認された最大送信 SEQ
512 * snd_max: 送信した最大 SEQ
513 */
514 idle = (cep->snd_max == cep->snd_una);
515
516 /*
517 * idle: アイドル時間
518 * rxtcur: 現在の再送タイムアウト
519 */
520 if (idle && cep->idle >= cep->rxtcur)
521
522 /*
523 * snd_cwnd: 輻輳ウィンドサイズ
524 * maxseg : 相手の最大受信セグメントサイズ
525 *
526 * 長時間アイドルだったのでスロースタート制御に設定する。
527 */
528 cep->snd_cwnd = cep->maxseg;
529
530 while (error == E_OK && sendalot) {
531 sendalot = false;
532
533 /*
534 * snd_nxt: 次に送信する SEQ、この時点では、前回送信した SEQ
535 * snd_una: 未確認の最小送信 SEQ、または確認された最大送信 SEQ
536 *
537 * doff: 送信を開始するオフセット。
538 * swbuf_count (送信バッファにあるオクテット数)
539 * 0 V
540 * +-------------------------------------------+
541 * | sbuf |
542 * +-------------------------------------------+
543 * ^ ^
544 * |<------------->snd_nxt (前回送信した SEQ)
545 * | doff
546 * snd_una (まだ確認されていない)
547 */
548 doff = (uint_t)(cep->snd_nxt - cep->snd_una);
549
550 /*
551 * snd_wnd: 相手の受信可能ウィンドサイズ
552 * snd_cwnd: 輻輳ウィンドサイズ
553 *
554 * win: どちらか小さいウィンドサイズに設定する。
555 */
556 win = cep->snd_wnd < cep->snd_cwnd ? cep->snd_wnd : cep->snd_cwnd;
557
558 /* 出力フラグの設定 */
559 flags = tcp_outflags[cep->fsm_state];
560 if (cep->flags & TCP_CEP_FLG_NEED_FIN)
561 flags |= TCP_FLG_FIN;
562 if (cep->flags & TCP_CEP_FLG_NEED_SYN)
563 flags |= TCP_FLG_SYN;
564 if (cep->flags & TCP_CEP_FLG_FORCE) {
565
566 /*
567 * もし、送信ウインドサイズ (win) が 0 なら 1 オクテット送信する。
568 * そうでなければ、持続タイムアウトをキャンセルし、
569 * 再送信回数 (rxtshift) を 0 にする。
570 */
571 if (win == 0) {
572
573 /*
574 * doff: 送信するオクテット数。
575 * swbuf_count: 送信バッファの使用中サイズ
576 *
577 * 送信バッファに残っているオクテットが、これから
578 * 送信しようとしているオクテット数より多ければ
579 * FIN フラグをクリアする。
580 */
581 if (doff < cep->swbuf_count)
582 flags &=~TCP_FLG_FIN;
583 win = 1;
584 }
585 else {
586 /*
587 * TCP_TIM_PERSIST: 持続タイマ
588 * rxtshift: 再送信回数の log(2)
589 */
590 cep->timer[TCP_TIM_PERSIST] = 0;
591 cep->rxtshift = 0;
592 }
593 }
594
595 /*
596 * len: 今回送信するオクテット数
597 * swbuf_count (送信バッファにあるオクテット数)
598 * |
599 * 0 V
600 * +-------------------------------------------+
601 * | sbuf | |
602 * +-------------------------------------------+
603 * ^ ^<------------->
604 * | | len
605 * |<------------->snd_nxt (前回送信した SEQ)
606 * | doff
607 * snd_una (まだ確認されていない)
608 */
609 if (cep->swbuf_count < win)
610 len = (int32_t)cep->swbuf_count - doff;
611 else
612 len = (int32_t)win - doff;
613
614 /*
615 * すでに送信されていれば、SYN ビットをオフする。
616 * しかし、以下の条件では送信を控える。
617 *
618 * ・状態が SYN 送信。
619 * ・セグメントがデータを含んでいる。
620 */
621 if ((flags & TCP_FLG_SYN) && SEQ_GT(cep->snd_nxt, cep->snd_una)) {
622 flags &= ~TCP_FLG_SYN;
623 doff --; /* -1 は SYN フラグ分 */
624 len ++; /* +1 は SYN フラグ分 */
625 if (len > 0 && cep->fsm_state == TCP_FSM_SYN_SENT)
626 break;
627 }
628
629 if (flags & TCP_FLG_SYN) {
630 len = 0;
631 flags &= ~TCP_FLG_FIN;
632 }
633
634 if (len < 0) {
635
636 /*
637 * len が 0 以下なら、0 に設定する。
638 * もし、送信ウィンドウサイズが 0 なら、
639 * 再送信タイマをキャンセルし、
640 * 前回送信した SEQ (snd_nxt) を
641 * 確認された最大送信 SEQ (snd_una) に戻す。
642 * そして、持続タイマーが止まっていれば、再設定する。
643 */
644 len = 0;
645 if (win == 0) {
646 cep->timer[TCP_TIM_REXMT] = 0;
647 cep->rxtshift = 0;
648 cep->snd_nxt = cep->snd_una;
649 if (cep->timer[TCP_TIM_PERSIST] == 0)
650 tcp_set_persist_timer(cep);
651 }
652 }
653
654
655 /*
656 * 今回送信するオクテット数 (len) は、
657 * 相手の最大受信セグメントサイズ (maxseg) を超えないようにする。
658 */
659 if (len > cep->maxseg) {
660 len = cep->maxseg;
661 sendalot = true;
662 }
663
664 /*
665 * swbuf_count (送信バッファにあるオクテット数)
666 * |
667 * 0 V
668 * +-------------------------------------------+
669 * | sbuf | |
670 * +-------------------------------------------+
671 * ^ ^<------------->
672 * | | len
673 * |<------------->snd_nxt (前回送信した SEQ)
674 * | doff
675 * snd_una (まだ確認されていない)
676 *
677 * 今回送信後も、送信バッファにデータが残っていれば
678 * FIN フラグをクリアする。
679 */
680 if (SEQ_LT(cep->snd_nxt + len, cep->snd_una + cep->swbuf_count))
681 flags &= ~TCP_FLG_FIN;
682
683 /*
684 * ここから win は、受信ウィンドウサイズ。
685 * 受信バッファの空き容量
686 */
687 win = cep->rbufsz - cep->rwbuf_count;
688
689 /*
690 * 愚かなウィンドウ・シンドロームの回避処理 (送信側)
691 *
692 * 以下の条件で、送信を行う。
693 *
694 * ・フルサイズ (maxseg) のセグメントを送ることができる。
695 * ・相手の最大の受信ウィンドウサイズの 1/2 のデータを
696 * 送ることができる。
697 * ・送信バッファを空にでき、アイドルか非遅延オプションが有効なとき。
698 */
699 if (len) {
700
701 /*
702 * 今回送信するオクテット数 (len) が
703 * 相手の最大受信セグメントサイズ (maxseg) に
704 * 一致するときは送信する。
705 */
706 if (len == cep->maxseg) {
707 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
708 continue;
709 }
710
711 /*
712 * 今回の送信で、送信バッファを空にでき、
713 * アイドルか非 PUSH オプションが有効なとき。
714 */
715 if ((idle || (cep->flags & TCP_CEP_FLG_NO_DELAY)) &&
716 (cep->flags & TCP_CEP_FLG_NO_PUSH) == 0 &&
717 len + doff >= cep->swbuf_count) {
718 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
719 continue;
720 }
721
722 /*
723 * max_sndwnd: 今までの最大送信ウィンドサイズ
724 * snd_nxt: 次に送信する SEQ
725 * snd_max: 送信した最大 SEQ
726 *
727 * 次の条件では送信を行う。
728 *
729 * ・強制送信フラグがセットされている。
730 * ・データ長が相手の最大の受信ウィンドウサイズの 1/2 以上で、
731 * 相手の最大の受信ウィンドウサイズが 0 より大きい。
732 * ・次に送信する SEQ が送信した最大 SEQ より小さい、
733 * つまり、再送するとき。
734 */
735 if ((cep->flags & TCP_CEP_FLG_FORCE) ||
736 (len >= cep->max_sndwnd / 2 && cep->max_sndwnd > 0) ||
737 SEQ_LT(cep->snd_nxt, cep->snd_max)) {
738 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
739 continue;
740 }
741 }
742
743
744 /*
745 * 愚かなウィンドウ・シンドロームの回避処理 (受信側)
746 *
747 * ウィンドウサイズがフルサイズの 2 倍のセグメント、あるいは
748 * 受信バッファ容量の 1/2 の、いずれか小さいほうの
749 * サイズで増加される場合は、ウィンドウサイズの更新を行う。
750 */
751 if (win > 0) {
752 long adv;
753
754 /*
755 * win: 受信バッファの空き容量
756 * MAX_TCP_WIN_SIZE: TCP ヘッダの win フィールドに設定できる最大値
757 * rcv_adv: 受信を期待している最大の SEQ
758 * rcv_nxt: 受信を期待している最小の SEQ
759 */
760 if (win < MAX_TCP_WIN_SIZE)
761 adv = win - (cep->rcv_adv - cep->rcv_nxt);
762 else
763 adv = MAX_TCP_WIN_SIZE - (cep->rcv_adv - cep->rcv_nxt);
764
765 if (adv >= (long)(cep->maxseg * 2) ||
766 adv * 2 >= (long) cep->rbufsz) {
767 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
768 continue;
769 }
770 }
771
772 /*
773 * ACK を送信する。
774 */
775 if (cep->flags & TCP_CEP_FLG_ACK_NOW) {
776 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
777 continue;
778 }
779
780 if ( (flags & TCP_FLG_RST) ||
781 ((flags & TCP_FLG_SYN) && (cep->flags & TCP_CEP_FLG_NEED_SYN) == 0)) {
782 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
783 continue;
784 }
785
786#ifdef TCP_CFG_EXTENTIONS
787
788 if (SEQ_GT(cep->snd_up, cep->snd_una)) {
789 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
790 continue;
791 }
792
793#endif /* of #ifdef TCP_CFG_EXTENTIONS */
794
795 /*
796 * snd_nxt: 次に送信する SEQ
797 * snd_una: 未確認の最小送信 SEQ、または確認された最大送信 SEQ
798 *
799 * 相手から FIN を受信し、まだ FIN を送信していないか、
800 * 送るデータがないときは、FIN を相手に届けるため、
801 * セグメントを送信する。
802 */
803 if ((flags & TCP_FLG_FIN) &&
804 ((cep->flags & TCP_CEP_FLG_SENT_FIN) == 0 || cep->snd_nxt == cep->snd_una)) {
805 error = send_segment(&sendalot, cep, doff, win, (uint_t)len, flags);
806 continue;
807 }
808
809 /*
810 * 送信すべきデータがあり、再送タイマと持続タイマが切れているときは
811 * 持続タイマを設定する。
812 */
813 if (cep->swbuf_count && cep->timer[TCP_TIM_REXMT ] == 0 &&
814 cep->timer[TCP_TIM_PERSIST] == 0) {
815 cep->rxtshift = 0;
816 tcp_set_persist_timer(cep);
817 break;
818 }
819
820 }
821 }
822
823#ifdef TCP_CFG_SWBUF_CSAVE
824
825/*
826 * tcptsk_alloc_swbufq -- 送信ウィンドバッファ割り当て
827 */
828
829static void
830tcptsk_alloc_swbufq (T_TCP_CEP *cep)
831{
832 ER error;
833 uint_t win;
834
835 /*
836 * snd_wnd: 相手の受信可能ウィンドサイズ
837 * snd_cwnd: 輻輳ウィンドサイズ
838 *
839 * win: どちらか小さいウィンドサイズに設定する。
840 */
841 win = cep->snd_wnd < cep->snd_cwnd ? cep->snd_wnd : cep->snd_cwnd;
842
843 /*
844 * 相手の受信ウィンドが閉じている場合は、開くまで待機する。
845 */
846 if (win == 0) {
847 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_WOPEN_PEND;
848 }
849 else {
850
851#ifdef TCP_CFG_NON_BLOCKING
852
853 /* ノンブロッキングコール */
854 if (!IS_PTR_DEFINED(cep->callback)) {
855 syslog(LOG_WARNING, "[TCP] no call back, CEP: %d.", GET_TCP_CEPID(cep));
856
857 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
858 cep->snd_tskid = TA_NULL;
859 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
860 return;
861 }
862
863#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
864
865 if ((error = tcpn_get_segment(&cep->swbufq, cep, 0,
866 (uint_t)TCP_CFG_SWBUF_CSAVE_MIN_SIZE,
867 (uint_t)TCP_CFG_SWBUF_CSAVE_MAX_SIZE,
868 (ATR)(NBA_SEARCH_DESCENT |
869 NBA_RESERVE_TCP |
870 (GET_TCP_CEPID(cep) & NBA_ID_MASK)), TMO_POL)) != E_OK) {
871
872 /* ネットワークバッファを予約する。*/
873 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_NBUF_PEND;
874 }
875 else {
876
877 /* 送信ウィンドバッファを初期化する。*/
878 tcp_init_swbuf(cep);
879
880#ifdef TCP_CFG_NON_BLOCKING
881
882 if (cep->snd_nblk_tfn == TFN_TCP_GET_BUF) {
883
884 uint_t len;
885
886 /* 送信ウィンドバッファの書き込みアドレスを設定する。*/
887 len = TCP_GET_SWBUF_ADDR(cep, cep->snd_p_buf);
888
889 /* コールバック関数を呼び出す。*/
890#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
891 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)(uint32_t)len);
892#else
893 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&len);
894#endif
895
896
897 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
898 cep->snd_tskid = TA_NULL;
899 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
900 }
901
902 else { /* cep->snd_nblk_tfn == TFN_TCP_SND_DAT || */
903 /* cep->snd_nblk_tfn == TFN_TCP_SND_OOB */
904
905 uint_t len;
906
907 /* 送信ウィンドバッファにデータを書き込む。*/
908 len = TCP_WRITE_SWBUF(cep, cep->snd_data, (uint_t)cep->snd_len);
909
910#ifdef TCP_CFG_EXTENTIONS
911
912 /* 送信緊急ポインタを設定する。*/
913 if (cep->snd_nblk_tfn == TFN_TCP_SND_OOB)
914 cep->snd_up = cep->snd_una + len;
915
916#endif /* of #ifdef TCP_CFG_EXTENTIONS */
917
918 /* フラグを、送信可能に設定し、強制的に送信する。*/
919 cep->flags |= TCP_CEP_FLG_FORCE | TCP_CEP_FLG_FORCE_CLEAR | TCP_CEP_FLG_POST_OUTPUT;
920
921 /* コールバック関数を呼び出す。*/
922#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
923 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)(uint32_t)len);
924#else
925 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&len);
926#endif
927
928
929 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
930 cep->snd_tskid = TA_NULL;
931 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
932 }
933
934#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
935
936 }
937 }
938 }
939
940/*
941 * tcptsk_free_swbufq -- 送信ウィンドバッファ開放
942 */
943
944static void
945tcptsk_free_swbufq (T_TCP_CEP *cep)
946{
947 /*
948 * 受信確認が完了し、ネットワークインタフェースからの
949 * 出力も完了したときは、送信ウィンドバッファキューを解放する。
950 */
951
952 /* 送信ウィンドバッファの使用中サイズをリセットする。*/
953 cep->swbuf_count = 0;
954
955 /* 送信ウィンドバッファキューのネットワークバッファを解放する。*/
956 syscall(rel_net_buf(cep->swbufq));
957 cep->swbufq = NULL;
958
959 /* フラグを空きに設定する。*/
960 cep->flags = (cep->flags & ~TCP_CEP_FLG_WBCS_MASK) | TCP_CEP_FLG_WBCS_FREE;
961
962 /* 送信ウィンドバッファに空きができたことを知らせる。*/
963 syscall(set_flg(cep->snd_flgid, TCP_CEP_EVT_SWBUF_READY));
964
965 /* 送信ウィンドバッファの空き待ちのときは、TCP 出力タスクを起動する。*/
966 if ((cep->flags & TCP_CEP_FLG_WBCS_NBUF_REQ) != 0) {
967 sig_sem(SEM_TCP_POST_OUTPUT);
968 }
969 }
970
971#endif /* of #ifdef TCP_CFG_SWBUF_CSAVE */
972
973/*
974 * TCP 出力タスク
975 */
976
977void
978tcp_output_task (intptr_t exinf)
979{
980 static int_t last_ix = 0;
981
982 T_TCP_CEP *cep;
983 ID tskid;
984 int_t ix, sel_ix;
985
986 get_tid(&tskid);
987 syslog(LOG_NOTICE, "[TCP OUTPUT:%d] started.", tskid);
988
989 tcp_init();
990
991#ifdef _IP6_CFG
992
993 /* IPv6 のステートレス・アドレス自動設定を実行する。*/
994 in6_if_up(IF_GET_IFNET());
995
996#endif /* of #ifdef _IP6_CFG */
997
998 while (true) {
999
1000 /* 出力がポストされるまで待つ。*/
1001 syscall(wai_sem(SEM_TCP_POST_OUTPUT));
1002
1003 if (++ last_ix == tmax_tcp_cepid)
1004 last_ix = 0;
1005 sel_ix = ix = last_ix;
1006 do {
1007 cep = &tcp_cep[ix];
1008
1009#ifdef TCP_CFG_SWBUF_CSAVE
1010
1011 if ((cep->flags & TCP_CEP_FLG_WBCS_NBUF_REQ) != 0 &&
1012 ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_FREE ||
1013 (cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_NBUF_RSVD)) {
1014 tcptsk_alloc_swbufq(cep);
1015 sel_ix = ix;
1016 }
1017
1018 if ((cep->flags & TCP_CEP_FLG_WBCS_MASK) == TCP_CEP_FLG_WBCS_ACKED &&
1019 (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) == 0) {
1020 tcptsk_free_swbufq(cep);
1021 sel_ix = ix;
1022 }
1023
1024 /*
1025 * ネットワークインタフェースから送信が終わっていないときは、
1026 * 送信を予約する。
1027 */
1028 if (cep->flags & TCP_CEP_FLG_POST_OUTPUT &&
1029 (cep->flags & TCP_CEP_FLG_WBCS_MASK) >= TCP_CEP_FLG_WBCS_SENT) {
1030 syscall(wai_sem(cep->semid_lock));
1031 if (cep->swbufq == NULL)
1032 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1033 else if (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) {
1034 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1035 cep->flags |= TCP_CEP_FLG_RESERVE_OUTPUT;
1036 }
1037 syscall(sig_sem(cep->semid_lock));
1038 }
1039
1040 /*
1041 * 送信予約中に、ネットワークインタフェースから送信が終了したら、
1042 * 送信を開始する。ただし、完全に送信が終了したときは何もしない。
1043 */
1044 if (cep->flags & TCP_CEP_FLG_RESERVE_OUTPUT) {
1045 syscall(wai_sem(cep->semid_lock));
1046 if (cep->swbufq != NULL && (cep->swbufq->flags & NB_FLG_NOREL_IFOUT) == 0) {
1047 cep->flags |= TCP_CEP_FLG_POST_OUTPUT;
1048 }
1049 syscall(sig_sem(cep->semid_lock));
1050 cep->flags &= ~TCP_CEP_FLG_RESERVE_OUTPUT;
1051 }
1052
1053#endif /* of #ifdef TCP_CFG_SWBUF_CSAVE */
1054
1055 if (cep->flags & TCP_CEP_FLG_POST_OUTPUT) {
1056
1057 cep->flags &= ~TCP_CEP_FLG_POST_OUTPUT;
1058
1059#ifdef TCP_CFG_NON_BLOCKING
1060
1061 if (cep->snd_nblk_tfn == TFN_TCP_CON_CEP && cep->myaddr.portno == TCP_PORTANY) {
1062 ER error;
1063
1064 /*
1065 * tcp_con_cep のノンブロッキングコールで、
1066 * 未割当のの場合は、ポート番号を割り当てる。
1067 * p_myaddr が NADR (-1) か、
1068 * 自ポート番号が TCP_PORTANY なら、自動で割り当てる。
1069 */
1070 if (cep->p_myaddr == NADR || cep->p_myaddr->portno == TCP_PORTANY)
1071 tcp_alloc_auto_port(cep);
1072 else if ((error = tcp_alloc_port(cep, cep->p_myaddr->portno)) != E_OK) {
1073
1074 if (IS_PTR_DEFINED(cep->callback))
1075#ifdef TCP_CFG_NON_BLOCKING_COMPAT14
1076 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)error);
1077#else
1078 (*cep->callback)(GET_TCP_CEPID(cep), cep->snd_nblk_tfn, (void*)&error);
1079#endif
1080 else
1081 syslog(LOG_WARNING, "[TCP] no call back, CEP: %d.", GET_TCP_CEPID(cep));
1082
1083 /* 記憶されているタスク ID と API 機能コードをクリアーする。*/
1084 cep->snd_tfn = cep->snd_nblk_tfn = TFN_TCP_UNDEF;
1085 cep->snd_tskid = TA_NULL;
1086 continue;
1087 }
1088 }
1089
1090#endif /* of #ifdef TCP_CFG_NON_BLOCKING */
1091
1092 tcp_output(cep);
1093
1094 if (cep->flags & TCP_CEP_FLG_CLOSE_AFTER_OUTPUT) {
1095 /* コネクションを閉じる。*/
1096 tcp_close(cep);
1097 cep->flags &= ~TCP_CEP_FLG_CLOSE_AFTER_OUTPUT;
1098 }
1099
1100 if (cep->flags & TCP_CEP_FLG_RESTORE_NEXT_OUTPUT) {
1101 /* snd_nxt を元に戻す。*/
1102 if (SEQ_GT(cep->snd_old_nxt, cep->snd_nxt))
1103 cep->snd_nxt = cep->snd_old_nxt;
1104 cep->flags &= ~TCP_CEP_FLG_RESTORE_NEXT_OUTPUT;
1105 }
1106
1107 sel_ix = ix;
1108 }
1109
1110 if (++ ix == tmax_tcp_cepid)
1111 ix = 0;
1112 } while (ix != last_ix);
1113
1114 /* 次回は、処理した通信端点を後回しにする。*/
1115 last_ix = sel_ix;
1116 }
1117 }
1118
1119#endif /* of #ifdef SUPPORT_TCP */
Note: See TracBrowser for help on using the repository browser.