/** @file\r
Functions implementation related with DHCPv6 for UefiPxeBc Driver.\r
\r
- Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>\r
\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions of the BSD License\r
\r
#include "PxeBcImpl.h"\r
\r
+//\r
+// Well-known multi-cast address defined in section-24.1 of rfc-3315\r
+//\r
+// ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2\r
+//\r
+EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};\r
\r
/**\r
Parse out a DHCPv6 option by OptTag, and find the position in buffer.\r
)\r
{\r
UINT16 PrefixLen;\r
- UINT8 *BootFileNamePtr;\r
- UINT8 *BootFileName;\r
+ CHAR8 *BootFileNamePtr;\r
+ CHAR8 *BootFileName;\r
UINT16 BootFileNameLen;\r
CHAR8 *TmpStr;\r
CHAR8 TmpChar;\r
CHAR8 *ServerAddressOption;\r
CHAR8 *ServerAddress;\r
+ CHAR8 *ModeStr;\r
EFI_STATUS Status;\r
\r
//\r
//\r
// Get the part of BOOTFILE_NAME string.\r
//\r
- BootFileNamePtr = (UINT8*)((UINTN)ServerAddress + 1);\r
+ BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);\r
if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {\r
FreePool (TmpStr);\r
return EFI_INVALID_PARAMETER;\r
++BootFileNamePtr;\r
BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);\r
if (BootFileNameLen != 0 || FileName != NULL) {\r
+ //\r
+ // Remove trailing mode=octet if present and ignore. All other modes are\r
+ // invalid for netboot6, so reject them.\r
+ //\r
+ ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");\r
+ if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {\r
+ *ModeStr = '\0';\r
+ } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
//\r
// Extract boot file name from URL.\r
//\r
- BootFileName = (UINT8 *) AllocateZeroPool (BootFileNameLen);\r
+ BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);\r
if (BootFileName == NULL) {\r
FreePool (TmpStr);\r
return EFI_OUT_OF_RESOURCES;\r
}\r
- *FileName = BootFileName;\r
+ *FileName = (UINT8*) BootFileName;\r
\r
//\r
// Decode percent-encoding in boot file name.\r
\r
Status = PxeBc->UdpRead (\r
PxeBc,\r
- OpFlags,\r
+ EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP,\r
&Private->StationIp,\r
&SrcPort,\r
&Private->ServerIp,\r
Mode = Private->PxeBc.Mode;\r
Private->IsDoDiscover = FALSE;\r
Offer = &Private->OfferBuffer[Index].Dhcp6;\r
-\r
- ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
- //\r
- // Parse out the next server address from the last offer, and store it\r
- //\r
- Status = PxeBcExtractBootFileUrl (\r
- &Private->BootFileName,\r
- &Private->ServerIp.v6,\r
- (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
- NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
- );\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
+ if (Offer->OfferType == PxeOfferTypeDhcpBinl) {\r
+ //\r
+ // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.\r
+ //\r
+ CopyMem (\r
+ &Private->ServerIp.v6,\r
+ &mAllDhcpRelayAndServersAddress,\r
+ sizeof (EFI_IPv6_ADDRESS)\r
+ );\r
+ } else {\r
+ ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);\r
+ //\r
+ // Parse out the next server address from the last offer, and store it\r
+ //\r
+ Status = PxeBcExtractBootFileUrl (\r
+ &Private->BootFileName,\r
+ &Private->ServerIp.v6,\r
+ (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),\r
+ NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
}\r
\r
//\r
EFI_EVENT TimeOutEvt;\r
EFI_EVENT MappedEvt;\r
EFI_STATUS Status;\r
+ UINT64 DadTriggerTime;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
\r
Status = EFI_SUCCESS;\r
TimeOutEvt = NULL;\r
goto ON_EXIT;\r
}\r
\r
+ //\r
+ // Get Duplicate Address Detection Transmits count.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ &DataSize,\r
+ &DadXmits\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto ON_EXIT;\r
+ }\r
+\r
//\r
// Create a timer as setting address timeout event since DAD in IP6 driver.\r
//\r
// Start the 5 secondes timer to wait for setting address.\r
//\r
Status = EFI_NO_MAPPING;\r
- gBS->SetTimer (TimeOutEvt, TimerRelative, PXEBC_DHCP6_MAPPING_TIMEOUT);\r
+ DadTriggerTime = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;\r
+ gBS->SetTimer (TimeOutEvt, TimerRelative, DadTriggerTime);\r
\r
while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {\r
Ip6->Poll (Ip6);\r
}\r
ReadSize = (UINTN) Reply->Size;\r
\r
+ //\r
+ // Start Udp6Read instance\r
+ //\r
+ Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+ \r
Status = PxeBc->UdpRead (\r
PxeBc,\r
OpFlags,\r
&ReadSize,\r
(VOID *) &Reply->Dhcp6\r
);\r
+ //\r
+ // Stop Udp6Read instance\r
+ //\r
+ Private->Udp6Read->Configure (Private->Udp6Read, NULL);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
UINT8 Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];\r
UINT32 OptCount;\r
EFI_STATUS Status;\r
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;\r
+ EFI_STATUS TimerStatus;\r
+ EFI_EVENT Timer;\r
+ UINT64 GetMappingTimeOut;\r
+ UINTN DataSize;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
\r
Status = EFI_SUCCESS;\r
PxeMode = Private->PxeBc.Mode;\r
+ Ip6Cfg = Private->Ip6Cfg;\r
+ Timer = NULL;\r
\r
//\r
// Build option list for the request packet.\r
Config.IaInfoEvent = NULL;\r
Config.RapidCommit = FALSE;\r
Config.ReconfigureAccept = FALSE;\r
- Config.IaDescriptor.IaId = 1;\r
+ Config.IaDescriptor.IaId = Private->IaId;\r
Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;\r
Config.SolicitRetransmission = Retransmit;\r
Retransmit->Irt = 4;\r
// Configure the DHCPv6 instance for PXE boot.\r
//\r
Status = Dhcp6->Configure (Dhcp6, &Config);\r
+ FreePool (Retransmit);\r
if (EFI_ERROR (Status)) {\r
- FreePool (Retransmit);\r
return Status;\r
}\r
\r
// Start DHCPv6 S.A.R.R. process to acquire IPv6 address.\r
//\r
Status = Dhcp6->Start (Dhcp6);\r
+ if (Status == EFI_NO_MAPPING) {\r
+ //\r
+ // IP6 Linklocal address is not available for use, so stop current Dhcp process\r
+ // and wait for duplicate address detection to finish.\r
+ //\r
+ Dhcp6->Stop (Dhcp6);\r
+\r
+ //\r
+ // Get Duplicate Address Detection Transmits count.\r
+ //\r
+ DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);\r
+ Status = Ip6Cfg->GetData (\r
+ Ip6Cfg,\r
+ Ip6ConfigDataTypeDupAddrDetectTransmits,\r
+ &DataSize,\r
+ &DadXmits\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
+ if (EFI_ERROR (Status)) {\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+ return Status;\r
+ }\r
+\r
+ GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;\r
+ Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);\r
+ if (EFI_ERROR (Status)) {\r
+ gBS->CloseEvent (Timer);\r
+ Dhcp6->Configure (Dhcp6, NULL);\r
+ return Status;\r
+ }\r
+\r
+ do {\r
+ \r
+ TimerStatus = gBS->CheckEvent (Timer);\r
+ if (!EFI_ERROR (TimerStatus)) {\r
+ Status = Dhcp6->Start (Dhcp6);\r
+ }\r
+ } while (TimerStatus == EFI_NOT_READY);\r
+ \r
+ gBS->CloseEvent (Timer);\r
+ }\r
if (EFI_ERROR (Status)) {\r
if (Status == EFI_ICMP_ERROR) {\r
PxeMode->IcmpErrorReceived = TRUE;\r
return Status;\r
}\r
\r
- Status = PxeBcFlushStaionIp (Private, &Private->StationIp, NULL);\r
+ Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);\r
if (EFI_ERROR (Status)) {\r
PxeBcUnregisterIp6Address (Private);\r
Dhcp6->Stop (Dhcp6);\r