/** @file\r
Dhcp6 support functions implementation.\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
EFI_DHCP6_DUID *Duid;\r
EFI_TIME Time;\r
UINT32 Stamp;\r
+ EFI_GUID Uuid;\r
+\r
\r
//\r
// Attempt to get client Id from variable to keep it constant.\r
// See details in section-9 of rfc-3315.\r
//\r
- Duid = GetVariable (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid);\r
+ GetVariable2 (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid, (VOID**)&Duid, NULL);\r
if (Duid != NULL) {\r
return Duid;\r
}\r
\r
- //\r
- // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.\r
- //\r
- gRT->GetTime (&Time, NULL);\r
- Stamp = (UINT32)\r
- (\r
- (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *\r
- 60 +\r
- Time.Second\r
- );\r
-\r
//\r
// The format of client identifier option:\r
//\r
// . .\r
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
//\r
- //\r
- // The format of DUID-LLT:\r
- //\r
- // 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\r
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
- // | Duid type (1) | hardware type (16 bits) |\r
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
- // | time (32 bits) |\r
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
- // . .\r
- // . link-layer address (variable length) .\r
- // . .\r
- // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
- //\r
\r
//\r
- // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes\r
+ // If System UUID is found from SMBIOS Table, use DUID-UUID type.\r
//\r
- Duid = AllocateZeroPool (10 + Mode->HwAddressSize);\r
- if (Duid == NULL) {\r
- return NULL;\r
- }\r
+ if (!EFI_ERROR (NetLibGetSystemGuid (&Uuid))) {\r
+ //\r
+ //\r
+ // The format of DUID-UUID:\r
+ // \r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | DUID-Type (4) | UUID (128 bits) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |\r
+ // | |\r
+ // | |\r
+ // | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-\r
\r
- //\r
- // sizeof (Duid-type + hardware-type + time) = 8 bytes\r
- //\r
- Duid->Length = (UINT16) (Mode->HwAddressSize + 8);\r
+ //\r
+ // sizeof (option-len + Duid-type + UUID-size) = 20 bytes\r
+ //\r
+ Duid = AllocateZeroPool (2 + 2 + sizeof (EFI_GUID));\r
+ if (Duid == NULL) {\r
+ return NULL;\r
+ }\r
\r
- //\r
- // Set the Duid-type, hardware-type, time and copy the hardware address.\r
- //\r
- WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));\r
- WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));\r
- WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));\r
+ //\r
+ // sizeof (Duid-type + UUID-size) = 18 bytes\r
+ //\r
+ Duid->Length = (UINT16) (18);\r
+ \r
+ //\r
+ // Set the Duid-type and copy UUID.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeUuid));\r
+ \r
+ CopyMem (Duid->Duid + 2, &Uuid, sizeof(EFI_GUID));\r
+\r
+ } else {\r
+ \r
+ //\r
+ //\r
+ // The format of DUID-LLT:\r
+ //\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | Duid type (1) | hardware type (16 bits) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | time (32 bits) |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // . .\r
+ // . link-layer address (variable length) .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ //\r
\r
- CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);\r
+ //\r
+ // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.\r
+ //\r
+ gRT->GetTime (&Time, NULL);\r
+ Stamp = (UINT32)\r
+ (\r
+ (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *\r
+ 60 +\r
+ Time.Second\r
+ );\r
+\r
+ //\r
+ // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes\r
+ //\r
+ Duid = AllocateZeroPool (10 + Mode->HwAddressSize);\r
+ if (Duid == NULL) {\r
+ return NULL;\r
+ }\r
+ \r
+ //\r
+ // sizeof (Duid-type + hardware-type + time) = 8 bytes\r
+ //\r
+ Duid->Length = (UINT16) (Mode->HwAddressSize + 8);\r
+ \r
+ //\r
+ // Set the Duid-type, hardware-type, time and copy the hardware address.\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid)), HTONS (Dhcp6DuidTypeLlt));\r
+ WriteUnaligned16 ((UINT16 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 2), HTONS (NET_IFTYPE_ETHERNET));\r
+ WriteUnaligned32 ((UINT32 *) ((UINT8 *) Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 4), HTONL (Stamp));\r
+\r
+ CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);\r
+ }\r
\r
Status = gRT->SetVariable (\r
L"ClientId",\r
Duid->Length + 2,\r
(VOID *) Duid\r
);\r
- ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (Duid);\r
+ return NULL;\r
+ }\r
\r
return Duid;\r
}\r
IN DHCP6_IA_CB *IaCb\r
)\r
{\r
- EFI_DHCP6_IA_ADDRESS *IaAddr;\r
UINT32 MinLt;\r
UINT32 MaxLt;\r
UINTN Index;\r
// valid life time.\r
//\r
for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {\r
- IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
- MinLt = MIN (MinLt, IaAddr->ValidLifetime);\r
- MaxLt = MAX (MinLt, IaAddr->ValidLifetime);\r
+ MinLt = MIN (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime);\r
+ MaxLt = MAX (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime);\r
}\r
\r
//\r
return Buf;\r
}\r
\r
+/**\r
+ Append the appointed IA Address option to Buf, and move Buf to the end.\r
+\r
+ @param[in, out] Buf The pointer to the position to append.\r
+ @param[in] IaAddr The pointer to the IA Address.\r
+ @param[in] MessageType Message type of DHCP6 package.\r
+\r
+ @return Buf The position to append the next option.\r
+\r
+**/\r
+UINT8 *\r
+Dhcp6AppendIaAddrOption (\r
+ IN OUT UINT8 *Buf,\r
+ IN EFI_DHCP6_IA_ADDRESS *IaAddr,\r
+ IN UINT32 MessageType\r
+)\r
+{\r
+\r
+ // The format of the IA Address option is:\r
+ //\r
+ // 0 1 2 3\r
+ // 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\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | OPTION_IAADDR | option-len |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | |\r
+ // | IPv6 address |\r
+ // | |\r
+ // | |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | preferred-lifetime |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // | valid-lifetime |\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ // . .\r
+ // . IAaddr-options .\r
+ // . .\r
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\r
+ \r
+ //\r
+ // Fill the value of Ia Address option type\r
+ //\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptIaAddr));\r
+ Buf += 2;\r
+\r
+ WriteUnaligned16 ((UINT16 *) Buf, HTONS (sizeof (EFI_DHCP6_IA_ADDRESS)));\r
+ Buf += 2;\r
+\r
+ CopyMem (Buf, &IaAddr->IpAddress, sizeof(EFI_IPv6_ADDRESS));\r
+ Buf += sizeof(EFI_IPv6_ADDRESS);\r
+\r
+ //\r
+ // Fill the value of preferred-lifetime and valid-lifetime.\r
+ // According to RFC3315 Chapter 18.1.2, the preferred-lifetime and valid-lifetime fields\r
+ // should set to 0 when initiate a Confirm message.\r
+ //\r
+ if (MessageType != Dhcp6MsgConfirm) {\r
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL (IaAddr->PreferredLifetime));\r
+ }\r
+ Buf += 4;\r
+\r
+ if (MessageType != Dhcp6MsgConfirm) {\r
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL (IaAddr->ValidLifetime));\r
+ }\r
+ Buf += 4;\r
+\r
+ return Buf;\r
+}\r
+\r
\r
/**\r
Append the appointed Ia option to Buf, and move Buf to the end.\r
@param[in] Ia The pointer to the Ia.\r
@param[in] T1 The time of T1.\r
@param[in] T2 The time of T2.\r
+ @param[in] MessageType Message type of DHCP6 package.\r
\r
@return Buf The position to append the next Ia option.\r
\r
IN OUT UINT8 *Buf,\r
IN EFI_DHCP6_IA *Ia,\r
IN UINT32 T1,\r
- IN UINT32 T2\r
+ IN UINT32 T2,\r
+ IN UINT32 MessageType\r
)\r
{\r
UINT8 *AddrOpt;\r
UINT16 *Len;\r
UINTN Index;\r
- UINT16 Length;\r
\r
//\r
// The format of IA_NA and IA_TA option:\r
// Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.\r
//\r
if (Ia->Descriptor.Type == Dhcp6OptIana) {\r
- WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));\r
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL ((T1 != 0) ? T1 : 0xffffffff));\r
Buf += 4;\r
- WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));\r
+ WriteUnaligned32 ((UINT32 *) Buf, HTONL ((T2 != 0) ? T2 : 0xffffffff));\r
Buf += 4;\r
}\r
\r
// Fill all the addresses belong to the Ia\r
//\r
for (Index = 0; Index < Ia->IaAddressCount; Index++) {\r
-\r
- AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
- Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));\r
- Buf = Dhcp6AppendOption (\r
- Buf,\r
- HTONS (Dhcp6OptIaAddr),\r
- Length,\r
- AddrOpt\r
- );\r
+ AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);\r
+ Buf = Dhcp6AppendIaAddrOption (Buf, (EFI_DHCP6_IA_ADDRESS *) AddrOpt, MessageType);\r
}\r
\r
//\r
\r
//\r
// Sentinel value of 0 means that this is the first DHCP packet that we are\r
- // sending and that we need to initialize the value. First DHCP Solicit\r
+ // sending and that we need to initialize the value. First DHCP message\r
// gets 0 elapsed-time. Otherwise, calculate based on StartTime.\r
//\r
if (Instance->StartTime == 0) {\r
return Option;\r
}\r
\r
+/**\r
+ Check whether the incoming IPv6 address in IaAddr is one of the maintained \r
+ addresses in the IA control blcok.\r
+\r
+ @param[in] IaAddr The pointer to the IA Address to be checked.\r
+ @param[in] CurrentIa The pointer to the IA in IA control block.\r
+\r
+ @retval TRUE Yes, this Address is already in IA control block.\r
+ @retval FALSE No, this Address is NOT in IA control block.\r
+\r
+**/\r
+BOOLEAN\r
+Dhcp6AddrIsInCurrentIa (\r
+ IN EFI_DHCP6_IA_ADDRESS *IaAddr,\r
+ IN EFI_DHCP6_IA *CurrentIa\r
+ )\r
+{\r
+ UINT32 Index;\r
+\r
+ ASSERT (IaAddr != NULL && CurrentIa != NULL);\r
+ \r
+ for (Index = 0; Index < CurrentIa->IaAddressCount; Index++) {\r
+ if (EFI_IP6_EQUAL(&IaAddr->IpAddress, &CurrentIa->IaAddress[Index].IpAddress)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
\r
/**\r
Parse the address option and update the address infomation.\r
\r
+ @param[in] CurrentIa The pointer to the Ia Address in control blcok.\r
@param[in] IaInnerOpt The pointer to the buffer.\r
@param[in] IaInnerLen The length to parse.\r
@param[out] AddrNum The number of addresses.\r
**/\r
VOID\r
Dhcp6ParseAddrOption (\r
+ IN EFI_DHCP6_IA *CurrentIa,\r
IN UINT8 *IaInnerOpt,\r
IN UINT16 IaInnerLen,\r
OUT UINT32 *AddrNum,\r
UINT16 DataLen;\r
UINT16 OpCode;\r
UINT32 ValidLt;\r
+ UINT32 PreferredLt;\r
+ EFI_DHCP6_IA_ADDRESS *IaAddr;\r
\r
//\r
// The format of the IA Address option:\r
\r
while (Cursor < IaInnerOpt + IaInnerLen) {\r
//\r
- // Count the Ia address option with non-0 valid time.\r
+ // Refer to RFC3315 Chapter 18.1.8, we need to update lifetimes for any addresses in the IA option\r
+ // that the client already has recorded in the IA, and discard the Ia address option with 0 valid time.\r
//\r
OpCode = ReadUnaligned16 ((UINT16 *) Cursor);\r
- ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));\r
- if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {\r
-\r
+ PreferredLt = NTOHL (ReadUnaligned32 ((UINT32 *) (Cursor + 20)));\r
+ ValidLt = NTOHL (ReadUnaligned32 ((UINT32 *) (Cursor + 24)));\r
+ IaAddr = (EFI_DHCP6_IA_ADDRESS *) (Cursor + 4);\r
+ if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt >= PreferredLt &&\r
+ (Dhcp6AddrIsInCurrentIa(IaAddr, CurrentIa) || ValidLt !=0)) {\r
if (AddrBuf != NULL) {\r
- CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));\r
- AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);\r
- AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime);\r
+ CopyMem (AddrBuf, IaAddr, sizeof (EFI_DHCP6_IA_ADDRESS));\r
+ AddrBuf->PreferredLifetime = PreferredLt;\r
+ AddrBuf->ValidLifetime = ValidLt;\r
AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));\r
}\r
-\r
(*AddrNum)++;\r
}\r
DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));\r
@retval EFI_NOT_FOUND No valid IA option is found.\r
@retval EFI_SUCCESS Create an IA control block successfully.\r
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.\r
+ @retval EFI_DEVICE_ERROR An unexpected error.\r
\r
**/\r
EFI_STATUS\r
EFI_DHCP6_IA *Ia;\r
\r
if (Instance->IaCb.Ia == NULL) {\r
- return EFI_NOT_FOUND;\r
+ return EFI_DEVICE_ERROR;\r
}\r
\r
//\r
// Calculate the number of addresses for this Ia, excluding the addresses with\r
// the value 0 of valid lifetime.\r
//\r
- Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);\r
+ Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, NULL);\r
\r
if (AddrNum == 0) {\r
return EFI_NOT_FOUND;\r
Ia->State = Instance->IaCb.Ia->State;\r
Ia->IaAddressCount = AddrNum;\r
CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));\r
- Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);\r
+ Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);\r
\r
//\r
// Free original IA resource.\r
Instance->IaCb.Ia = NewIa;\r
}\r
}\r
+\r
+/**\r
+ Calculate the Dhcp6 get mapping timeout by adding additinal delay to the IP6 DAD transmits count.\r
+\r
+ @param[in] Ip6Cfg The pointer to Ip6 config protocol.\r
+ @param[out] TimeOut The time out value in 100ns units.\r
+\r
+ @retval EFI_INVALID_PARAMETER Input parameters are invalid.\r
+ @retval EFI_SUCCESS Calculate the time out value successfully.\r
+**/\r
+EFI_STATUS\r
+Dhcp6GetMappingTimeOut (\r
+ IN EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg,\r
+ OUT UINTN *TimeOut\r
+ ) \r
+{\r
+ EFI_STATUS Status;\r
+ UINTN DataSize;\r
+ EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;\r
+\r
+ if (Ip6Cfg == NULL || TimeOut == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\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
+ return Status;\r
+ }\r
+ \r
+ *TimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + DHCP6_DAD_ADDITIONAL_DELAY;\r
+ \r
+ return EFI_SUCCESS;\r
+}\r