/** @file Dhcp6 support functions implementation. (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "Dhcp6Impl.h" // // Verifies the packet cursor is within the packet // otherwise it is invalid // #define IS_INVALID_PACKET_CURSOR(PacketCursor, Packet) \ (((*PacketCursor) < (Packet)->Dhcp6.Option) || \ ((*PacketCursor) >= (Packet)->Dhcp6.Option + ((Packet)->Size - sizeof(EFI_DHCP6_HEADER))) \ ) \ /** 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, update the length of packet, and move Buf to the end. @param[in, out] Packet A pointer to the packet, on success Packet->Length will be updated. @param[in, out] PacketCursor The pointer in the packet, on success PacketCursor will be moved to the end of the option. @param[in] OptType The option type. @param[in] OptLen The length of option contents. @param[in] Data The pointer to the option content. @retval EFI_INVALID_PARAMETER An argument provided to the function was invalid @retval EFI_BUFFER_TOO_SMALL The buffer is too small to append the option. @retval EFI_SUCCESS The option is appended successfully. **/ EFI_STATUS Dhcp6AppendOption ( IN OUT EFI_DHCP6_PACKET *Packet, IN OUT UINT8 **PacketCursor, IN UINT16 OptType, IN UINT16 OptLen, IN UINT8 *Data ) { UINT32 Length; UINT32 BytesNeeded; // // 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) | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // // Verify the arguments are valid // if (Packet == NULL) { return EFI_INVALID_PARAMETER; } if ((PacketCursor == NULL) || (*PacketCursor == NULL)) { return EFI_INVALID_PARAMETER; } if (Data == NULL) { return EFI_INVALID_PARAMETER; } if (OptLen == 0) { return EFI_INVALID_PARAMETER; } // // Verify the PacketCursor is within the packet // if (IS_INVALID_PACKET_CURSOR (PacketCursor, Packet)) { return EFI_INVALID_PARAMETER; } // // Calculate the bytes needed for the option // BytesNeeded = DHCP6_SIZE_OF_COMBINED_CODE_AND_LEN + NTOHS (OptLen); // // Space remaining in the packet // Length = Packet->Size - Packet->Length; if (Length < BytesNeeded) { return EFI_BUFFER_TOO_SMALL; } WriteUnaligned16 ((UINT16 *)*PacketCursor, OptType); *PacketCursor += DHCP6_SIZE_OF_OPT_CODE; WriteUnaligned16 ((UINT16 *)*PacketCursor, OptLen); *PacketCursor += DHCP6_SIZE_OF_OPT_LEN; CopyMem (*PacketCursor, Data, NTOHS (OptLen)); *PacketCursor += NTOHS (OptLen); // Update the packet length by the length of the option + 4 bytes Packet->Length += BytesNeeded; return EFI_SUCCESS; } /** Append the appointed IA Address option to Buf, and move Buf to the end. @param[in, out] Packet A pointer to the packet, on success Packet->Length will be updated. @param[in, out] PacketCursor The pointer in the packet, on success PacketCursor will be moved to the end of the option. @param[in] IaAddr The pointer to the IA Address. @param[in] MessageType Message type of DHCP6 package. @retval EFI_INVALID_PARAMETER An argument provided to the function was invalid @retval EFI_BUFFER_TOO_SMALL The buffer is too small to append the option. @retval EFI_SUCCESS The option is appended successfully. **/ EFI_STATUS Dhcp6AppendIaAddrOption ( IN OUT EFI_DHCP6_PACKET *Packet, IN OUT UINT8 **PacketCursor, IN EFI_DHCP6_IA_ADDRESS *IaAddr, IN UINT32 MessageType ) { UINT32 BytesNeeded; UINT32 Length; // 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 . // . . // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Verify the arguments are valid // if (Packet == NULL) { return EFI_INVALID_PARAMETER; } if ((PacketCursor == NULL) || (*PacketCursor == NULL)) { return EFI_INVALID_PARAMETER; } if (IaAddr == NULL) { return EFI_INVALID_PARAMETER; } // // Verify the PacketCursor is within the packet // if (IS_INVALID_PACKET_CURSOR (PacketCursor, Packet)) { return EFI_INVALID_PARAMETER; } BytesNeeded = DHCP6_SIZE_OF_COMBINED_CODE_AND_LEN; BytesNeeded += sizeof (EFI_IPv6_ADDRESS); // // Even if the preferred-lifetime is 0, it still needs to store it. // BytesNeeded += sizeof (IaAddr->PreferredLifetime); // // Even if the valid-lifetime is 0, it still needs to store it. // BytesNeeded += sizeof (IaAddr->ValidLifetime); // // Space remaining in the packet // Length = Packet->Size - Packet->Length; if (Length < BytesNeeded) { return EFI_BUFFER_TOO_SMALL; } // // Fill the value of Ia Address option type // WriteUnaligned16 ((UINT16 *)*PacketCursor, HTONS (Dhcp6OptIaAddr)); *PacketCursor += DHCP6_SIZE_OF_OPT_CODE; WriteUnaligned16 ((UINT16 *)*PacketCursor, HTONS (sizeof (EFI_DHCP6_IA_ADDRESS))); *PacketCursor += DHCP6_SIZE_OF_OPT_LEN; CopyMem (*PacketCursor, &IaAddr->IpAddress, sizeof (EFI_IPv6_ADDRESS)); *PacketCursor += 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 *)*PacketCursor, HTONL (IaAddr->PreferredLifetime)); } *PacketCursor += sizeof (IaAddr->PreferredLifetime); if (MessageType != Dhcp6MsgConfirm) { WriteUnaligned32 ((UINT32 *)*PacketCursor, HTONL (IaAddr->ValidLifetime)); } *PacketCursor += sizeof (IaAddr->ValidLifetime); // // Update the packet length // Packet->Length += BytesNeeded; return EFI_SUCCESS; } /** Append the appointed Ia option to Buf, and move Buf to the end. @param[in, out] Packet A pointer to the packet, on success Packet->Length will be updated. @param[in, out] PacketCursor The pointer in the packet, on success PacketCursor will be moved to the end of the option. @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. @retval EFI_INVALID_PARAMETER An argument provided to the function was invalid @retval EFI_BUFFER_TOO_SMALL The buffer is too small to append the option. @retval EFI_SUCCESS The option is appended successfully. **/ EFI_STATUS Dhcp6AppendIaOption ( IN OUT EFI_DHCP6_PACKET *Packet, IN OUT UINT8 **PacketCursor, IN EFI_DHCP6_IA *Ia, IN UINT32 T1, IN UINT32 T2, IN UINT32 MessageType ) { UINT8 *AddrOpt; UINT16 *Len; UINTN Index; UINT32 BytesNeeded; UINT32 Length; EFI_STATUS Status; // // 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 . // . . // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // // Verify the arguments are valid // if (Packet == NULL) { return EFI_INVALID_PARAMETER; } if ((PacketCursor == NULL) || (*PacketCursor == NULL)) { return EFI_INVALID_PARAMETER; } if (Ia == NULL) { return EFI_INVALID_PARAMETER; } // // Verify the PacketCursor is within the packet // if (IS_INVALID_PACKET_CURSOR (PacketCursor, Packet)) { return EFI_INVALID_PARAMETER; } BytesNeeded = DHCP6_SIZE_OF_COMBINED_CODE_AND_LEN; BytesNeeded += sizeof (Ia->Descriptor.IaId); // // + N for the IA_NA-options/IA_TA-options // Dhcp6AppendIaAddrOption will need to check the length for each address // if (Ia->Descriptor.Type == Dhcp6OptIana) { BytesNeeded += sizeof (T1) + sizeof (T2); } // // Space remaining in the packet // Length = (UINT16)(Packet->Size - Packet->Length); if (Length < BytesNeeded) { return EFI_BUFFER_TOO_SMALL; } // // Fill the value of Ia option type // WriteUnaligned16 ((UINT16 *)*PacketCursor, HTONS (Ia->Descriptor.Type)); *PacketCursor += DHCP6_SIZE_OF_OPT_CODE; // // Fill the len of Ia option later, keep the pointer first // Len = (UINT16 *)*PacketCursor; *PacketCursor += DHCP6_SIZE_OF_OPT_LEN; // // Fill the value of iaid // WriteUnaligned32 ((UINT32 *)*PacketCursor, HTONL (Ia->Descriptor.IaId)); *PacketCursor += sizeof (Ia->Descriptor.IaId); // // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified. // if (Ia->Descriptor.Type == Dhcp6OptIana) { WriteUnaligned32 ((UINT32 *)*PacketCursor, HTONL ((T1 != 0) ? T1 : 0xffffffff)); *PacketCursor += sizeof (T1); WriteUnaligned32 ((UINT32 *)*PacketCursor, HTONL ((T2 != 0) ? T2 : 0xffffffff)); *PacketCursor += sizeof (T2); } // // Update the packet length // Packet->Length += BytesNeeded; // // 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); Status = Dhcp6AppendIaAddrOption (Packet, PacketCursor, (EFI_DHCP6_IA_ADDRESS *)AddrOpt, MessageType); if (EFI_ERROR (Status)) { return Status; } } // // Fill the value of Ia option length // *Len = HTONS ((UINT16)(*PacketCursor - (UINT8 *)Len - 2)); return EFI_SUCCESS; } /** Append the appointed Elapsed time option to Buf, and move Buf to the end. @param[in, out] Packet A pointer to the packet, on success Packet->Length will be updated. @param[in, out] PacketCursor The pointer in the packet, on success PacketCursor will be moved to the end of the option. @param[in] Instance The pointer to the Dhcp6 instance. @param[out] Elapsed The pointer to the elapsed time value in the generated packet. @retval EFI_INVALID_PARAMETER An argument provided to the function was invalid @retval EFI_BUFFER_TOO_SMALL The buffer is too small to append the option. @retval EFI_SUCCESS The option is appended successfully. **/ EFI_STATUS Dhcp6AppendETOption ( IN OUT EFI_DHCP6_PACKET *Packet, IN OUT UINT8 **PacketCursor, IN DHCP6_INSTANCE *Instance, OUT UINT16 **Elapsed ) { UINT32 BytesNeeded; UINT32 Length; // // 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 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // // Verify the arguments are valid // if (Packet == NULL) { return EFI_INVALID_PARAMETER; } if ((PacketCursor == NULL) || (*PacketCursor == NULL)) { return EFI_INVALID_PARAMETER; } if (Instance == NULL) { return EFI_INVALID_PARAMETER; } if (Elapsed == NULL) { return EFI_INVALID_PARAMETER; } // // Verify the PacketCursor is within the packet // if (IS_INVALID_PACKET_CURSOR (PacketCursor, Packet)) { return EFI_INVALID_PARAMETER; } BytesNeeded = DHCP6_SIZE_OF_COMBINED_CODE_AND_LEN; // // + 2 for elapsed-time // BytesNeeded += sizeof (UINT16); // // Space remaining in the packet // Length = Packet->Size - Packet->Length; if (Length < BytesNeeded) { return EFI_BUFFER_TOO_SMALL; } // // Fill the value of elapsed-time option type. // WriteUnaligned16 ((UINT16 *)*PacketCursor, HTONS (Dhcp6OptElapsedTime)); *PacketCursor += DHCP6_SIZE_OF_OPT_CODE; // // Fill the len of elapsed-time option, which is fixed. // WriteUnaligned16 ((UINT16 *)*PacketCursor, HTONS (2)); *PacketCursor += DHCP6_SIZE_OF_OPT_LEN; // // 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 *)*PacketCursor, HTONS (0)); *Elapsed = (UINT16 *)*PacketCursor; *PacketCursor += sizeof (UINT16); Packet->Length += BytesNeeded; return EFI_SUCCESS; } /** 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; }