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

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

mruby版ECNLプロトタイピング・ツールを追加

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