]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Output.c
1. Add DPC protocol and DpcLib library in MdeModulePkg.
[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
36ee91ca 147 Wnd = MIN (Wnd >> Tcb->RcvWndScale, 0xffff);\r
8a67d61d 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
36ee91ca 233 Len = MIN (Win, Left);\r
8a67d61d 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
36ee91ca 382 Seg->Urg = (UINT16) MIN (\r
8a67d61d 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
36ee91ca 689 Len = MIN (Len, Tcb->SndMss);\r
8a67d61d 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
4eb65aff 1006 UINT32 TcpNow;\r
1007\r
1008 TcpNow = TcpRcvWinNow (Tcb);\r
8a67d61d 1009 //\r
1010 // Generally, TCP should send a delayed ACK unless:\r
1011 // 1. ACK at least every other FULL sized segment received,\r
1012 // 2. Packets received out of order\r
1013 // 3. Receiving window is open\r
1014 //\r
1015 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||\r
1016 (Tcb->DelayedAck >= 1) ||\r
4eb65aff 1017 (TcpNow > TcpRcvWinOld (Tcb))\r
8a67d61d 1018 ) {\r
1019 TcpSendAck (Tcb);\r
1020 return;\r
1021 }\r
1022\r
1023 TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed"\r
1024 " ACK for TCB %x\n", Tcb));\r
1025\r
1026 //\r
1027 // schedule a delayed ACK\r
1028 //\r
1029 Tcb->DelayedAck++;\r
1030}\r
1031\r
1032\r
1033/**\r
1034 Send a RESET segment in response to the segment received.\r
1035\r
1036 @param Tcb Pointer to the TCP_CB of this TCP instance, may be NULL.\r
1037 @param Head TCP header of the segment that triggers the reset.\r
1038 @param Len Length of the segment that triggers the reset.\r
1039 @param Local Local IP address.\r
1040 @param Remote Remote peer's IP address.\r
1041\r
1042 @retval 0 A reset is sent or no need to send it.\r
1043 @retval -1 No reset is sent.\r
1044\r
1045**/\r
1046INTN\r
1047TcpSendReset (\r
1048 IN TCP_CB *Tcb,\r
1049 IN TCP_HEAD *Head,\r
1050 IN INT32 Len,\r
1051 IN UINT32 Local,\r
1052 IN UINT32 Remote\r
1053 )\r
1054{\r
1055 NET_BUF *Nbuf;\r
1056 TCP_HEAD *Nhead;\r
1057 UINT16 HeadSum;\r
1058\r
1059 //\r
1060 // Don't respond to a Reset with reset\r
1061 //\r
1062 if (Head->Flag & TCP_FLG_RST) {\r
1063 return 0;\r
1064 }\r
1065\r
1066 Nbuf = NetbufAlloc (TCP_MAX_HEAD);\r
1067\r
1068 if (Nbuf == NULL) {\r
1069 return -1;\r
1070 }\r
1071\r
1072 Nhead = (TCP_HEAD *) NetbufAllocSpace (\r
1073 Nbuf,\r
1074 sizeof (TCP_HEAD),\r
1075 NET_BUF_TAIL\r
1076 );\r
1077\r
1078 ASSERT (Nhead != NULL);\r
1079\r
1080 Nbuf->Tcp = Nhead;\r
1081 Nhead->Flag = TCP_FLG_RST;\r
1082\r
1083 //\r
1084 // Derive Seq/ACK from the segment if no TCB\r
1085 // associated with it, otherwise from the Tcb\r
1086 //\r
1087 if (Tcb == NULL) {\r
1088\r
1089 if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {\r
1090 Nhead->Seq = Head->Ack;\r
1091 Nhead->Ack = 0;\r
1092 } else {\r
1093 Nhead->Seq = 0;\r
1094 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1095 Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);\r
1096 }\r
1097 } else {\r
1098\r
1099 Nhead->Seq = HTONL (Tcb->SndNxt);\r
1100 Nhead->Ack = HTONL (Tcb->RcvNxt);\r
1101 TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);\r
1102 }\r
1103\r
1104 Nhead->SrcPort = Head->DstPort;\r
1105 Nhead->DstPort = Head->SrcPort;\r
1106 Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);\r
1107 Nhead->Res = 0;\r
1108 Nhead->Wnd = HTONS (0xFFFF);\r
1109 Nhead->Checksum = 0;\r
1110 Nhead->Urg = 0;\r
1111\r
1112 HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);\r
1113 Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);\r
1114\r
1115 TcpSendIpPacket (Tcb, Nbuf, Local, Remote);\r
1116\r
1117 NetbufFree (Nbuf);\r
1118 return 0;\r
1119}\r
1120\r
1121\r
1122/**\r
1123 Verify that the segment is in good shape.\r
1124\r
1125 @param Nbuf Buffer that contains the segment to be checked.\r
1126\r
1127 @retval 0 The segment is broken.\r
1128 @retval 1 The segment is in good shape.\r
1129\r
1130**/\r
1131INTN\r
1132TcpVerifySegment (\r
1133 IN NET_BUF *Nbuf\r
1134 )\r
1135{\r
1136 TCP_HEAD *Head;\r
1137 TCP_SEG *Seg;\r
1138 UINT32 Len;\r
1139\r
1140 if (Nbuf == NULL) {\r
1141 return 1;\r
1142 }\r
1143\r
1144 NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);\r
1145\r
1146 Seg = TCPSEG_NETBUF (Nbuf);\r
1147 Len = Nbuf->TotalSize;\r
1148 Head = Nbuf->Tcp;\r
1149\r
1150 if (Head != NULL) {\r
1151 if (Head->Flag != Seg->Flag) {\r
1152 return 0;\r
1153 }\r
1154\r
1155 Len -= (Head->HeadLen << 2);\r
1156 }\r
1157\r
1158 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {\r
1159 Len++;\r
1160 }\r
1161\r
1162 if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {\r
1163 Len++;\r
1164 }\r
1165\r
1166 if (Seg->Seq + Len != Seg->End) {\r
1167 return 0;\r
1168 }\r
1169\r
1170 return 1;\r
1171}\r
1172\r
1173\r
1174/**\r
1175 Verify that all the segments in SndQue are in good shape.\r
1176\r
1177 @param Head Pointer to the head node of the SndQue.\r
1178\r
1179 @retval 0 At least one segment is broken.\r
1180 @retval 1 All segments in the specific queue are in good shape.\r
1181\r
1182**/\r
1183INTN\r
1184TcpCheckSndQue (\r
1185 IN NET_LIST_ENTRY *Head\r
1186 )\r
1187{\r
1188 NET_LIST_ENTRY *Entry;\r
1189 NET_BUF *Nbuf;\r
1190 TCP_SEQNO Seq;\r
1191\r
1192 if (NetListIsEmpty (Head)) {\r
1193 return 1;\r
1194 }\r
1195 //\r
1196 // Initialize the Seq\r
1197 //\r
1198 Entry = Head->ForwardLink;\r
1199 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1200 Seq = TCPSEG_NETBUF (Nbuf)->Seq;\r
1201\r
1202 NET_LIST_FOR_EACH (Entry, Head) {\r
1203 Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
1204\r
1205 if (TcpVerifySegment (Nbuf) == 0) {\r
1206 return 0;\r
1207 }\r
1208\r
1209 //\r
1210 // All the node in the SndQue should has:\r
1211 // SEG.SEQ = LAST_SEG.END\r
1212 //\r
1213 if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {\r
1214 return 0;\r
1215 }\r
1216\r
1217 Seq = TCPSEG_NETBUF (Nbuf)->End;\r
1218 }\r
1219\r
1220 return 1;\r
1221}\r