1 /** @file 2 EFI DHCP protocol implementation. 3 4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 16 #include "Dhcp4Impl.h" 17 18 UINT32 mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 }; 19 20 21 /** 22 Send an initial DISCOVER or REQUEST message according to the 23 DHCP service's current state. 24 25 @param[in] DhcpSb The DHCP service instance 26 27 @retval EFI_SUCCESS The request has been sent 28 @retval other Some error occurs when sending the request. 29 30 **/ 31 EFI_STATUS 32 DhcpInitRequest ( 33 IN DHCP_SERVICE *DhcpSb 34 ) 35 { 36 EFI_STATUS Status; 37 38 ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot)); 39 40 // 41 // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message. 42 // 43 DhcpSb->ActiveChild->ElaspedTime= 0; 44 45 if (DhcpSb->DhcpState == Dhcp4Init) { 46 DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE); 47 Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL); 48 49 if (EFI_ERROR (Status)) { 50 DhcpSb->DhcpState = Dhcp4Init; 51 return Status; 52 } 53 } else { 54 DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE); 55 Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL); 56 57 if (EFI_ERROR (Status)) { 58 DhcpSb->DhcpState = Dhcp4InitReboot; 59 return Status; 60 } 61 } 62 63 return EFI_SUCCESS; 64 } 65 66 67 /** 68 Call user provided callback function, and return the value the 69 function returns. If the user doesn't provide a callback, a 70 proper return value is selected to let the caller continue the 71 normal process. 72 73 @param[in] DhcpSb The DHCP service instance 74 @param[in] Event The event as defined in the spec 75 @param[in] Packet The current packet trigger the event 76 @param[out] NewPacket The user's return new packet 77 78 @retval EFI_NOT_READY Direct the caller to continue collecting the offer. 79 @retval EFI_SUCCESS The user function returns success. 80 @retval EFI_ABORTED The user function ask it to abort. 81 82 **/ 83 EFI_STATUS 84 DhcpCallUser ( 85 IN DHCP_SERVICE *DhcpSb, 86 IN EFI_DHCP4_EVENT Event, 87 IN EFI_DHCP4_PACKET *Packet, OPTIONAL 88 OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL 89 ) 90 { 91 EFI_DHCP4_CONFIG_DATA *Config; 92 EFI_STATUS Status; 93 94 if (NewPacket != NULL) { 95 *NewPacket = NULL; 96 } 97 98 // 99 // If user doesn't provide the call back function, return the value 100 // that directs the client to continue the normal process. 101 // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting 102 // the offers and select a offer, EFI_NOT_READY tells the client to 103 // collect more offers. 104 // 105 Config = &DhcpSb->ActiveConfig; 106 107 if (Config->Dhcp4Callback == NULL) { 108 if (Event == Dhcp4RcvdOffer) { 109 return EFI_NOT_READY; 110 } 111 112 return EFI_SUCCESS; 113 } 114 115 Status = Config->Dhcp4Callback ( 116 &DhcpSb->ActiveChild->Dhcp4Protocol, 117 Config->CallbackContext, 118 (EFI_DHCP4_STATE) DhcpSb->DhcpState, 119 Event, 120 Packet, 121 NewPacket 122 ); 123 124 // 125 // User's callback should only return EFI_SUCCESS, EFI_NOT_READY, 126 // and EFI_ABORTED. If it returns values other than those, assume 127 // it to be EFI_ABORTED. 128 // 129 if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) { 130 return Status; 131 } 132 133 return EFI_ABORTED; 134 } 135 136 137 /** 138 Notify the user about the operation result. 139 140 @param DhcpSb DHCP service instance 141 @param Which Which notify function to signal 142 143 **/ 144 VOID 145 DhcpNotifyUser ( 146 IN DHCP_SERVICE *DhcpSb, 147 IN INTN Which 148 ) 149 { 150 DHCP_PROTOCOL *Child; 151 152 if ((Child = DhcpSb->ActiveChild) == NULL) { 153 return ; 154 } 155 156 if ((Child->CompletionEvent != NULL) && 157 ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL)) 158 ) { 159 160 gBS->SignalEvent (Child->CompletionEvent); 161 Child->CompletionEvent = NULL; 162 } 163 164 if ((Child->RenewRebindEvent != NULL) && 165 ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL)) 166 ) { 167 168 gBS->SignalEvent (Child->RenewRebindEvent); 169 Child->RenewRebindEvent = NULL; 170 } 171 } 172 173 174 175 /** 176 Set the DHCP state. If CallUser is true, it will try to notify 177 the user before change the state by DhcpNotifyUser. It returns 178 EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns 179 EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test 180 the return value of this function. 181 182 @param DhcpSb The DHCP service instance 183 @param State The new DHCP state to change to 184 @param CallUser Whether we need to call user 185 186 @retval EFI_SUCCESS The state is changed 187 @retval EFI_ABORTED The user asks to abort the DHCP process. 188 189 **/ 190 EFI_STATUS 191 DhcpSetState ( 192 IN OUT DHCP_SERVICE *DhcpSb, 193 IN INTN State, 194 IN BOOLEAN CallUser 195 ) 196 { 197 EFI_STATUS Status; 198 199 if (CallUser) { 200 Status = EFI_SUCCESS; 201 202 if (State == Dhcp4Renewing) { 203 Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL); 204 205 } else if (State == Dhcp4Rebinding) { 206 Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL); 207 208 } else if (State == Dhcp4Bound) { 209 Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL); 210 211 } 212 213 if (EFI_ERROR (Status)) { 214 return Status; 215 } 216 } 217 218 // 219 // Update the retransmission timer during the state transition. 220 // This will clear the retry count. This is also why the rule 221 // first transit the state, then send packets. 222 // 223 if (State == Dhcp4Selecting) { 224 DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount; 225 } else { 226 DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount; 227 } 228 229 if (DhcpSb->MaxRetries == 0) { 230 DhcpSb->MaxRetries = 4; 231 } 232 233 DhcpSb->CurRetry = 0; 234 DhcpSb->PacketToLive = 0; 235 DhcpSb->LastTimeout = 0; 236 DhcpSb->DhcpState = State; 237 return EFI_SUCCESS; 238 } 239 240 241 /** 242 Set the retransmit timer for the packet. It will select from either 243 the discover timeouts/request timeouts or the default timeout values. 244 245 @param DhcpSb The DHCP service instance. 246 247 **/ 248 VOID 249 DhcpSetTransmitTimer ( 250 IN OUT DHCP_SERVICE *DhcpSb 251 ) 252 { 253 UINT32 *Times; 254 255 ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry); 256 257 if (DhcpSb->DhcpState == Dhcp4Selecting) { 258 Times = DhcpSb->ActiveConfig.DiscoverTimeout; 259 } else { 260 Times = DhcpSb->ActiveConfig.RequestTimeout; 261 } 262 263 if (Times == NULL) { 264 Times = mDhcp4DefaultTimeout; 265 } 266 267 DhcpSb->PacketToLive = Times[DhcpSb->CurRetry]; 268 DhcpSb->LastTimeout = DhcpSb->PacketToLive; 269 270 return; 271 } 272 273 /** 274 Compute the lease. If the server grants a permanent lease, just 275 process it as a normal timeout value since the lease will last 276 more than 100 years. 277 278 @param DhcpSb The DHCP service instance 279 @param Para The DHCP parameter extracted from the server's 280 response. 281 **/ 282 VOID 283 DhcpComputeLease ( 284 IN OUT DHCP_SERVICE *DhcpSb, 285 IN DHCP_PARAMETER *Para 286 ) 287 { 288 ASSERT (Para != NULL); 289 290 DhcpSb->Lease = Para->Lease; 291 DhcpSb->T2 = Para->T2; 292 DhcpSb->T1 = Para->T1; 293 294 if (DhcpSb->Lease == 0) { 295 DhcpSb->Lease = DHCP_DEFAULT_LEASE; 296 } 297 298 if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) { 299 DhcpSb->T2 = Para->Lease - (Para->Lease >> 3); 300 } 301 302 if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) { 303 DhcpSb->T1 = DhcpSb->Lease >> 1; 304 } 305 } 306 307 308 /** 309 Configure a UDP IO port to use the acquired lease address. 310 DHCP driver needs this port to unicast packet to the server 311 such as DHCP release. 312 313 @param[in] UdpIo The UDP IO to configure 314 @param[in] Context Dhcp service instance. 315 316 @retval EFI_SUCCESS The UDP IO port is successfully configured. 317 @retval Others It failed to configure the port. 318 319 **/ 320 EFI_STATUS 321 EFIAPI 322 DhcpConfigLeaseIoPort ( 323 IN UDP_IO *UdpIo, 324 IN VOID *Context 325 ) 326 { 327 EFI_UDP4_CONFIG_DATA UdpConfigData; 328 EFI_IPv4_ADDRESS Subnet; 329 EFI_IPv4_ADDRESS Gateway; 330 DHCP_SERVICE *DhcpSb; 331 EFI_STATUS Status; 332 IP4_ADDR Ip; 333 334 DhcpSb = (DHCP_SERVICE *) Context; 335 336 UdpConfigData.AcceptBroadcast = FALSE; 337 UdpConfigData.AcceptPromiscuous = FALSE; 338 UdpConfigData.AcceptAnyPort = FALSE; 339 UdpConfigData.AllowDuplicatePort = TRUE; 340 UdpConfigData.TypeOfService = 0; 341 UdpConfigData.TimeToLive = 64; 342 UdpConfigData.DoNotFragment = FALSE; 343 UdpConfigData.ReceiveTimeout = 1; 344 UdpConfigData.TransmitTimeout = 0; 345 346 UdpConfigData.UseDefaultAddress = FALSE; 347 UdpConfigData.StationPort = DHCP_CLIENT_PORT; 348 UdpConfigData.RemotePort = DHCP_SERVER_PORT; 349 350 Ip = HTONL (DhcpSb->ClientAddr); 351 CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); 352 353 Ip = HTONL (DhcpSb->Netmask); 354 CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); 355 356 ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS)); 357 358 Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData); 359 360 if (EFI_ERROR (Status)) { 361 return Status; 362 } 363 364 // 365 // Add a default route if received from the server. 366 // 367 if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) { 368 ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS)); 369 370 Ip = HTONL (DhcpSb->Para->Router); 371 CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS)); 372 373 UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway); 374 } 375 376 return EFI_SUCCESS; 377 } 378 379 380 /** 381 Update the lease states when a new lease is acquired. It will not only 382 save the acquired the address and lease time, it will also create a UDP 383 child to provide address resolution for the address. 384 385 @param DhcpSb The DHCP service instance 386 387 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. 388 @retval EFI_SUCCESS The lease is recorded. 389 390 **/ 391 EFI_STATUS 392 DhcpLeaseAcquired ( 393 IN OUT DHCP_SERVICE *DhcpSb 394 ) 395 { 396 INTN Class; 397 398 DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr); 399 400 if (DhcpSb->Para != NULL) { 401 DhcpSb->Netmask = DhcpSb->Para->NetMask; 402 DhcpSb->ServerAddr = DhcpSb->Para->ServerId; 403 } 404 405 if (DhcpSb->Netmask == 0) { 406 Class = NetGetIpClass (DhcpSb->ClientAddr); 407 ASSERT (Class < IP4_ADDR_CLASSE); 408 DhcpSb->Netmask = gIp4AllMasks[Class << 3]; 409 } 410 411 if (DhcpSb->LeaseIoPort != NULL) { 412 UdpIoFreeIo (DhcpSb->LeaseIoPort); 413 } 414 415 // 416 // Create a UDP/IP child to provide ARP service for the Leased IP, 417 // and transmit unicast packet with it as source address. Don't 418 // start receive on this port, the queued packet will be timeout. 419 // 420 DhcpSb->LeaseIoPort = UdpIoCreateIo ( 421 DhcpSb->Controller, 422 DhcpSb->Image, 423 DhcpConfigLeaseIoPort, 424 UDP_IO_UDP4_VERSION, 425 DhcpSb 426 ); 427 428 if (DhcpSb->LeaseIoPort == NULL) { 429 return EFI_OUT_OF_RESOURCES; 430 } 431 432 if (!DHCP_IS_BOOTP (DhcpSb->Para)) { 433 DhcpComputeLease (DhcpSb, DhcpSb->Para); 434 } 435 436 return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE); 437 } 438 439 440 /** 441 Clean up the DHCP related states, IoStatus isn't reset. 442 443 @param DhcpSb The DHCP instance service. 444 445 **/ 446 VOID 447 DhcpCleanLease ( 448 IN DHCP_SERVICE *DhcpSb 449 ) 450 { 451 DhcpSb->DhcpState = Dhcp4Init; 452 DhcpSb->Xid = DhcpSb->Xid + 1; 453 DhcpSb->ClientAddr = 0; 454 DhcpSb->Netmask = 0; 455 DhcpSb->ServerAddr = 0; 456 457 if (DhcpSb->LastOffer != NULL) { 458 FreePool (DhcpSb->LastOffer); 459 DhcpSb->LastOffer = NULL; 460 } 461 462 if (DhcpSb->Selected != NULL) { 463 FreePool (DhcpSb->Selected); 464 DhcpSb->Selected = NULL; 465 } 466 467 if (DhcpSb->Para != NULL) { 468 FreePool (DhcpSb->Para); 469 DhcpSb->Para = NULL; 470 } 471 472 DhcpSb->Lease = 0; 473 DhcpSb->T1 = 0; 474 DhcpSb->T2 = 0; 475 DhcpSb->ExtraRefresh = FALSE; 476 477 if (DhcpSb->LeaseIoPort != NULL) { 478 UdpIoFreeIo (DhcpSb->LeaseIoPort); 479 DhcpSb->LeaseIoPort = NULL; 480 } 481 482 if (DhcpSb->LastPacket != NULL) { 483 FreePool (DhcpSb->LastPacket); 484 DhcpSb->LastPacket = NULL; 485 } 486 487 DhcpSb->PacketToLive = 0; 488 DhcpSb->LastTimeout = 0; 489 DhcpSb->CurRetry = 0; 490 DhcpSb->MaxRetries = 0; 491 DhcpSb->LeaseLife = 0; 492 493 // 494 // Clean active config data. 495 // 496 DhcpCleanConfigure (&DhcpSb->ActiveConfig); 497 } 498 499 500 /** 501 Select a offer among all the offers collected. If the offer selected is 502 of BOOTP, the lease is recorded and user notified. If the offer is of 503 DHCP, it will request the offer from the server. 504 505 @param[in] DhcpSb The DHCP service instance. 506 507 @retval EFI_SUCCESS One of the offer is selected. 508 509 **/ 510 EFI_STATUS 511 DhcpChooseOffer ( 512 IN DHCP_SERVICE *DhcpSb 513 ) 514 { 515 EFI_DHCP4_PACKET *Selected; 516 EFI_DHCP4_PACKET *NewPacket; 517 EFI_DHCP4_PACKET *TempPacket; 518 EFI_STATUS Status; 519 520 ASSERT (DhcpSb->LastOffer != NULL); 521 522 // 523 // User will cache previous offers if he wants to select 524 // from multiple offers. If user provides an invalid packet, 525 // use the last offer, otherwise use the provided packet. 526 // 527 NewPacket = NULL; 528 Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket); 529 530 if (EFI_ERROR (Status)) { 531 return Status; 532 } 533 534 Selected = DhcpSb->LastOffer; 535 536 if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) { 537 TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size); 538 if (TempPacket != NULL) { 539 CopyMem (TempPacket, NewPacket, NewPacket->Size); 540 FreePool (Selected); 541 Selected = TempPacket; 542 } 543 } 544 545 DhcpSb->Selected = Selected; 546 DhcpSb->LastOffer = NULL; 547 DhcpSb->Para = NULL; 548 DhcpValidateOptions (Selected, &DhcpSb->Para); 549 550 // 551 // A bootp offer has been selected, save the lease status, 552 // enter bound state then notify the user. 553 // 554 if (DHCP_IS_BOOTP (DhcpSb->Para)) { 555 Status = DhcpLeaseAcquired (DhcpSb); 556 557 if (EFI_ERROR (Status)) { 558 return Status; 559 } 560 561 DhcpSb->IoStatus = EFI_SUCCESS; 562 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL); 563 return EFI_SUCCESS; 564 } 565 566 // 567 // Send a DHCP requests 568 // 569 Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE); 570 571 if (EFI_ERROR (Status)) { 572 return Status; 573 } 574 575 return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL); 576 } 577 578 579 /** 580 Terminate the current address acquire. All the allocated resources 581 are released. Be careful when calling this function. A rule related 582 to this is: only call DhcpEndSession at the highest level, such as 583 DhcpInput, DhcpOnTimerTick...At the other level, just return error. 584 585 @param[in] DhcpSb The DHCP service instance 586 @param[in] Status The result of the DHCP process. 587 588 **/ 589 VOID 590 DhcpEndSession ( 591 IN DHCP_SERVICE *DhcpSb, 592 IN EFI_STATUS Status 593 ) 594 { 595 if (DHCP_CONNECTED (DhcpSb->DhcpState)) { 596 DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL); 597 } else { 598 DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL); 599 } 600 601 DhcpCleanLease (DhcpSb); 602 603 DhcpSb->IoStatus = Status; 604 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL); 605 } 606 607 608 /** 609 Handle packets in DHCP select state. 610 611 @param[in] DhcpSb The DHCP service instance 612 @param[in] Packet The DHCP packet received 613 @param[in] Para The DHCP parameter extracted from the packet. That 614 is, all the option value that we care. 615 616 @retval EFI_SUCCESS The packet is successfully processed. 617 @retval Others Some error occured. 618 619 **/ 620 EFI_STATUS 621 DhcpHandleSelect ( 622 IN DHCP_SERVICE *DhcpSb, 623 IN EFI_DHCP4_PACKET *Packet, 624 IN DHCP_PARAMETER *Para 625 ) 626 { 627 EFI_STATUS Status; 628 629 Status = EFI_SUCCESS; 630 631 // 632 // First validate the message: 633 // 1. the offer is a unicast 634 // 2. if it is a DHCP message, it must contains a server ID. 635 // Don't return a error for these two case otherwise the session is ended. 636 // 637 if (!DHCP_IS_BOOTP (Para) && 638 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0)) 639 ) { 640 goto ON_EXIT; 641 } 642 643 // 644 // Call the user's callback. The action according to the return is as: 645 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now 646 // 2. EFI_NOT_READY: wait for more offers 647 // 3. EFI_ABORTED: abort the address acquiring. 648 // 649 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL); 650 651 if (Status == EFI_SUCCESS) { 652 if (DhcpSb->LastOffer != NULL) { 653 FreePool (DhcpSb->LastOffer); 654 } 655 656 DhcpSb->LastOffer = Packet; 657 658 return DhcpChooseOffer (DhcpSb); 659 660 } else if (Status == EFI_NOT_READY) { 661 if (DhcpSb->LastOffer != NULL) { 662 FreePool (DhcpSb->LastOffer); 663 } 664 665 DhcpSb->LastOffer = Packet; 666 667 } else if (Status == EFI_ABORTED) { 668 // 669 // DhcpInput will end the session upon error return. Remember 670 // only to call DhcpEndSession at the top level call. 671 // 672 goto ON_EXIT; 673 } 674 675 return EFI_SUCCESS; 676 677 ON_EXIT: 678 FreePool (Packet); 679 return Status; 680 } 681 682 683 /** 684 Handle packets in DHCP request state. 685 686 @param[in] DhcpSb The DHCP service instance 687 @param[in] Packet The DHCP packet received 688 @param[in] Para The DHCP parameter extracted from the packet. That 689 is, all the option value that we care. 690 691 @retval EFI_SUCCESS The packet is successfully processed. 692 @retval Others Some error occured. 693 694 **/ 695 EFI_STATUS 696 DhcpHandleRequest ( 697 IN DHCP_SERVICE *DhcpSb, 698 IN EFI_DHCP4_PACKET *Packet, 699 IN DHCP_PARAMETER *Para 700 ) 701 { 702 EFI_DHCP4_HEADER *Head; 703 EFI_DHCP4_HEADER *Selected; 704 EFI_STATUS Status; 705 UINT8 *Message; 706 707 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para)); 708 709 Head = &Packet->Dhcp4.Header; 710 Selected = &DhcpSb->Selected->Dhcp4.Header; 711 712 // 713 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK. 714 // 715 if (DHCP_IS_BOOTP (Para) || 716 (Para->ServerId != DhcpSb->Para->ServerId) || 717 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK)) 718 ) { 719 720 Status = EFI_SUCCESS; 721 goto ON_EXIT; 722 } 723 724 // 725 // Received a NAK, end the session no matter what the user returns 726 // 727 Status = EFI_DEVICE_ERROR; 728 729 if (Para->DhcpType == DHCP_MSG_NAK) { 730 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); 731 goto ON_EXIT; 732 } 733 734 // 735 // Check whether the ACK matches the selected offer 736 // 737 Message = NULL; 738 739 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) { 740 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer"; 741 goto REJECT; 742 } 743 744 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); 745 746 if (EFI_ERROR (Status)) { 747 Message = (UINT8 *) "Lease is denied upon received ACK"; 748 goto REJECT; 749 } 750 751 // 752 // Record the lease, transit to BOUND state, then notify the user 753 // 754 Status = DhcpLeaseAcquired (DhcpSb); 755 756 if (EFI_ERROR (Status)) { 757 Message = (UINT8 *) "Lease is denied upon entering bound"; 758 goto REJECT; 759 } 760 761 DhcpSb->IoStatus = EFI_SUCCESS; 762 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION); 763 764 FreePool (Packet); 765 return EFI_SUCCESS; 766 767 REJECT: 768 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message); 769 770 ON_EXIT: 771 FreePool (Packet); 772 return Status; 773 } 774 775 776 /** 777 Handle packets in DHCP renew/rebound state. 778 779 @param[in] DhcpSb The DHCP service instance 780 @param[in] Packet The DHCP packet received 781 @param[in] Para The DHCP parameter extracted from the packet. That 782 is, all the option value that we care. 783 784 @retval EFI_SUCCESS The packet is successfully processed. 785 @retval Others Some error occured. 786 787 **/ 788 EFI_STATUS 789 DhcpHandleRenewRebind ( 790 IN DHCP_SERVICE *DhcpSb, 791 IN EFI_DHCP4_PACKET *Packet, 792 IN DHCP_PARAMETER *Para 793 ) 794 { 795 EFI_DHCP4_HEADER *Head; 796 EFI_DHCP4_HEADER *Selected; 797 EFI_STATUS Status; 798 799 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para)); 800 801 Head = &Packet->Dhcp4.Header; 802 Selected = &DhcpSb->Selected->Dhcp4.Header; 803 804 // 805 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK 806 // 807 if (DHCP_IS_BOOTP (Para) || 808 (Para->ServerId != DhcpSb->Para->ServerId) || 809 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK)) 810 ) { 811 812 Status = EFI_SUCCESS; 813 goto ON_EXIT; 814 } 815 816 // 817 // Received a NAK, ignore the user's return then terminate the process 818 // 819 Status = EFI_DEVICE_ERROR; 820 821 if (Para->DhcpType == DHCP_MSG_NAK) { 822 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); 823 goto ON_EXIT; 824 } 825 826 // 827 // The lease is different from the selected. Don't send a DECLINE 828 // since it isn't existed in the client's FSM. 829 // 830 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) { 831 goto ON_EXIT; 832 } 833 834 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); 835 836 if (EFI_ERROR (Status)) { 837 goto ON_EXIT; 838 } 839 840 // 841 // Record the lease, start timer for T1 and T2, 842 // 843 DhcpComputeLease (DhcpSb, Para); 844 DhcpSb->LeaseLife = 0; 845 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE); 846 847 if (DhcpSb->ExtraRefresh != 0) { 848 DhcpSb->ExtraRefresh = FALSE; 849 850 DhcpSb->IoStatus = EFI_SUCCESS; 851 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND); 852 } 853 854 ON_EXIT: 855 FreePool (Packet); 856 return Status; 857 } 858 859 860 /** 861 Handle packets in DHCP reboot state. 862 863 @param[in] DhcpSb The DHCP service instance 864 @param[in] Packet The DHCP packet received 865 @param[in] Para The DHCP parameter extracted from the packet. That 866 is, all the option value that we care. 867 868 @retval EFI_SUCCESS The packet is successfully processed. 869 @retval Others Some error occured. 870 871 **/ 872 EFI_STATUS 873 DhcpHandleReboot ( 874 IN DHCP_SERVICE *DhcpSb, 875 IN EFI_DHCP4_PACKET *Packet, 876 IN DHCP_PARAMETER *Para 877 ) 878 { 879 EFI_DHCP4_HEADER *Head; 880 EFI_STATUS Status; 881 882 Head = &Packet->Dhcp4.Header; 883 884 // 885 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK 886 // 887 if (DHCP_IS_BOOTP (Para) || 888 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK)) 889 ) { 890 891 Status = EFI_SUCCESS; 892 goto ON_EXIT; 893 } 894 895 // 896 // If a NAK is received, transit to INIT and try again. 897 // 898 if (Para->DhcpType == DHCP_MSG_NAK) { 899 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL); 900 901 DhcpSb->ClientAddr = 0; 902 DhcpSb->DhcpState = Dhcp4Init; 903 904 Status = DhcpInitRequest (DhcpSb); 905 goto ON_EXIT; 906 } 907 908 // 909 // Check whether the ACK matches the selected offer 910 // 911 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) { 912 Status = EFI_DEVICE_ERROR; 913 goto ON_EXIT; 914 } 915 916 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL); 917 if (EFI_ERROR (Status)) { 918 goto ON_EXIT; 919 } 920 921 // 922 // OK, get the parameter from server, record the lease 923 // 924 DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para); 925 if (DhcpSb->Para == NULL) { 926 Status = EFI_OUT_OF_RESOURCES; 927 goto ON_EXIT; 928 } 929 930 DhcpSb->Selected = Packet; 931 Status = DhcpLeaseAcquired (DhcpSb); 932 if (EFI_ERROR (Status)) { 933 return Status; 934 } 935 936 DhcpSb->IoStatus = EFI_SUCCESS; 937 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION); 938 return EFI_SUCCESS; 939 940 ON_EXIT: 941 FreePool (Packet); 942 return Status; 943 } 944 945 946 /** 947 Handle the received DHCP packets. This function drives the DHCP 948 state machine. 949 950 @param UdpPacket The UDP packets received. 951 @param EndPoint The local/remote UDP access point 952 @param IoStatus The status of the UDP receive 953 @param Context The opaque parameter to the function. 954 955 **/ 956 VOID 957 EFIAPI 958 DhcpInput ( 959 NET_BUF *UdpPacket, 960 UDP_END_POINT *EndPoint, 961 EFI_STATUS IoStatus, 962 VOID *Context 963 ) 964 { 965 DHCP_SERVICE *DhcpSb; 966 EFI_DHCP4_HEADER *Head; 967 EFI_DHCP4_PACKET *Packet; 968 DHCP_PARAMETER *Para; 969 EFI_STATUS Status; 970 UINT32 Len; 971 972 Packet = NULL; 973 DhcpSb = (DHCP_SERVICE *) Context; 974 975 // 976 // Don't restart receive if error occurs or DHCP is destroyed. 977 // 978 if (EFI_ERROR (IoStatus)) { 979 return ; 980 } else if (DhcpSb->ServiceState == DHCP_DESTROY) { 981 NetbufFree (UdpPacket); 982 return ; 983 } 984 985 ASSERT (UdpPacket != NULL); 986 987 if (DhcpSb->DhcpState == Dhcp4Stopped) { 988 goto RESTART; 989 } 990 991 // 992 // Validate the packet received 993 // 994 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) { 995 goto RESTART; 996 } 997 998 // 999 // Copy the DHCP message to a continuous memory block 1000 // 1001 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER); 1002 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len); 1003 1004 if (Packet == NULL) { 1005 goto RESTART; 1006 } 1007 1008 Packet->Size = Len; 1009 Head = &Packet->Dhcp4.Header; 1010 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head); 1011 1012 if (Packet->Length != UdpPacket->TotalSize) { 1013 goto RESTART; 1014 } 1015 1016 // 1017 // Is this packet the answer to our packet? 1018 // 1019 if ((Head->OpCode != BOOTP_REPLY) || 1020 (NTOHL (Head->Xid) != DhcpSb->Xid) || 1021 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) { 1022 goto RESTART; 1023 } 1024 1025 // 1026 // Validate the options and retrieve the interested options 1027 // 1028 Para = NULL; 1029 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) && 1030 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) && 1031 EFI_ERROR (DhcpValidateOptions (Packet, &Para))) { 1032 1033 goto RESTART; 1034 } 1035 1036 // 1037 // Call the handler for each state. The handler should return 1038 // EFI_SUCCESS if the process can go on no matter whether the 1039 // packet is ignored or not. If the return is EFI_ERROR, the 1040 // session will be terminated. Packet's ownership is handled 1041 // over to the handlers. If operation succeeds, the handler 1042 // must notify the user. It isn't necessary to do if EFI_ERROR 1043 // is returned because the DhcpEndSession will notify the user. 1044 // 1045 Status = EFI_SUCCESS; 1046 1047 switch (DhcpSb->DhcpState) { 1048 case Dhcp4Selecting: 1049 Status = DhcpHandleSelect (DhcpSb, Packet, Para); 1050 break; 1051 1052 case Dhcp4Requesting: 1053 Status = DhcpHandleRequest (DhcpSb, Packet, Para); 1054 break; 1055 1056 case Dhcp4InitReboot: 1057 case Dhcp4Init: 1058 case Dhcp4Bound: 1059 // 1060 // Ignore the packet in INITREBOOT, INIT and BOUND states 1061 // 1062 FreePool (Packet); 1063 Status = EFI_SUCCESS; 1064 break; 1065 1066 case Dhcp4Renewing: 1067 case Dhcp4Rebinding: 1068 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para); 1069 break; 1070 1071 case Dhcp4Rebooting: 1072 Status = DhcpHandleReboot (DhcpSb, Packet, Para); 1073 break; 1074 } 1075 1076 if (Para != NULL) { 1077 FreePool (Para); 1078 } 1079 1080 Packet = NULL; 1081 1082 if (EFI_ERROR (Status)) { 1083 NetbufFree (UdpPacket); 1084 UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0); 1085 DhcpEndSession (DhcpSb, Status); 1086 return ; 1087 } 1088 1089 RESTART: 1090 NetbufFree (UdpPacket); 1091 1092 if (Packet != NULL) { 1093 FreePool (Packet); 1094 } 1095 1096 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0); 1097 1098 if (EFI_ERROR (Status)) { 1099 DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR); 1100 } 1101 } 1102 1103 1104 /** 1105 Release the packet. 1106 1107 @param[in] Arg The packet to release 1108 1109 **/ 1110 VOID 1111 EFIAPI 1112 DhcpReleasePacket ( 1113 IN VOID *Arg 1114 ) 1115 { 1116 FreePool (Arg); 1117 } 1118 1119 1120 /** 1121 Release the net buffer when packet is sent. 1122 1123 @param UdpPacket The UDP packets received. 1124 @param EndPoint The local/remote UDP access point 1125 @param IoStatus The status of the UDP receive 1126 @param Context The opaque parameter to the function. 1127 1128 **/ 1129 VOID 1130 EFIAPI 1131 DhcpOnPacketSent ( 1132 NET_BUF *Packet, 1133 UDP_END_POINT *EndPoint, 1134 EFI_STATUS IoStatus, 1135 VOID *Context 1136 ) 1137 { 1138 NetbufFree (Packet); 1139 } 1140 1141 1142 1143 /** 1144 Build and transmit a DHCP message according to the current states. 1145 This function implement the Table 5. of RFC 2131. Always transits 1146 the state (as defined in Figure 5. of the same RFC) before sending 1147 a DHCP message. The table is adjusted accordingly. 1148 1149 @param[in] DhcpSb The DHCP service instance 1150 @param[in] Seed The seed packet which the new packet is based on 1151 @param[in] Para The DHCP parameter of the Seed packet 1152 @param[in] Type The message type to send 1153 @param[in] Msg The human readable message to include in the packet 1154 sent. 1155 1156 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet 1157 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP 1158 @retval EFI_SUCCESS The message is sent 1159 @retval other Other error occurs 1160 1161 **/ 1162 EFI_STATUS 1163 DhcpSendMessage ( 1164 IN DHCP_SERVICE *DhcpSb, 1165 IN EFI_DHCP4_PACKET *Seed, 1166 IN DHCP_PARAMETER *Para, 1167 IN UINT8 Type, 1168 IN UINT8 *Msg 1169 ) 1170 { 1171 EFI_DHCP4_CONFIG_DATA *Config; 1172 EFI_DHCP4_PACKET *Packet; 1173 EFI_DHCP4_PACKET *NewPacket; 1174 EFI_DHCP4_HEADER *Head; 1175 EFI_DHCP4_HEADER *SeedHead; 1176 UDP_IO *UdpIo; 1177 UDP_END_POINT EndPoint; 1178 NET_BUF *Wrap; 1179 NET_FRAGMENT Frag; 1180 EFI_STATUS Status; 1181 IP4_ADDR IpAddr; 1182 UINT8 *Buf; 1183 UINT16 MaxMsg; 1184 UINT32 Len; 1185 UINT32 Index; 1186 1187 // 1188 // Allocate a big enough memory block to hold the DHCP packet 1189 // 1190 Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen; 1191 1192 if (Msg != NULL) { 1193 Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg); 1194 } 1195 1196 Packet = AllocatePool (Len); 1197 1198 if (Packet == NULL) { 1199 return EFI_OUT_OF_RESOURCES; 1200 } 1201 1202 Packet->Size = Len; 1203 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32); 1204 1205 // 1206 // Fill in the DHCP header fields 1207 // 1208 Config = &DhcpSb->ActiveConfig; 1209 SeedHead = NULL; 1210 1211 if (Seed != NULL) { 1212 SeedHead = &Seed->Dhcp4.Header; 1213 } 1214 1215 Head = &Packet->Dhcp4.Header; 1216 ZeroMem (Head, sizeof (EFI_DHCP4_HEADER)); 1217 1218 Head->OpCode = BOOTP_REQUEST; 1219 Head->HwType = DhcpSb->HwType; 1220 Head->HwAddrLen = DhcpSb->HwLen; 1221 Head->Xid = HTONL (DhcpSb->Xid); 1222 Head->Reserved = HTONS (0x8000); //Server, broadcast the message please. 1223 1224 EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr); 1225 CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen); 1226 1227 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) { 1228 Head->Seconds = 0; 1229 } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) { 1230 // 1231 // Use the same value as the original DHCPDISCOVER message. 1232 // 1233 Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds; 1234 } else { 1235 SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild); 1236 } 1237 1238 // 1239 // Append the DHCP message type 1240 // 1241 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC; 1242 Buf = Packet->Dhcp4.Option; 1243 Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type); 1244 1245 // 1246 // Append the serverid option if necessary: 1247 // 1. DHCP decline message 1248 // 2. DHCP release message 1249 // 3. DHCP request to confirm one lease. 1250 // 1251 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) || 1252 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) 1253 ) { 1254 1255 ASSERT ((Para != NULL) && (Para->ServerId != 0)); 1256 1257 IpAddr = HTONL (Para->ServerId); 1258 Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr); 1259 } 1260 1261 // 1262 // Append the requested IP option if necessary: 1263 // 1. DHCP request to use the previously allocated address 1264 // 2. DHCP request to confirm one lease 1265 // 3. DHCP decline to decline one lease 1266 // 1267 IpAddr = 0; 1268 1269 if (Type == DHCP_MSG_REQUEST) { 1270 if (DhcpSb->DhcpState == Dhcp4Rebooting) { 1271 IpAddr = EFI_IP4 (Config->ClientAddress); 1272 1273 } else if (DhcpSb->DhcpState == Dhcp4Requesting) { 1274 ASSERT (SeedHead != NULL); 1275 IpAddr = EFI_IP4 (SeedHead->YourAddr); 1276 } 1277 1278 } else if (Type == DHCP_MSG_DECLINE) { 1279 ASSERT (SeedHead != NULL); 1280 IpAddr = EFI_IP4 (SeedHead->YourAddr); 1281 } 1282 1283 if (IpAddr != 0) { 1284 Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr); 1285 } 1286 1287 // 1288 // Append the Max Message Length option if it isn't a DECLINE 1289 // or RELEASE to direct the server use large messages instead of 1290 // override the BOOTFILE and SERVER fields in the message head. 1291 // 1292 if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) { 1293 MaxMsg = HTONS (0xFF00); 1294 Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg); 1295 } 1296 1297 // 1298 // Append the user's message if it isn't NULL 1299 // 1300 if (Msg != NULL) { 1301 Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255); 1302 Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg); 1303 } 1304 1305 // 1306 // Append the user configured options 1307 // 1308 if (DhcpSb->UserOptionLen != 0) { 1309 for (Index = 0; Index < Config->OptionCount; Index++) { 1310 // 1311 // We can't use any option other than the client ID from user 1312 // if it is a DHCP decline or DHCP release . 1313 // 1314 if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) && 1315 (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) { 1316 continue; 1317 } 1318 1319 Buf = DhcpAppendOption ( 1320 Buf, 1321 Config->OptionList[Index]->OpCode, 1322 Config->OptionList[Index]->Length, 1323 Config->OptionList[Index]->Data 1324 ); 1325 } 1326 } 1327 1328 *(Buf++) = DHCP_TAG_EOP; 1329 Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option); 1330 1331 // 1332 // OK, the message is built, call the user to override it. 1333 // 1334 Status = EFI_SUCCESS; 1335 NewPacket = NULL; 1336 1337 if (Type == DHCP_MSG_DISCOVER) { 1338 Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket); 1339 1340 } else if (Type == DHCP_MSG_REQUEST) { 1341 Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket); 1342 1343 } else if (Type == DHCP_MSG_DECLINE) { 1344 Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket); 1345 } 1346 1347 if (EFI_ERROR (Status)) { 1348 FreePool (Packet); 1349 return Status; 1350 } 1351 1352 if (NewPacket != NULL) { 1353 FreePool (Packet); 1354 Packet = NewPacket; 1355 } 1356 1357 // 1358 // Save the Client Address will be sent out 1359 // 1360 CopyMem ( 1361 &DhcpSb->ClientAddressSendOut[0], 1362 &Packet->Dhcp4.Header.ClientHwAddr[0], 1363 Packet->Dhcp4.Header.HwAddrLen 1364 ); 1365 1366 1367 // 1368 // Wrap it into a netbuf then send it. 1369 // 1370 Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header; 1371 Frag.Len = Packet->Length; 1372 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet); 1373 1374 if (Wrap == NULL) { 1375 FreePool (Packet); 1376 return EFI_OUT_OF_RESOURCES; 1377 } 1378 1379 // 1380 // Save it as the last sent packet for retransmission 1381 // 1382 if (DhcpSb->LastPacket != NULL) { 1383 FreePool (DhcpSb->LastPacket); 1384 } 1385 1386 DhcpSb->LastPacket = Packet; 1387 DhcpSetTransmitTimer (DhcpSb); 1388 1389 // 1390 // Broadcast the message, unless we know the server address. 1391 // Use the lease UdpIo port to send the unicast packet. 1392 // 1393 EndPoint.RemoteAddr.Addr[0] = 0xffffffff; 1394 EndPoint.LocalAddr.Addr[0] = 0; 1395 EndPoint.RemotePort = DHCP_SERVER_PORT; 1396 EndPoint.LocalPort = DHCP_CLIENT_PORT; 1397 UdpIo = DhcpSb->UdpIo; 1398 1399 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) { 1400 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr; 1401 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr; 1402 UdpIo = DhcpSb->LeaseIoPort; 1403 } 1404 1405 ASSERT (UdpIo != NULL); 1406 NET_GET_REF (Wrap); 1407 1408 Status = UdpIoSendDatagram ( 1409 UdpIo, 1410 Wrap, 1411 &EndPoint, 1412 NULL, 1413 DhcpOnPacketSent, 1414 DhcpSb 1415 ); 1416 1417 if (EFI_ERROR (Status)) { 1418 NET_PUT_REF (Wrap); 1419 return EFI_ACCESS_DENIED; 1420 } 1421 1422 return EFI_SUCCESS; 1423 } 1424 1425 1426 /** 1427 Retransmit a saved packet. Only DISCOVER and REQUEST messages 1428 will be retransmitted. 1429 1430 @param[in] DhcpSb The DHCP service instance 1431 1432 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port 1433 @retval EFI_SUCCESS The packet is retransmitted. 1434 1435 **/ 1436 EFI_STATUS 1437 DhcpRetransmit ( 1438 IN DHCP_SERVICE *DhcpSb 1439 ) 1440 { 1441 UDP_IO *UdpIo; 1442 UDP_END_POINT EndPoint; 1443 NET_BUF *Wrap; 1444 NET_FRAGMENT Frag; 1445 EFI_STATUS Status; 1446 1447 ASSERT (DhcpSb->LastPacket != NULL); 1448 1449 // 1450 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields. 1451 // 1452 if (DhcpSb->DhcpState != Dhcp4Requesting) { 1453 SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild); 1454 } 1455 1456 // 1457 // Wrap it into a netbuf then send it. 1458 // 1459 Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header; 1460 Frag.Len = DhcpSb->LastPacket->Length; 1461 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket); 1462 1463 if (Wrap == NULL) { 1464 return EFI_OUT_OF_RESOURCES; 1465 } 1466 1467 // 1468 // Broadcast the message, unless we know the server address. 1469 // 1470 EndPoint.RemotePort = DHCP_SERVER_PORT; 1471 EndPoint.LocalPort = DHCP_CLIENT_PORT; 1472 EndPoint.RemoteAddr.Addr[0] = 0xffffffff; 1473 EndPoint.LocalAddr.Addr[0] = 0; 1474 UdpIo = DhcpSb->UdpIo; 1475 1476 if (DhcpSb->DhcpState == Dhcp4Renewing) { 1477 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr; 1478 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr; 1479 UdpIo = DhcpSb->LeaseIoPort; 1480 } 1481 1482 ASSERT (UdpIo != NULL); 1483 1484 NET_GET_REF (Wrap); 1485 Status = UdpIoSendDatagram ( 1486 UdpIo, 1487 Wrap, 1488 &EndPoint, 1489 NULL, 1490 DhcpOnPacketSent, 1491 DhcpSb 1492 ); 1493 1494 if (EFI_ERROR (Status)) { 1495 NET_PUT_REF (Wrap); 1496 return EFI_ACCESS_DENIED; 1497 } 1498 1499 return EFI_SUCCESS; 1500 } 1501 1502 1503 /** 1504 Each DHCP service has three timer. Two of them are count down timer. 1505 One for the packet retransmission. The other is to collect the offers. 1506 The third timer increaments the lease life which is compared to T1, T2, 1507 and lease to determine the time to renew and rebind the lease. 1508 DhcpOnTimerTick will be called once every second. 1509 1510 @param[in] Event The timer event 1511 @param[in] Context The context, which is the DHCP service instance. 1512 1513 **/ 1514 VOID 1515 EFIAPI 1516 DhcpOnTimerTick ( 1517 IN EFI_EVENT Event, 1518 IN VOID *Context 1519 ) 1520 { 1521 LIST_ENTRY *Entry; 1522 LIST_ENTRY *Next; 1523 DHCP_SERVICE *DhcpSb; 1524 DHCP_PROTOCOL *Instance; 1525 EFI_STATUS Status; 1526 1527 DhcpSb = (DHCP_SERVICE *) Context; 1528 Instance = DhcpSb->ActiveChild; 1529 1530 // 1531 // 0xffff is the maximum supported value for elapsed time according to RFC. 1532 // 1533 if (Instance != NULL && Instance->ElaspedTime < 0xffff) { 1534 Instance->ElaspedTime++; 1535 } 1536 1537 // 1538 // Check the retransmit timer 1539 // 1540 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) { 1541 1542 // 1543 // Select offer at each timeout if any offer received. 1544 // 1545 if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) { 1546 1547 Status = DhcpChooseOffer (DhcpSb); 1548 1549 if (EFI_ERROR(Status)) { 1550 if (DhcpSb->LastOffer != NULL) { 1551 FreePool (DhcpSb->LastOffer); 1552 DhcpSb->LastOffer = NULL; 1553 } 1554 } else { 1555 goto ON_EXIT; 1556 } 1557 } 1558 1559 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) { 1560 // 1561 // Still has another try 1562 // 1563 DhcpRetransmit (DhcpSb); 1564 DhcpSetTransmitTimer (DhcpSb); 1565 1566 } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) { 1567 1568 // 1569 // Retransmission failed, if the DHCP request is initiated by 1570 // user, adjust the current state according to the lease life. 1571 // Otherwise do nothing to wait the lease to timeout 1572 // 1573 if (DhcpSb->ExtraRefresh != 0) { 1574 Status = EFI_SUCCESS; 1575 1576 if (DhcpSb->LeaseLife < DhcpSb->T1) { 1577 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE); 1578 1579 } else if (DhcpSb->LeaseLife < DhcpSb->T2) { 1580 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE); 1581 1582 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) { 1583 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE); 1584 1585 } else { 1586 goto END_SESSION; 1587 1588 } 1589 1590 DhcpSb->IoStatus = EFI_TIMEOUT; 1591 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND); 1592 } 1593 } else { 1594 goto END_SESSION; 1595 } 1596 } 1597 1598 // 1599 // If an address has been acquired, check whether need to 1600 // refresh or whether it has expired. 1601 // 1602 if (DHCP_CONNECTED (DhcpSb->DhcpState)) { 1603 DhcpSb->LeaseLife++; 1604 1605 // 1606 // Don't timeout the lease, only count the life if user is 1607 // requesting extra renew/rebind. Adjust the state after that. 1608 // 1609 if (DhcpSb->ExtraRefresh != 0) { 1610 return ; 1611 } 1612 1613 if (DhcpSb->LeaseLife == DhcpSb->Lease) { 1614 // 1615 // Lease expires, end the session 1616 // 1617 goto END_SESSION; 1618 1619 } else if (DhcpSb->LeaseLife == DhcpSb->T2) { 1620 // 1621 // T2 expires, transit to rebinding then send a REQUEST to any server 1622 // 1623 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) { 1624 goto END_SESSION; 1625 } 1626 1627 if (Instance != NULL) { 1628 Instance->ElaspedTime= 0; 1629 } 1630 1631 Status = DhcpSendMessage ( 1632 DhcpSb, 1633 DhcpSb->Selected, 1634 DhcpSb->Para, 1635 DHCP_MSG_REQUEST, 1636 NULL 1637 ); 1638 1639 if (EFI_ERROR (Status)) { 1640 goto END_SESSION; 1641 } 1642 1643 } else if (DhcpSb->LeaseLife == DhcpSb->T1) { 1644 // 1645 // T1 expires, transit to renewing, then send a REQUEST to the server 1646 // 1647 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) { 1648 goto END_SESSION; 1649 } 1650 1651 if (Instance != NULL) { 1652 Instance->ElaspedTime= 0; 1653 } 1654 1655 Status = DhcpSendMessage ( 1656 DhcpSb, 1657 DhcpSb->Selected, 1658 DhcpSb->Para, 1659 DHCP_MSG_REQUEST, 1660 NULL 1661 ); 1662 1663 if (EFI_ERROR (Status)) { 1664 goto END_SESSION; 1665 } 1666 } 1667 } 1668 1669 ON_EXIT: 1670 // 1671 // Iterate through all the DhcpSb Children. 1672 // 1673 NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) { 1674 Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link); 1675 1676 if ((Instance != NULL) && (Instance->Token != NULL)) { 1677 Instance->Timeout--; 1678 if (Instance->Timeout == 0) { 1679 PxeDhcpDone (Instance); 1680 } 1681 } 1682 } 1683 1684 return ; 1685 1686 END_SESSION: 1687 DhcpEndSession (DhcpSb, EFI_TIMEOUT); 1688 1689 return ; 1690 } 1691