CloverBootloader/NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c

1306 lines
39 KiB
C
Raw Normal View History

/** @file
Dhcp6 support functions implementation.
(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Dhcp6Impl.h"
/**
Generate client Duid in the format of Duid-llt.
@param[in] Mode The pointer to the mode of SNP.
@retval NULL If it failed to generate a client Id.
@retval others The pointer to the new client id.
**/
EFI_DHCP6_DUID *
Dhcp6GenerateClientId (
IN EFI_SIMPLE_NETWORK_MODE *Mode
)
{
EFI_STATUS Status;
EFI_DHCP6_DUID *Duid;
EFI_TIME Time;
UINT32 Stamp;
EFI_GUID Uuid;
//
// Attempt to get client Id from variable to keep it constant.
// See details in section-9 of rfc-3315.
//
GetVariable2 (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid, (VOID **)&Duid, NULL);
if (Duid != NULL) {
return Duid;
}
//
// The format of client identifier option:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | OPTION_CLIENTID | option-len |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . DUID .
// . (variable length) .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// If System UUID is found from SMBIOS Table, use DUID-UUID type.
//
if ((PcdGet8 (PcdDhcp6UidType) == Dhcp6DuidTypeUuid) && !EFI_ERROR (NetLibGetSystemGuid (&Uuid)) && !CompareGuid (&Uuid, &gZeroGuid)) {
//
//
// The format of DUID-UUID:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | DUID-Type (4) | UUID (128 bits) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
// | |
// | |
// | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
//
// sizeof (option-len + Duid-type + UUID-size) = 20 bytes
//
Duid = AllocateZeroPool (2 + 2 + sizeof (EFI_GUID));
if (Duid == NULL) {
return NULL;
}
//
// sizeof (Duid-type + UUID-size) = 18 bytes
//
Duid->Length = (UINT16)(18);
//
// Set the Duid-type and copy UUID.
//
WriteUnaligned16 ((UINT16 *)(Duid->Duid), HTONS (Dhcp6DuidTypeUuid));
CopyMem (Duid->Duid + 2, &Uuid, sizeof (EFI_GUID));
} else {
//
//
// The format of DUID-LLT:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Duid type (1) | hardware type (16 bits) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | time (32 bits) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . link-layer address (variable length) .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.
//
gRT->GetTime (&Time, NULL);
Stamp = (UINT32)
(
((((UINT32)(Time.Year - 2000) * 360 + (Time.Month - 1) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *
60 +
Time.Second
);
//
// sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes
//
Duid = AllocateZeroPool (10 + Mode->HwAddressSize);
if (Duid == NULL) {
return NULL;
}
//
// sizeof (Duid-type + hardware-type + time) = 8 bytes
//
Duid->Length = (UINT16)(Mode->HwAddressSize + 8);
//
// Set the Duid-type, hardware-type, time and copy the hardware address.
//
WriteUnaligned16 ((UINT16 *)((UINT8 *)Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid)), HTONS (Dhcp6DuidTypeLlt));
WriteUnaligned16 ((UINT16 *)((UINT8 *)Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 2), HTONS (NET_IFTYPE_ETHERNET));
WriteUnaligned32 ((UINT32 *)((UINT8 *)Duid + OFFSET_OF (EFI_DHCP6_DUID, Duid) + 4), HTONL (Stamp));
CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);
}
Status = gRT->SetVariable (
L"ClientId",
&gEfiDhcp6ServiceBindingProtocolGuid,
(EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
Duid->Length + 2,
(VOID *)Duid
);
if (EFI_ERROR (Status)) {
FreePool (Duid);
return NULL;
}
return Duid;
}
/**
Copy the Dhcp6 configure data.
@param[in] DstCfg The pointer to the destination configure data.
@param[in] SorCfg The pointer to the source configure data.
@retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
**/
EFI_STATUS
Dhcp6CopyConfigData (
IN EFI_DHCP6_CONFIG_DATA *DstCfg,
IN EFI_DHCP6_CONFIG_DATA *SorCfg
)
{
UINTN Index;
UINTN OptionListSize;
UINTN OptionSize;
CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));
//
// Allocate another buffer for solicitretransmission, and copy it.
//
if (SorCfg->SolicitRetransmission != NULL) {
DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
if (DstCfg->SolicitRetransmission == NULL) {
//
// Error will be handled out of this function.
//
return EFI_OUT_OF_RESOURCES;
}
CopyMem (
DstCfg->SolicitRetransmission,
SorCfg->SolicitRetransmission,
sizeof (EFI_DHCP6_RETRANSMISSION)
);
}
if ((SorCfg->OptionList != NULL) && (SorCfg->OptionCount != 0)) {
OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);
DstCfg->OptionList = AllocateZeroPool (OptionListSize);
if (DstCfg->OptionList == NULL) {
//
// Error will be handled out of this function.
//
return EFI_OUT_OF_RESOURCES;
}
for (Index = 0; Index < SorCfg->OptionCount; Index++) {
OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;
DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);
if (DstCfg->OptionList[Index] == NULL) {
//
// Error will be handled out of this function.
//
return EFI_OUT_OF_RESOURCES;
}
CopyMem (
DstCfg->OptionList[Index],
SorCfg->OptionList[Index],
OptionSize
);
}
}
return EFI_SUCCESS;
}
/**
Clean up the configure data.
@param[in, out] CfgData The pointer to the configure data.
**/
VOID
Dhcp6CleanupConfigData (
IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
)
{
UINTN Index;
ASSERT (CfgData != NULL);
//
// Clean up all fields in config data including the reference buffers, but do
// not free the config data buffer itself.
//
if (CfgData->OptionList != NULL) {
for (Index = 0; Index < CfgData->OptionCount; Index++) {
if (CfgData->OptionList[Index] != NULL) {
FreePool (CfgData->OptionList[Index]);
}
}
FreePool (CfgData->OptionList);
}
if (CfgData->SolicitRetransmission != NULL) {
FreePool (CfgData->SolicitRetransmission);
}
ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));
}
/**
Clean up the mode data.
@param[in, out] ModeData The pointer to the mode data.
**/
VOID
Dhcp6CleanupModeData (
IN OUT EFI_DHCP6_MODE_DATA *ModeData
)
{
ASSERT (ModeData != NULL);
//
// Clean up all fields in mode data including the reference buffers, but do
// not free the mode data buffer itself.
//
if (ModeData->ClientId != NULL) {
FreePool (ModeData->ClientId);
}
if (ModeData->Ia != NULL) {
if (ModeData->Ia->ReplyPacket != NULL) {
FreePool (ModeData->Ia->ReplyPacket);
}
FreePool (ModeData->Ia);
}
ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));
}
/**
Calculate the expire time by the algorithm defined in rfc.
@param[in] Base The base value of the time.
@param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
@param[in] NeedSigned If TRUE, the signed factor is needed.
@return Expire The calculated result for the new expire time.
**/
UINT32
Dhcp6CalculateExpireTime (
IN UINT32 Base,
IN BOOLEAN IsFirstRt,
IN BOOLEAN NeedSigned
)
{
EFI_TIME Time;
BOOLEAN Signed;
UINT32 Seed;
UINT32 Expire;
//
// Take the 10bits of microsecond in system time as a uniform distribution.
// Take the 10th bit as a flag to determine it's signed or not.
//
gRT->GetTime (&Time, NULL);
Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);
Signed = (BOOLEAN)((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);
Signed = (BOOLEAN)(NeedSigned ? Signed : FALSE);
//
// Calculate expire by the following algo:
// 1. base + base * (-0.1 ~ 0) for the first solicit
// 2. base + base * (-0.1 ~ 0.1) for the first other messages
// 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages
// 4. base + base * (-0.1 ~ 0) for the more than mrt timeout
//
// The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).
//
if (IsFirstRt && Signed) {
Expire = Base - (UINT32)(Base * Seed / DHCP6_10_BIT_MASK / 10);
} else if (IsFirstRt && !Signed) {
Expire = Base + (UINT32)(Base * Seed / DHCP6_10_BIT_MASK / 10);
} else if (!IsFirstRt && Signed) {
Expire = 2 * Base - (UINT32)(Base * Seed / DHCP6_10_BIT_MASK / 10);
} else {
Expire = 2 * Base + (UINT32)(Base * Seed / DHCP6_10_BIT_MASK / 10);
}
Expire = (Expire != 0) ? Expire : 1;
return Expire;
}
/**
Calculate the lease time by the algorithm defined in rfc.
@param[in] IaCb The pointer to the Ia control block.
**/
VOID
Dhcp6CalculateLeaseTime (
IN DHCP6_IA_CB *IaCb
)
{
UINT32 MinLt;
UINT32 MaxLt;
UINTN Index;
ASSERT (IaCb->Ia->IaAddressCount > 0);
MinLt = (UINT32)(-1);
MaxLt = 0;
//
// Calculate minlt as min of all valid life time, and maxlt as max of all
// valid life time.
//
for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {
MinLt = MIN (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime);
MaxLt = MAX (MinLt, IaCb->Ia->IaAddress[Index].ValidLifetime);
}
//
// Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer
// such information.
//
IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);
IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);
IaCb->AllExpireTime = MaxLt;
IaCb->LeaseTime = 0;
}
/**
Check whether the addresses are all included by the configured Ia.
@param[in] Ia The pointer to the Ia.
@param[in] AddressCount The number of addresses.
@param[in] Addresses The pointer to the addresses buffer.
@retval EFI_SUCCESS The addresses are all included by the configured IA.
@retval EFI_NOT_FOUND The addresses are not included by the configured IA.
**/
EFI_STATUS
Dhcp6CheckAddress (
IN EFI_DHCP6_IA *Ia,
IN UINT32 AddressCount,
IN EFI_IPv6_ADDRESS *Addresses
)
{
UINTN Index1;
UINTN Index2;
BOOLEAN Found;
//
// Check whether the addresses are all included by the configured IA. And it
// will return success if address count is zero, which means all addresses.
//
for (Index1 = 0; Index1 < AddressCount; Index1++) {
Found = FALSE;
for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
if (CompareMem (
&Addresses[Index1],
&Ia->IaAddress[Index2],
sizeof (EFI_IPv6_ADDRESS)
) == 0)
{
Found = TRUE;
break;
}
}
if (!Found) {
return EFI_NOT_FOUND;
}
}
return EFI_SUCCESS;
}
/**
Deprive the addresses from current Ia, and generate another eliminated Ia.
@param[in] Ia The pointer to the Ia.
@param[in] AddressCount The number of addresses.
@param[in] Addresses The pointer to the addresses buffer.
@retval NULL If it failed to generate the deprived Ia.
@retval others The pointer to the deprived Ia.
**/
EFI_DHCP6_IA *
Dhcp6DepriveAddress (
IN EFI_DHCP6_IA *Ia,
IN UINT32 AddressCount,
IN EFI_IPv6_ADDRESS *Addresses
)
{
EFI_DHCP6_IA *IaCopy;
UINTN IaCopySize;
UINTN Index1;
UINTN Index2;
BOOLEAN Found;
if (AddressCount == 0) {
//
// It means release all Ia addresses if address count is zero.
//
AddressCount = Ia->IaAddressCount;
}
ASSERT (AddressCount != 0);
IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
IaCopy = AllocateZeroPool (IaCopySize);
if (IaCopy == NULL) {
return NULL;
}
if (AddressCount == Ia->IaAddressCount) {
//
// If release all Ia addresses, just copy the configured Ia and then set
// its address count as zero.
// We may decline/release part of addresses at the beginning. So it's a
// forwarding step to update address infor for decline/release, while the
// other infor such as Ia state will be updated when receiving reply.
//
CopyMem (IaCopy, Ia, IaCopySize);
Ia->IaAddressCount = 0;
return IaCopy;
}
CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));
//
// Move the addresses from the Ia of instance to the deprived Ia.
//
for (Index1 = 0; Index1 < AddressCount; Index1++) {
Found = FALSE;
for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
if (CompareMem (
&Addresses[Index1],
&Ia->IaAddress[Index2],
sizeof (EFI_IPv6_ADDRESS)
) == 0)
{
//
// Copy the deprived address to the copy of Ia
//
CopyMem (
&IaCopy->IaAddress[Index1],
&Ia->IaAddress[Index2],
sizeof (EFI_DHCP6_IA_ADDRESS)
);
//
// Delete the deprived address from the instance Ia
//
if (Index2 + 1 < Ia->IaAddressCount) {
CopyMem (
&Ia->IaAddress[Index2],
&Ia->IaAddress[Index2 + 1],
(Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)
);
}
Found = TRUE;
break;
}
}
ASSERT (Found == TRUE);
}
Ia->IaAddressCount -= AddressCount;
IaCopy->IaAddressCount = AddressCount;
return IaCopy;
}
/**
The dummy ext buffer free callback routine.
@param[in] Arg The pointer to the parameter.
**/
VOID
EFIAPI
Dhcp6DummyExtFree (
IN VOID *Arg
)
{
}
/**
The callback routine once message transmitted.
@param[in] Wrap The pointer to the received net buffer.
@param[in] EndPoint The pointer to the udp end point.
@param[in] IoStatus The return status from udp io.
@param[in] Context The opaque parameter to the function.
**/
VOID
EFIAPI
Dhcp6OnTransmitted (
IN NET_BUF *Wrap,
IN UDP_END_POINT *EndPoint,
IN EFI_STATUS IoStatus,
IN VOID *Context
)
{
NetbufFree (Wrap);
}
/**
Append the option to Buf, and move Buf to the end.
@param[in, out] Buf The pointer to the buffer.
@param[in] OptType The option type.
@param[in] OptLen The length of option contents.
@param[in] Data The pointer to the option content.
@return Buf The position to append the next option.
**/
UINT8 *
Dhcp6AppendOption (
IN OUT UINT8 *Buf,
IN UINT16 OptType,
IN UINT16 OptLen,
IN UINT8 *Data
)
{
//
// The format of Dhcp6 option:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | option-code | option-len (option data) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | option-data |
// | (option-len octets) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
ASSERT (OptLen != 0);
WriteUnaligned16 ((UINT16 *)Buf, OptType);
Buf += 2;
WriteUnaligned16 ((UINT16 *)Buf, OptLen);
Buf += 2;
CopyMem (Buf, Data, NTOHS (OptLen));
Buf += NTOHS (OptLen);
return Buf;
}
/**
Append the appointed IA Address option to Buf, and move Buf to the end.
@param[in, out] Buf The pointer to the position to append.
@param[in] IaAddr The pointer to the IA Address.
@param[in] MessageType Message type of DHCP6 package.
@return Buf The position to append the next option.
**/
UINT8 *
Dhcp6AppendIaAddrOption (
IN OUT UINT8 *Buf,
IN EFI_DHCP6_IA_ADDRESS *IaAddr,
IN UINT32 MessageType
)
{
// The format of the IA Address option is:
//
// 0 1 2 3
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | OPTION_IAADDR | option-len |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | IPv6 address |
// | |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | preferred-lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | valid-lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . IAaddr-options .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// Fill the value of Ia Address option type
//
WriteUnaligned16 ((UINT16 *)Buf, HTONS (Dhcp6OptIaAddr));
Buf += 2;
WriteUnaligned16 ((UINT16 *)Buf, HTONS (sizeof (EFI_DHCP6_IA_ADDRESS)));
Buf += 2;
CopyMem (Buf, &IaAddr->IpAddress, sizeof (EFI_IPv6_ADDRESS));
Buf += sizeof (EFI_IPv6_ADDRESS);
//
// Fill the value of preferred-lifetime and valid-lifetime.
// According to RFC3315 Chapter 18.1.2, the preferred-lifetime and valid-lifetime fields
// should set to 0 when initiate a Confirm message.
//
if (MessageType != Dhcp6MsgConfirm) {
WriteUnaligned32 ((UINT32 *)Buf, HTONL (IaAddr->PreferredLifetime));
}
Buf += 4;
if (MessageType != Dhcp6MsgConfirm) {
WriteUnaligned32 ((UINT32 *)Buf, HTONL (IaAddr->ValidLifetime));
}
Buf += 4;
return Buf;
}
/**
Append the appointed Ia option to Buf, and move Buf to the end.
@param[in, out] Buf The pointer to the position to append.
@param[in] Ia The pointer to the Ia.
@param[in] T1 The time of T1.
@param[in] T2 The time of T2.
@param[in] MessageType Message type of DHCP6 package.
@return Buf The position to append the next Ia option.
**/
UINT8 *
Dhcp6AppendIaOption (
IN OUT UINT8 *Buf,
IN EFI_DHCP6_IA *Ia,
IN UINT32 T1,
IN UINT32 T2,
IN UINT32 MessageType
)
{
UINT8 *AddrOpt;
UINT16 *Len;
UINTN Index;
//
// The format of IA_NA and IA_TA option:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | OPTION_IA_NA | option-len |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | IAID (4 octets) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | T1 (only for IA_NA) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | T2 (only for IA_NA) |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// . IA_NA-options/IA_TA-options .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Fill the value of Ia option type
//
WriteUnaligned16 ((UINT16 *)Buf, HTONS (Ia->Descriptor.Type));
Buf += 2;
//
// Fill the len of Ia option later, keep the pointer first
//
Len = (UINT16 *)Buf;
Buf += 2;
//
// Fill the value of iaid
//
WriteUnaligned32 ((UINT32 *)Buf, HTONL (Ia->Descriptor.IaId));
Buf += 4;
//
// Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.
//
if (Ia->Descriptor.Type == Dhcp6OptIana) {
WriteUnaligned32 ((UINT32 *)Buf, HTONL ((T1 != 0) ? T1 : 0xffffffff));
Buf += 4;
WriteUnaligned32 ((UINT32 *)Buf, HTONL ((T2 != 0) ? T2 : 0xffffffff));
Buf += 4;
}
//
// Fill all the addresses belong to the Ia
//
for (Index = 0; Index < Ia->IaAddressCount; Index++) {
AddrOpt = (UINT8 *)Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
Buf = Dhcp6AppendIaAddrOption (Buf, (EFI_DHCP6_IA_ADDRESS *)AddrOpt, MessageType);
}
//
// Fill the value of Ia option length
//
*Len = HTONS ((UINT16)(Buf - (UINT8 *)Len - 2));
return Buf;
}
/**
Append the appointed Elapsed time option to Buf, and move Buf to the end.
@param[in, out] Buf The pointer to the position to append.
@param[in] Instance The pointer to the Dhcp6 instance.
@param[out] Elapsed The pointer to the elapsed time value in
the generated packet.
@return Buf The position to append the next Ia option.
**/
UINT8 *
Dhcp6AppendETOption (
IN OUT UINT8 *Buf,
IN DHCP6_INSTANCE *Instance,
OUT UINT16 **Elapsed
)
{
//
// The format of elapsed time option:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | OPTION_ELAPSED_TIME | option-len |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | elapsed-time |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Fill the value of elapsed-time option type.
//
WriteUnaligned16 ((UINT16 *)Buf, HTONS (Dhcp6OptElapsedTime));
Buf += 2;
//
// Fill the len of elapsed-time option, which is fixed.
//
WriteUnaligned16 ((UINT16 *)Buf, HTONS (2));
Buf += 2;
//
// Fill in elapsed time value with 0 value for now. The actual value is
// filled in later just before the packet is transmitted.
//
WriteUnaligned16 ((UINT16 *)Buf, HTONS (0));
*Elapsed = (UINT16 *)Buf;
Buf += 2;
return Buf;
}
/**
Set the elapsed time based on the given instance and the pointer to the
elapsed time option.
@param[in] Elapsed The pointer to the position to append.
@param[in] Instance The pointer to the Dhcp6 instance.
**/
VOID
SetElapsedTime (
IN UINT16 *Elapsed,
IN DHCP6_INSTANCE *Instance
)
{
EFI_TIME Time;
UINT64 CurrentStamp;
UINT64 ElapsedTimeValue;
//
// Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.
//
gRT->GetTime (&Time, NULL);
CurrentStamp = MultU64x32 (
((((UINT32)(Time.Year - 2000) * 360 + (Time.Month - 1) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) * 60 + Time.Second,
100
) +
DivU64x32 (
Time.Nanosecond,
10000000
);
//
// Sentinel value of 0 means that this is the first DHCP packet that we are
// sending and that we need to initialize the value. First DHCP message
// gets 0 elapsed-time. Otherwise, calculate based on StartTime.
//
if (Instance->StartTime == 0) {
ElapsedTimeValue = 0;
Instance->StartTime = CurrentStamp;
} else {
ElapsedTimeValue = CurrentStamp - Instance->StartTime;
//
// If elapsed time cannot fit in two bytes, set it to 0xffff.
//
if (ElapsedTimeValue > 0xffff) {
ElapsedTimeValue = 0xffff;
}
}
WriteUnaligned16 (Elapsed, HTONS ((UINT16)ElapsedTimeValue));
}
/**
Seek the address of the first byte of the option header.
@param[in] Buf The pointer to the buffer.
@param[in] SeekLen The length to seek.
@param[in] OptType The option type.
@retval NULL If it failed to seek the option.
@retval others The position to the option.
**/
UINT8 *
Dhcp6SeekOption (
IN UINT8 *Buf,
IN UINT32 SeekLen,
IN UINT16 OptType
)
{
UINT8 *Cursor;
UINT8 *Option;
UINT16 DataLen;
UINT16 OpCode;
Option = NULL;
Cursor = Buf;
//
// The format of Dhcp6 option refers to Dhcp6AppendOption().
//
while (Cursor < Buf + SeekLen) {
OpCode = ReadUnaligned16 ((UINT16 *)Cursor);
if (OpCode == HTONS (OptType)) {
Option = Cursor;
break;
}
DataLen = NTOHS (ReadUnaligned16 ((UINT16 *)(Cursor + 2)));
Cursor += (DataLen + 4);
}
return Option;
}
/**
Seek the address of the first byte of the Ia option header.
@param[in] Buf The pointer to the buffer.
@param[in] SeekLen The length to seek.
@param[in] IaDesc The pointer to the Ia descriptor.
@retval NULL If it failed to seek the Ia option.
@retval others The position to the Ia option.
**/
UINT8 *
Dhcp6SeekIaOption (
IN UINT8 *Buf,
IN UINT32 SeekLen,
IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
)
{
UINT8 *Cursor;
UINT8 *Option;
UINT16 DataLen;
UINT16 OpCode;
UINT32 IaId;
//
// The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().
//
Option = NULL;
Cursor = Buf;
while (Cursor < Buf + SeekLen) {
OpCode = ReadUnaligned16 ((UINT16 *)Cursor);
IaId = ReadUnaligned32 ((UINT32 *)(Cursor + 4));
if ((OpCode == HTONS (IaDesc->Type)) && (IaId == HTONL (IaDesc->IaId))) {
Option = Cursor;
break;
}
DataLen = NTOHS (ReadUnaligned16 ((UINT16 *)(Cursor + 2)));
Cursor += (DataLen + 4);
}
return Option;
}
/**
Check whether the incoming IPv6 address in IaAddr is one of the maintained
addresses in the IA control block.
@param[in] IaAddr The pointer to the IA Address to be checked.
@param[in] CurrentIa The pointer to the IA in IA control block.
@retval TRUE Yes, this Address is already in IA control block.
@retval FALSE No, this Address is NOT in IA control block.
**/
BOOLEAN
Dhcp6AddrIsInCurrentIa (
IN EFI_DHCP6_IA_ADDRESS *IaAddr,
IN EFI_DHCP6_IA *CurrentIa
)
{
UINT32 Index;
ASSERT (IaAddr != NULL && CurrentIa != NULL);
for (Index = 0; Index < CurrentIa->IaAddressCount; Index++) {
if (EFI_IP6_EQUAL (&IaAddr->IpAddress, &CurrentIa->IaAddress[Index].IpAddress)) {
return TRUE;
}
}
return FALSE;
}
/**
Parse the address option and update the address information.
@param[in] CurrentIa The pointer to the Ia Address in control block.
@param[in] IaInnerOpt The pointer to the buffer.
@param[in] IaInnerLen The length to parse.
@param[out] AddrNum The number of addresses.
@param[in, out] AddrBuf The pointer to the address buffer.
**/
VOID
Dhcp6ParseAddrOption (
IN EFI_DHCP6_IA *CurrentIa,
IN UINT8 *IaInnerOpt,
IN UINT16 IaInnerLen,
OUT UINT32 *AddrNum,
IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
)
{
UINT8 *Cursor;
UINT16 DataLen;
UINT16 OpCode;
UINT32 ValidLt;
UINT32 PreferredLt;
EFI_DHCP6_IA_ADDRESS *IaAddr;
//
// The format of the IA Address option:
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | OPTION_IAADDR | option-len |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | |
// | IPv6 address |
// | |
// | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | preferred-lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | valid-lifetime |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// . .
// . IAaddr-options .
// . .
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
//
// Two usage model:
//
// 1. Pass addrbuf == null, to get the addrnum over the Ia inner options.
// 2. Pass addrbuf != null, to resolve the addresses over the Ia inner
// options to the addrbuf.
//
Cursor = IaInnerOpt;
*AddrNum = 0;
while (Cursor < IaInnerOpt + IaInnerLen) {
//
// Refer to RFC3315 Chapter 18.1.8, we need to update lifetimes for any addresses in the IA option
// that the client already has recorded in the IA, and discard the Ia address option with 0 valid time.
//
OpCode = ReadUnaligned16 ((UINT16 *)Cursor);
PreferredLt = NTOHL (ReadUnaligned32 ((UINT32 *)(Cursor + 20)));
ValidLt = NTOHL (ReadUnaligned32 ((UINT32 *)(Cursor + 24)));
IaAddr = (EFI_DHCP6_IA_ADDRESS *)(Cursor + 4);
if ((OpCode == HTONS (Dhcp6OptIaAddr)) && (ValidLt >= PreferredLt) &&
(Dhcp6AddrIsInCurrentIa (IaAddr, CurrentIa) || (ValidLt != 0)))
{
if (AddrBuf != NULL) {
CopyMem (AddrBuf, IaAddr, sizeof (EFI_DHCP6_IA_ADDRESS));
AddrBuf->PreferredLifetime = PreferredLt;
AddrBuf->ValidLifetime = ValidLt;
AddrBuf = (EFI_DHCP6_IA_ADDRESS *)((UINT8 *)AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));
}
(*AddrNum)++;
}
DataLen = NTOHS (ReadUnaligned16 ((UINT16 *)(Cursor + 2)));
Cursor += (DataLen + 4);
}
}
/**
Create a control block for the Ia according to the corresponding options.
@param[in] Instance The pointer to DHCP6 Instance.
@param[in] IaInnerOpt The pointer to the inner options in the Ia option.
@param[in] IaInnerLen The length of all the inner options in the Ia option.
@param[in] T1 T1 time in the Ia option.
@param[in] T2 T2 time in the Ia option.
@retval EFI_NOT_FOUND No valid IA option is found.
@retval EFI_SUCCESS Create an IA control block successfully.
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
@retval EFI_DEVICE_ERROR An unexpected error.
**/
EFI_STATUS
Dhcp6GenerateIaCb (
IN DHCP6_INSTANCE *Instance,
IN UINT8 *IaInnerOpt,
IN UINT16 IaInnerLen,
IN UINT32 T1,
IN UINT32 T2
)
{
UINT32 AddrNum;
UINT32 IaSize;
EFI_DHCP6_IA *Ia;
if (Instance->IaCb.Ia == NULL) {
return EFI_DEVICE_ERROR;
}
//
// Calculate the number of addresses for this Ia, excluding the addresses with
// the value 0 of valid lifetime.
//
Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, NULL);
if (AddrNum == 0) {
return EFI_NOT_FOUND;
}
//
// Allocate for new IA.
//
IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
Ia = AllocateZeroPool (IaSize);
if (Ia == NULL) {
return EFI_OUT_OF_RESOURCES;
}
//
// Fill up this new IA fields.
//
Ia->State = Instance->IaCb.Ia->State;
Ia->IaAddressCount = AddrNum;
CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));
Dhcp6ParseAddrOption (Instance->IaCb.Ia, IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);
//
// Free original IA resource.
//
if (Instance->IaCb.Ia->ReplyPacket != NULL) {
FreePool (Instance->IaCb.Ia->ReplyPacket);
}
FreePool (Instance->IaCb.Ia);
ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));
//
// Update IaCb to use new IA.
//
Instance->IaCb.Ia = Ia;
//
// Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.
//
Instance->IaCb.T1 = T1;
Instance->IaCb.T2 = T2;
Dhcp6CalculateLeaseTime (&Instance->IaCb);
return EFI_SUCCESS;
}
/**
Cache the current IA configuration information.
@param[in] Instance The pointer to DHCP6 Instance.
@retval EFI_SUCCESS Cache the current IA successfully.
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
**/
EFI_STATUS
Dhcp6CacheIa (
IN DHCP6_INSTANCE *Instance
)
{
UINTN IaSize;
EFI_DHCP6_IA *Ia;
Ia = Instance->IaCb.Ia;
if ((Instance->CacheIa == NULL) && (Ia != NULL)) {
//
// Cache the current IA.
//
IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
Instance->CacheIa = AllocateZeroPool (IaSize);
if (Instance->CacheIa == NULL) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem (Instance->CacheIa, Ia, IaSize);
}
return EFI_SUCCESS;
}
/**
Append CacheIa to the current IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
@param[in] Instance The pointer to DHCP6 instance.
**/
VOID
Dhcp6AppendCacheIa (
IN DHCP6_INSTANCE *Instance
)
{
UINT8 *Ptr;
UINTN Index;
UINTN IaSize;
UINTN NewIaSize;
EFI_DHCP6_IA *Ia;
EFI_DHCP6_IA *NewIa;
EFI_DHCP6_IA *CacheIa;
Ia = Instance->IaCb.Ia;
CacheIa = Instance->CacheIa;
if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {
//
// There are old addresses existing. Merge with current addresses.
//
NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
NewIa = AllocateZeroPool (NewIaSize);
if (NewIa == NULL) {
return;
}
IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
CopyMem (NewIa, Ia, IaSize);
//
// Clear old address.ValidLifetime
//
for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {
CacheIa->IaAddress[Index].ValidLifetime = 0;
}
NewIa->IaAddressCount += CacheIa->IaAddressCount;
Ptr = (UINT8 *)&NewIa->IaAddress[Ia->IaAddressCount];
CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));
//
// Migrate to the NewIa and free previous.
//
FreePool (Instance->CacheIa);
FreePool (Instance->IaCb.Ia);
Instance->CacheIa = NULL;
Instance->IaCb.Ia = NewIa;
}
}
/**
Calculate the Dhcp6 get mapping timeout by adding additional delay to the IP6 DAD transmits count.
@param[in] Ip6Cfg The pointer to Ip6 config protocol.
@param[out] TimeOut The time out value in 100ns units.
@retval EFI_INVALID_PARAMETER Input parameters are invalid.
@retval EFI_SUCCESS Calculate the time out value successfully.
**/
EFI_STATUS
Dhcp6GetMappingTimeOut (
IN EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg,
OUT UINTN *TimeOut
)
{
EFI_STATUS Status;
UINTN DataSize;
EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS DadXmits;
if ((Ip6Cfg == NULL) || (TimeOut == NULL)) {
return EFI_INVALID_PARAMETER;
}
DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
Status = Ip6Cfg->GetData (
Ip6Cfg,
Ip6ConfigDataTypeDupAddrDetectTransmits,
&DataSize,
&DadXmits
);
if (EFI_ERROR (Status)) {
return Status;
}
*TimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + DHCP6_DAD_ADDITIONAL_DELAY;
return EFI_SUCCESS;
}