1 /** @file 2 TCP timer related functions. 3 4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php. 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "TcpMain.h" 17 18 UINT32 mTcpTick = 1000; 19 20 /** 21 Connect timeout handler. 22 23 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 24 25 **/ 26 VOID 27 TcpConnectTimeout ( 28 IN OUT TCP_CB *Tcb 29 ); 30 31 /** 32 Timeout handler for TCP retransmission timer. 33 34 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 35 36 **/ 37 VOID 38 TcpRexmitTimeout ( 39 IN OUT TCP_CB *Tcb 40 ); 41 42 /** 43 Timeout handler for window probe timer. 44 45 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 46 47 **/ 48 VOID 49 TcpProbeTimeout ( 50 IN OUT TCP_CB *Tcb 51 ); 52 53 /** 54 Timeout handler for keepalive timer. 55 56 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 57 58 **/ 59 VOID 60 TcpKeepaliveTimeout ( 61 IN OUT TCP_CB *Tcb 62 ); 63 64 /** 65 Timeout handler for FIN_WAIT_2 timer. 66 67 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 68 69 **/ 70 VOID 71 TcpFinwait2Timeout ( 72 IN OUT TCP_CB *Tcb 73 ); 74 75 /** 76 Timeout handler for 2MSL timer. 77 78 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 79 80 **/ 81 VOID 82 Tcp2MSLTimeout ( 83 IN OUT TCP_CB *Tcb 84 ); 85 86 TCP_TIMER_HANDLER mTcpTimerHandler[TCP_TIMER_NUMBER] = { 87 TcpConnectTimeout, 88 TcpRexmitTimeout, 89 TcpProbeTimeout, 90 TcpKeepaliveTimeout, 91 TcpFinwait2Timeout, 92 Tcp2MSLTimeout, 93 }; 94 95 /** 96 Close the TCP connection. 97 98 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 99 100 **/ 101 VOID 102 TcpClose ( 103 IN OUT TCP_CB *Tcb 104 ) 105 { 106 NetbufFreeList (&Tcb->SndQue); 107 NetbufFreeList (&Tcb->RcvQue); 108 109 TcpSetState (Tcb, TCP_CLOSED); 110 } 111 112 /** 113 Backoff the RTO. 114 115 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 116 117 **/ 118 VOID 119 TcpBackoffRto ( 120 IN OUT TCP_CB *Tcb 121 ) 122 { 123 // 124 // Fold the RTT estimate if too many times, the estimate 125 // may be wrong, fold it. So the next time a valid 126 // measurement is sampled, we can start fresh. 127 // 128 if ((Tcb->LossTimes >= TCP_FOLD_RTT) && (Tcb->SRtt != 0)) { 129 Tcb->RttVar += Tcb->SRtt >> 2; 130 Tcb->SRtt = 0; 131 } 132 133 Tcb->Rto <<= 1; 134 135 if (Tcb->Rto < TCP_RTO_MIN) { 136 137 Tcb->Rto = TCP_RTO_MIN; 138 } else if (Tcb->Rto > TCP_RTO_MAX) { 139 140 Tcb->Rto = TCP_RTO_MAX; 141 } 142 } 143 144 /** 145 Connect timeout handler. 146 147 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 148 149 **/ 150 VOID 151 TcpConnectTimeout ( 152 IN OUT TCP_CB *Tcb 153 ) 154 { 155 if (!TCP_CONNECTED (Tcb->State)) { 156 DEBUG ( 157 (EFI_D_ERROR, 158 "TcpConnectTimeout: connection closed because conenction timer timeout for TCB %p\n", 159 Tcb) 160 ); 161 162 if (EFI_ABORTED == Tcb->Sk->SockError) { 163 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); 164 } 165 166 if (TCP_SYN_RCVD == Tcb->State) { 167 DEBUG ( 168 (EFI_D_WARN, 169 "TcpConnectTimeout: send reset because connection timer timeout for TCB %p\n", 170 Tcb) 171 ); 172 173 TcpResetConnection (Tcb); 174 175 } 176 177 TcpClose (Tcb); 178 } 179 } 180 181 182 /** 183 Timeout handler for TCP retransmission timer. 184 185 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 186 187 **/ 188 VOID 189 TcpRexmitTimeout ( 190 IN OUT TCP_CB *Tcb 191 ) 192 { 193 UINT32 FlightSize; 194 195 DEBUG ( 196 (EFI_D_WARN, 197 "TcpRexmitTimeout: transmission timeout for TCB %p\n", 198 Tcb) 199 ); 200 201 // 202 // Set the congestion window. FlightSize is the 203 // amount of data that has been sent but not 204 // yet ACKed. 205 // 206 FlightSize = TCP_SUB_SEQ (Tcb->SndNxt, Tcb->SndUna); 207 Tcb->Ssthresh = MAX ((UINT32) (2 * Tcb->SndMss), FlightSize / 2); 208 209 Tcb->CWnd = Tcb->SndMss; 210 Tcb->LossRecover = Tcb->SndNxt; 211 212 Tcb->LossTimes++; 213 if ((Tcb->LossTimes > Tcb->MaxRexmit) && !TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_CONNECT)) { 214 215 DEBUG ( 216 (EFI_D_ERROR, 217 "TcpRexmitTimeout: connection closed because too many timeouts for TCB %p\n", 218 Tcb) 219 ); 220 221 if (EFI_ABORTED == Tcb->Sk->SockError) { 222 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); 223 } 224 225 TcpClose (Tcb); 226 return ; 227 } 228 229 TcpBackoffRto (Tcb); 230 TcpRetransmit (Tcb, Tcb->SndUna); 231 TcpSetTimer (Tcb, TCP_TIMER_REXMIT, Tcb->Rto); 232 233 Tcb->CongestState = TCP_CONGEST_LOSS; 234 235 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_RTT_ON); 236 } 237 238 /** 239 Timeout handler for window probe timer. 240 241 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 242 243 **/ 244 VOID 245 TcpProbeTimeout ( 246 IN OUT TCP_CB *Tcb 247 ) 248 { 249 // 250 // This is the timer for sender's SWSA. RFC1122 requires 251 // a timer set for sender's SWSA, and suggest combine it 252 // with window probe timer. If data is sent, don't set 253 // the probe timer, since retransmit timer is on. 254 // 255 if ((TcpDataToSend (Tcb, 1) != 0) && (TcpToSendData (Tcb, 1) > 0)) { 256 257 ASSERT (TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_REXMIT) != 0); 258 Tcb->ProbeTimerOn = FALSE; 259 return ; 260 } 261 262 TcpSendZeroProbe (Tcb); 263 TcpSetProbeTimer (Tcb); 264 } 265 266 /** 267 Timeout handler for keepalive timer. 268 269 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 270 271 **/ 272 VOID 273 TcpKeepaliveTimeout ( 274 IN OUT TCP_CB *Tcb 275 ) 276 { 277 Tcb->KeepAliveProbes++; 278 279 // 280 // Too many Keep-alive probes, drop the connection 281 // 282 if (Tcb->KeepAliveProbes > Tcb->MaxKeepAlive) { 283 284 if (EFI_ABORTED == Tcb->Sk->SockError) { 285 SOCK_ERROR (Tcb->Sk, EFI_TIMEOUT); 286 } 287 288 TcpClose (Tcb); 289 return ; 290 } 291 292 TcpSendZeroProbe (Tcb); 293 TcpSetKeepaliveTimer (Tcb); 294 } 295 296 /** 297 Timeout handler for FIN_WAIT_2 timer. 298 299 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 300 301 **/ 302 VOID 303 TcpFinwait2Timeout ( 304 IN OUT TCP_CB *Tcb 305 ) 306 { 307 DEBUG ( 308 (EFI_D_WARN, 309 "TcpFinwait2Timeout: connection closed because FIN_WAIT2 timer timeouts for TCB %p\n", 310 Tcb) 311 ); 312 313 TcpClose (Tcb); 314 } 315 316 /** 317 Timeout handler for 2MSL timer. 318 319 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 320 321 **/ 322 VOID 323 Tcp2MSLTimeout ( 324 IN OUT TCP_CB *Tcb 325 ) 326 { 327 DEBUG ( 328 (EFI_D_WARN, 329 "Tcp2MSLTimeout: connection closed because TIME_WAIT timer timeouts for TCB %p\n", 330 Tcb) 331 ); 332 333 TcpClose (Tcb); 334 } 335 336 /** 337 Update the timer status and the next expire time according to the timers 338 to expire in a specific future time slot. 339 340 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 341 342 **/ 343 VOID 344 TcpUpdateTimer ( 345 IN OUT TCP_CB *Tcb 346 ) 347 { 348 UINT16 Index; 349 350 // 351 // Don't use a too large value to init NextExpire 352 // since mTcpTick wraps around as sequence no does. 353 // 354 Tcb->NextExpire = TCP_EXPIRE_TIME; 355 TCP_CLEAR_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); 356 357 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { 358 359 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && 360 TCP_TIME_LT (Tcb->Timer[Index], mTcpTick + Tcb->NextExpire) 361 ) { 362 363 Tcb->NextExpire = TCP_SUB_TIME (Tcb->Timer[Index], mTcpTick); 364 TCP_SET_FLG (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON); 365 } 366 } 367 } 368 369 /** 370 Enable a TCP timer. 371 372 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 373 @param[in] Timer The index of the timer to be enabled. 374 @param[in] TimeOut The timeout value of this timer. 375 376 **/ 377 VOID 378 TcpSetTimer ( 379 IN OUT TCP_CB *Tcb, 380 IN UINT16 Timer, 381 IN UINT32 TimeOut 382 ) 383 { 384 TCP_SET_TIMER (Tcb->EnabledTimer, Timer); 385 Tcb->Timer[Timer] = mTcpTick + TimeOut; 386 387 TcpUpdateTimer (Tcb); 388 } 389 390 /** 391 Clear one TCP timer. 392 393 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 394 @param[in] Timer The index of the timer to be cleared. 395 396 **/ 397 VOID 398 TcpClearTimer ( 399 IN OUT TCP_CB *Tcb, 400 IN UINT16 Timer 401 ) 402 { 403 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Timer); 404 TcpUpdateTimer (Tcb); 405 } 406 407 /** 408 Clear all TCP timers. 409 410 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 411 412 **/ 413 VOID 414 TcpClearAllTimer ( 415 IN OUT TCP_CB *Tcb 416 ) 417 { 418 Tcb->EnabledTimer = 0; 419 TcpUpdateTimer (Tcb); 420 } 421 422 /** 423 Enable the window prober timer and set the timeout value. 424 425 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 426 427 **/ 428 VOID 429 TcpSetProbeTimer ( 430 IN OUT TCP_CB *Tcb 431 ) 432 { 433 if (!Tcb->ProbeTimerOn) { 434 Tcb->ProbeTime = Tcb->Rto; 435 Tcb->ProbeTimerOn = TRUE; 436 437 } else { 438 Tcb->ProbeTime <<= 1; 439 } 440 441 if (Tcb->ProbeTime < TCP_RTO_MIN) { 442 443 Tcb->ProbeTime = TCP_RTO_MIN; 444 } else if (Tcb->ProbeTime > TCP_RTO_MAX) { 445 446 Tcb->ProbeTime = TCP_RTO_MAX; 447 } 448 449 TcpSetTimer (Tcb, TCP_TIMER_PROBE, Tcb->ProbeTime); 450 } 451 452 /** 453 Enable the keepalive timer and set the timeout value. 454 455 @param[in, out] Tcb Pointer to the TCP_CB of this TCP instance. 456 457 **/ 458 VOID 459 TcpSetKeepaliveTimer ( 460 IN OUT TCP_CB *Tcb 461 ) 462 { 463 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_NO_KEEPALIVE)) { 464 return ; 465 466 } 467 468 // 469 // Set the timer to KeepAliveIdle if either 470 // 1. the keepalive timer is off 471 // 2. The keepalive timer is on, but the idle 472 // is less than KeepAliveIdle, that means the 473 // connection is alive since our last probe. 474 // 475 if (!TCP_TIMER_ON (Tcb->EnabledTimer, TCP_TIMER_KEEPALIVE) || 476 (Tcb->Idle < Tcb->KeepAliveIdle) 477 ) { 478 479 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAliveIdle); 480 Tcb->KeepAliveProbes = 0; 481 482 } else { 483 484 TcpSetTimer (Tcb, TCP_TIMER_KEEPALIVE, Tcb->KeepAlivePeriod); 485 } 486 } 487 488 /** 489 Heart beat timer handler. 490 491 @param[in] Context Context of the timer event, ignored. 492 493 **/ 494 VOID 495 EFIAPI 496 TcpTickingDpc ( 497 IN VOID *Context 498 ) 499 { 500 LIST_ENTRY *Entry; 501 LIST_ENTRY *Next; 502 TCP_CB *Tcb; 503 INT16 Index; 504 505 mTcpTick++; 506 mTcpGlobalIss += TCP_ISS_INCREMENT_2; 507 508 // 509 // Don't use LIST_FOR_EACH, which isn't delete safe. 510 // 511 for (Entry = mTcpRunQue.ForwardLink; Entry != &mTcpRunQue; Entry = Next) { 512 513 Next = Entry->ForwardLink; 514 515 Tcb = NET_LIST_USER_STRUCT (Entry, TCP_CB, List); 516 517 if (Tcb->State == TCP_CLOSED) { 518 continue; 519 } 520 // 521 // The connection is doing RTT measurement. 522 // 523 if (TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_RTT_ON)) { 524 Tcb->RttMeasure++; 525 } 526 527 Tcb->Idle++; 528 529 if (Tcb->DelayedAck != 0) { 530 TcpSendAck (Tcb); 531 } 532 533 if (Tcb->IpInfo->IpVersion == IP_VERSION_6 && Tcb->Tick > 0) { 534 Tcb->Tick--; 535 } 536 537 // 538 // No timer is active or no timer expired 539 // 540 if (!TCP_FLG_ON (Tcb->CtrlFlag, TCP_CTRL_TIMER_ON) || ((--Tcb->NextExpire) > 0)) { 541 542 continue; 543 } 544 545 // 546 // Call the timeout handler for each expired timer. 547 // 548 for (Index = 0; Index < TCP_TIMER_NUMBER; Index++) { 549 550 if (TCP_TIMER_ON (Tcb->EnabledTimer, Index) && TCP_TIME_LEQ (Tcb->Timer[Index], mTcpTick)) { 551 // 552 // disable the timer before calling the handler 553 // in case the handler enables it again. 554 // 555 TCP_CLEAR_TIMER (Tcb->EnabledTimer, Index); 556 mTcpTimerHandler[Index](Tcb); 557 558 // 559 // The Tcb may have been deleted by the timer, or 560 // no other timer is set. 561 // 562 if ((Next->BackLink != Entry) || (Tcb->EnabledTimer == 0)) { 563 break; 564 } 565 } 566 } 567 568 // 569 // If the Tcb still exist or some timer is set, update the timer 570 // 571 if (Index == TCP_TIMER_NUMBER) { 572 TcpUpdateTimer (Tcb); 573 } 574 } 575 } 576 577 /** 578 Heart beat timer handler, queues the DPC at TPL_CALLBACK. 579 580 @param[in] Event Timer event signaled, ignored. 581 @param[in] Context Context of the timer event, ignored. 582 583 **/ 584 VOID 585 EFIAPI 586 TcpTicking ( 587 IN EFI_EVENT Event, 588 IN VOID *Context 589 ) 590 { 591 QueueDpc (TPL_CALLBACK, TcpTickingDpc, Context); 592 } 593 594