1 /** @file 2 This EFI_DHCP6_PROTOCOL interface implementation. 3 4 Copyright (c) 2009 - 2012, 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 "Dhcp6Impl.h" 17 18 // 19 // Well-known multi-cast address defined in section-24.1 of rfc-3315 20 // 21 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2 22 // ALL_DHCP_Servers address: FF05::1:3 23 // 24 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}}; 25 EFI_IPv6_ADDRESS mAllDhcpServersAddress = {{0xFF, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 3}}; 26 27 EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = { 28 EfiDhcp6GetModeData, 29 EfiDhcp6Configure, 30 EfiDhcp6Start, 31 EfiDhcp6InfoRequest, 32 EfiDhcp6RenewRebind, 33 EfiDhcp6Decline, 34 EfiDhcp6Release, 35 EfiDhcp6Stop, 36 EfiDhcp6Parse 37 }; 38 39 /** 40 Starts the DHCPv6 standard S.A.R.R. process. 41 42 The Start() function starts the DHCPv6 standard process. This function can 43 be called only when the state of Dhcp6 instance is in the Dhcp6Init state. 44 If the DHCP process completes successfully, the state of the Dhcp6 instance 45 will be transferred through Dhcp6Selecting and Dhcp6Requesting to the 46 Dhcp6Bound state. 47 Refer to rfc-3315 for precise state transitions during this process. At the 48 time when each event occurs in this process, the callback function that was set 49 by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this 50 opportunity to control the process. 51 52 @param[in] This The pointer to Dhcp6 protocol. 53 54 @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has 55 completed when CompletionEvent is NULL. 56 @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured. 57 @retval EFI_INVALID_PARAMETER This is NULL. 58 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 59 @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no 60 response was received from the server within the 61 specified timeout value. 62 @retval EFI_ABORTED The user aborted the DHCPv6 process. 63 @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6 64 standard process. 65 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 66 @retval EFI_NO_MEDIA There was a media error. 67 68 **/ 69 EFI_STATUS 70 EFIAPI 71 EfiDhcp6Start ( 72 IN EFI_DHCP6_PROTOCOL *This 73 ) 74 { 75 EFI_STATUS Status; 76 EFI_TPL OldTpl; 77 DHCP6_INSTANCE *Instance; 78 DHCP6_SERVICE *Service; 79 80 if (This == NULL) { 81 return EFI_INVALID_PARAMETER; 82 } 83 84 Instance = DHCP6_INSTANCE_FROM_THIS (This); 85 Service = Instance->Service; 86 87 // 88 // The instance hasn't been configured. 89 // 90 if (Instance->Config == NULL) { 91 return EFI_ACCESS_DENIED; 92 } 93 94 ASSERT (Instance->IaCb.Ia != NULL); 95 96 // 97 // The instance has already been started. 98 // 99 if (Instance->IaCb.Ia->State != Dhcp6Init) { 100 return EFI_ALREADY_STARTED; 101 } 102 103 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 104 Instance->UdpSts = EFI_ALREADY_STARTED; 105 106 // 107 // Send the solicit message to start S.A.R.R process. 108 // 109 Status = Dhcp6SendSolicitMsg (Instance); 110 111 if (EFI_ERROR (Status)) { 112 goto ON_ERROR; 113 } 114 115 // 116 // Register receive callback for the stateful exchange process. 117 // 118 Status = UdpIoRecvDatagram( 119 Service->UdpIo, 120 Dhcp6ReceivePacket, 121 Service, 122 0 123 ); 124 125 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 126 goto ON_ERROR; 127 } 128 129 gBS->RestoreTPL (OldTpl); 130 131 // 132 // Poll udp out of the net tpl if synchronous call. 133 // 134 if (Instance->Config->IaInfoEvent == NULL) { 135 136 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 137 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); 138 } 139 return Instance->UdpSts; 140 } 141 142 return EFI_SUCCESS; 143 144 ON_ERROR: 145 146 gBS->RestoreTPL (OldTpl); 147 return Status; 148 } 149 150 151 /** 152 Stops the DHCPv6 standard S.A.R.R. process. 153 154 The Stop() function is used to stop the DHCPv6 standard process. After this 155 function is called successfully, the state of Dhcp6 instance is transferred 156 into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called 157 before DHCPv6 standard process can be started again. This function can be 158 called when the Dhcp6 instance is in any state. 159 160 @param[in] This The pointer to the Dhcp6 protocol. 161 162 @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state. 163 @retval EFI_INVALID_PARAMETER This is NULL. 164 165 **/ 166 EFI_STATUS 167 EFIAPI 168 EfiDhcp6Stop ( 169 IN EFI_DHCP6_PROTOCOL *This 170 ) 171 { 172 EFI_TPL OldTpl; 173 EFI_STATUS Status; 174 EFI_UDP6_PROTOCOL *Udp6; 175 DHCP6_INSTANCE *Instance; 176 DHCP6_SERVICE *Service; 177 178 if (This == NULL) { 179 return EFI_INVALID_PARAMETER; 180 } 181 182 Instance = DHCP6_INSTANCE_FROM_THIS (This); 183 Service = Instance->Service; 184 Udp6 = Service->UdpIo->Protocol.Udp6; 185 Status = EFI_SUCCESS; 186 187 // 188 // The instance hasn't been configured. 189 // 190 if (Instance->Config == NULL) { 191 return Status; 192 } 193 194 ASSERT (Instance->IaCb.Ia != NULL); 195 196 // 197 // No valid REPLY message received yet, cleanup this instance directly. 198 // 199 if (Instance->IaCb.Ia->State == Dhcp6Init || 200 Instance->IaCb.Ia->State == Dhcp6Selecting || 201 Instance->IaCb.Ia->State == Dhcp6Requesting 202 ) { 203 goto ON_EXIT; 204 } 205 206 // 207 // Release the current ready Ia. 208 // 209 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 210 211 Instance->UdpSts = EFI_ALREADY_STARTED; 212 Status = Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia); 213 gBS->RestoreTPL (OldTpl); 214 if (EFI_ERROR (Status)) { 215 goto ON_EXIT; 216 } 217 218 // 219 // Poll udp out of the net tpl if synchoronus call. 220 // 221 if (Instance->Config->IaInfoEvent == NULL) { 222 ASSERT (Udp6 != NULL); 223 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 224 Udp6->Poll (Udp6); 225 } 226 Status = Instance->UdpSts; 227 } 228 229 ON_EXIT: 230 // 231 // Clean up the session data for the released Ia. 232 // 233 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 234 Dhcp6CleanupSession (Instance, EFI_SUCCESS); 235 gBS->RestoreTPL (OldTpl); 236 237 return Status; 238 } 239 240 241 /** 242 Returns the current operating mode data for the Dhcp6 instance. 243 244 The GetModeData() function returns the current operating mode and 245 cached data packet for the Dhcp6 instance. 246 247 @param[in] This The pointer to the Dhcp6 protocol. 248 @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data. 249 @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data. 250 251 @retval EFI_SUCCESS The mode data was returned. 252 @retval EFI_INVALID_PARAMETER This is NULL. 253 @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not 254 configured. 255 **/ 256 EFI_STATUS 257 EFIAPI 258 EfiDhcp6GetModeData ( 259 IN EFI_DHCP6_PROTOCOL *This, 260 OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL, 261 OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL 262 ) 263 { 264 EFI_TPL OldTpl; 265 EFI_DHCP6_IA *Ia; 266 DHCP6_INSTANCE *Instance; 267 DHCP6_SERVICE *Service; 268 UINT32 IaSize; 269 UINT32 IdSize; 270 271 if (This == NULL || (Dhcp6ModeData == NULL && Dhcp6ConfigData == NULL)) { 272 return EFI_INVALID_PARAMETER; 273 } 274 275 Instance = DHCP6_INSTANCE_FROM_THIS (This); 276 Service = Instance->Service; 277 278 if (Instance->Config == NULL && Dhcp6ConfigData != NULL) { 279 return EFI_ACCESS_DENIED; 280 } 281 282 ASSERT (Service->ClientId != NULL); 283 284 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 285 286 // 287 // User needs a copy of instance config data. 288 // 289 if (Dhcp6ConfigData != NULL) { 290 ZeroMem (Dhcp6ConfigData, sizeof(EFI_DHCP6_CONFIG_DATA)); 291 // 292 // Duplicate config data, including all reference buffers. 293 // 294 if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) { 295 goto ON_ERROR; 296 } 297 } 298 299 // 300 // User need a copy of instance mode data. 301 // 302 if (Dhcp6ModeData != NULL) { 303 ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA)); 304 // 305 // Duplicate a copy of EFI_DHCP6_DUID for client Id. 306 // 307 IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length); 308 309 Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize); 310 if (Dhcp6ModeData->ClientId == NULL) { 311 goto ON_ERROR; 312 } 313 314 CopyMem ( 315 Dhcp6ModeData->ClientId, 316 Service->ClientId, 317 IdSize 318 ); 319 320 Ia = Instance->IaCb.Ia; 321 if (Ia != NULL) { 322 // 323 // Duplicate a copy of EFI_DHCP6_IA for configured Ia. 324 // 325 IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS); 326 327 Dhcp6ModeData->Ia = AllocateZeroPool (IaSize); 328 if (Dhcp6ModeData->Ia == NULL) { 329 goto ON_ERROR; 330 } 331 332 CopyMem ( 333 Dhcp6ModeData->Ia, 334 Ia, 335 IaSize 336 ); 337 338 // 339 // Duplicate a copy of reply packet if has. 340 // 341 if (Ia->ReplyPacket != NULL) { 342 Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size); 343 if (Dhcp6ModeData->Ia->ReplyPacket == NULL) { 344 goto ON_ERROR; 345 } 346 CopyMem ( 347 Dhcp6ModeData->Ia->ReplyPacket, 348 Ia->ReplyPacket, 349 Ia->ReplyPacket->Size 350 ); 351 } 352 } 353 } 354 355 gBS->RestoreTPL (OldTpl); 356 357 return EFI_SUCCESS; 358 359 ON_ERROR: 360 361 if (Dhcp6ConfigData != NULL) { 362 Dhcp6CleanupConfigData (Dhcp6ConfigData); 363 } 364 if (Dhcp6ModeData != NULL) { 365 Dhcp6CleanupModeData (Dhcp6ModeData); 366 } 367 gBS->RestoreTPL (OldTpl); 368 369 return EFI_OUT_OF_RESOURCES; 370 } 371 372 373 /** 374 Initializes, changes, or resets the operational settings for the Dhcp6 instance. 375 376 The Configure() function is used to initialize or clean up the configuration 377 data of the Dhcp6 instance: 378 - When Dhcp6CfgData is not NULL and Configure() is called successfully, the 379 configuration data will be initialized in the Dhcp6 instance, and the state 380 of the configured IA will be transferred into Dhcp6Init. 381 - When Dhcp6CfgData is NULL and Configure() is called successfully, the 382 configuration data will be cleaned up and no IA will be associated with 383 the Dhcp6 instance. 384 To update the configuration data for an Dhcp6 instance, the original data 385 must be cleaned up before setting the new configuration data. 386 387 @param[in] This The pointer to the Dhcp6 protocol 388 @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA. 389 390 @retval EFI_SUCCESS The Dhcp6 is configured successfully with the 391 Dhcp6Init state, or cleaned up the original 392 configuration setting. 393 @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured. 394 The Dhcp6 instance has already started the 395 DHCPv6 S.A.R.R when Dhcp6CfgData is NULL. 396 @retval EFI_INVALID_PARAMETER Some of the parameter is invalid. 397 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 398 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 399 400 **/ 401 EFI_STATUS 402 EFIAPI 403 EfiDhcp6Configure ( 404 IN EFI_DHCP6_PROTOCOL *This, 405 IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL 406 ) 407 { 408 EFI_TPL OldTpl; 409 EFI_STATUS Status; 410 LIST_ENTRY *Entry; 411 DHCP6_INSTANCE *Other; 412 DHCP6_INSTANCE *Instance; 413 DHCP6_SERVICE *Service; 414 UINTN Index; 415 416 if (This == NULL) { 417 return EFI_INVALID_PARAMETER; 418 } 419 420 Instance = DHCP6_INSTANCE_FROM_THIS (This); 421 Service = Instance->Service; 422 423 // 424 // Check the parameter of configure data. 425 // 426 if (Dhcp6CfgData != NULL) { 427 if (Dhcp6CfgData->OptionCount > 0 && Dhcp6CfgData->OptionList == NULL) { 428 return EFI_INVALID_PARAMETER; 429 } 430 if (Dhcp6CfgData->OptionList != NULL) { 431 for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) { 432 if (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId || 433 Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit || 434 Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept || 435 Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana || 436 Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata 437 ) { 438 return EFI_INVALID_PARAMETER; 439 } 440 } 441 } 442 443 if (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA && 444 Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA 445 ) { 446 return EFI_INVALID_PARAMETER; 447 } 448 449 if (Dhcp6CfgData->IaInfoEvent == NULL && Dhcp6CfgData->SolicitRetransmission == NULL) { 450 return EFI_INVALID_PARAMETER; 451 } 452 453 if (Dhcp6CfgData->SolicitRetransmission != NULL && 454 Dhcp6CfgData->SolicitRetransmission->Mrc == 0 && 455 Dhcp6CfgData->SolicitRetransmission->Mrd == 0 456 ) { 457 return EFI_INVALID_PARAMETER; 458 } 459 460 // 461 // Make sure the (IaId, IaType) is unique over all the instances. 462 // 463 NET_LIST_FOR_EACH (Entry, &Service->Child) { 464 Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link); 465 if (Other->IaCb.Ia != NULL && 466 Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type && 467 Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId 468 ) { 469 return EFI_INVALID_PARAMETER; 470 } 471 } 472 } 473 474 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 475 476 if (Dhcp6CfgData != NULL) { 477 // 478 // It's not allowed to configure one instance twice without configure null. 479 // 480 if (Instance->Config != NULL) { 481 gBS->RestoreTPL (OldTpl); 482 return EFI_ACCESS_DENIED; 483 } 484 485 // 486 // Duplicate config data including all reference buffers. 487 // 488 Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA)); 489 if (Instance->Config == NULL) { 490 gBS->RestoreTPL (OldTpl); 491 return EFI_OUT_OF_RESOURCES; 492 } 493 494 Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData); 495 if (EFI_ERROR(Status)) { 496 FreePool (Instance->Config); 497 gBS->RestoreTPL (OldTpl); 498 return EFI_OUT_OF_RESOURCES; 499 } 500 501 // 502 // Initialize the Ia descriptor from the config data, and leave the other 503 // fields of the Ia as default value 0. 504 // 505 Instance->IaCb.Ia = AllocateZeroPool (sizeof(EFI_DHCP6_IA)); 506 if (Instance->IaCb.Ia == NULL) { 507 Dhcp6CleanupConfigData (Instance->Config); 508 FreePool (Instance->Config); 509 gBS->RestoreTPL (OldTpl); 510 return EFI_OUT_OF_RESOURCES; 511 } 512 CopyMem ( 513 &Instance->IaCb.Ia->Descriptor, 514 &Dhcp6CfgData->IaDescriptor, 515 sizeof(EFI_DHCP6_IA_DESCRIPTOR) 516 ); 517 518 } else { 519 520 if (Instance->Config == NULL) { 521 ASSERT (Instance->IaCb.Ia == NULL); 522 gBS->RestoreTPL (OldTpl); 523 return EFI_SUCCESS; 524 } 525 526 // 527 // It's not allowed to configure a started instance as null. 528 // 529 if (Instance->IaCb.Ia->State != Dhcp6Init) { 530 gBS->RestoreTPL (OldTpl); 531 return EFI_ACCESS_DENIED; 532 } 533 534 Dhcp6CleanupConfigData (Instance->Config); 535 FreePool (Instance->Config); 536 Instance->Config = NULL; 537 538 FreePool (Instance->IaCb.Ia); 539 Instance->IaCb.Ia = NULL; 540 } 541 542 gBS->RestoreTPL (OldTpl); 543 544 return EFI_SUCCESS; 545 } 546 547 548 /** 549 Request configuration information without the assignment of any 550 Ia addresses of the client. 551 552 The InfoRequest() function is used to request configuration information 553 without the assignment of any IPv6 address of the client. The client sends 554 out an Information Request packet to obtain the required configuration 555 information, and DHCPv6 server responds with a Reply packet containing 556 the information for the client. The received Reply packet will be passed 557 to the user by ReplyCallback function. If the user returns EFI_NOT_READY from 558 ReplyCallback, the Dhcp6 instance will continue to receive other Reply 559 packets unless timeout according to the Retransmission parameter. 560 Otherwise, the Information Request exchange process will be finished 561 successfully if user returns EFI_SUCCESS from ReplyCallback. 562 563 @param[in] This The pointer to the Dhcp6 protocol. 564 @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client 565 Identifier option and include it into Information Request 566 packet. Otherwise, Client Identifier option will not be included. 567 @param[in] OptionRequest The pointer to the buffer of option request options. 568 @param[in] OptionCount The option number in the OptionList. 569 @param[in] OptionList The list of appended options. 570 @param[in] Retransmission The pointer to the retransmission of the message. 571 @param[in] TimeoutEvent The event of timeout. 572 @param[in] ReplyCallback The callback function when the reply was received. 573 @param[in] CallbackContext The pointer to the parameter passed to the callback. 574 575 @retval EFI_SUCCESS The DHCPv6 information request exchange process 576 completed when TimeoutEvent is NULL. Information 577 Request packet has been sent to DHCPv6 server when 578 TimeoutEvent is not NULL. 579 @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed 580 because of no response, or not all requested-options 581 are responded by DHCPv6 servers when Timeout happened. 582 @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted 583 by user. 584 @retval EFI_INVALID_PARAMETER Some parameter is NULL. 585 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. 586 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 587 588 **/ 589 EFI_STATUS 590 EFIAPI 591 EfiDhcp6InfoRequest ( 592 IN EFI_DHCP6_PROTOCOL *This, 593 IN BOOLEAN SendClientId, 594 IN EFI_DHCP6_PACKET_OPTION *OptionRequest, 595 IN UINT32 OptionCount, 596 IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL, 597 IN EFI_DHCP6_RETRANSMISSION *Retransmission, 598 IN EFI_EVENT TimeoutEvent OPTIONAL, 599 IN EFI_DHCP6_INFO_CALLBACK ReplyCallback, 600 IN VOID *CallbackContext OPTIONAL 601 ) 602 { 603 EFI_STATUS Status; 604 DHCP6_INSTANCE *Instance; 605 DHCP6_SERVICE *Service; 606 UINTN Index; 607 EFI_EVENT Timer; 608 EFI_STATUS TimerStatus; 609 UINTN GetMappingTimeOut; 610 611 if (This == NULL || OptionRequest == NULL || Retransmission == NULL || ReplyCallback == NULL) { 612 return EFI_INVALID_PARAMETER; 613 } 614 615 if (Retransmission != NULL && Retransmission->Mrc == 0 && Retransmission->Mrd == 0) { 616 return EFI_INVALID_PARAMETER; 617 } 618 619 if (OptionCount > 0 && OptionList == NULL) { 620 return EFI_INVALID_PARAMETER; 621 } 622 623 if (OptionList != NULL) { 624 for (Index = 0; Index < OptionCount; Index++) { 625 if (OptionList[Index]->OpCode == Dhcp6OptClientId || OptionList[Index]->OpCode == Dhcp6OptRequestOption) { 626 return EFI_INVALID_PARAMETER; 627 } 628 } 629 } 630 631 Instance = DHCP6_INSTANCE_FROM_THIS (This); 632 Service = Instance->Service; 633 634 Status = Dhcp6StartInfoRequest ( 635 Instance, 636 SendClientId, 637 OptionRequest, 638 OptionCount, 639 OptionList, 640 Retransmission, 641 TimeoutEvent, 642 ReplyCallback, 643 CallbackContext 644 ); 645 if (Status == EFI_NO_MAPPING) { 646 // 647 // The link local address is not ready, wait for some time and restart 648 // the DHCP6 information request process. 649 // 650 Status = Dhcp6GetMappingTimeOut(Service->Ip6Cfg, &GetMappingTimeOut); 651 if (EFI_ERROR(Status)) { 652 return Status; 653 } 654 655 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer); 656 if (EFI_ERROR (Status)) { 657 return Status; 658 } 659 660 // 661 // Start the timer, wait for link local address DAD to finish. 662 // 663 Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut); 664 if (EFI_ERROR (Status)) { 665 gBS->CloseEvent (Timer); 666 return Status; 667 } 668 669 do { 670 TimerStatus = gBS->CheckEvent (Timer); 671 if (!EFI_ERROR (TimerStatus)) { 672 Status = Dhcp6StartInfoRequest ( 673 Instance, 674 SendClientId, 675 OptionRequest, 676 OptionCount, 677 OptionList, 678 Retransmission, 679 TimeoutEvent, 680 ReplyCallback, 681 CallbackContext 682 ); 683 } 684 } while (TimerStatus == EFI_NOT_READY); 685 686 gBS->CloseEvent (Timer); 687 } 688 if (EFI_ERROR (Status)) { 689 return Status; 690 } 691 692 // 693 // Poll udp out of the net tpl if synchoronus call. 694 // 695 if (TimeoutEvent == NULL) { 696 697 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 698 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); 699 } 700 return Instance->UdpSts; 701 } 702 703 return EFI_SUCCESS; 704 } 705 706 707 /** 708 Manually extend the valid and preferred lifetimes for the IPv6 addresses 709 of the configured IA and update other configuration parameters by sending a 710 Renew or Rebind packet. 711 712 The RenewRebind() function is used to manually extend the valid and preferred 713 lifetimes for the IPv6 addresses of the configured IA, and update other 714 configuration parameters by sending Renew or Rebind packet. 715 - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound, 716 it sends Renew packet to the previously DHCPv6 server and transfer the 717 state of the configured IA to Dhcp6Renewing. If valid Reply packet received, 718 the state transfers to Dhcp6Bound and the valid and preferred timer restarts. 719 If fails, the state transfers to Dhcp6Bound, but the timer continues. 720 - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound, 721 it will send a Rebind packet. If valid Reply packet is received, the state transfers 722 to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state 723 transfers to Dhcp6Init, and the IA can't be used. 724 725 @param[in] This The pointer to the Dhcp6 protocol. 726 @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state. 727 Otherwise, Renew packet will be sent and enter Dhcp6Renewing state. 728 729 @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has 730 completed and at least one IPv6 address of the 731 configured IA has been bound again when 732 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL. 733 The EFI DHCPv6 Protocol instance has sent Renew 734 or Rebind packet when 735 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL. 736 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the 737 state of the configured IA is not in Dhcp6Bound. 738 @retval EFI_ALREADY_STARTED The state of the configured IA has already entered 739 Dhcp6Renewing when RebindRequest is FALSE. 740 The state of the configured IA has already entered 741 Dhcp6Rebinding when RebindRequest is TRUE. 742 @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted 743 by the user. 744 @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed 745 because of no response. 746 @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured 747 IA after the DHCPv6 renew/rebind exchange process. 748 @retval EFI_INVALID_PARAMETER Some parameter is NULL. 749 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 750 751 **/ 752 EFI_STATUS 753 EFIAPI 754 EfiDhcp6RenewRebind ( 755 IN EFI_DHCP6_PROTOCOL *This, 756 IN BOOLEAN RebindRequest 757 ) 758 { 759 EFI_STATUS Status; 760 EFI_TPL OldTpl; 761 DHCP6_INSTANCE *Instance; 762 DHCP6_SERVICE *Service; 763 764 if (This == NULL) { 765 return EFI_INVALID_PARAMETER; 766 } 767 768 Instance = DHCP6_INSTANCE_FROM_THIS (This); 769 Service = Instance->Service; 770 771 // 772 // The instance hasn't been configured. 773 // 774 if (Instance->Config == NULL) { 775 return EFI_ACCESS_DENIED; 776 } 777 778 ASSERT (Instance->IaCb.Ia != NULL); 779 780 // 781 // The instance has already entered renewing or rebinding state. 782 // 783 if ((Instance->IaCb.Ia->State == Dhcp6Rebinding && RebindRequest) || 784 (Instance->IaCb.Ia->State == Dhcp6Renewing && !RebindRequest) 785 ) { 786 return EFI_ALREADY_STARTED; 787 } 788 789 if (Instance->IaCb.Ia->State != Dhcp6Bound) { 790 return EFI_ACCESS_DENIED; 791 } 792 793 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 794 Instance->UdpSts = EFI_ALREADY_STARTED; 795 796 // 797 // Send renew/rebind message to start exchange process. 798 // 799 Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest); 800 801 if (EFI_ERROR (Status)) { 802 goto ON_ERROR; 803 } 804 805 // 806 // Register receive callback for the stateful exchange process. 807 // 808 Status = UdpIoRecvDatagram( 809 Service->UdpIo, 810 Dhcp6ReceivePacket, 811 Service, 812 0 813 ); 814 815 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 816 goto ON_ERROR; 817 } 818 819 gBS->RestoreTPL (OldTpl); 820 821 // 822 // Poll udp out of the net tpl if synchoronus call. 823 // 824 if (Instance->Config->IaInfoEvent == NULL) { 825 826 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 827 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); 828 } 829 return Instance->UdpSts; 830 } 831 832 return EFI_SUCCESS; 833 834 ON_ERROR: 835 836 gBS->RestoreTPL (OldTpl); 837 return Status; 838 } 839 840 841 /** 842 Inform that one or more addresses assigned by a server are already 843 in use by another node. 844 845 The Decline() function is used to manually decline the assignment of 846 IPv6 addresses, which have been already used by another node. If all 847 IPv6 addresses of the configured IA are declined through this function, 848 the state of the IA will switch through Dhcp6Declining to Dhcp6Init. 849 Otherwise, the state of the IA will restore to Dhcp6Bound after the 850 declining process. The Decline() can only be called when the IA is in 851 Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, 852 this function is a blocking operation. It will return after the 853 declining process finishes, or aborted by user. 854 855 @param[in] This The pointer to EFI_DHCP6_PROTOCOL. 856 @param[in] AddressCount The number of declining addresses. 857 @param[in] Addresses The pointer to the buffer stored the declining 858 addresses. 859 860 @retval EFI_SUCCESS The DHCPv6 decline exchange process completed 861 when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL. 862 The Dhcp6 instance sent Decline packet when 863 EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL. 864 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the 865 state of the configured IA is not in Dhcp6Bound. 866 @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user. 867 @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with 868 the configured IA for this instance. 869 @retval EFI_INVALID_PARAMETER Some parameter is NULL. 870 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 871 872 **/ 873 EFI_STATUS 874 EFIAPI 875 EfiDhcp6Decline ( 876 IN EFI_DHCP6_PROTOCOL *This, 877 IN UINT32 AddressCount, 878 IN EFI_IPv6_ADDRESS *Addresses 879 ) 880 { 881 EFI_STATUS Status; 882 EFI_TPL OldTpl; 883 EFI_DHCP6_IA *DecIa; 884 DHCP6_INSTANCE *Instance; 885 DHCP6_SERVICE *Service; 886 887 if (This == NULL || AddressCount == 0 || Addresses == NULL) { 888 return EFI_INVALID_PARAMETER; 889 } 890 891 Instance = DHCP6_INSTANCE_FROM_THIS (This); 892 Service = Instance->Service; 893 894 // 895 // The instance hasn't been configured. 896 // 897 if (Instance->Config == NULL) { 898 return EFI_ACCESS_DENIED; 899 } 900 901 ASSERT (Instance->IaCb.Ia != NULL); 902 903 if (Instance->IaCb.Ia->State != Dhcp6Bound) { 904 return EFI_ACCESS_DENIED; 905 } 906 907 // 908 // Check whether all the declined addresses belongs to the configured Ia. 909 // 910 Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); 911 912 if (EFI_ERROR(Status)) { 913 return Status; 914 } 915 916 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 917 Instance->UdpSts = EFI_ALREADY_STARTED; 918 919 // 920 // Deprive of all the declined addresses from the configured Ia, and create a 921 // DeclineIa used to create decline message. 922 // 923 DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); 924 925 if (DecIa == NULL) { 926 Status = EFI_OUT_OF_RESOURCES; 927 goto ON_ERROR; 928 } 929 930 // 931 // Send the decline message to start exchange process. 932 // 933 Status = Dhcp6SendDeclineMsg (Instance, DecIa); 934 935 if (EFI_ERROR (Status)) { 936 goto ON_ERROR; 937 } 938 939 // 940 // Register receive callback for the stateful exchange process. 941 // 942 Status = UdpIoRecvDatagram( 943 Service->UdpIo, 944 Dhcp6ReceivePacket, 945 Service, 946 0 947 ); 948 949 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 950 goto ON_ERROR; 951 } 952 953 FreePool (DecIa); 954 gBS->RestoreTPL (OldTpl); 955 956 // 957 // Poll udp out of the net tpl if synchoronus call. 958 // 959 if (Instance->Config->IaInfoEvent == NULL) { 960 961 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 962 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); 963 } 964 return Instance->UdpSts; 965 } 966 967 return EFI_SUCCESS; 968 969 ON_ERROR: 970 971 if (DecIa != NULL) { 972 FreePool (DecIa); 973 } 974 gBS->RestoreTPL (OldTpl); 975 976 return Status; 977 } 978 979 980 /** 981 Release one or more addresses associated with the configured Ia 982 for current instance. 983 984 The Release() function is used to manually release one or more 985 IPv6 addresses. If AddressCount is zero, it will release all IPv6 986 addresses of the configured IA. If all IPv6 addresses of the IA are 987 released through this function, the state of the IA will switch 988 through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the 989 IA will restore to Dhcp6Bound after the releasing process. 990 The Release() can only be called when the IA is in Dhcp6Bound state. 991 If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is 992 a blocking operation. It will return after the releasing process 993 finishes, or is aborted by user. 994 995 @param[in] This The pointer to the Dhcp6 protocol. 996 @param[in] AddressCount The number of releasing addresses. 997 @param[in] Addresses The pointer to the buffer stored the releasing 998 addresses. 999 1000 @retval EFI_SUCCESS The DHCPv6 release exchange process 1001 completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent 1002 was NULL. The Dhcp6 instance was sent Release 1003 packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent 1004 was not NULL. 1005 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the 1006 state of the configured IA is not in Dhcp6Bound. 1007 @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user. 1008 @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with 1009 the configured IA for this instance. 1010 @retval EFI_INVALID_PARAMETER Some parameter is NULL. 1011 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. 1012 1013 **/ 1014 EFI_STATUS 1015 EFIAPI 1016 EfiDhcp6Release ( 1017 IN EFI_DHCP6_PROTOCOL *This, 1018 IN UINT32 AddressCount, 1019 IN EFI_IPv6_ADDRESS *Addresses 1020 ) 1021 { 1022 EFI_STATUS Status; 1023 EFI_TPL OldTpl; 1024 EFI_DHCP6_IA *RelIa; 1025 DHCP6_INSTANCE *Instance; 1026 DHCP6_SERVICE *Service; 1027 1028 if (This == NULL || (AddressCount != 0 && Addresses == NULL)) { 1029 return EFI_INVALID_PARAMETER; 1030 } 1031 1032 Instance = DHCP6_INSTANCE_FROM_THIS (This); 1033 Service = Instance->Service; 1034 1035 // 1036 // The instance hasn't been configured. 1037 // 1038 if (Instance->Config == NULL) { 1039 return EFI_ACCESS_DENIED; 1040 } 1041 1042 ASSERT (Instance->IaCb.Ia != NULL); 1043 1044 if (Instance->IaCb.Ia->State != Dhcp6Bound) { 1045 return EFI_ACCESS_DENIED; 1046 } 1047 1048 // 1049 // Check whether all the released addresses belongs to the configured Ia. 1050 // 1051 Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses); 1052 1053 if (EFI_ERROR(Status)) { 1054 return Status; 1055 } 1056 1057 OldTpl = gBS->RaiseTPL (TPL_CALLBACK); 1058 Instance->UdpSts = EFI_ALREADY_STARTED; 1059 1060 // 1061 // Deprive of all the released addresses from the configured Ia, and create a 1062 // ReleaseIa used to create release message. 1063 // 1064 RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses); 1065 1066 if (RelIa == NULL) { 1067 Status = EFI_OUT_OF_RESOURCES; 1068 goto ON_ERROR; 1069 } 1070 1071 // 1072 // Send the release message to start exchange process. 1073 // 1074 Status = Dhcp6SendReleaseMsg (Instance, RelIa); 1075 1076 if (EFI_ERROR (Status)) { 1077 goto ON_ERROR; 1078 } 1079 1080 // 1081 // Register receive callback for the stateful exchange process. 1082 // 1083 Status = UdpIoRecvDatagram( 1084 Service->UdpIo, 1085 Dhcp6ReceivePacket, 1086 Service, 1087 0 1088 ); 1089 1090 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) { 1091 goto ON_ERROR; 1092 } 1093 1094 FreePool (RelIa); 1095 gBS->RestoreTPL (OldTpl); 1096 1097 // 1098 // Poll udp out of the net tpl if synchoronus call. 1099 // 1100 if (Instance->Config->IaInfoEvent == NULL) { 1101 while (Instance->UdpSts == EFI_ALREADY_STARTED) { 1102 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6); 1103 } 1104 return Instance->UdpSts; 1105 } 1106 1107 return EFI_SUCCESS; 1108 1109 ON_ERROR: 1110 1111 if (RelIa != NULL) { 1112 FreePool (RelIa); 1113 } 1114 gBS->RestoreTPL (OldTpl); 1115 1116 return Status; 1117 } 1118 1119 1120 /** 1121 Parse the option data in the Dhcp6 packet. 1122 1123 The Parse() function is used to retrieve the option list in the DHCPv6 packet. 1124 1125 @param[in] This The pointer to the Dhcp6 protocol. 1126 @param[in] Packet The pointer to the Dhcp6 packet. 1127 @param[in, out] OptionCount The number of option in the packet. 1128 @param[out] PacketOptionList The array of pointers to each option in the packet. 1129 1130 @retval EFI_SUCCESS The packet was successfully parsed. 1131 @retval EFI_INVALID_PARAMETER Some parameter is NULL. 1132 @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options 1133 that were found in the Packet. 1134 1135 **/ 1136 EFI_STATUS 1137 EFIAPI 1138 EfiDhcp6Parse ( 1139 IN EFI_DHCP6_PROTOCOL *This, 1140 IN EFI_DHCP6_PACKET *Packet, 1141 IN OUT UINT32 *OptionCount, 1142 OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL 1143 ) 1144 { 1145 UINT32 OptCnt; 1146 UINT32 OptLen; 1147 UINT16 DataLen; 1148 UINT8 *Start; 1149 UINT8 *End; 1150 1151 if (This == NULL || Packet == NULL || OptionCount == NULL) { 1152 return EFI_INVALID_PARAMETER; 1153 } 1154 1155 if (*OptionCount != 0 && PacketOptionList == NULL) { 1156 return EFI_INVALID_PARAMETER; 1157 } 1158 1159 if (Packet->Length > Packet->Size || Packet->Length < sizeof (EFI_DHCP6_HEADER)) { 1160 return EFI_INVALID_PARAMETER; 1161 } 1162 1163 // 1164 // The format of Dhcp6 option: 1165 // 1166 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 1167 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1168 // | option-code | option-len (option data) | 1169 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1170 // | option-data | 1171 // | (option-len octets) | 1172 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1173 // 1174 1175 OptCnt = 0; 1176 OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER); 1177 Start = Packet->Dhcp6.Option; 1178 End = Start + OptLen; 1179 1180 // 1181 // Calculate the number of option in the packet. 1182 // 1183 while (Start < End) { 1184 DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; 1185 Start += (NTOHS (DataLen) + 4); 1186 OptCnt++; 1187 } 1188 1189 // 1190 // It will return buffer too small if pass-in option count is smaller than the 1191 // actual count of options in the packet. 1192 // 1193 if (OptCnt > *OptionCount) { 1194 *OptionCount = OptCnt; 1195 return EFI_BUFFER_TOO_SMALL; 1196 } 1197 1198 ZeroMem ( 1199 PacketOptionList, 1200 (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *)) 1201 ); 1202 1203 OptCnt = 0; 1204 Start = Packet->Dhcp6.Option; 1205 1206 while (Start < End) { 1207 1208 PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *) Start; 1209 DataLen = ((EFI_DHCP6_PACKET_OPTION *) Start)->OpLen; 1210 Start += (NTOHS (DataLen) + 4); 1211 OptCnt++; 1212 } 1213 1214 return EFI_SUCCESS; 1215 } 1216 1217