]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
Import SnpDxe, Tcp4Dxe, Udp4Dxe and MnpDxe.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Tcp4Dxe / Tcp4Output.c
CommitLineData
8a67d61d 1/** @file\r
2\r
3Copyright (c) 2005 - 2006, Intel Corporation\r
4All rights reserved. This program and the accompanying materials\r
5are licensed and made available under the terms and conditions of the BSD License\r
6which accompanies this distribution. The full text of the license may be found at\r
7http://opensource.org/licenses/bsd-license.php\r
8\r
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11\r
12Module Name:\r
13\r
14 Tcp4Output.c\r
15\r
16Abstract:\r
17\r
18 TCP output process routines.\r
19\r
20\r
21**/\r
22\r
23#include "Tcp4Main.h"\r
24\r
25STATIC UINT8 mTcpOutFlag[] = {\r
26 0, // TCP_CLOSED\r
27 0, // TCP_LISTEN\r
28 TCP_FLG_SYN, // TCP_SYN_SENT\r
29 TCP_FLG_SYN | TCP_FLG_ACK, // TCP_SYN_RCVD\r
30 TCP_FLG_ACK, // TCP_ESTABLISHED\r
31 TCP_FLG_FIN | TCP_FLG_ACK, // TCP_FIN_WAIT_1\r
32 TCP_FLG_ACK, // TCP_FIN_WAIT_2\r
33 TCP_FLG_ACK | TCP_FLG_FIN, // TCP_CLOSING\r
34 TCP_FLG_ACK, // TCP_TIME_WAIT\r
35 TCP_FLG_ACK, // TCP_CLOSE_WAIT\r
36 TCP_FLG_FIN | TCP_FLG_ACK // TCP_LAST_ACK\r
37};\r
38\r
39\r
40/**\r
41 Compute the sequence space left in the old receive window.\r
42\r
43 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
44\r
45 @return The sequence space left in the old receive window.\r
46\r
47**/\r
48UINT32\r
49TcpRcvWinOld (\r
50 IN TCP_CB *Tcb\r
51 )\r
52{\r
53 UINT32 OldWin;\r
54\r
55 OldWin = 0;\r
56\r
57 if (TCP_SEQ_GT (Tcb->RcvWl2 + Tcb->RcvWnd, Tcb->RcvNxt)) {\r
58\r
59 OldWin = TCP_SUB_SEQ (\r
60 Tcb->RcvWl2 + Tcb->RcvWnd,\r
61 Tcb->RcvNxt\r
62 );\r
63 }\r
64\r
65 return OldWin;\r
66}\r
67\r
68\r
69/**\r
70 Compute the current receive window.\r
71\r
72 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
73\r
74 @return The size of the current receive window, in bytes.\r
75\r
76**/\r
77UINT32\r
78TcpRcvWinNow (\r
79 IN TCP_CB *Tcb\r
80 )\r
81{\r
82 SOCKET *Sk;\r
83 UINT32 Win;\r
84 UINT32 Increase;\r
85 UINT32 OldWin;\r
86\r
87 Sk = Tcb->Sk;\r
88 ASSERT (Sk);\r
89\r
90 OldWin = TcpRcvWinOld (Tcb);\r
91\r
92 Win = SockGetFreeSpace (Sk, SOCK_RCV_BUF);\r
93\r
94 Increase = 0;\r
95 if (Win > OldWin) {\r
96 Increase = Win - OldWin;\r
97 }\r
98\r
99 //\r
100 // Receiver's SWS: don't advertise a bigger window\r
101 // unless it can be increased by at least one Mss or\r
102 // half of the receive buffer.\r
103 //\r
104 if ((Increase > Tcb->SndMss) ||\r
105 (2 * Increase >= GET_RCV_BUFFSIZE (Sk))) {\r
106\r
107 return Win;\r
108 }\r
109\r
110 return OldWin;\r
111}\r
112\r
113\r
114/**\r
115 Compute the value to fill in the window size field\r
116 of the outgoing segment.\r
117\r
118 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
119 @param Syn The flag to indicate whether the outgoing segment is a SYN\r
120 segment.\r
121\r
122 @return The value of the local receive window size used to fill the outing segment.\r
123\r
124**/\r
125STATIC\r
126UINT16\r
127TcpComputeWnd (\r
128 IN TCP_CB *Tcb,\r
129 IN BOOLEAN Syn\r
130 )\r
131{\r
132 UINT32 Wnd;\r
133\r
134 //\r
135 // RFC requires that initial window not be scaled\r
136 //\r
137 if (Syn) {\r
138\r
139 Wnd = GET_RCV_BUFFSIZE (Tcb->Sk);\r
140 } else {\r
141\r
142 Wnd = TcpRcvWinNow (Tcb);\r
143\r
144 Tcb->RcvWnd = Wnd;\r
145 }\r
146\r
147 Wnd = NET_MIN (Wnd >> Tcb->RcvWndScale, 0xffff);\r
148 return NTOHS ((UINT16) Wnd);\r
149}\r
150\r
151\r
152/**\r
153 Get the maximum SndNxt.\r
154\r
155 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
156\r
157 @return The sequence number of the maximum SndNxt.\r
158\r
159**/\r
160TCP_SEQNO\r
161TcpGetMaxSndNxt (\r
162 IN TCP_CB *Tcb\r
163 )\r
164{\r
165 NET_LIST_ENTRY *Entry;\r
166 NET_BUF *Nbuf;\r
167\r
168 if (NetListIsEmpty (&Tcb->SndQue)) {\r
169 return Tcb->SndNxt;\r
170 }\r
171\r
172 Entry = Tcb->SndQue.BackLink;\r
173 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
174\r
175 ASSERT (TCP_SEQ_GEQ (TCPSEG_NETBUF (Nbuf)->End, Tcb->SndNxt));\r
176 return TCPSEG_NETBUF (Nbuf)->End;\r
177}\r
178\r
179\r
180/**\r
181 Compute how much data to send.\r
182\r
183 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
184 @param Force Whether to ignore the sender's SWS avoidance algorithm and send\r
185 out data by force.\r
186\r
187 @return The length of the data can be sent, if 0, no data can be sent.\r
188\r
189**/\r
190UINT32\r
191TcpDataToSend (\r
192 IN TCP_CB *Tcb,\r
193 IN INTN Force\r
194 )\r
195{\r
196 SOCKET *Sk;\r
197 UINT32 Win;\r
198 UINT32 Len;\r
199 UINT32 Left;\r
200 UINT32 Limit;\r
201\r
202 Sk = Tcb->Sk;\r
203 ASSERT (Sk);\r
204\r
205 //\r
206 // TCP should NOT send data beyond the send window\r
207 // and congestion window. The right edge of send\r
208 // window is defined as SND.WL2 + SND.WND. The right\r
209 // edge of congestion window is defined as SND.UNA +\r
210 // CWND.\r
211 //\r
212 Win = 0;\r
213 Limit = Tcb->SndWl2 + Tcb->SndWnd;\r
214\r
215 if (TCP_SEQ_GT (Limit, Tcb->SndUna + Tcb->CWnd)) {\r
216\r
217 Limit = Tcb->SndUna + Tcb->CWnd;\r
218 }\r
219\r
220 if (TCP_SEQ_GT (Limit, Tcb->SndNxt)) {\r
221 Win = TCP_SUB_SEQ (Limit, Tcb->SndNxt);\r
222 }\r
223\r
224 //\r
225 // The data to send contains two parts: the data on the\r
226 // socket send queue, and the data on the TCB's send\r
227 // buffer. The later can be non-zero if the peer shrinks\r
228 // its advertised window.\r
229 //\r
230 Left = GET_SND_DATASIZE (Sk) +\r
231 TCP_SUB_SEQ (TcpGetMaxSndNxt (Tcb), Tcb->SndNxt);\r
232\r
233 Len = NET_MIN (Win, Left);\r
234\r
235 if (Len > Tcb->SndMss) {\r
236 Len = Tcb->SndMss;\r
237 }\r
238\r
239 if (Force || (Len == 0 && Left == 0)) {\r
240 return Len;\r
241 }\r
242\r
243 if (Len == 0 && Left != 0) {\r
244 goto SetPersistTimer;\r
245 }\r
246\r
247 //\r
248 // Sender's SWS avoidance: Don't send a small segment unless\r
249 // a)A full-sized segment can be sent,\r
250 // b)at least one-half of the maximum sized windows that\r
251 // the other end has ever advertised.\r
252 // c)It can send everything it has and either it isn't\r
253 // expecting an ACK or the Nagle algorithm is disabled.\r
254 //\r
255 if ((Len == Tcb->SndMss) || (2 * Len >= Tcb->SndWndMax)) {\r
256\r
257 return Len;\r
258 }\r
259\r
260 if ((Len == Left) &&\r
261 ((Tcb->SndNxt == Tcb->SndUna) ||\r
262 TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_NAGLE))) {\r
263\r
264 return Len;\r
265 }\r
266\r
267 //\r
268 // RFC1122 suggests to set a timer when SWSA forbids TCP\r
269 // sending more data, and combine it with probe timer.\r
270 //\r
271SetPersistTimer:\r
272 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
273\r
274 TCP4_DEBUG_WARN (\r
275 ("TcpDataToSend: enter persistent state for TCB %x\n",\r
276 Tcb)\r
277 );\r
278\r
279 TcpSetProbeTimer (Tcb);\r
280 }\r
281\r
282 return 0;\r
283}\r
284\r
285\r
286/**\r
287 Build the TCP header of the TCP segment and transmit the\r
288 segment by IP.\r
289\r
290 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
291 @param Nbuf Pointer to the buffer containing the segment to be sent out.\r
292\r
293 @retval 0 The segment is sent out successfully.\r
294 @retval other Error condition occurred.\r
295\r
296**/\r
297STATIC\r
298INTN\r
299TcpTransmitSegment (\r
300 IN TCP_CB *Tcb,\r
301 IN NET_BUF *Nbuf\r
302 )\r
303{\r
304 UINT16 Len;\r
305 TCP_HEAD *Head;\r
306 TCP_SEG *Seg;\r
307 BOOLEAN Syn;\r
308 UINT32 DataLen;\r
309\r
310 ASSERT (Nbuf && (Nbuf->Tcp == NULL) && TcpVerifySegment (Nbuf));\r
311\r
312 DataLen = Nbuf->TotalSize;\r
313\r
314 Seg = TCPSEG_NETBUF (Nbuf);\r
315 Syn = TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN);\r
316\r
317 if (Syn) {\r
318\r
319 Len = TcpSynBuildOption (Tcb, Nbuf);\r
320 } else {\r
321\r
322 Len = TcpBuildOption (Tcb, Nbuf);\r
323 }\r
324\r
325 ASSERT ((Len % 4 == 0) && (Len <= 40));\r
326\r
327 Len += sizeof (TCP_HEAD);\r
328\r
329 Head = (TCP_HEAD *) NetbufAllocSpace (\r
330 Nbuf,\r
331 sizeof (TCP_HEAD),\r
332 NET_BUF_HEAD\r
333 );\r
334\r
335 ASSERT (Head != NULL);\r
336\r
337 Nbuf->Tcp = Head;\r
338\r
339 Head->SrcPort = Tcb->LocalEnd.Port;\r
340 Head->DstPort = Tcb->RemoteEnd.Port;\r
341 Head->Seq = NTOHL (Seg->Seq);\r
342 Head->Ack = NTOHL (Tcb->RcvNxt);\r
343 Head->HeadLen = (UINT8) (Len >> 2);\r
344 Head->Res = 0;\r
345 Head->Wnd = TcpComputeWnd (Tcb, Syn);\r
346 Head->Checksum = 0;\r
347\r
348 //\r
349 // Check whether to set the PSH flag.\r
350 //\r
351 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_PSH);\r
352\r
353 if (DataLen != 0) {\r
354 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_PSH) &&\r
355 TCP_SEQ_BETWEEN (Seg->Seq, Tcb->SndPsh, Seg->End)) {\r
356\r
357 TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
358 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_SND_PSH);\r
359\r
360 } else if ((Seg->End == Tcb->SndNxt) &&\r
361 (GET_SND_DATASIZE (Tcb->Sk) == 0)) {\r
362\r
363 TCP_SET_FLG (Seg->Flag, TCP_FLG_PSH);\r
364 }\r
365 }\r
366\r
367 //\r
368 // Check whether to set the URG flag and the urgent pointer.\r
369 //\r
370 TCP_CLEAR_FLG (Seg->Flag, TCP_FLG_URG);\r
371\r
372 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_SND_URG) &&\r
373 TCP_SEQ_LEQ (Seg->Seq, Tcb->SndUp)) {\r
374\r
375 TCP_SET_FLG (Seg->Flag, TCP_FLG_URG);\r
376\r
377 if (TCP_SEQ_LT (Tcb->SndUp, Seg->End)) {\r
378\r
379 Seg->Urg = (UINT16) TCP_SUB_SEQ (Tcb->SndUp, Seg->Seq);\r
380 } else {\r
381\r
382 Seg->Urg = (UINT16) NET_MIN (\r
383 TCP_SUB_SEQ (Tcb->SndUp,\r
384 Seg->Seq),\r
385 0xffff\r
386 );\r
387 }\r
388 }\r
389\r
390 Head->Flag = Seg->Flag;\r
391 Head->Urg = NTOHS (Seg->Urg);\r
392 Head->Checksum = TcpChecksum (Nbuf, Tcb->HeadSum);\r
393\r
394 //\r
395 // update the TCP session's control information\r
396 //\r
397 Tcb->RcvWl2 = Tcb->RcvNxt;\r
398 if (Syn) {\r
399 Tcb->RcvWnd = NTOHS (Head->Wnd);\r
400 }\r
401\r
402 //\r
403 // clear delayedack flag\r
404 //\r
405 Tcb->DelayedAck = 0;\r
406\r
407 return TcpSendIpPacket (Tcb, Nbuf, Tcb->LocalEnd.Ip, Tcb->RemoteEnd.Ip);\r
408}\r
409\r
410\r
411/**\r
412 Get a segment from the Tcb's SndQue.\r
413\r
414 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
415 @param Seq The sequence number of the segment.\r
416 @param Len The maximum length of the segment.\r
417\r
418 @return Pointer to the segment, if NULL some error occurred.\r
419\r
420**/\r
421NET_BUF *\r
422TcpGetSegmentSndQue (\r
423 IN TCP_CB *Tcb,\r
424 IN TCP_SEQNO Seq,\r
425 IN UINT32 Len\r
426 )\r
427{\r
428 NET_LIST_ENTRY *Head;\r
429 NET_LIST_ENTRY *Cur;\r
430 NET_BUF *Node;\r
431 TCP_SEG *Seg;\r
432 NET_BUF *Nbuf;\r
433 TCP_SEQNO End;\r
434 UINT8 *Data;\r
435 UINT8 Flag;\r
436 INT32 Offset;\r
437 INT32 CopyLen;\r
438\r
439 ASSERT (Tcb && TCP_SEQ_LEQ (Seq, Tcb->SndNxt) && (Len > 0));\r
440\r
441 //\r
442 // Find the segment that contains the Seq.\r
443 //\r
444 Head = &Tcb->SndQue;\r
445\r
446 Node = NULL;\r
447 Seg = NULL;\r
448\r
449 NET_LIST_FOR_EACH (Cur, Head) {\r
450 Node = NET_LIST_USER_STRUCT (Cur, NET_BUF, List);\r
451 Seg = TCPSEG_NETBUF (Node);\r
452\r
453 if (TCP_SEQ_LT (Seq, Seg->End) && TCP_SEQ_LEQ (Seg->Seq, Seq)) {\r
454\r
455 break;\r
456 }\r
457 }\r
458\r
459 ASSERT (Cur != Head);\r
460\r
461 //\r
462 // Return the buffer if it can be returned without\r
463 // adjustment:\r
464 //\r
465 if ((Seg->Seq == Seq) &&\r
466 TCP_SEQ_LEQ (Seg->End, Seg->Seq + Len) &&\r
467 !NET_BUF_SHARED (Node)) {\r
468\r
469 NET_GET_REF (Node);\r
470 return Node;\r
471 }\r
472\r
473 //\r
474 // Create a new buffer and copy data there.\r
475 //\r
476 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
477\r
478 if (Nbuf == NULL) {\r
479 return NULL;\r
480 }\r
481\r
482 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
483\r
484 Flag = Seg->Flag;\r
485 End = Seg->End;\r
486\r
487 if (TCP_SEQ_LT (Seq + Len, Seg->End)) {\r
488 End = Seq + Len;\r
489 }\r
490\r
491 CopyLen = TCP_SUB_SEQ (End, Seq);\r
492 Offset = TCP_SUB_SEQ (Seq, Seg->Seq);\r
493\r
494 //\r
495 // If SYN is set and out of the range, clear the flag.\r
496 // Becuase the sequence of the first byte is SEG.SEQ+1,\r
497 // adjust Offset by -1. If SYN is in the range, copy\r
498 // one byte less.\r
499 //\r
500 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
501\r
502 if (TCP_SEQ_LT (Seg->Seq, Seq)) {\r
503\r
504 TCP_CLEAR_FLG (Flag, TCP_FLG_SYN);\r
505 Offset--;\r
506 } else {\r
507\r
508 CopyLen--;\r
509 }\r
510 }\r
511\r
512 //\r
513 // If FIN is set and in the range, copy one byte less,\r
514 // and if it is out of the range, clear the flag.\r
515 //\r
516 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
517\r
518 if (Seg->End == End) {\r
519\r
520 CopyLen--;\r
521 } else {\r
522\r
523 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
524 }\r
525 }\r
526\r
527 ASSERT (CopyLen >= 0);\r
528\r
529 //\r
530 // copy data to the segment\r
531 //\r
532 if (CopyLen) {\r
533 Data = NetbufAllocSpace (Nbuf, CopyLen, NET_BUF_TAIL);\r
534 ASSERT (Data);\r
535\r
536 if ((INT32) NetbufCopy (Node, Offset, CopyLen, Data) != CopyLen) {\r
537 goto OnError;\r
538 }\r
539 }\r
540\r
541 NetCopyMem (TCPSEG_NETBUF (Nbuf), Seg, sizeof (TCP_SEG));\r
542\r
543 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
544 TCPSEG_NETBUF (Nbuf)->End = End;\r
545 TCPSEG_NETBUF (Nbuf)->Flag = Flag;\r
546\r
547 return Nbuf;\r
548\r
549OnError:\r
550 NetbufFree (Nbuf);\r
551 return NULL;\r
552}\r
553\r
554\r
555/**\r
556 Get a segment from the Tcb's socket buffer.\r
557\r
558 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
559 @param Seq The sequence number of the segment.\r
560 @param Len The maximum length of the segment.\r
561\r
562 @return Pointer to the segment, if NULL some error occurred.\r
563\r
564**/\r
565NET_BUF *\r
566TcpGetSegmentSock (\r
567 IN TCP_CB *Tcb,\r
568 IN TCP_SEQNO Seq,\r
569 IN UINT32 Len\r
570 )\r
571{\r
572 NET_BUF *Nbuf;\r
573 UINT8 *Data;\r
574 UINT32 DataGet;\r
575\r
576 ASSERT (Tcb && Tcb->Sk);\r
577\r
578 Nbuf = NetbufAlloc (Len + TCP_MAX_HEAD);\r
579\r
580 if (Nbuf == NULL) {\r
581 TCP4_DEBUG_ERROR (("TcpGetSegmentSock: failed to allocate "\r
582 "a netbuf for TCB %x\n",Tcb));\r
583\r
584 return NULL;\r
585 }\r
586\r
587 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
588\r
589 DataGet = 0;\r
590\r
591 if (Len) {\r
592 //\r
593 // copy data to the segment.\r
594 //\r
595 Data = NetbufAllocSpace (Nbuf, Len, NET_BUF_TAIL);\r
596 ASSERT (Data);\r
597\r
598 DataGet = SockGetDataToSend (Tcb->Sk, 0, Len, Data);\r
599 }\r
600\r
601 NET_GET_REF (Nbuf);\r
602\r
603 TCPSEG_NETBUF (Nbuf)->Seq = Seq;\r
604 TCPSEG_NETBUF (Nbuf)->End = Seq + Len;\r
605\r
606 NetListInsertTail (&(Tcb->SndQue), &(Nbuf->List));\r
607\r
608 if (DataGet != 0) {\r
609\r
610 SockDataSent (Tcb->Sk, DataGet);\r
611 }\r
612\r
613 return Nbuf;\r
614}\r
615\r
616\r
617/**\r
618 Get a segment starting from sequence Seq of a maximum\r
619 length of Len.\r
620\r
621 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
622 @param Seq The sequence number of the segment.\r
623 @param Len The maximum length of the segment.\r
624\r
625 @return Pointer to the segment, if NULL some error occurred.\r
626\r
627**/\r
628NET_BUF *\r
629TcpGetSegment (\r
630 IN TCP_CB *Tcb,\r
631 IN TCP_SEQNO Seq,\r
632 IN UINT32 Len\r
633 )\r
634{\r
635 NET_BUF *Nbuf;\r
636\r
637 ASSERT (Tcb);\r
638\r
639 //\r
640 // Compare the SndNxt with the max sequence number sent.\r
641 //\r
642 if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {\r
643\r
644 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
645 } else {\r
646\r
647 Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);\r
648 }\r
649\r
650 ASSERT (TcpVerifySegment (Nbuf));\r
651 return Nbuf;\r
652}\r
653\r
654\r
655/**\r
656 Retransmit the segment from sequence Seq.\r
657\r
658 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
659 @param Seq The sequence number of the segment to be retransmitted.\r
660\r
661 @retval 0 Retransmission succeeded.\r
662 @retval -1 Error condition occurred.\r
663\r
664**/\r
665INTN\r
666TcpRetransmit (\r
667 IN TCP_CB *Tcb,\r
668 IN TCP_SEQNO Seq\r
669 )\r
670{\r
671 NET_BUF *Nbuf;\r
672 UINT32 Len;\r
673\r
674 //\r
675 // Compute the maxium length of retransmission. It is\r
676 // limited by three factors:\r
677 // 1. Less than SndMss\r
678 // 2. must in the current send window\r
679 // 3. will not change the boundaries of queued segments.\r
680 //\r
681 if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {\r
682 TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled "\r
683 "because send window too small for TCB %x\n", Tcb));\r
684\r
685 return 0;\r
686 }\r
687\r
688 Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);\r
689 Len = NET_MIN (Len, Tcb->SndMss);\r
690\r
691 Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);\r
692 if (Nbuf == NULL) {\r
693 return -1;\r
694 }\r
695\r
696 ASSERT (TcpVerifySegment (Nbuf));\r
697\r
698 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
699 goto OnError;\r
700 }\r
701\r
702 //\r
703 // The retransmitted buffer may be on the SndQue,\r
704 // trim TCP head because all the buffer on SndQue\r
705 // are headless.\r
706 //\r
707 ASSERT (Nbuf->Tcp);\r
708 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
709 Nbuf->Tcp = NULL;\r
710\r
711 NetbufFree (Nbuf);\r
712 return 0;\r
713\r
714OnError:\r
715 if (Nbuf != NULL) {\r
716 NetbufFree (Nbuf);\r
717 }\r
718\r
719 return -1;\r
720}\r
721\r
722\r
723/**\r
724 Check whether to send data/SYN/FIN and piggy back an ACK.\r
725\r
726 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
727 @param Force Whether to ignore the sender's SWS avoidance algorithm and send\r
728 out data by force.\r
729\r
730 @return The number of bytes sent.\r
731\r
732**/\r
733INTN\r
734TcpToSendData (\r
735 IN TCP_CB *Tcb,\r
736 IN INTN Force\r
737 )\r
738{\r
739 UINT32 Len;\r
740 INTN Sent;\r
741 UINT8 Flag;\r
742 NET_BUF *Nbuf;\r
743 TCP_SEG *Seg;\r
744 TCP_SEQNO Seq;\r
745 TCP_SEQNO End;\r
746\r
747 ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN));\r
748\r
749 Sent = 0;\r
750\r
751 if ((Tcb->State == TCP_CLOSED) ||\r
752 TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {\r
753\r
754 return 0;\r
755 }\r
756\r
757SEND_AGAIN:\r
758 //\r
759 // compute how much data can be sent\r
760 //\r
761 Len = TcpDataToSend (Tcb, Force);\r
762 Seq = Tcb->SndNxt;\r
763\r
764 Flag = mTcpOutFlag[Tcb->State];\r
765\r
766 if (Flag & TCP_FLG_SYN) {\r
767\r
768 Seq = Tcb->Iss;\r
769 Len = 0;\r
770 }\r
771\r
772 //\r
773 // only send a segment without data if SYN or\r
774 // FIN is set.\r
775 //\r
776 if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) {\r
777 return Sent;\r
778 }\r
779\r
780 Nbuf = TcpGetSegment (Tcb, Seq, Len);\r
781\r
782 if (Nbuf == NULL) {\r
783 TCP4_DEBUG_ERROR (\r
784 ("TcpToSendData: failed to get a segment for TCB %x\n",\r
785 Tcb)\r
786 );\r
787\r
788 goto OnError;\r
789 }\r
790\r
791 Seg = TCPSEG_NETBUF (Nbuf);\r
792\r
793 //\r
794 // Set the TcpSeg in Nbuf.\r
795 //\r
796 Len = Nbuf->TotalSize;\r
797 End = Seq + Len;\r
798 if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {\r
799 End++;\r
800 }\r
801\r
802 if (Flag & TCP_FLG_FIN) {\r
803 //\r
804 // Send FIN if all data is sent, and FIN is\r
805 // in the window\r
806 //\r
807 if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&\r
808 (GET_SND_DATASIZE (Tcb->Sk) == 0) &&\r
809 TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)\r
810 ) {\r
811\r
812 TCP4_DEBUG_TRACE (("TcpToSendData: send FIN "\r
813 "to peer for TCB %x in state %d\n", Tcb, Tcb->State));\r
814\r
815 End++;\r
816 } else {\r
817 TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);\r
818 }\r
819 }\r
820\r
821 Seg->Seq = Seq;\r
822 Seg->End = End;\r
823 Seg->Flag = Flag;\r
824\r
825 ASSERT (TcpVerifySegment (Nbuf));\r
826 ASSERT (TcpCheckSndQue (&Tcb->SndQue));\r
827\r
828 //\r
829 // don't send an empty segment here.\r
830 //\r
831 if (Seg->End == Seg->Seq) {\r
832 TCP4_DEBUG_WARN (("TcpToSendData: created a empty"\r
833 " segment for TCB %x, free it now\n", Tcb));\r
834\r
835 NetbufFree (Nbuf);\r
836 return Sent;\r
837 }\r
838\r
839 if (TcpTransmitSegment (Tcb, Nbuf) != 0) {\r
840 //\r
841 // TODO: double check this\r
842 //\r
843 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
844 Nbuf->Tcp = NULL;\r
845\r
846 if (Flag & TCP_FLG_FIN) {\r
847 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
848 }\r
849\r
850 goto OnError;\r
851 }\r
852\r
853 Sent += TCP_SUB_SEQ (End, Seq);\r
854\r
855 //\r
856 // All the buffer in the SndQue is headless\r
857 //\r
858 ASSERT (Nbuf->Tcp);\r
859\r
860 NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);\r
861 Nbuf->Tcp = NULL;\r
862\r
863 NetbufFree (Nbuf);\r
864\r
865 //\r
866 // update status in TCB\r
867 //\r
868 Tcb->DelayedAck = 0;\r
869\r
870 if (Flag & TCP_FLG_FIN) {\r
871 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);\r
872 }\r
873\r
874 if (TCP_SEQ_GT (End, Tcb->SndNxt)) {\r
875 Tcb->SndNxt = End;\r
876 }\r
877\r
878 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {\r
879 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);\r
880 }\r
881\r
882 //\r
883 // Enable RTT measurement only if not in retransmit.\r
884 // Karn's algorithm reqires not to update RTT when in loss.\r
885 //\r
886 if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&\r
887 !TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {\r
888\r
889 TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure "\r
890 "sequence %d for TCB %x\n", Seq, Tcb));\r
891\r
892 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);\r
893 Tcb->RttSeq = Seq;\r
894 Tcb->RttMeasure = 0;\r
895 }\r
896\r
897 if (Len == Tcb->SndMss) {\r
898 goto SEND_AGAIN;\r
899 }\r
900\r
901 return Sent;\r
902\r
903OnError:\r
904 if (Nbuf != NULL) {\r
905 NetbufFree (Nbuf);\r
906 }\r
907\r
908 return Sent;\r
909}\r
910\r
911\r
912/**\r
913 Send an ACK immediately.\r
914\r
915 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
916\r
917 @return None.\r
918\r
919**/\r
920VOID\r
921TcpSendAck (\r
922 IN TCP_CB *Tcb\r
923 )\r
924{\r
925 NET_BUF *Nbuf;\r
926 TCP_SEG *Seg;\r
927\r
928 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
929\r
930 if (Nbuf == NULL) {\r
931 return;\r
932 }\r
933\r
934 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
935\r
936 Seg = TCPSEG_NETBUF (Nbuf);\r
937 Seg->Seq = Tcb->SndNxt;\r
938 Seg->End = Tcb->SndNxt;\r
939 Seg->Flag = TCP_FLG_ACK;\r
940\r
941 if (TcpTransmitSegment (Tcb, Nbuf) == 0) {\r
942 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);\r
943 Tcb->DelayedAck = 0;\r
944 }\r
945\r
946 NetbufFree (Nbuf);\r
947}\r
948\r
949\r
950/**\r
951 Send a zero probe segment. It can be used by keepalive\r
952 and zero window probe.\r
953\r
954 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
955\r
956 @retval 0 The zero probe segment was sent out successfully.\r
957 @retval other Error condition occurred.\r
958\r
959**/\r
960INTN\r
961TcpSendZeroProbe (\r
962 IN TCP_CB *Tcb\r
963 )\r
964{\r
965 NET_BUF *Nbuf;\r
966 TCP_SEG *Seg;\r
967 INTN Result;\r
968\r
969 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
970\r
971 if (Nbuf == NULL) {\r
972 return -1;\r
973 }\r
974\r
975 NetbufReserve (Nbuf, TCP_MAX_HEAD);\r
976\r
977 //\r
978 // SndNxt-1 is out of window. The peer should respond\r
979 // with an ACK.\r
980 //\r
981 Seg = TCPSEG_NETBUF (Nbuf);\r
982 Seg->Seq = Tcb->SndNxt - 1;\r
983 Seg->End = Tcb->SndNxt - 1;\r
984 Seg->Flag = TCP_FLG_ACK;\r
985\r
986 Result = TcpTransmitSegment (Tcb, Nbuf);\r
987 NetbufFree (Nbuf);\r
988\r
989 return Result;\r
990}\r
991\r
992\r
993/**\r
994 Check whether to send an ACK or delayed ACK.\r
995\r
996 @param Tcb Pointer to the TCP_CB of this TCP instance.\r
997\r
998 @return None.\r
999\r
1000**/\r
1001VOID\r
1002TcpToSendAck (\r
1003 IN TCP_CB *Tcb\r
1004 )\r
1005{\r
1006 //\r
1007 // Generally, TCP should send a delayed ACK unless:\r
1008 // 1. ACK at least every other FULL sized segment received,\r
1009 // 2. Packets received out of order\r
1010 // 3. Receiving window is open\r
1011 //\r
1012 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||\r
1013 (Tcb->DelayedAck >= 1) ||\r
1014 (TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb))\r
1015 ) {\r
1016 TcpSendAck (Tcb);\r
1017 return;\r
1018 }\r
1019\r
1020 TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed"\r
1021 " ACK for TCB %x\n", Tcb));\r
1022\r
1023 //\r
1024 // schedule a delayed ACK\r
1025 //\r
1026 Tcb->DelayedAck++;\r
1027}\r
1028\r
1029\r
1030/**\r
1031 Send a RESET segment in response to the segment received.\r
1032\r
1033 @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.\r
1034 @param Head TCP header of the segment that triggers the reset.\r
1035 @param Len Length of the segment that triggers the reset.\r
1036 @param Local Local IP address.\r
1037 @param Remote Remote peer's IP address.\r
1038\r
1039 @retval 0 A reset is sent or no need to send it.\r
1040 @retval -1 No reset is sent.\r
1041\r
1042**/\r
1043INTN\r
1044TcpSendReset (\r
1045 IN TCP_CB *Tcb,\r
1046 IN TCP_HEAD *Head,\r
1047 IN INT32 Len,\r
1048 IN UINT32 Local,\r
1049 IN UINT32 Remote\r
1050 )\r
1051{\r
1052 NET_BUF *Nbuf;\r
1053 TCP_HEAD *Nhead;\r
1054 UINT16 HeadSum;\r
1055\r
1056 //\r
1057 // Don't respond to a Reset with reset\r
1058 //\r
1059 if (Head->Flag & TCP_FLG_RST) {\r
1060 return 0;\r
1061 }\r
1062\r
1063 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1064\r
1065 if (Nbuf == NULL) {\r
1066 return -1;\r
1067 }\r
1068\r
1069 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
1070 Nbuf,\r
1071 sizeof (TCP_HEAD),\r
1072 NET_BUF_TAIL\r
1073 );\r
1074\r
1075 ASSERT (Nhead != NULL);\r
1076\r
1077 Nbuf->Tcp = Nhead;\r
1078 Nhead->Flag = TCP_FLG_RST;\r
1079\r
1080 //\r
1081 // Derive Seq/ACK from the segment if no TCB\r
1082 // associated with it, otherwise from the Tcb\r
1083 //\r
1084 if (Tcb == NULL) {\r
1085\r
1086 if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
1087 Nhead->Seq = Head->Ack;\r
1088 Nhead->Ack = 0;\r
1089 } else {\r
1090 Nhead->Seq = 0;\r
1091 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1092 Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
1093 }\r
1094 } else {\r
1095\r
1096 Nhead->Seq = HTONL (Tcb->SndNxt);\r
1097 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
1098 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1099 }\r
1100\r
1101 Nhead->SrcPort = Head->DstPort;\r
1102 Nhead->DstPort = Head->SrcPort;\r
1103 Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);\r
1104 Nhead->Res = 0;\r
1105 Nhead->Wnd = HTONS (0xFFFF);\r
1106 Nhead->Checksum = 0;\r
1107 Nhead->Urg = 0;\r
1108\r
1109 HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);\r
1110 Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
1111\r
1112 TcpSendIpPacket (Tcb, Nbuf, Local, Remote);\r
1113\r
1114 NetbufFree (Nbuf);\r
1115 return 0;\r
1116}\r
1117\r
1118\r
1119/**\r
1120 Verify that the segment is in good shape.\r
1121\r
1122 @param Nbuf Buffer that contains the segment to be checked.\r
1123\r
1124 @retval 0 The segment is broken.\r
1125 @retval 1 The segment is in good shape.\r
1126\r
1127**/\r
1128INTN\r
1129TcpVerifySegment (\r
1130 IN NET_BUF *Nbuf\r
1131 )\r
1132{\r
1133 TCP_HEAD *Head;\r
1134 TCP_SEG *Seg;\r
1135 UINT32 Len;\r
1136\r
1137 if (Nbuf == NULL) {\r
1138 return 1;\r
1139 }\r
1140\r
1141 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
1142\r
1143 Seg = TCPSEG_NETBUF (Nbuf);\r
1144 Len = Nbuf->TotalSize;\r
1145 Head = Nbuf->Tcp;\r
1146\r
1147 if (Head != NULL) {\r
1148 if (Head->Flag != Seg->Flag) {\r
1149 return 0;\r
1150 }\r
1151\r
1152 Len -= (Head->HeadLen << 2);\r
1153 }\r
1154\r
1155 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1156 Len++;\r
1157 }\r
1158\r
1159 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
1160 Len++;\r
1161 }\r
1162\r
1163 if (Seg->Seq + Len != Seg->End) {\r
1164 return 0;\r
1165 }\r
1166\r
1167 return 1;\r
1168}\r
1169\r
1170\r
1171/**\r
1172 Verify that all the segments in SndQue are in good shape.\r
1173\r
1174 @param Head Pointer to the head node of the SndQue.\r
1175\r
1176 @retval 0 At least one segment is broken.\r
1177 @retval 1 All segments in the specific queue are in good shape.\r
1178\r
1179**/\r
1180INTN\r
1181TcpCheckSndQue (\r
1182 IN NET_LIST_ENTRY *Head\r
1183 )\r
1184{\r
1185 NET_LIST_ENTRY *Entry;\r
1186 NET_BUF *Nbuf;\r
1187 TCP_SEQNO Seq;\r
1188\r
1189 if (NetListIsEmpty (Head)) {\r
1190 return 1;\r
1191 }\r
1192 //\r
1193 // Initialize the Seq\r
1194 //\r
1195 Entry = Head->ForwardLink;\r
1196 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1197 Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
1198\r
1199 NET_LIST_FOR_EACH (Entry, Head) {\r
1200 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1201\r
1202 if (TcpVerifySegment (Nbuf) == 0) {\r
1203 return 0;\r
1204 }\r
1205\r
1206 //\r
1207 // All the node in the SndQue should has:\r
1208 // SEG.SEQ = LAST_SEG.END\r
1209 //\r
1210 if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
1211 return 0;\r
1212 }\r
1213\r
1214 Seq = TCPSEG_NETBUF (Nbuf)->End;\r
1215 }\r
1216\r
1217 return 1;\r
1218}\r