source: azure_iot_hub/trunk/asp3_dcre/tinet/netinet/tcp_output.c@ 388

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

Azure IoT Hub Device C SDK を使ったサンプルの追加

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