tcp4output.c
来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,320 行 · 第 1/2 页
C
1,320 行
}
NET_BUF *
TcpGetSegment (
IN TCP_CB *Tcb,
IN TCP_SEQNO Seq,
IN UINT32 Len
)
/*++
Routine Description:
Get a segment starting from sequence Seq of a maximum
length of Len.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Seq - The sequence number of the segment.
Len - The maximum length of the segment.
Returns:
Pointer to the segment, if NULL some error occurred.
--*/
{
NET_BUF *Nbuf;
ASSERT (Tcb);
//
// Compare the SndNxt with the max sequence number sent.
//
if ((Len != 0) && TCP_SEQ_LT (Seq, TcpGetMaxSndNxt (Tcb))) {
Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
} else {
Nbuf = TcpGetSegmentSock (Tcb, Seq, Len);
}
ASSERT (TcpVerifySegment (Nbuf));
return Nbuf;
}
INTN
TcpRetransmit (
IN TCP_CB *Tcb,
IN TCP_SEQNO Seq
)
/*++
Routine Description:
Retransmit the segment from sequence Seq.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Seq - The sequence number of the segment to be retransmitted.
Returns:
0 - Retransmission succeeded.
-1 - Error condition occurred.
--*/
{
NET_BUF *Nbuf;
UINT32 Len;
//
// Compute the maxium length of retransmission. It is
// limited by three factors:
// 1. Less than SndMss
// 2. must in the current send window
// 3. will not change the boundaries of queued segments.
//
if (TCP_SEQ_LT (Tcb->SndWl2 + Tcb->SndWnd, Seq)) {
TCP4_DEBUG_WARN (("TcpRetransmit: retransmission cancelled "
"because send window too small for TCB %x\n", Tcb));
return 0;
}
Len = TCP_SUB_SEQ (Tcb->SndWl2 + Tcb->SndWnd, Seq);
Len = NET_MIN (Len, Tcb->SndMss);
Nbuf = TcpGetSegmentSndQue (Tcb, Seq, Len);
if (Nbuf == NULL) {
return -1;
}
ASSERT (TcpVerifySegment (Nbuf));
if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
goto OnError;
}
//
// The retransmitted buffer may be on the SndQue,
// trim TCP head because all the buffer on SndQue
// are headless.
//
ASSERT (Nbuf->Tcp);
NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
Nbuf->Tcp = NULL;
NetbufFree (Nbuf);
return 0;
OnError:
if (Nbuf != NULL) {
NetbufFree (Nbuf);
}
return -1;
}
INTN
TcpToSendData (
IN TCP_CB *Tcb,
IN INTN Force
)
/*++
Routine Description:
Check whether to send data/SYN/FIN and piggy back an ACK.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Force - Whether to ignore the sender's SWS avoidance algorithm
and send out data by force.
Returns:
The number of bytes sent.
--*/
{
UINT32 Len;
INTN Sent;
UINT8 Flag;
NET_BUF *Nbuf;
TCP_SEG *Seg;
TCP_SEQNO Seq;
TCP_SEQNO End;
ASSERT (Tcb && Tcb->Sk && (Tcb->State != TCP_LISTEN));
Sent = 0;
if ((Tcb->State == TCP_CLOSED) ||
TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT)) {
return 0;
}
SEND_AGAIN:
//
// compute how much data can be sent
//
Len = TcpDataToSend (Tcb, Force);
Seq = Tcb->SndNxt;
Flag = mTcpOutFlag[Tcb->State];
if (Flag & TCP_FLG_SYN) {
Seq = Tcb->Iss;
Len = 0;
}
//
// only send a segment without data if SYN or
// FIN is set.
//
if ((Len == 0) && !(Flag & (TCP_FLG_SYN | TCP_FLG_FIN))) {
return Sent;
}
Nbuf = TcpGetSegment (Tcb, Seq, Len);
if (Nbuf == NULL) {
TCP4_DEBUG_ERROR (
("TcpToSendData: failed to get a segment for TCB %x\n",
Tcb)
);
goto OnError;
}
Seg = TCPSEG_NETBUF (Nbuf);
//
// Set the TcpSeg in Nbuf.
//
Len = Nbuf->TotalSize;
End = Seq + Len;
if (TCP_FLG_ON (Flag, TCP_FLG_SYN)) {
End++;
}
if (Flag & TCP_FLG_FIN) {
//
// Send FIN if all data is sent, and FIN is
// in the window
//
if ((TcpGetMaxSndNxt (Tcb) == Tcb->SndNxt) &&
(GET_SND_DATASIZE (Tcb->Sk) == 0) &&
TCP_SEQ_LT (End + 1, Tcb->SndWnd + Tcb->SndWl2)
) {
TCP4_DEBUG_TRACE (("TcpToSendData: send FIN "
"to peer for TCB %x in state %d\n", Tcb, Tcb->State));
End++;
} else {
TCP_CLEAR_FLG (Flag, TCP_FLG_FIN);
}
}
Seg->Seq = Seq;
Seg->End = End;
Seg->Flag = Flag;
ASSERT (TcpVerifySegment (Nbuf));
ASSERT (TcpCheckSndQue (&Tcb->SndQue));
//
// don't send an empty segment here.
//
if (Seg->End == Seg->Seq) {
TCP4_DEBUG_WARN (("TcpToSendData: created a empty"
" segment for TCB %x, free it now\n", Tcb));
NetbufFree (Nbuf);
return Sent;
}
if (TcpTransmitSegment (Tcb, Nbuf) != 0) {
//
// TODO: double check this
//
NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
Nbuf->Tcp = NULL;
if (Flag & TCP_FLG_FIN) {
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
}
goto OnError;
}
Sent += TCP_SUB_SEQ (End, Seq);
//
// All the buffer in the SndQue is headless
//
ASSERT (Nbuf->Tcp);
NetbufTrim (Nbuf, (Nbuf->Tcp->HeadLen << 2), NET_BUF_HEAD);
Nbuf->Tcp = NULL;
NetbufFree (Nbuf);
//
// update status in TCB
//
Tcb->DelayedAck = 0;
if (Flag & TCP_FLG_FIN) {
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_FIN_SENT);
}
if (TCP_SEQ_GT (End, Tcb->SndNxt)) {
Tcb->SndNxt = End;
}
if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT)) {
TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto);
}
//
// Enable RTT measurement only if not in retransmit.
// Karn's algorithm reqires not to update RTT when in loss.
//
if ((Tcb->CongestState == TCP_CONGEST_OPEN) &&
!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) {
TCP4_DEBUG_TRACE (("TcpToSendData: set RTT measure "
"sequence %d for TCB %x\n", Seq, Tcb));
TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON);
Tcb->RttSeq = Seq;
Tcb->RttMeasure = 0;
}
if (Len == Tcb->SndMss) {
goto SEND_AGAIN;
}
return Sent;
OnError:
if (Nbuf != NULL) {
NetbufFree (Nbuf);
}
return Sent;
}
VOID
TcpSendAck (
IN TCP_CB *Tcb
)
/*++
Routine Description:
Send an ACK immediately.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Returns:
None.
--*/
{
NET_BUF *Nbuf;
TCP_SEG *Seg;
Nbuf = NetbufAlloc (TCP_MAX_HEAD);
if (Nbuf == NULL) {
return;
}
NetbufReserve (Nbuf, TCP_MAX_HEAD);
Seg = TCPSEG_NETBUF (Nbuf);
Seg->Seq = Tcb->SndNxt;
Seg->End = Tcb->SndNxt;
Seg->Flag = TCP_FLG_ACK;
if (TcpTransmitSegment (Tcb, Nbuf) == 0) {
TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW);
Tcb->DelayedAck = 0;
}
NetbufFree (Nbuf);
}
INTN
TcpSendZeroProbe (
IN TCP_CB *Tcb
)
/*++
Routine Description:
Send a zero probe segment. It can be used by keepalive
and zero window probe.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Returns:
0 - The zero probe segment was sent out successfully.
other - Error condition occurred.
--*/
{
NET_BUF *Nbuf;
TCP_SEG *Seg;
INTN Result;
Nbuf = NetbufAlloc (TCP_MAX_HEAD);
if (Nbuf == NULL) {
return -1;
}
NetbufReserve (Nbuf, TCP_MAX_HEAD);
//
// SndNxt-1 is out of window. The peer should respond
// with an ACK.
//
Seg = TCPSEG_NETBUF (Nbuf);
Seg->Seq = Tcb->SndNxt - 1;
Seg->End = Tcb->SndNxt - 1;
Seg->Flag = TCP_FLG_ACK;
Result = TcpTransmitSegment (Tcb, Nbuf);
NetbufFree (Nbuf);
return Result;
}
VOID
TcpToSendAck (
IN TCP_CB *Tcb
)
/*++
Routine Description:
Check whether to send an ACK or delayed ACK.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance.
Returns:
None.
--*/
{
//
// Generally, TCP should send a delayed ACK unless:
// 1. ACK at least every other FULL sized segment received,
// 2. Packets received out of order
// 3. Receiving window is open
//
if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_ACK_NOW) ||
(Tcb->DelayedAck >= 1) ||
(TcpRcvWinNow (Tcb) > TcpRcvWinOld (Tcb))
) {
TcpSendAck (Tcb);
return;
}
TCP4_DEBUG_TRACE (("TcpToSendAck: scheduled a delayed"
" ACK for TCB %x\n", Tcb));
//
// schedule a delayed ACK
//
Tcb->DelayedAck++;
}
INTN
TcpSendReset (
IN TCP_CB *Tcb,
IN TCP_HEAD *Head,
IN INT32 Len,
IN UINT32 Local,
IN UINT32 Remote
)
/*++
Routine Description:
Send a RESET segment in response to the segment received.
Arguments:
Tcb - Pointer to the TCP_CB of this TCP instance, may be NULL.
Head - TCP header of the segment that triggers the reset.
Len - Length of the segment that triggers the reset.
Local - Local IP address.
Remote - Remote peer's IP address.
Returns:
0 - A reset is sent or no need to send it.
-1 - No reset is sent.
--*/
{
NET_BUF *Nbuf;
TCP_HEAD *Nhead;
UINT16 HeadSum;
//
// Don't respond to a Reset with reset
//
if (Head->Flag & TCP_FLG_RST) {
return 0;
}
Nbuf = NetbufAlloc (TCP_MAX_HEAD);
if (Nbuf == NULL) {
return -1;
}
Nhead = (TCP_HEAD *) NetbufAllocSpace (
Nbuf,
sizeof (TCP_HEAD),
NET_BUF_TAIL
);
ASSERT (Nhead != NULL);
Nbuf->Tcp = Nhead;
Nhead->Flag = TCP_FLG_RST;
//
// Derive Seq/ACK from the segment if no TCB
// associated with it, otherwise from the Tcb
//
if (Tcb == NULL) {
if (TCP_FLG_ON (Head->Flag, TCP_FLG_ACK)) {
Nhead->Seq = Head->Ack;
Nhead->Ack = 0;
} else {
Nhead->Seq = 0;
TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
Nhead->Ack = HTONL (NTOHL (Head->Seq) + Len);
}
} else {
Nhead->Seq = HTONL (Tcb->SndNxt);
Nhead->Ack = HTONL (Tcb->RcvNxt);
TCP_SET_FLG (Nhead->Flag, TCP_FLG_ACK);
}
Nhead->SrcPort = Head->DstPort;
Nhead->DstPort = Head->SrcPort;
Nhead->HeadLen = (sizeof (TCP_HEAD) >> 2);
Nhead->Res = 0;
Nhead->Wnd = HTONS (0xFFFF);
Nhead->Checksum = 0;
Nhead->Urg = 0;
HeadSum = NetPseudoHeadChecksum (Local, Remote, 6, 0);
Nhead->Checksum = TcpChecksum (Nbuf, HeadSum);
TcpSendIpPacket (Tcb, Nbuf, Local, Remote);
NetbufFree (Nbuf);
return 0;
}
INTN
TcpVerifySegment (
IN NET_BUF *Nbuf
)
/*++
Routine Description:
Verify that the segment is in good shape.
Arguments:
Nbuf - Buffer that contains the segment to be checked.
Returns:
0 - The segment is broken.
1 - The segment is in good shape.
--*/
{
TCP_HEAD *Head;
TCP_SEG *Seg;
UINT32 Len;
if (Nbuf == NULL) {
return 1;
}
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
Seg = TCPSEG_NETBUF (Nbuf);
Len = Nbuf->TotalSize;
Head = Nbuf->Tcp;
if (Head != NULL) {
if (Head->Flag != Seg->Flag) {
return 0;
}
Len -= (Head->HeadLen << 2);
}
if (TCP_FLG_ON (Seg->Flag, TCP_FLG_SYN)) {
Len++;
}
if (TCP_FLG_ON (Seg->Flag, TCP_FLG_FIN)) {
Len++;
}
if (Seg->Seq + Len != Seg->End) {
return 0;
}
return 1;
}
INTN
TcpCheckSndQue (
IN NET_LIST_ENTRY *Head
)
/*++
Routine Description:
Verify that all the segments in SndQue are in good shape.
Arguments:
Head - Pointer to the head node of the SndQue.
Returns:
0 - At least one segment is broken.
1 - All segments in the specific queue are in good shape.
--*/
{
NET_LIST_ENTRY *Entry;
NET_BUF *Nbuf;
TCP_SEQNO Seq;
if (NetListIsEmpty (Head)) {
return 1;
}
//
// Initialize the Seq
//
Entry = Head->ForwardLink;
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
Seq = TCPSEG_NETBUF (Nbuf)->Seq;
NET_LIST_FOR_EACH (Entry, Head) {
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
if (TcpVerifySegment (Nbuf) == 0) {
return 0;
}
//
// All the node in the SndQue should has:
// SEG.SEQ = LAST_SEG.END
//
if (Seq != TCPSEG_NETBUF (Nbuf)->Seq) {
return 0;
}
Seq = TCPSEG_NETBUF (Nbuf)->End;
}
return 1;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?