0056001 /*
0056002 tcp_recv.c
0056003
0056004 Copyright 1995 Philip Homburg
0056005 */
0056006
0056007 #include "inet.h"
0056008 #include "buf.h"
0056009 #include "clock.h"
0056010 #include "event.h"
0056011 #include "type.h"
0056012
0056013 #include "io.h"
0056014 #include "tcp_int.h"
0056015 #include "tcp.h"
0056016 #include "assert.h"
0056017
0056018 THIS_FILE
0056019
0056020 FORWARD void create_RST ARGS(( tcp_conn_t *tcp_conn,
0056021 ip_hdr_t *ip_hdr, tcp_hdr_t *tcp_hdr, int data_len ));
0056022 FORWARD void process_data ARGS(( tcp_conn_t *tcp_conn,
0056023 tcp_hdr_t *tcp_hdr, acc_t *tcp_data, int data_len ));
0056024 FORWARD void process_advanced_data ARGS(( tcp_conn_t *tcp_conn,
0056025 tcp_hdr_t *tcp_hdr, acc_t *tcp_data, int data_len ));
0056026
0056027 PUBLIC void tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, tcp_data, data_len)
0056028 tcp_conn_t *tcp_conn;
0056029 ip_hdr_t *ip_hdr;
0056030 tcp_hdr_t *tcp_hdr;
0056031 acc_t *tcp_data;
0056032 size_t data_len;
tcp_frag2conn()
0056033 {
0056034 tcp_fd_t *connuser;
0056035 int tcp_hdr_flags;
0056036 int ip_hdr_len, tcp_hdr_len;
0056037 u32_t seg_ack, seg_seq, rcv_hi;
0056038 u16_t seg_wnd;
0056039 int acceptable_ACK, segm_acceptable;
0056040
0056041 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
0056042 tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2;
0056043
0056044 tcp_hdr_flags= tcp_hdr->th_flags & TH_FLAGS_MASK;
0056045 seg_ack= ntohl(tcp_hdr->th_ack_nr);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056046 seg_seq= ntohl(tcp_hdr->th_seq_nr);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056047 seg_wnd= ntohs(tcp_hdr->th_window);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056048
0056049 switch (tcp_conn->tc_state)
0056050 {
0056051 case TCS_CLOSED:
0056052 /*
0056053 CLOSED:
0056054 discard all data.
0056055 !RST ?
0056056 ACK ?
0056057 <SEQ=SEG.ACK><CTL=RST>
0056058 exit
0056059 :
0056060 <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK>
0056061 exit
0056062 :
0056063 discard packet
0056064 exit
0056065 */
0056066
0056067 if (!(tcp_hdr_flags & THF_RST))
0056068 {
0056069 create_RST(tcp_conn, ip_hdr, tcp_hdr, data_len);
0056070 tcp_conn_write(tcp_conn, 1);
0056071 }
0056072 break;
0056073 case TCS_LISTEN:
0056074 /*
0056075 LISTEN:
0056076 RST ?
0056077 discard packet
0056078 exit
0056079 ACK ?
0056080 <SEQ=SEG.ACK><CTL=RST>
0056081 exit
0056082 SYN ?
0056083 BUG: no security check
0056084 RCV.NXT= SEG.SEQ+1
0056085 IRS= SEG.SEQ
0056086 ISS should already be selected
0056087 <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
0056088 SND.NXT=ISS+1
0056089 SND.UNA=ISS
0056090 state= SYN-RECEIVED
0056091 exit
0056092 :
0056093 shouldnot occur
0056094 discard packet
0056095 exit
0056096 */
0056097 if (tcp_hdr_flags & THF_RST)
0056098 break;
0056099 if (tcp_hdr_flags & THF_ACK)
0056100 {
0056101 create_RST (tcp_conn, ip_hdr, tcp_hdr, data_len);
0056102 tcp_conn_write(tcp_conn, 1);
0056103 break;
0056104 }
0056105 if (tcp_hdr_flags & THF_SYN)
0056106 {
0056107 tcp_extract_ipopt(tcp_conn, ip_hdr);
0056108 tcp_extract_tcpopt(tcp_conn, tcp_hdr);
0056109 tcp_conn->tc_RCV_LO= seg_seq+1;
0056110 tcp_conn->tc_RCV_NXT= seg_seq+1;
0056111 tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO+
0056112 tcp_conn->tc_rcv_wnd;
0056113 tcp_conn->tc_RCV_UP= seg_seq;
0056114 tcp_conn->tc_IRS= seg_seq;
0056115 tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS;
0056116 tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS;
0056117 tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS+1;
0056118 tcp_conn->tc_SND_UP= tcp_conn->tc_ISS-1;
0056119 tcp_conn->tc_SND_PSH= tcp_conn->tc_ISS-1;
0056120 tcp_conn->tc_state= TCS_SYN_RECEIVED;
0056121 tcp_conn->tc_stt= 0;
0056122 assert (tcp_check_conn(tcp_conn));
0056123 tcp_conn->tc_locaddr= ip_hdr->ih_dst;
0056124 tcp_conn->tc_locport= tcp_hdr->th_dstport;
0056125 tcp_conn->tc_remaddr= ip_hdr->ih_src;
0056126 tcp_conn->tc_remport= tcp_hdr->th_srcport;
0056127 tcp_conn_write(tcp_conn, 1);
0056128
0056129 DIFBLOCK(0x10, seg_seq == 0,
0056130 printf("warning got 0 IRS from ");
0056131 writeIpAddr(tcp_conn->tc_remaddr);
0056132 printf("\n"));
0056133
0056134 /* Start the timer (if necessary) */
0056135 tcp_set_send_timer(tcp_conn);
0056136
0056137 break;
0056138 }
0056139 /* do nothing */
0056140 break;
0056141 case TCS_SYN_SENT:
0056142 /*
0056143 SYN-SENT:
0056144 ACK ?
0056145 SEG.ACK <= ISS || SEG.ACK > SND.NXT ?
0056146 RST ?
0056147 discard packet
0056148 exit
0056149 :
0056150 <SEQ=SEG.ACK><CTL=RST>
0056151 exit
0056152 SND.UNA <= SEG.ACK && SEG.ACK <= SND.NXT ?
0056153 ACK is acceptable
0056154 :
0056155 ACK is !acceptable
0056156 :
0056157 ACK is !acceptable
0056158 RST ?
0056159 ACK acceptable ?
0056160 discard segment
0056161 state= CLOSED
0056162 error "connection refused"
0056163 exit
0056164 :
0056165 discard packet
0056166 exit
0056167 BUG: no security check
0056168 SYN ?
0056169 IRS= SEG.SEQ
0056170 RCV.NXT= IRS+1
0056171 ACK ?
0056172 SND.UNA= SEG.ACK
0056173 SND.UNA > ISS ?
0056174 state= ESTABLISHED
0056175 <SEQ=SND.NXT><ACK= RCV.NXT><CTL=ACK>
0056176 process ev. URG and text
0056177 exit
0056178 :
0056179 state= SYN-RECEIVED
0056180 SND.WND= SEG.WND
0056181 SND.WL1= SEG.SEQ
0056182 SND.WL2= SEG.ACK
0056183 <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK>
0056184 exit
0056185 :
0056186 discard segment
0056187 exit
0056188 */
0056189 if (tcp_hdr_flags & THF_ACK)
0056190 {
0056191 if (tcp_LEmod4G(seg_ack, tcp_conn->tc_ISS) ||
0056192 tcp_Gmod4G(seg_ack, tcp_conn->tc_SND_NXT))
0056193 if (tcp_hdr_flags & THF_RST)
0056194 break;
0056195 else
0056196 {
0056197 create_RST (tcp_conn, ip_hdr,
0056198 tcp_hdr, data_len);
0056199 tcp_conn_write(tcp_conn, 1);
0056200 break;
0056201 }
0056202 acceptable_ACK= (tcp_LEmod4G(tcp_conn->tc_SND_UNA,
0056203 seg_ack) && tcp_LEmod4G(seg_ack,
0056204 tcp_conn->tc_SND_NXT));
0056205 }
0056206 else
0056207 acceptable_ACK= FALSE;
0056208 if (tcp_hdr_flags & THF_RST)
0056209 {
0056210 if (acceptable_ACK)
0056211 {
0056212 DBLOCK(1, printf(
0056213 "calling tcp_close_connection\n"));
0056214
0056215 tcp_close_connection(tcp_conn,
0056216 ECONNREFUSED);
0056217 }
0056218 break;
0056219 }
0056220 if (tcp_hdr_flags & THF_SYN)
0056221 {
0056222 tcp_conn->tc_RCV_LO= seg_seq+1;
0056223 tcp_conn->tc_RCV_NXT= seg_seq+1;
0056224 tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO +
0056225 tcp_conn->tc_rcv_wnd;
0056226 tcp_conn->tc_RCV_UP= seg_seq;
0056227 tcp_conn->tc_IRS= seg_seq;
0056228 if (tcp_hdr_flags & THF_ACK)
0056229 tcp_conn->tc_SND_UNA= seg_ack;
0056230 if (tcp_Gmod4G(tcp_conn->tc_SND_UNA,
0056231 tcp_conn->tc_ISS))
0056232 {
0056233 tcp_conn->tc_state= TCS_ESTABLISHED;
0056234 tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD;
0056235
0056236 assert (tcp_check_conn(tcp_conn));
0056237 assert(tcp_conn->tc_connInprogress);
0056238
0056239 tcp_restart_connect(tcp_conn->tc_fd);
0056240
0056241 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056242 tcp_conn_write(tcp_conn, 1);
0056243 if (data_len != 0)
0056244 {
0056245 tcp_frag2conn(tcp_conn, ip_hdr,
0056246 tcp_hdr, tcp_data, data_len);
0056247 /* tcp_data is already freed */
0056248 return;
0056249 }
0056250 break;
0056251 }
0056252 tcp_conn->tc_state= TCS_SYN_RECEIVED;
0056253
0056254 assert (tcp_check_conn(tcp_conn));
0056255
0056256 tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS;
0056257 tcp_conn_write(tcp_conn, 1);
0056258 }
0056259 break;
0056260
0056261 case TCS_SYN_RECEIVED:
0056262 /*
0056263 SYN-RECEIVED:
0056264 test if segment is acceptable:
0056265 Segment Receive Test
0056266 Length Window
0056267 0 0 SEG.SEQ == RCV.NXT
0056268 0 >0 RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND
0056269 >0 0 not acceptable
0056270 >0 >0 (RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND)
0056271 || (RCV.NXT <= SEG.SEQ+SEG.LEN-1 &&
0056272 SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND)
0056273 for urgent data: use RCV.WND+1 for RCV.WND
0056274 */
0056275 rcv_hi= tcp_conn->tc_RCV_HI;
0056276 if (tcp_hdr_flags & THF_URG)
0056277 rcv_hi++;
0056278 if (!data_len)
0056279 {
0056280 if (rcv_hi == tcp_conn->tc_RCV_NXT)
0056281 segm_acceptable= (seg_seq == rcv_hi);
0056282 else
0056283 {
0056284 assert (tcp_Gmod4G(rcv_hi,
0056285 tcp_conn->tc_RCV_NXT));
0056286 segm_acceptable= (tcp_LEmod4G(tcp_conn->
0056287 tc_RCV_NXT, seg_seq) &&
0056288 tcp_Lmod4G(seg_seq, rcv_hi));
0056289 }
0056290 }
0056291 else
0056292 {
0056293 if (tcp_Gmod4G(rcv_hi, tcp_conn->tc_RCV_NXT))
0056294 {
0056295 segm_acceptable= (tcp_LEmod4G(tcp_conn->
0056296 tc_RCV_NXT, seg_seq) &&
0056297 tcp_Lmod4G(seg_seq, rcv_hi)) ||
0056298 (tcp_LEmod4G(tcp_conn->tc_RCV_NXT,
0056299 seg_seq+data_len-1) &&
0056300 tcp_Lmod4G(seg_seq+data_len-1,
0056301 rcv_hi));
0056302 }
0056303 else
0056304 {
0056305 segm_acceptable= FALSE;
0056306 }
0056307 }
0056308 /*
0056309 !segment acceptable ?
0056310 RST ?
0056311 discard packet
0056312 exit
0056313 :
0056314 <SEG=SND.NXT><ACK=RCV.NXT><CTL=ACK>
0056315 exit
0056316 */
0056317 if (!segm_acceptable)
0056318 {
0056319 if (!(tcp_hdr_flags & THF_RST))
0056320 {
0056321 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056322 tcp_conn_write(tcp_conn, 1);
0056323 }
0056324 break;
0056325 }
0056326 /*
0056327 RST ?
0056328 initiated by a LISTEN ?
0056329 state= LISTEN
0056330 exit
0056331 :
0056332 state= CLOSED
0056333 error "connection refused"
0056334 exit
0056335 */
0056336 if (tcp_hdr_flags & THF_RST)
0056337 {
0056338 if (tcp_conn->tc_orglisten)
0056339 {
0056340 connuser= tcp_conn->tc_fd;
0056341
0056342 tcp_conn->tc_connInprogress= 0;
0056343 tcp_conn->tc_fd= NULL;
0056344
0056345 tcp_close_connection (tcp_conn, ECONNREFUSED);
0056346 if (connuser)
0056347 (void)tcp_su4listen(connuser);
0056348 break;
0056349 }
0056350 else
0056351 {
0056352 tcp_close_connection(tcp_conn, ECONNREFUSED);
0056353 break;
0056354 }
0056355 }
0056356 /*
0056357 SYN in window ?
0056358 initiated by a LISTEN ?
0056359 state= LISTEN
0056360 exit
0056361 :
0056362 state= CLOSED
0056363 error "connection reset"
0056364 exit
0056365 */
0056366 if ((tcp_hdr_flags & THF_SYN) && tcp_GEmod4G(seg_seq,
0056367 tcp_conn->tc_RCV_NXT))
0056368 {
0056369 if (tcp_conn->tc_orglisten)
0056370 {
0056371 connuser= tcp_conn->tc_fd;
0056372
0056373 tcp_conn->tc_connInprogress= 0;
0056374 tcp_conn->tc_fd= NULL;
0056375
0056376 tcp_close_connection(tcp_conn, ECONNRESET);
0056377 if (connuser)
0056378 (void)tcp_su4listen(connuser);
0056379 break;
0056380 }
0056381 tcp_close_connection(tcp_conn, ECONNRESET);
0056382 break;
0056383 }
0056384 /*
0056385 !ACK ?
0056386 discard packet
0056387 exit
0056388 */
0056389 if (!(tcp_hdr_flags & THF_ACK))
0056390 break;
0056391 /*
0056392 SND.UNA < SEG.ACK <= SND.NXT ?
0056393 state= ESTABLISHED
0056394 :
0056395 <SEG=SEG.ACK><CTL=RST>
0056396 exit
0056397 */
0056398 if (tcp_Lmod4G(tcp_conn->tc_SND_UNA, seg_ack) &&
0056399 tcp_LEmod4G(seg_ack, tcp_conn->tc_SND_NXT))
0056400 {
0056401 tcp_conn->tc_state= TCS_ESTABLISHED;
0056402 tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD;
0056403
0056404 tcp_release_retrans(tcp_conn, seg_ack, seg_wnd);
0056405
0056406 assert (tcp_check_conn(tcp_conn));
0056407 assert(tcp_conn->tc_connInprogress);
0056408
0056409 tcp_restart_connect(tcp_conn->tc_fd);
0056410 tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, tcp_data,
0056411 data_len);
0056412 /* tcp_data is already freed */
0056413 return;
0056414 }
0056415 else
0056416 {
0056417 create_RST (tcp_conn, ip_hdr, tcp_hdr, data_len);
0056418 tcp_conn_write(tcp_conn, 1);
0056419 break;
0056420 }
0056421 break;
0056422
0056423 case TCS_ESTABLISHED:
0056424 case TCS_CLOSING:
0056425 /*
0056426 ESTABLISHED:
0056427 FIN-WAIT-1:
0056428 FIN-WAIT-2:
0056429 CLOSE-WAIT:
0056430 CLOSING:
0056431 LAST-ACK:
0056432 TIME-WAIT:
0056433 test if segment is acceptable:
0056434 Segment Receive Test
0056435 Length Window
0056436 0 0 SEG.SEQ == RCV.NXT
0056437 0 >0 RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND
0056438 >0 0 not acceptable
0056439 >0 >0 (RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND)
0056440 || (RCV.NXT <= SEG.SEQ+SEG.LEN-1 &&
0056441 SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND)
0056442 for urgent data: use RCV.WND+1 for RCV.WND
0056443 */
0056444 rcv_hi= tcp_conn->tc_RCV_HI;
0056445 if (tcp_hdr_flags & THF_URG)
0056446 rcv_hi++;
0056447 if (!data_len)
0056448 {
0056449 if (rcv_hi == tcp_conn->tc_RCV_NXT)
0056450 segm_acceptable= (seg_seq == rcv_hi);
0056451 else
0056452 {
0056453 assert (tcp_Gmod4G(rcv_hi,
0056454 tcp_conn->tc_RCV_NXT));
0056455 segm_acceptable= (tcp_LEmod4G(tcp_conn->
0056456 tc_RCV_NXT, seg_seq) &&
0056457 tcp_Lmod4G(seg_seq, rcv_hi));
0056458 }
0056459 }
0056460 else
0056461 {
0056462 if (tcp_Gmod4G(rcv_hi, tcp_conn->tc_RCV_NXT))
0056463 {
0056464 segm_acceptable= (tcp_LEmod4G(tcp_conn->
0056465 tc_RCV_NXT, seg_seq) &&
0056466 tcp_Lmod4G(seg_seq, rcv_hi)) ||
0056467 (tcp_LEmod4G(tcp_conn->tc_RCV_NXT,
0056468 seg_seq+data_len-1) &&
0056469 tcp_Lmod4G(seg_seq+data_len-1,
0056470 rcv_hi));
0056471 }
0056472 else
0056473 {
0056474 segm_acceptable= FALSE;
0056475 }
0056476 }
0056477 /*
0056478 !segment acceptable ?
0056479 RST ?
0056480 discard packet
0056481 exit
0056482 :
0056483 <SEG=SND.NXT><ACK=RCV.NXT><CTL=ACK>
0056484 exit
0056485 */
0056486 if (!segm_acceptable)
0056487 {
0056488 if (!(tcp_hdr_flags & THF_RST))
0056489 {
0056490 DBLOCK(0x20,
0056491 printf("segment is not acceptable\n");
0056492 printf("\t");
0056493 tcp_print_pack(ip_hdr, tcp_hdr);
0056494 printf("\n\t");
0056495 tcp_print_conn(tcp_conn);
0056496 printf("\n"));
0056497 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056498 tcp_conn_write(tcp_conn, 1);
0056499
0056500 /* Sometimes, a retransmission sets the PSH
0056501 * flag (Solaris 2.4)
0056502 */
0056503 if (tcp_conn->tc_rcvd_data != NULL &&
0056504 (tcp_hdr_flags & THF_PSH))
0056505 {
0056506 tcp_conn->tc_flags |= TCF_RCV_PUSH;
0056507 if (tcp_conn->tc_fd &&
0056508 (tcp_conn->tc_fd->tf_flags &
0056509 TFF_READ_IP))
0056510 {
0056511 tcp_fd_read(tcp_conn, 1);
0056512 }
0056513 }
0056514 }
0056515 break;
0056516 }
0056517 /*
0056518 RST ?
0056519 state == CLOSING || state == LAST-ACK ||
0056520 state == TIME-WAIT ?
0056521 state= CLOSED
0056522 exit
0056523 :
0056524 state= CLOSED
0056525 error "connection reset"
0056526 exit
0056527 */
0056528 if (tcp_hdr_flags & THF_RST)
0056529 {
0056530 if ((tcp_conn->tc_flags &
0056531 (TCF_FIN_SENT|TCF_FIN_RECV)) ==
0056532 (TCF_FIN_SENT|TCF_FIN_RECV) &&
0056533 tcp_conn->tc_send_data == NULL)
0056534 {
0056535 /* Clean shutdown, but the other side
0056536 * doesn't want to ACK our FIN.
0056537 */
0056538 tcp_close_connection (tcp_conn, 0);
0056539 }
0056540 else
0056541 tcp_close_connection(tcp_conn, ECONNRESET);
0056542 break;
0056543 }
0056544 /*
0056545 SYN in window ?
0056546 state= CLOSED
0056547 error "connection reset"
0056548 exit
0056549 */
0056550 if ((tcp_hdr_flags & THF_SYN) && tcp_GEmod4G(seg_seq,
0056551 tcp_conn->tc_RCV_NXT))
0056552 {
0056553 tcp_close_connection(tcp_conn, ECONNRESET);
0056554 break;
0056555 }
0056556 /*
0056557 !ACK ?
0056558 discard packet
0056559 exit
0056560 */
0056561 if (!(tcp_hdr_flags & THF_ACK))
0056562 break;
0056563
0056564 /*
0056565 SND.UNA < SEG.ACK <= SND.NXT ?
0056566 SND.UNA= SEG.ACK
0056567 reply "send ok"
0056568 SND.WL1 < SEG.SEQ || (SND.WL1 == SEG.SEQ &&
0056569 SND.WL2 <= SEG.ACK ?
0056570 SND.WND= SEG.WND
0056571 SND.Wl1= SEG.SEQ
0056572 SND.WL2= SEG.ACK
0056573 SEG.ACK <= SND.UNA ?
0056574 ignore ACK
0056575 SEG.ACK > SND.NXT ?
0056576 <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
0056577 discard packet
0056578 exit
0056579 */
0056580
0056581 /* Always reset the send timer after a valid ack is
0056582 * received. The assumption is that either the ack really
0056583 * acknowledges some data (normal case), contains a zero
0056584 * window, or the remote host has another reason not
0056585 * to accept any data. In all cases, the remote host is
0056586 * alive, so the connection should stay alive too.
0056587 * Do not reset stt if the state is CLOSING, i.e. if
0056588 * the user closed the connection and we still have
0056589 * some data to deliver. We don't want a zero window
0056590 * to keep us from closing the connection.
0056591 */
0056592 if (tcp_conn->tc_state != TCS_CLOSING)
0056593 tcp_conn->tc_stt= 0;
0056594
0056595 if (seg_ack == tcp_conn->tc_SND_UNA)
0056596 {
0056597 /* This ACK doesn't acknowledge any new data, this
0056598 * is a likely situation if we are only receiving
0056599 * data. We only update the window if we are
0056600 * actually sending or if we currently have a
0056601 * zero window.
0056602 */
0056603 if (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_UNA &&
0056604 seg_wnd != 0)
0056605 {
0056606 DBLOCK(2, printf("zero window opened\n"));
0056607 /* The other side opened up its receive
0056608 * window. */
0056609 if (seg_wnd > 2*tcp_conn->tc_mss)
0056610 seg_wnd= 2*tcp_conn->tc_mss;
0056611 tcp_conn->tc_snd_cwnd=
0056612 tcp_conn->tc_SND_UNA+seg_wnd;
0056613 tcp_conn_write(tcp_conn, 1);
0056614 }
0056615 if (seg_wnd == 0)
0056616 {
0056617 tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_TRM=
0056618 tcp_conn->tc_SND_UNA;
0056619 }
0056620 }
0056621 else if (tcp_Lmod4G(tcp_conn->tc_SND_UNA, seg_ack)
0056622 && tcp_LEmod4G(seg_ack, tcp_conn->
0056623 tc_SND_NXT))
0056624 {
0056625 tcp_release_retrans(tcp_conn, seg_ack, seg_wnd);
0056626 if (tcp_conn->tc_state == TCS_CLOSED)
0056627 break;
0056628 }
0056629 else if (tcp_Gmod4G(seg_ack,
0056630 tcp_conn->tc_SND_NXT))
0056631 {
0056632 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056633 tcp_conn_write(tcp_conn, 1);
0056634 DBLOCK(1, printf(
0056635 "got an ack of something I haven't send\n");
0056636 printf( "seg_ack= %lu, SND_NXT= %lu\n",
0056637 seg_ack, tcp_conn->tc_SND_NXT));
0056638 break;
0056639 }
0056640
0056641 /*
0056642 process data...
0056643 */
0056644 tcp_extract_ipopt(tcp_conn, ip_hdr);
0056645 tcp_extract_tcpopt(tcp_conn, tcp_hdr);
0056646
0056647 if (data_len)
0056648 {
0056649 if (tcp_LEmod4G(seg_seq, tcp_conn->tc_RCV_NXT))
0056650 {
0056651 process_data (tcp_conn, tcp_hdr,
0056652 tcp_data, data_len);
0056653 }
0056654 else
0056655 {
0056656 process_advanced_data (tcp_conn,
0056657 tcp_hdr, tcp_data, data_len);
0056658 }
0056659 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056660 tcp_conn_write(tcp_conn, 1);
0056661
0056662 /* Don't process a FIN if we got new data */
0056663 break;
0056664 }
0056665 /*
0056666 FIN ?
0056667 reply pending receives
0056668 advace RCV.NXT over the FIN
0056669 <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK>
0056670
0056671 state == ESTABLISHED ?
0056672 state= CLOSE-WAIT
0056673 state == FIN-WAIT-1 ?
0056674 state= CLOSING
0056675 state == FIN-WAIT-2 ?
0056676 state= TIME-WAIT
0056677 state == TIME-WAIT ?
0056678 restart the TIME-WAIT timer
0056679 exit
0056680 */
0056681 if ((tcp_hdr_flags & THF_FIN) && tcp_LEmod4G(seg_seq,
0056682 tcp_conn->tc_RCV_NXT))
0056683 {
0056684 if (!(tcp_conn->tc_flags & TCF_FIN_RECV) &&
0056685 tcp_Lmod4G(tcp_conn->tc_RCV_NXT,
0056686 tcp_conn->tc_RCV_HI))
0056687 {
0056688 tcp_conn->tc_RCV_NXT++;
0056689 tcp_conn->tc_flags |= TCF_FIN_RECV;
0056690 }
0056691 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056692 tcp_conn_write(tcp_conn, 1);
0056693 if (tcp_conn->tc_fd &&
0056694 (tcp_conn->tc_fd->tf_flags & TFF_READ_IP))
0056695 {
0056696 tcp_fd_read(tcp_conn, 1);
0056697 }
0056698 }
0056699 break;
0056700 default:
0056701 #if !CRAMPED
0056702 printf("tcp_frag2conn: unknown state ");
0056703 tcp_print_state(tcp_conn);
0056704 #endif
0056705 break;
0056706 }
0056707 if (tcp_data != NULL)
0056708 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056709 }
0056710
0056711
0056712 PRIVATE void
0056713 process_data(tcp_conn, tcp_hdr, tcp_data, data_len)
0056714 tcp_conn_t *tcp_conn;
0056715 tcp_hdr_t *tcp_hdr;
0056716 acc_t *tcp_data;
0056717 int data_len;
0056718 {
0056719 u32_t lo_seq, hi_seq, urg_seq, seq_nr, adv_seq, nxt;
0056720 u16_t urgptr;
0056721 int tcp_hdr_flags;
0056722 unsigned int offset;
0056723 acc_t *tmp_data, *rcvd_data, *adv_data;
0056724 int len_diff;
0056725
0056726 assert(tcp_conn->tc_busy);
0056727
0056728 /* Note, tcp_data will be freed by the caller. */
0056729 assert (!(tcp_hdr->th_flags & THF_SYN));
0056730
0056731 seq_nr= ntohl(tcp_hdr->th_seq_nr);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056732 urgptr= ntohs(tcp_hdr->th_urgptr);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056733
0056734 tcp_data->acc_linkC++;
0056735
0056736 lo_seq= seq_nr;
0056737 tcp_hdr_flags= tcp_hdr->th_flags & TH_FLAGS_MASK;
0056738
0056739 if (tcp_hdr_flags & THF_URG)
0056740 {
0056741 if (urgptr > data_len)
0056742 urgptr= data_len;
0056743 urg_seq= lo_seq+ urgptr;
0056744
0056745 if (tcp_GEmod4G(urg_seq, tcp_conn->tc_RCV_HI))
0056746 urg_seq= tcp_conn->tc_RCV_HI;
0056747 if (tcp_conn->tc_flags & TCF_BSD_URG)
0056748 {
0056749 if (tcp_Gmod4G(tcp_conn->tc_RCV_NXT,
0056750 tcp_conn->tc_RCV_LO))
0056751 {
0056752 DBLOCK(1, printf(
0056753 "ignoring urgent data\n"));
0056754
0056755 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056756 /* Should set advertised window to
0056757 * zero */
0056758
0056759 /* Flush */
0056760 tcp_conn->tc_flags |= TCF_RCV_PUSH;
0056761 if (tcp_conn->tc_fd &&
0056762 (tcp_conn->tc_fd->tf_flags &
0056763 TFF_READ_IP))
0056764 {
0056765 tcp_fd_read(tcp_conn, 1);
0056766 }
0056767 return;
0056768 }
0056769 }
0056770 if (tcp_Gmod4G(urg_seq, tcp_conn->tc_RCV_UP))
0056771 tcp_conn->tc_RCV_UP= urg_seq;
0056772 if (urgptr < data_len)
0056773 {
0056774 data_len= urgptr;
0056775 tmp_data= bf_cut(tcp_data, 0, data_len);
bf_cut()
If a section of a linked list needs to be duplicated, bf_cut(data, offset, length) is called. For example, if a section of length 50 starting at an offset of 75 of the linked list below needs to be duplicated, bf_cut(data, 75, 50) is called:
Note that the original linked list remains unchanged and that acc_linkC for all the accessors in the new linked list is one.
If length (the second parameter) is zero, simply duplicate the first accessor in the linked list but set acc_length=0 and acc_next=null. In other words, create a linked list of length one accessor whose acc_length is 0.
bf_cut() is used in a number of scenarios, including cutting a received ethernet packet to size.
For a full description of the network service's buffer management, click here.
0056776 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056777 tcp_data= tmp_data;
0056778 tcp_hdr_flags &= ~THF_FIN;
0056779 }
0056780 tcp_conn->tc_flags |= TCF_RCV_PUSH;
0056781 }
0056782 else
0056783 {
0056784 /* Normal data. */
0056785 }
0056786
0056787 if (tcp_hdr_flags & THF_PSH)
0056788 {
0056789 tcp_conn->tc_flags |= TCF_RCV_PUSH;
0056790 }
0056791
0056792 if (tcp_Lmod4G(lo_seq, tcp_conn->tc_RCV_NXT))
0056793 {
0056794 DBLOCK(0x10,
0056795 printf("segment is a retransmission\n"));
0056796 offset= tcp_conn->tc_RCV_NXT-lo_seq;
0056797 tcp_data= bf_delhead(tcp_data, offset);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0056798 lo_seq += offset;
0056799 data_len -= offset;
0056800 }
0056801 assert (lo_seq == tcp_conn->tc_RCV_NXT);
0056802
0056803 hi_seq= lo_seq+data_len;
0056804 if (tcp_Gmod4G(hi_seq, tcp_conn->tc_RCV_HI))
0056805 {
0056806 data_len= tcp_conn->tc_RCV_HI-lo_seq;
0056807 tmp_data= bf_cut(tcp_data, 0, data_len);
bf_cut()
If a section of a linked list needs to be duplicated, bf_cut(data, offset, length) is called. For example, if a section of length 50 starting at an offset of 75 of the linked list below needs to be duplicated, bf_cut(data, 75, 50) is called:
Note that the original linked list remains unchanged and that acc_linkC for all the accessors in the new linked list is one.
If length (the second parameter) is zero, simply duplicate the first accessor in the linked list but set acc_length=0 and acc_next=null. In other words, create a linked list of length one accessor whose acc_length is 0.
bf_cut() is used in a number of scenarios, including cutting a received ethernet packet to size.
For a full description of the network service's buffer management, click here.
0056808 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056809 tcp_data= tmp_data;
0056810 hi_seq= lo_seq+data_len;
0056811 tcp_hdr_flags &= ~THF_FIN;
0056812 }
0056813 assert (tcp_LEmod4G (hi_seq, tcp_conn->tc_RCV_HI));
0056814
0056815 rcvd_data= tcp_conn->tc_rcvd_data;
0056816 tcp_conn->tc_rcvd_data= 0;
0056817 tmp_data= bf_append(rcvd_data, tcp_data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0056818 tcp_conn->tc_rcvd_data= tmp_data;
0056819 tcp_conn->tc_RCV_NXT= hi_seq;
0056820
0056821 if ((tcp_hdr_flags & THF_FIN) &&
0056822 tcp_Lmod4G(tcp_conn->tc_RCV_NXT, tcp_conn->tc_RCV_HI) &&
0056823 !(tcp_conn->tc_flags & TCF_FIN_RECV))
0056824 {
0056825 tcp_conn->tc_RCV_NXT++;
0056826 tcp_conn->tc_flags |= TCF_FIN_RECV;
0056827 }
0056828
0056829 if (tcp_conn->tc_fd && (tcp_conn->tc_fd->tf_flags & TFF_READ_IP))
0056830 tcp_fd_read(tcp_conn, 1);
0056831
0056832 DIFBLOCK(2, (tcp_conn->tc_RCV_NXT == tcp_conn->tc_RCV_HI),
0056833 printf("conn[[%d] full receive buffer\n",
0056834 tcp_conn-tcp_conn_table));
0056835
0056836 if (tcp_conn->tc_adv_data == NULL)
0056837 return;
0056838 if (tcp_hdr_flags & THF_FIN)
0056839 {
0056840 #if !CRAMPED
0056841 printf("conn[%d]: advanced data after FIN\n",
0056842 tcp_conn-tcp_conn_table);
0056843 #endif
0056844 tcp_data= tcp_conn->tc_adv_data;
0056845 tcp_conn->tc_adv_data= NULL;
0056846 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056847 return;
0056848 }
0056849
0056850 lo_seq= tcp_conn->tc_adv_seq;
0056851 if (tcp_Gmod4G(lo_seq, tcp_conn->tc_RCV_NXT))
0056852 return; /* Not yet */
0056853
0056854 tcp_data= tcp_conn->tc_adv_data;
0056855 tcp_conn->tc_adv_data= NULL;
0056856
0056857 data_len= bf_bufsize(tcp_data);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0056858 if (tcp_Lmod4G(lo_seq, tcp_conn->tc_RCV_NXT))
0056859 {
0056860 offset= tcp_conn->tc_RCV_NXT-lo_seq;
0056861 if (offset >= data_len)
0056862 {
0056863 bf_afree(tcp_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056864 return;
0056865 }
0056866 tcp_data= bf_delhead(tcp_data, offset);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0056867 lo_seq += offset;
0056868 data_len -= offset;
0056869 }
0056870 assert (lo_seq == tcp_conn->tc_RCV_NXT);
0056871
0056872 hi_seq= lo_seq+data_len;
0056873 assert (tcp_LEmod4G (hi_seq, tcp_conn->tc_RCV_HI));
0056874
0056875 rcvd_data= tcp_conn->tc_rcvd_data;
0056876 tcp_conn->tc_rcvd_data= 0;
0056877 tmp_data= bf_append(rcvd_data, tcp_data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0056878 tcp_conn->tc_rcvd_data= tmp_data;
0056879 tcp_conn->tc_RCV_NXT= hi_seq;
0056880
0056881 assert (tcp_conn->tc_RCV_LO + bf_bufsize(tcp_conn->tc_rcvd_data) ==
0056882 tcp_conn->tc_RCV_NXT ||
0056883 (tcp_print_conn(tcp_conn), printf("\n"), 0));
0056884
0056885 if (tcp_conn->tc_fd && (tcp_conn->tc_fd->tf_flags & TFF_READ_IP))
0056886 tcp_fd_read(tcp_conn, 1);
0056887
0056888 adv_data= tcp_conn->tc_adv_data;
0056889 if (adv_data != NULL)
0056890 {
0056891 /* Try to use advanced data. */
0056892 adv_seq= tcp_conn->tc_adv_seq;
0056893 nxt= tcp_conn->tc_RCV_NXT;
0056894
0056895 if (tcp_Gmod4G(adv_seq, nxt))
0056896 return; /* not yet */
0056897
0056898 tcp_conn->tc_adv_data= NULL;
0056899 data_len= bf_bufsize(adv_data);
0056900
0056901 if (tcp_Lmod4G(adv_seq, nxt))
0056902 {
0056903 if (tcp_LEmod4G(adv_seq+data_len, nxt))
0056904 {
0056905 /* Data is not needed anymore. */
0056906 bf_afree(adv_data);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0056907 return;
0056908 }
0056909
0056910 len_diff= nxt-adv_seq;
0056911 adv_data= bf_delhead(adv_data, len_diff);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0056912 data_len -= len_diff;
0056913 }
0056914
0056915 DBLOCK(1, printf("using advanced data\n"));
0056916
0056917 /* Append data to the input buffer */
0056918 if (tcp_conn->tc_rcvd_data == NULL)
0056919 {
0056920 tcp_conn->tc_rcvd_data= adv_data;
0056921 }
0056922 else
0056923 {
0056924 tcp_conn->tc_rcvd_data=
0056925 bf_append(tcp_conn->tc_rcvd_data, adv_data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0056926 }
0056927 tcp_conn->tc_SND_NXT += data_len;
0056928 assert(tcp_check_conn(tcp_conn));
0056929
0056930 if (tcp_conn->tc_fd &&
0056931 (tcp_conn->tc_fd->tf_flags & TFF_READ_IP))
0056932 {
0056933 tcp_fd_read(tcp_conn, 1);
0056934 }
0056935 }
0056936 }
0056937
0056938 PRIVATE void process_advanced_data(tcp_conn, tcp_hdr, tcp_data, data_len)
0056939 tcp_conn_t *tcp_conn;
0056940 tcp_hdr_t *tcp_hdr;
0056941 acc_t *tcp_data;
0056942 int data_len;
0056943 {
0056944 u32_t seq, adv_seq;
0056945 acc_t *adv_data;
0056946
0056947 assert(tcp_conn->tc_busy);
0056948
0056949 /* Note, tcp_data will be freed by the caller. */
0056950
0056951 /* Always send an ACK, this allows the sender to do a fast
0056952 * retransmit.
0056953 */
0056954 tcp_conn->tc_flags |= TCF_SEND_ACK;
0056955 tcp_conn_write(tcp_conn, 1);
0056956
0056957 if (tcp_hdr->th_flags & THF_URG)
0056958 return; /* Urgent data is to complicated */
0056959 if (tcp_hdr->th_flags & THF_PSH)
0056960 tcp_conn->tc_flags |= TCF_RCV_PUSH;
0056961 seq= ntohl(tcp_hdr->th_seq_nr);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0056962
0056963 /* Make sure that the packet doesn't fall outside of the window
0056964 * we offered.
0056965 */
0056966 if (tcp_Gmod4G(seq+data_len, tcp_conn->tc_RCV_HI))
0056967 return;
0056968
0056969 adv_data= tcp_conn->tc_adv_data;
0056970 adv_seq= tcp_conn->tc_adv_seq;
0056971 tcp_conn->tc_adv_data= NULL;
0056972
0056973 tcp_data->acc_linkC++;
0056974 if (adv_data == NULL)
0056975 {
0056976 adv_seq= seq;
0056977 adv_data= tcp_data;
0056978 }
0056979 else if (seq + data_len == adv_seq)
0056980 {
0056981 /* New data fits right before exiting data. */
0056982 adv_data= bf_append(tcp_data, adv_data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0056983 adv_seq= seq;
0056984 }
0056985 else if (adv_seq + bf_bufsize(adv_data) == seq)
0056986 {
0056987 /* New data fits right after exiting data. */
0056988 adv_data= bf_append(adv_data, tcp_data);
bf_append()
bf_append() appends one accessor linked list to another accessor linked list. For example, if the payload of an ethernet packet (1500 bytes) is appended to an ethernet header (14 bytes):
the resulting linked list is as follows:
0056989 }
0056990 else
0056991 {
0056992 /* New data doesn't fit. */
0056993 bf_afree(tcp_data);
0056994 }
0056995 tcp_conn->tc_adv_data= adv_data;
0056996 tcp_conn->tc_adv_seq= adv_seq;
0056997 }
0056998
0056999 PRIVATE void create_RST(tcp_conn, ip_hdr, tcp_hdr, data_len)
0057000 tcp_conn_t *tcp_conn;
0057001 ip_hdr_t *ip_hdr;
0057002 tcp_hdr_t *tcp_hdr;
0057003 int data_len;
0057004 {
0057005 acc_t *tmp_ipopt, *tmp_tcpopt, *tcp_pack;
0057006 ip_hdropt_t ip_hdropt;
0057007 tcp_hdropt_t tcp_hdropt;
0057008 acc_t *RST_acc;
0057009 ip_hdr_t *RST_ip_hdr;
0057010 tcp_hdr_t *RST_tcp_hdr;
0057011 char *ptr2RSThdr;
0057012 size_t pack_size, ip_hdr_len;
0057013
0057014 DBLOCK(0x10, printf("in create_RST, bad pack is:\n");
0057015 tcp_print_pack(ip_hdr, tcp_hdr); tcp_print_state(tcp_conn);
0057016 printf("\n"));
0057017
0057018 assert(tcp_conn->tc_busy);
0057019
0057020 /* Only send RST packets in reponse to actual data (or SYN, FIN)
0057021 * this solves a problem during connection shutdown. The problem
0057022 * is the follow senario: a senders closes the connection instead
0057023 * of doing a shutdown and waiting for the receiver to shutdown.
0057024 * The receiver is slow in processing the last data. After the
0057025 * sender has completely closed the connection, the receiver
0057026 * sends a window update which triggers the sender to send a
0057027 * RST. The receiver closes the connection in reponse to the RST.
0057028 */
0057029 if ((tcp_hdr->th_flags & (THF_FIN|THF_SYN)) == 0 &&
0057030 data_len == 0)
0057031 {
0057032 #if DEBUG
0057033 { printf("tcp_recv`create_RST: no data, no RST\n"); }
0057034 #endif
0057035 return;
0057036 }
0057037
0057038 tmp_ipopt= tcp_conn->tc_remipopt;
0057039 if (tmp_ipopt)
0057040 tmp_ipopt->acc_linkC++;
0057041 tmp_tcpopt= tcp_conn->tc_tcpopt;
0057042 if (tmp_tcpopt)
0057043 tmp_tcpopt->acc_linkC++;
0057044
0057045 tcp_extract_ipopt (tcp_conn, ip_hdr);
0057046 tcp_extract_tcpopt (tcp_conn, tcp_hdr);
0057047
0057048 RST_acc= tcp_make_header (tcp_conn, &RST_ip_hdr, &RST_tcp_hdr,
0057049 (acc_t *)0);
0057050
0057051 if (tcp_conn->tc_remipopt)
0057052 bf_afree(tcp_conn->tc_remipopt);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0057053 tcp_conn->tc_remipopt= tmp_ipopt;
0057054 if (tcp_conn->tc_tcpopt)
0057055 bf_afree(tcp_conn->tc_tcpopt);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0057056 tcp_conn->tc_tcpopt= tmp_tcpopt;
0057057
0057058 RST_ip_hdr->ih_src= ip_hdr->ih_dst;
0057059 RST_ip_hdr->ih_dst= ip_hdr->ih_src;
0057060
0057061 RST_tcp_hdr->th_srcport= tcp_hdr->th_dstport;
0057062 RST_tcp_hdr->th_dstport= tcp_hdr->th_srcport;
0057063 if (tcp_hdr->th_flags & THF_ACK)
0057064 {
0057065 RST_tcp_hdr->th_seq_nr= tcp_hdr->th_ack_nr;
0057066 RST_tcp_hdr->th_flags= THF_RST;
0057067 }
0057068 else
0057069 {
0057070 RST_tcp_hdr->th_seq_nr= 0;
0057071 RST_tcp_hdr->th_ack_nr=
0057072 htonl(
0057073 ntohl(tcp_hdr->th_seq_nr)+
0057074 data_len +
0057075 (tcp_hdr->th_flags & THF_SYN ? 1 : 0) +
0057076 (tcp_hdr->th_flags & THF_FIN ? 1 : 0));
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0057077 RST_tcp_hdr->th_flags= THF_RST|THF_ACK;
0057078 }
0057079
0057080 pack_size= bf_bufsize(RST_acc);
bf_bufsize()
bf_bufsize() returns the total buffer size of a linked list of accessors (i.e., the sum of acc_length for the accessors in a linked list).
For a detailed description of the network service's buffer management, click here.
0057081 RST_ip_hdr->ih_length= htons(pack_size);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0057082 RST_tcp_hdr->th_window= htons(tcp_conn->tc_rcv_wnd);
htons() / ntohs() / htonl() / ntohl()
From htons(3):
"htons() converts a 16-bit quantity from host byte order to network byte order."
Different CPU architectures group multiple bytes differently. For example, on a "little-endian" machine (an example of which is the Intel CPU), the value 0x1234 is stored in memory as 0x3412. However, on a "big-endian" machine, the value 0x1234 is stored in memory as 0x1234.
It is important that values in a header are sent across a network in a consistent manner independent of the architecture of the sending or receiving system. For this reason, a standard was chosen. The standard chosen was big-endian although it could have just as well been little-endian.
htons() is defined in /include/net/hton.h, as:
#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00))
ntohs() converts a 16-bit quantity from network byte order to host byte order, the reverse of htons().
htonl() and ntohl() are identical to htons() and ntohs() except that they convert 32-bit quantities instead of 16-bit quantities.
Processes generally supply header information when sending packets. The data in these fields is converted to the network format (i.e., big-endian) by the process before the process copies the data to the network service.
0057083 RST_tcp_hdr->th_chksum= 0;
0057084
0057085 RST_acc->acc_linkC++;
0057086 ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2;
0057087 tcp_pack= bf_delhead(RST_acc, ip_hdr_len);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0057088 RST_tcp_hdr->th_chksum= ~tcp_pack_oneCsum (RST_ip_hdr, tcp_pack);
tcp_pack_oneCsum()
tcp_pack_oneCsum() computes the checksum of a tcp packet (including several fields of its ip header). It accomplishes this by computing the checksum (by calling oneC_sum()) of each of the tcp packet's buffers.
Note that a checksum is used to determine if errors occurred during the transmission of data.
0057089 bf_afree(tcp_pack);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0057090
0057091 DBLOCK(2, tcp_print_pack(ip_hdr, tcp_hdr); printf("\n");
0057092 tcp_print_pack(RST_ip_hdr, RST_tcp_hdr); printf("\n"));
0057093
0057094 if (tcp_conn->tc_frag2send)
0057095 bf_afree(tcp_conn->tc_frag2send);
bf_afree()
After a chain of accessors is no longer needed, the chain (and not simply the single accessor passed as the parameter) can be freed by calling bf_free(). However, if either acc_linkC or buf_linkC of one of the accessors in the linked list is not equal to one (1), the entire chain will not be freed. For example, if buf_afree(acc1) is called for the following chain:
Then the resulting chain will be:
bf_afree() returns acc1 (accessors[63]) to acc_freelist (recall that acc_freelist is the linked list of acc_t's without an associated buffer). However, buffers512[127] cannot be freed because acc2 (accessors[64]) still references it.
bf_afree() is called after an accessor's associated data is no longer needed (for example, after a packet has been sent off by the ethernet driver).
0057096 tcp_conn->tc_frag2send= RST_acc;
0057097 tcp_conn_write(tcp_conn, 1);
0057098 }
0057099
0057100 PUBLIC void
0057101 tcp_fd_read(tcp_conn, enq)
0057102 tcp_conn_t *tcp_conn;
0057103 int enq; /* Enqueue writes. */
0057104 {
0057105 tcp_fd_t *tcp_fd;
0057106 size_t data_size, read_size;
0057107 acc_t *data;
0057108 int fin_recv, urg, push, result;
0057109 i32_t old_window, new_window;
0057110
0057111 assert(tcp_conn->tc_busy);
0057112
0057113 tcp_fd= tcp_conn->tc_fd;
0057114
0057115 assert (tcp_fd->tf_flags & TFF_READ_IP);
0057116 if (tcp_conn->tc_state == TCS_CLOSED)
0057117 {
0057118 if (tcp_fd->tf_read_offset)
0057119 tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset);
0057120 else
0057121 tcp_reply_read (tcp_fd, tcp_conn->tc_error);
0057122 return;
0057123 }
0057124
0057125 urg= tcp_Gmod4G(tcp_conn->tc_RCV_UP, tcp_conn->tc_RCV_LO);
0057126 push= (tcp_conn->tc_flags & TCF_RCV_PUSH);
0057127 fin_recv= (tcp_conn->tc_flags & TCF_FIN_RECV);
0057128
0057129 data_size= tcp_conn->tc_RCV_NXT-tcp_conn->tc_RCV_LO;
0057130 if (fin_recv)
0057131 data_size--;
0057132 if (urg)
0057133 read_size= tcp_conn->tc_RCV_UP-tcp_conn->tc_RCV_LO;
0057134 else
0057135 read_size= data_size;
0057136
0057137 if (read_size >= tcp_fd->tf_read_count)
0057138 read_size= tcp_fd->tf_read_count;
0057139 else if (!push && !fin_recv && !urg &&
0057140 data_size < TCP_MIN_RCV_WND_SIZE)
0057141 {
0057142 /* Defer the copy out until later. */
0057143 return;
0057144 }
0057145 else if (data_size == 0 && !fin_recv)
0057146 {
0057147 /* No data, and no end of file. */
0057148 return;
0057149 }
0057150
0057151 if (read_size)
0057152 {
0057153 if (urg && !(tcp_fd->tf_flags & TFF_RECV_URG))
0057154 {
0057155 if (tcp_fd->tf_read_offset)
0057156 {
0057157 tcp_reply_read (tcp_fd,
0057158 tcp_fd->tf_read_offset);
0057159 }
0057160 else
0057161 {
0057162 tcp_reply_read (tcp_fd, EURG);
0057163 }
0057164 return;
0057165 }
0057166 else if (!urg && (tcp_fd->tf_flags & TFF_RECV_URG))
0057167 {
0057168 if (tcp_fd->tf_read_offset)
0057169 {
0057170 tcp_reply_read (tcp_fd,
0057171 tcp_fd->tf_read_offset);
0057172 }
0057173 else
0057174 {
0057175 tcp_reply_read(tcp_fd, ENOURG);
0057176 }
0057177 return;
0057178 }
0057179
0057180 if (read_size == data_size)
0057181 {
0057182 data= tcp_conn->tc_rcvd_data;
0057183 data->acc_linkC++;
0057184 }
0057185 else
0057186 {
0057187 data= bf_cut(tcp_conn->tc_rcvd_data, 0, read_size);
bf_cut()
If a section of a linked list needs to be duplicated, bf_cut(data, offset, length) is called. For example, if a section of length 50 starting at an offset of 75 of the linked list below needs to be duplicated, bf_cut(data, 75, 50) is called:
Note that the original linked list remains unchanged and that acc_linkC for all the accessors in the new linked list is one.
If length (the second parameter) is zero, simply duplicate the first accessor in the linked list but set acc_length=0 and acc_next=null. In other words, create a linked list of length one accessor whose acc_length is 0.
bf_cut() is used in a number of scenarios, including cutting a received ethernet packet to size.
For a full description of the network service's buffer management, click here.
0057188 }
0057189 result= (*tcp_fd->tf_put_userdata) (tcp_fd->tf_srfd,
0057190 tcp_fd->tf_read_offset, data, FALSE);
0057191 if (result<0)
0057192 {
0057193 if (tcp_fd->tf_read_offset)
0057194 tcp_reply_read(tcp_fd, tcp_fd->
0057195 tf_read_offset);
0057196 else
0057197 tcp_reply_read(tcp_fd, result);
0057198 return;
0057199 }
0057200 tcp_fd->tf_read_offset += read_size;
0057201 tcp_fd->tf_read_count -= read_size;
0057202
0057203 if (data_size == read_size)
0057204 {
0057205 bf_afree(tcp_conn->tc_rcvd_data);
0057206 tcp_conn->tc_rcvd_data= 0;
0057207 }
0057208 else
0057209 {
0057210 tcp_conn->tc_rcvd_data=
0057211 bf_delhead(tcp_conn->tc_rcvd_data,
0057212 read_size);
bf_delhead()
If only the beginning of a linked list can be freed, bf_delhead() is called. If acc_linkC and buf_linkC are one for all of the relevant accessors and their associated buffers in the linked list, the operation is straight-forward:
bf_delhead() is often called to remove the header (e.g., ip header) from a packet.
For a detailed description of the network service's buffer management, click here.
0057213 }
0057214 tcp_conn->tc_RCV_LO += read_size;
0057215 data_size -= read_size;
0057216 }
0057217 if (tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_LO <= (tcp_conn->
0057218 tc_rcv_wnd-tcp_conn->tc_mss))
0057219 {
0057220 old_window= tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_NXT;
0057221 tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO +
0057222 tcp_conn->tc_rcv_wnd;
0057223 new_window= tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_NXT;
0057224 assert(old_window >=0 && new_window >= old_window);
0057225 if (old_window < tcp_conn->tc_mss &&
0057226 new_window >= tcp_conn->tc_mss)
0057227 {
0057228 tcp_conn->tc_flags |= TCF_SEND_ACK;
0057229 DBLOCK(2, printf("opening window\n"));
0057230 tcp_conn_write(tcp_conn, 1);
0057231 }
0057232 }
0057233 if (tcp_conn->tc_rcvd_data == NULL &&
0057234 tcp_conn->tc_adv_data == NULL)
0057235 {
0057236 /* Out of data, clear PUSH flag and reply to a read. */
0057237 tcp_conn->tc_flags &= ~TCF_RCV_PUSH;
0057238 }
0057239 if (fin_recv || urg || !tcp_fd->tf_read_count)
0057240 {
0057241 tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset);
0057242 return;
0057243 }
0057244 if (tcp_fd->tf_read_offset)
0057245 {
0057246 tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset);
0057247 return;
0057248 }
0057249 }
0057250
0057251 /*
0057252 * $PchId: tcp_recv.c,v 1.13.2.1 2000/05/02 18:53:06 philip Exp $
0057253 */