From a3bcde70e6dc69000f85cc5deee98101d2ae200a Mon Sep 17 00:00:00 2001 From: hhtian Date: Mon, 1 Nov 2010 06:13:54 +0000 Subject: Add NetworkPkg (P.UDK2010.UP3.Network.P1) git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524 --- NetworkPkg/Dhcp6Dxe/Dhcp6Io.c | 2965 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2965 insertions(+) create mode 100644 NetworkPkg/Dhcp6Dxe/Dhcp6Io.c (limited to 'NetworkPkg/Dhcp6Dxe/Dhcp6Io.c') diff --git a/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c new file mode 100644 index 0000000..761f9c2 --- /dev/null +++ b/NetworkPkg/Dhcp6Dxe/Dhcp6Io.c @@ -0,0 +1,2965 @@ +/** @file + Dhcp6 internal functions implementation. + + Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "Dhcp6Impl.h" + + +/** + Enqueue the packet into the retry list in case of timeout. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 packet to retry. + @param[in] Elapsed The pointer to the elapsed time value in the packet. + @param[in] RetryCtl The pointer to the transmission control of the packet. + This parameter is optional and may be NULL. + + @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according + to its message type. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected message type. + +**/ +EFI_STATUS +Dhcp6EnqueueRetry ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed, + IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL + ) +{ + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + + ASSERT (Packet != NULL); + + IaCb = &Instance->IaCb; + TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB)); + + if (TxCb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Save tx packet pointer, and it will be destoryed when reply received. + // + TxCb->TxPacket = Packet; + TxCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Save pointer to elapsed-time value so we can update it on retransmits. + // + TxCb->Elapsed = Elapsed; + + // + // Calculate the retransmission according to the the message type. + // + switch (Packet->Dhcp6.Header.MessageType) { + case Dhcp6MsgSolicit: + // + // Calculate the retransmission threshold value for solicit packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + FALSE + ); + break; + + case Dhcp6MsgRequest: + // + // Calculate the retransmission threshold value for request packet. + // + TxCb->RetryCtl.Irt = DHCP6_REQ_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgConfirm: + // + // Calculate the retransmission threshold value for confirm packet. + // + TxCb->RetryCtl.Irt = DHCP6_CNF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRenew: + // + // Calculate the retransmission threshold value for renew packet. + // + TxCb->RetryCtl.Irt = DHCP6_REB_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REB_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REB_MRT; + TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRebind: + // + // Calculate the retransmission threshold value for rebind packet. + // + TxCb->RetryCtl.Irt = DHCP6_REN_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REN_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REN_MRT; + TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgDecline: + // + // Calculate the retransmission threshold value for decline packet. + // + TxCb->RetryCtl.Irt = DHCP6_DEC_IRT; + TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC; + TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT; + TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgRelease: + // + // Calculate the retransmission threshold value for release packet. + // + TxCb->RetryCtl.Irt = DHCP6_REL_IRT; + TxCb->RetryCtl.Mrc = DHCP6_REL_MRC; + TxCb->RetryCtl.Mrt = DHCP6_REL_MRT; + TxCb->RetryCtl.Mrd = DHCP6_REL_MRD; + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + case Dhcp6MsgInfoRequest: + // + // Calculate the retransmission threshold value for info-request packet. + // Use the default value by rfc-3315 if user doesn't configure. + // + if (RetryCtl == NULL) { + TxCb->RetryCtl.Irt = DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = DHCP6_INF_MRD; + } else { + TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT; + TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC; + TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT; + TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD; + } + + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Irt, + TRUE, + TRUE + ); + break; + + default: + // + // Unexpected message type. + // + return EFI_DEVICE_ERROR; + } + + // + // Insert into the retransmit list of the instance. + // + InsertTailList (&Instance->TxList, &TxCb->Link); + + return EFI_SUCCESS; +} + + +/** + Dequeue the packet from retry list if reply received or timeout at last. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] PacketXid The packet transaction id to match. + @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed. + Otherwise, this parameter is ignored. + + @retval EFI_SUCCESS Successfully dequeued the packet into retry list . + @retval EFI_NOT_FOUND There is no xid matched in retry list. + +**/ +EFI_STATUS +Dhcp6DequeueRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 PacketXid, + IN BOOLEAN NeedSignal + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Seek the retransmit node in the retransmit list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->Xid == PacketXid) { + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + + // + // Seek the info-request node in the info-request list by packet xid. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->Xid == PacketXid) { + // + // Remove the info-request node, and signal the event if timeout. + // + if (InfCb->TimeoutEvent != NULL && NeedSignal) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } + } + // + // Remove the retransmit node. + // + RemoveEntryList (&TxCb->Link); + ASSERT(TxCb->TxPacket); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + + +/** + Clean up the specific nodes in the retry list. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Scope The scope of cleanup nodes. + +**/ +VOID +Dhcp6CleanupRetry ( + IN DHCP6_INSTANCE *Instance, + IN UINT32 Scope + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_TX_CB *TxCb; + DHCP6_INF_CB *InfCb; + + // + // Clean up all the stateful messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) { + + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + } + + // + // Clean up all the stateless messages from the retransmit list. + // + if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) { + + // + // Clean up all the retransmit list for stateless messages. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + ASSERT(TxCb->TxPacket); + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + RemoveEntryList (&TxCb->Link); + FreePool (TxCb->TxPacket); + FreePool (TxCb); + } + } + + // + // Clean up all the info-request messages list. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) { + + InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link); + + if (InfCb->TimeoutEvent != NULL) { + gBS->SignalEvent (InfCb->TimeoutEvent); + } + RemoveEntryList (&InfCb->Link); + FreePool (InfCb); + } + } +} + + +/** + Clean up the session of the instance stateful exchange. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Status The return status from udp. + +**/ +VOID +Dhcp6CleanupSession ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_STATUS Status + ) +{ + UINTN Index; + EFI_DHCP6_IA *Ia; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + // + // Clean up the retransmit list for stateful messages. + // + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL); + + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + } + + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + // + // Reinitialize the Ia fields of the instance. + // + Instance->UdpSts = Status; + Instance->AdSelect = NULL; + Instance->AdPref = 0; + Instance->Unicast = NULL; + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.AllExpireTime = 0; + Instance->IaCb.LeaseTime = 0; + + // + // Clear start time + // + Instance->StartTime = 0; + + Ia = Instance->IaCb.Ia; + Ia->State = Dhcp6Init; + Ia->ReplyPacket = NULL; + + // + // Set the addresses as zero lifetime, and then the notify + // function in Ip6Config will remove these timeout address. + // + for (Index = 0; Index < Ia->IaAddressCount; Index++) { + Ia->IaAddress[Index].PreferredLifetime = 0; + Ia->IaAddress[Index].ValidLifetime = 0; + } + + // + // + // Signal the Ia information updated event to informal user. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } +} + + +/** + Callback to user when Dhcp6 transmit/receive occurs. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Event The current Dhcp6 event. + @param[in, out] Packet The pointer to the packet sending or received. + + @retval EFI_SUCCESS The user function returns success. + @retval EFI_NOT_READY Direct the caller to continue collecting the offer. + @retval EFI_ABORTED The user function ask it to abort. + +**/ +EFI_STATUS +EFIAPI +Dhcp6CallbackUser ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_EVENT Event, + IN OUT EFI_DHCP6_PACKET **Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *NewPacket; + EFI_DHCP6_CALLBACK Callback; + VOID *Context; + + ASSERT (Packet != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + NewPacket = NULL; + Status = EFI_SUCCESS; + Callback = Instance->Config->Dhcp6Callback; + Context = Instance->Config->CallbackContext; + + // + // Callback to user with the new message if has. + // + if (Callback != NULL) { + + Status = Callback ( + &Instance->Dhcp6, + Context, + Instance->IaCb.Ia->State, + Event, + *Packet, + &NewPacket + ); + // + // Updated the new packet from user to replace the original one. + // + if (NewPacket != NULL) { + ASSERT (*Packet != NULL); + FreePool (*Packet); + *Packet = NewPacket; + } + } + + return Status; +} + + +/** + Update Ia according to the new reply message. + + @param[in, out] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + + @retval EFI_SUCCESS Updated the Ia information successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6UpdateIaInfo ( + IN OUT DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_STATE State; + UINT8 *Option; + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + UINT32 T1; + UINT32 T2; + + ASSERT (Instance->Config != NULL); + // + // If the reply was received in reponse to a solicit with rapid commit option, + // request, renew or rebind message, the client updates the information it has + // recorded about IAs from the IA options contained in the reply message: + // 1. record the T1 and T2 times + // 2. add any new addresses in the IA + // 3. discard any addresses from the IA, that have a valid lifetime of 0 + // 4. update lifetimes for any addresses that alread recorded + // 5. leave unchanged any information about addresses + // + // See details in the section-18.1.8 of rfc-3315. + // + State = Dhcp6Init; + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // The format of the IA_NA 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_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA 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_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8))); + T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12))); + IaInnerOpt = Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12); + } else { + T1 = 0; + T2 = 0; + IaInnerOpt = Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4); + } + + // + // The format of the Status Code 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_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + StsCode = Dhcp6StsSuccess; + Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + + if (Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Generate control block for the Ia. + // + Status = Dhcp6GenerateIaCb ( + Instance, + IaInnerOpt, + IaInnerLen, + T1, + T2 + ); + + return Status; +} + + + +/** + Seek StatusCode Option in package. A Status Code option may appear in the + options field of a DHCP message and/or in the options field of another option. + See details in section 22.13, RFC3315. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to reply messages. + @param[out] Option The pointer to status code option. + + @retval EFI_SUCCESS Seek status code option successfully. + @retval EFI_DEVICE_ERROR An unexpected error. + +**/ +EFI_STATUS +Dhcp6SeekStsOption ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + OUT UINT8 **Option + ) +{ + UINT8 *IaInnerOpt; + UINT16 IaInnerLen; + UINT16 StsCode; + + // + // Seek StatusCode option directly in DHCP message body. That is, search in + // non-encapsulated option fields. + // + *Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptStatusCode + ); + + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Seek in encapsulated options, IA_NA and IA_TA. + // + *Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length - sizeof (EFI_DHCP6_HEADER), + &Instance->Config->IaDescriptor + ); + if (*Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // The format of the IA_NA 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_IA_NA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T1 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | T2 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_NA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // The format of the IA_TA 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_IA_TA | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | IAID (4 octets) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // . IA_TA-options . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len + IaId) = 8 + // sizeof (option-code + option-len + IaId + T1) = 12 + // sizeof (option-code + option-len + IaId + T1 + T2) = 16 + // + // The inner options still start with 2 bytes option-code and 2 bytes option-len. + // + if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) { + IaInnerOpt = *Option + 16; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12); + } else { + IaInnerOpt = *Option + 8; + IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4); + } + + // + // The format of the Status Code 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_STATUS_CODE | option-len | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | status-code | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | + // . . + // . status-message . + // . . + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + + // + // sizeof (option-code + option-len) = 4 + // + *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode); + if (*Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + return EFI_SUCCESS; +} + + +/** + Transmit Dhcp6 message by udpio. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to transmit message. + @param[in] Elapsed The pointer to the elapsed time value to fill in. + + @retval EFI_SUCCESS Successfully transmitted the packet. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Dhcp6TransmitPacket ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet, + IN UINT16 *Elapsed + ) +{ + EFI_STATUS Status; + NET_BUF *Wrap; + NET_FRAGMENT Frag; + UDP_END_POINT EndPt; + DHCP6_SERVICE *Service; + + Service = Instance->Service; + + // + // Wrap it into a netbuf then send it. + // + Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header; + Frag.Len = Packet->Length; + + // + // Do not register free packet here, which will be handled in retry list. + // + Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL); + + if (Wrap == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Multicast the Dhcp6 message, unless get the unicast server address by option. + // + ZeroMem (&EndPt, sizeof (UDP_END_POINT)); + + if (Instance->Unicast != NULL) { + CopyMem ( + &EndPt.RemoteAddr, + Instance->Unicast, + sizeof (EFI_IPv6_ADDRESS) + ); + } else { + CopyMem ( + &EndPt.RemoteAddr, + &mAllDhcpRelayAndServersAddress, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + EndPt.RemotePort = DHCP6_PORT_SERVER; + EndPt.LocalPort = DHCP6_PORT_CLIENT; + + // + // Update the elapsed time value. + // + if (Elapsed != NULL) { + SetElapsedTime (Elapsed, Instance); + } + + // + // Send out the message by the configured Udp6Io. + // + Status = UdpIoSendDatagram ( + Service->UdpIo, + Wrap, + &EndPt, + NULL, + Dhcp6OnTransmitted, + NULL + ); + + if (EFI_ERROR (Status)) { + NetbufFree (Wrap); + return Status; + } + + return EFI_SUCCESS; +} + + +/** + Create the solicit message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6SendSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = 0; + + ASSERT (Service->ClientId != NULL); + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send solicit packet with the state transition from Dhcp6init to + // Dhcp6selecting. + // + Instance->IaCb.Ia->State = Dhcp6Selecting; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry ( + Instance, + Packet, + Elapsed, + Instance->Config->SolicitRetransmission + ); +} + +/** + Configure some parameter to initiate SolicitMsg. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the solicit message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the solicit message. + +**/ +EFI_STATUS +Dhcp6InitSolicitMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + Instance->IaCb.T1 = 0; + Instance->IaCb.T2 = 0; + Instance->IaCb.Ia->IaAddressCount = 0; + + return Dhcp6SendSolicitMsg (Instance); +} + + +/** + Create the request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the request message. + +**/ +EFI_STATUS +Dhcp6SendRequestMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->AdSelect != NULL); + ASSERT(Instance->Config != NULL); + ASSERT(Instance->IaCb.Ia != NULL); + ASSERT(Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId != NULL); + + // + // Get the server Id from the selected advertisement message. + // + Option = Dhcp6SeekOption ( + Instance->AdSelect->Dhcp6.Option, + Instance->AdSelect->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for request message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send request packet with the state transition from Dhcp6selecting to + // Dhcp6requesting. + // + Instance->IaCb.Ia->State = Dhcp6Requesting; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the decline message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] DecIa The pointer to the decline Ia. + + @retval EFI_SUCCESS Created and sent the decline message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the decline message. + +**/ +EFI_STATUS +Dhcp6SendDeclineMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *DecIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT (ClientId != NULL); + ASSERT (LastReply != NULL); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + // + // EFI_DHCP6_DUID contains a length field of 2 bytes. + // + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0); + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send decline packet with the state transition from Dhcp6bound to + // Dhcp6declining. + // + Instance->IaCb.Ia->State = Dhcp6Declining; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the release message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RelIa The pointer to the release Ia. + + @retval EFI_SUCCESS Created and sent the release message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the release message. + +**/ +EFI_STATUS +Dhcp6SendReleaseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_IA *RelIa + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + LastReply = Instance->IaCb.Ia->ReplyPacket; + + ASSERT(ClientId); + ASSERT(LastReply); + + // + // Get the server Id from the last reply message. + // + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + // + // ServerId is extracted from packet, it's network order. + // + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0); + + // + // Determine the size/length of packet + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send release packet with the state transition from Dhcp6bound to + // Dhcp6releasing. + // + Instance->IaCb.Ia->State = Dhcp6Releasing; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the renew/rebind message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] RebindRequest If TRUE, it is a Rebind type message. + Otherwise, it is a Renew type message. + + @retval EFI_SUCCESS Created and sent the renew/rebind message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the renew/rebind message. + +**/ +EFI_STATUS +Dhcp6SendRenewRebindMsg ( + IN DHCP6_INSTANCE *Instance, + IN BOOLEAN RebindRequest + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET *LastReply; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_DUID *ServerId; + EFI_DHCP6_STATE State; + EFI_DHCP6_EVENT Event; + DHCP6_SERVICE *Service; + UINT8 *Option; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Service = Instance->Service; + ClientId = Service->ClientId; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for rebind/renew message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + if (!RebindRequest) { + // + // Get the server Id from the last reply message and + // insert it for rebind request. + // + LastReply = Instance->IaCb.Ia->ReplyPacket; + ASSERT (LastReply); + + Option = Dhcp6SeekOption ( + LastReply->Dhcp6.Option, + LastReply->Length - 4, + Dhcp6OptServerId + ); + if (Option == NULL) { + FreePool (Packet); + return EFI_DEVICE_ERROR; + } + + ServerId = (EFI_DHCP6_DUID *) (Option + 2); + + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptServerId), + ServerId->Length, + ServerId->Duid + ); + } + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing; + Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing; + + Status = Dhcp6CallbackUser (Instance, Event, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send renew/rebind packet with the state transition from Dhcp6bound to + // Dhcp6renew/rebind. + // And sync the lease time when send renew/rebind, in case that user send + // renew/rebind actively. + // + Instance->IaCb.Ia->State = State; + Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + +/** + Create the information request message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] InfCb The pointer to the information request control block. + @param[in] SendClientId If TRUE, the client identifier option will be included in + information request message. Otherwise, the client identifier + option will not be included. + @param[in] OptionRequest The pointer to the option request option. + @param[in] OptionCount The number options in the OptionList. + @param[in] OptionList The array pointers to the appended options. + @param[in] Retransmission The pointer to the retransmission control. + + @retval EFI_SUCCESS Created and sent the info-request message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to send the info-request message. + +**/ +EFI_STATUS +Dhcp6SendInfoRequestMsg ( + IN DHCP6_INSTANCE *Instance, + IN DHCP6_INF_CB *InfCb, + IN BOOLEAN SendClientId, + IN EFI_DHCP6_PACKET_OPTION *OptionRequest, + IN UINT32 OptionCount, + IN EFI_DHCP6_PACKET_OPTION *OptionList[], + IN EFI_DHCP6_RETRANSMISSION *Retransmission + ) +{ + EFI_STATUS Status; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Cursor; + UINT16 *Elapsed; + UINT32 UserLen; + UINTN Index; + UINT16 Length; + + ASSERT(OptionRequest); + + Service = Instance->Service; + ClientId = Service->ClientId; + UserLen = NTOHS (OptionRequest->OpLen) + 4; + + ASSERT(ClientId); + + // + // Calculate the added length of customized option list. + // + for (Index = 0; Index < OptionCount; Index++) { + UserLen += (NTOHS (OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize commone fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + InfCb->Xid = Packet->Dhcp6.Header.TransactionId; + + // + // Assembly Dhcp6 options for info-request message. + // + Cursor = Packet->Dhcp6.Option; + + if (SendClientId) { + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + } + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendOption ( + Cursor, + OptionRequest->OpCode, + OptionRequest->OpLen, + OptionRequest->Data + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < OptionCount; Index++) { + + UserOpt = OptionList[Index]; + Cursor = Dhcp6AppendOption( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Send info-request packet with no state. + // + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission); +} + + +/** + Create the Confirm message and send it. + + @param[in] Instance The pointer to the Dhcp6 instance. + + @retval EFI_SUCCESS Created and sent the confirm message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to send the confirm message. + +**/ +EFI_STATUS +Dhcp6SendConfirmMsg ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT8 *Cursor; + UINTN Index; + UINT16 Length; + UINT32 UserLen; + EFI_STATUS Status; + DHCP6_SERVICE *Service; + EFI_DHCP6_DUID *ClientId; + EFI_DHCP6_PACKET *Packet; + EFI_DHCP6_PACKET_OPTION *UserOpt; + UINT16 *Elapsed; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Instance->Service != NULL); + + Service = Instance->Service; + ClientId = Service->ClientId; + ASSERT (ClientId != NULL); + + // + // Calculate the added length of customized option list. + // + UserLen = 0; + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4); + } + + // + // Create the Dhcp6 packet and initialize common fields. + // + Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen); + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen; + Packet->Length = sizeof (EFI_DHCP6_HEADER); + Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm; + Packet->Dhcp6.Header.TransactionId = Service->Xid++; + + // + // Assembly Dhcp6 options for solicit message. + // + Cursor = Packet->Dhcp6.Option; + + Length = HTONS (ClientId->Length); + Cursor = Dhcp6AppendOption ( + Cursor, + HTONS (Dhcp6OptClientId), + Length, + ClientId->Duid + ); + + Cursor = Dhcp6AppendETOption ( + Cursor, + Instance, + &Elapsed + ); + + Cursor = Dhcp6AppendIaOption ( + Cursor, + Instance->IaCb.Ia, + Instance->IaCb.T1, + Instance->IaCb.T2 + ); + + // + // Append user-defined when configurate Dhcp6 service. + // + for (Index = 0; Index < Instance->Config->OptionCount; Index++) { + UserOpt = Instance->Config->OptionList[Index]; + Cursor = Dhcp6AppendOption ( + Cursor, + UserOpt->OpCode, + UserOpt->OpLen, + UserOpt->Data + ); + } + + // + // Determine the size/length of packet. + // + Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option); + ASSERT (Packet->Size > Packet->Length + 8); + + // + // Callback to user with the packet to be sent and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Send confirm packet with the state transition from Dhcp6Bound to + // Dhcp6Confirming. + // + Instance->IaCb.Ia->State = Dhcp6Confirming; + + Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed); + + if (EFI_ERROR (Status)) { + FreePool (Packet); + return Status; + } + + // + // Enqueue the sent packet for the retransmission in case reply timeout. + // + return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL); +} + + + +/** + Handle with the Dhcp6 reply message. + + @param[in] Instance The pointer to Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 reply message. + + @retval EFI_SUCCESS Processed the reply message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the reply message. + +**/ +EFI_STATUS +Dhcp6HandleReplyMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT16 StsCode; + + ASSERT (Instance->Config != NULL); + ASSERT (Instance->IaCb.Ia != NULL); + ASSERT (Packet != NULL); + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + return EFI_DEVICE_ERROR; + } + + // + // If the client subsequently receives a valid reply message that includes a + // rapid commit option since send a solicit with rapid commit option before, + // preocess the reply message and discard any reply messages received in + // response to the request message. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) { + return EFI_DEVICE_ERROR; + } + + // + // As to a valid reply packet in response to a request/renew/rebind packet, + // ignore the packet if not contains the Ia option + // + if (Instance->IaCb.Ia->State == Dhcp6Requesting || + Instance->IaCb.Ia->State == Dhcp6Renewing || + Instance->IaCb.Ia->State == Dhcp6Rebinding + ) { + + Option = Dhcp6SeekIaOption ( + Packet->Dhcp6.Option, + Packet->Length, + &Instance->Config->IaDescriptor + ); + if (Option == NULL) { + return EFI_DEVICE_ERROR; + } + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Dequeue the sent packet from retransmit list since reply received. + // + Status = Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // When receive a valid reply packet in response to a decline/release packet, + // the client considers the decline/release event completed regardless of the + // status code. + // + if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) { + + if (Instance->IaCb.Ia->IaAddressCount != 0) { + Instance->IaCb.Ia->State = Dhcp6Bound; + } else { + ASSERT (Instance->IaCb.Ia->ReplyPacket); + FreePool (Instance->IaCb.Ia->ReplyPacket); + Instance->IaCb.Ia->ReplyPacket = NULL; + Instance->IaCb.Ia->State = Dhcp6Init; + } + + // + // For sync, set the success flag out of polling in decline/release. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + return EFI_SUCCESS; + } + + // + // Upon the receipt of a valid reply packet in response to a solicit, request, + // confirm, renew and rebind, the behavior depends on the status code option. + // See the details in the section-18.1.8 of rfc-3315. + // + Option = NULL; + Status = Dhcp6SeekStsOption ( + Instance, + Packet, + &Option + ); + + if (!EFI_ERROR (Status)) { + // + // Reset start time for next exchange. + // + Instance->StartTime = 0; + + // + // No status code or no error status code means succeed to reply. + // + Status = Dhcp6UpdateIaInfo (Instance, Packet); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Set bound state and store the reply packet. + // + if (Instance->IaCb.Ia->ReplyPacket != NULL) { + FreePool (Instance->IaCb.Ia->ReplyPacket); + } + + Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size); + + if (Instance->IaCb.Ia->ReplyPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size); + + Instance->IaCb.Ia->State = Dhcp6Bound; + + // + // For sync, set the success flag out of polling in start/renewrebind. + // + Instance->UdpSts = EFI_SUCCESS; + + // + // Maybe this is a new round DHCP process due to some reason, such as NotOnLink + // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that + // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP + // consumers can be notified to flush old address. + // + Dhcp6AppendCacheIa (Instance); + + // + // For async, signal the Ia event to inform Ia infomation update. + // + if (Instance->Config->IaInfoEvent != NULL) { + gBS->SignalEvent (Instance->Config->IaInfoEvent); + } + } else if (Option != NULL) { + // + // Any error status code option is found. + // + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + switch (StsCode) { + case Dhcp6StsUnspecFail: + // + // It indicates the server is unable to process the message due to an + // unspecified failure condition, so just retry if possible. + // + break; + + case Dhcp6StsUseMulticast: + // + // It indicates the server receives a message via unicast from a client + // to which the server has not sent a unicast option, so retry it by + // multi-cast address. + // + if (Instance->Unicast != NULL) { + FreePool (Instance->Unicast); + Instance->Unicast = NULL; + } + break; + + case Dhcp6StsNotOnLink: + if (Instance->IaCb.Ia->State == Dhcp6Confirming) { + // + // Before initiate new round DHCP, cache the current IA. + // + Status = Dhcp6CacheIa (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Restart S.A.R.R process to acquire new address. + // + Status = Dhcp6InitSolicitMsg (Instance); + if (EFI_ERROR (Status)) { + return Status; + } + } + break; + + default: + // + // The other status code, just restart solicitation. + // + break; + } + } + + return EFI_SUCCESS; +} + + +/** + Select the appointed Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message. + + @retval EFI_SUCCESS Selected the right advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval Others Failed to select the advertise message. + +**/ +EFI_STATUS +Dhcp6SelectAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *AdSelect + ) +{ + EFI_STATUS Status; + UINT8 *Option; + + ASSERT (AdSelect != NULL); + + // + // Callback to user with the selected advertisement packet, and the user + // might overwrite it. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + Instance->AdSelect = AdSelect; + + // + // Dequeue the sent packet for the retransmission since advertisement selected. + // + Status = Dhcp6DequeueRetry ( + Instance, + AdSelect->Dhcp6.Header.TransactionId, + FALSE + ); + + if (EFI_ERROR(Status)) { + return Status; + } + + // + // Check whether there is server unicast option in the selected advertise + // packet, and update it. + // + Option = Dhcp6SeekOption( + AdSelect->Dhcp6.Option, + AdSelect->Length - 4, + Dhcp6OptServerUnicast + ); + + if (Option != NULL) { + + Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS)); + + if (Instance->Unicast == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS)); + } + + // + // Update the information of the Ia by the selected advertisement message. + // + Status = Dhcp6UpdateIaInfo (Instance, AdSelect); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Send the request message to continue the S.A.R.R. process. + // + return Dhcp6SendRequestMsg (Instance); +} + + +/** + Handle with the Dhcp6 advertisement message. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the Dhcp6 advertisement message. + + @retval EFI_SUCCESS Processed the advertisement message successfully. + @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others Failed to process the advertise message. + +**/ +EFI_STATUS +Dhcp6HandleAdvertiseMsg ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + UINT8 *Option; + UINT16 StsCode; + BOOLEAN Timeout; + + ASSERT(Instance->Config); + ASSERT(Instance->IaCb.Ia); + + Timeout = FALSE; + StsCode = Dhcp6StsSuccess; + + // + // If the client does receives a valid reply message that includes a rapid + // commit option since a solicit with rapid commit optioin sent before, select + // this reply message. Or else, process the advertise messages as normal. + // See details in the section-17.1.4 of rfc-3315. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptRapidCommit + ); + + if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) { + + return Dhcp6HandleReplyMsg (Instance, Packet); + } + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) { + return EFI_DEVICE_ERROR; + } + + // + // Client must ignore any advertise message that includes a status code option + // containing the value noaddrsavail, with the exception that the client may + // display the associated status message to the user. + // See the details in the section-17.1.3 of rfc-3315. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptStatusCode + ); + + if (Option != NULL) { + StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4))); + if (StsCode != Dhcp6StsSuccess) { + return EFI_DEVICE_ERROR; + } + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet); + + if (!EFI_ERROR (Status)) { + // + // Success means user choose the current advertisement packet. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + // + // Store the selected advertisement packet and set a flag. + // + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + Instance->AdPref = 0xff; + + } else if (Status == EFI_NOT_READY) { + // + // Not_ready means user wants to continue to receive more advertise packets. + // + if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) { + // + // It's a tricky point. The timer routine set adpref as 0xff if the first + // rt timeout and no advertisement received, which means any advertisement + // received will be selected after the first rt. + // + Timeout = TRUE; + } + + // + // Check whether the current packet has a 255 preference option or not. + // Take non-preference option as 0 value. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptPreference + ); + + if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) { + // + // No advertisements received before or preference is more than other + // advertisements received before. Then store the new packet and the + // preference value. + // + if (Instance->AdSelect != NULL) { + FreePool (Instance->AdSelect); + } + + Instance->AdSelect = AllocateZeroPool (Packet->Size); + + if (Instance->AdSelect == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + CopyMem (Instance->AdSelect, Packet, Packet->Size); + + if (Option != NULL) { + Instance->AdPref = *(Option + 4); + } + } else { + // + // Non-preference and other advertisements received before or current + // preference is less than other advertisements received before. + // Leave the packet alone. + } + + } else { + // + // Other error status means termination. + // + return Status; + } + + // + // Client must collect advertise messages as more as possible until the first + // RT has elapsed, or get a highest preference 255 advertise. + // See details in the section-17.1.2 of rfc-3315. + // + if (Instance->AdPref == 0xff || Timeout) { + Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + } + + return Status; +} + + +/** + The Dhcp6 stateful exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateful ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + EFI_DHCP6_DUID *ClientId; + DHCP6_SERVICE *Service; + UINT8 *Option; + + Service = Instance->Service; + ClientId = Service->ClientId; + Status = EFI_SUCCESS; + + if (Instance->InDestory || Instance->Config == NULL) { + goto ON_CONTINUE; + } + + ASSERT (ClientId); + ASSERT (Instance->Config); + ASSERT (Instance->IaCb.Ia); + + // + // Discard the packet if not advertisement or reply packet. + // + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_CONTINUE; + } + + // + // Check whether include client Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptClientId + ); + + if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) { + goto ON_CONTINUE; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_CONTINUE; + } + + switch (Instance->IaCb.Ia->State) { + case Dhcp6Selecting: + // + // Handle the advertisement message when in the Dhcp6Selecting state. + // Do not need check return status, if failed, just continue to the next. + // + Dhcp6HandleAdvertiseMsg (Instance, Packet); + break; + + case Dhcp6Requesting: + case Dhcp6Confirming: + case Dhcp6Renewing: + case Dhcp6Rebinding: + case Dhcp6Releasing: + case Dhcp6Declining: + // + // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing + // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state. + // If failed here, it should reset the current session. + // + Status = Dhcp6HandleReplyMsg (Instance, Packet); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + break; + default: + // + // Other state has not supported yet. + // + break; + } + +ON_CONTINUE: + // + // Continue to receive the following Dhcp6 message. + // + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); +ON_EXIT: + if (EFI_ERROR (Status)) { + Dhcp6CleanupSession (Instance, Status); + } +} + + +/** + The Dhcp6 stateless exchange process routine. + + @param[in] Instance The pointer to the Dhcp6 instance. + @param[in] Packet The pointer to the received Dhcp6 message. + +**/ +VOID +Dhcp6HandleStateless ( + IN DHCP6_INSTANCE *Instance, + IN EFI_DHCP6_PACKET *Packet + ) +{ + EFI_STATUS Status; + DHCP6_SERVICE *Service; + DHCP6_INF_CB *InfCb; + UINT8 *Option; + BOOLEAN IsMatched; + + Service = Instance->Service; + Status = EFI_SUCCESS; + IsMatched = FALSE; + InfCb = NULL; + + if (Instance->InDestory) { + goto ON_EXIT; + } + + if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) { + goto ON_EXIT; + } + + // + // Check whether it's a desired Info-request message by Xid. + // + while (!IsListEmpty (&Instance->InfList)) { + InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link); + if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) { + IsMatched = TRUE; + break; + } + } + + if (!IsMatched) { + goto ON_EXIT; + } + + // + // Check whether include server Id or not. + // + Option = Dhcp6SeekOption ( + Packet->Dhcp6.Option, + Packet->Length - 4, + Dhcp6OptServerId + ); + + if (Option == NULL) { + goto ON_EXIT; + } + + // + // Callback to user with the received packet and check the user's feedback. + // + Status = InfCb->ReplyCallback ( + &Instance->Dhcp6, + InfCb->CallbackContext, + Packet + ); + + if (Status == EFI_NOT_READY) { + // + // Success or aborted will both stop this info-request exchange process, + // but not ready means user wants to continue to receive reply. + // + goto ON_EXIT; + } + + // + // Dequeue the sent packet from the txlist if the xid matched, and ignore + // if no xid matched. + // + Dhcp6DequeueRetry ( + Instance, + Packet->Dhcp6.Header.TransactionId, + FALSE + ); + + // + // For sync, set the status out of polling for info-request. + // + Instance->UdpSts = Status; + +ON_EXIT: + + Status = UdpIoRecvDatagram ( + Service->UdpIo, + Dhcp6ReceivePacket, + Service, + 0 + ); + + if (EFI_ERROR (Status)) { + Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS); + } +} + + +/** + The receive callback function for Dhcp6 exchange process. + + @param[in] Udp6Wrap 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 +Dhcp6ReceivePacket ( + IN NET_BUF *Udp6Wrap, + IN UDP_END_POINT *EndPoint, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + EFI_DHCP6_HEADER *Head; + EFI_DHCP6_PACKET *Packet; + DHCP6_SERVICE *Service; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + UINT32 Size; + BOOLEAN IsDispatched; + BOOLEAN IsStateless; + LIST_ENTRY *Entry1; + LIST_ENTRY *Next1; + LIST_ENTRY *Entry2; + LIST_ENTRY *Next2; + + ASSERT (Udp6Wrap != NULL); + ASSERT (Context != NULL); + + Service = (DHCP6_SERVICE *) Context; + Instance = NULL; + Packet = NULL; + IsDispatched = FALSE; + IsStateless = FALSE; + + if (EFI_ERROR (IoStatus)) { + return ; + } + + // + // Copy the net buffer received from upd6 to a Dhcp6 packet. + // + Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize; + Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size); + + if (Packet == NULL) { + goto ON_CONTINUE; + } + + Packet->Size = Size; + Head = &Packet->Dhcp6.Header; + Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head); + + if (Packet->Length == 0) { + goto ON_CONTINUE; + } + + // + // Dispatch packet to right instance by transaction id. + // + NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) { + + Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link); + + NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link); + + if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) { + // + // Find the corresponding packet in tx list, and check it whether belongs + // to stateful exchange process. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + IsStateless = TRUE; + } + IsDispatched = TRUE; + break; + } + } + + if (IsDispatched) { + break; + } + } + + // + // Skip this packet if not dispatched to any instance. + // + if (!IsDispatched) { + goto ON_CONTINUE; + } + + // + // Dispatch the received packet ot the right instance. + // + if (IsStateless) { + Dhcp6HandleStateless (Instance, Packet); + } else { + Dhcp6HandleStateful (Instance, Packet); + } + +ON_CONTINUE: + + NetbufFree (Udp6Wrap); + + if (Packet != NULL) { + FreePool (Packet); + } +} + +/** + Detect Link movement for specified network device. + + This routine will try to invoke Snp->GetStatus() to get the media status. + If media present status switches from unpresent to present, a link movement + is detected. Note that the underlying UNDI driver may not support reporting + media status from GET_STATUS command. If that, fail to detect link movement. + + @param[in] Instance The pointer to DHCP6_INSTANCE. + + @retval TRUE A link movement is detected. + @retval FALSE A link movement is not detected. + +**/ +BOOLEAN +Dhcp6LinkMovDetect ( + IN DHCP6_INSTANCE *Instance + ) +{ + UINT32 InterruptStatus; + BOOLEAN MediaPresent; + EFI_STATUS Status; + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + + ASSERT (Instance != NULL); + Snp = Instance->Service->Snp; + MediaPresent = Instance->MediaPresent; + + // + // Check whether SNP support media detection + // + if (!Snp->Mode->MediaPresentSupported) { + return FALSE; + } + + // + // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data + // + Status = Snp->GetStatus (Snp, &InterruptStatus, NULL); + if (EFI_ERROR (Status)) { + return FALSE; + } + + Instance->MediaPresent = Snp->Mode->MediaPresent; + // + // Media transimit Unpresent to Present means new link movement is detected. + // + if (!MediaPresent && Instance->MediaPresent) { + return TRUE; + } + return FALSE; +} + + +/** + The timer routine of the Dhcp6 instance for each second. + + @param[in] Event The timer event. + @param[in] Context The opaque parameter to the function. + +**/ +VOID +EFIAPI +Dhcp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *NextEntry; + DHCP6_INSTANCE *Instance; + DHCP6_TX_CB *TxCb; + DHCP6_IA_CB *IaCb; + UINT32 LossTime; + + ASSERT (Context != NULL); + + Instance = (DHCP6_INSTANCE *) Context; + + // + // 1. Loop the tx list, count live time of every tx packet to check whether + // need re-transmit or not. + // + NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) { + + TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link); + + TxCb->TickTime++; + + if (TxCb->TickTime > TxCb->RetryExp) { + // + // Handle the first rt in the transmission of solicit specially. + // + if (TxCb->RetryCnt == 0 && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) { + if (Instance->AdSelect == NULL) { + // + // Set adpref as 0xff here to indicate select any advertisement + // afterwards. + // + Instance->AdPref = 0xff; + } else { + // + // Select the advertisement received before. + // + Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect); + return; + } + } + // + // Increase the retry count for the packet and add up the total loss time. + // + TxCb->RetryCnt++; + TxCb->RetryLos += TxCb->RetryExp; + + // + // Check whether overflow the max retry count limit for this packet + // + if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) { + goto ON_CLOSE; + } + + // + // Check whether overflow the max retry duration for this packet + // + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) { + goto ON_CLOSE; + } + + // + // Re-calculate retry expire timeout for the next time. + // + // Firstly, Check the new calculated time whether overflow the max retry + // expire time. + // + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryExp, + FALSE, + TRUE + ); + + if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) { + TxCb->RetryExp = Dhcp6CalculateExpireTime ( + TxCb->RetryCtl.Mrt, + TRUE, + TRUE + ); + } + + // + // Secondly, Check the new calculated time whether overflow the max retry + // duration time. + // + LossTime = TxCb->RetryLos + TxCb->RetryExp; + if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) { + TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos; + } + + // + // Reset the tick time for the next retransmission + // + TxCb->TickTime = 0; + + // + // Retransmit the last sent packet again. + // + Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed); + } + } + + // + // 2. Check the configured Ia, count lease time of every valid Ia to check + // whether need to renew or rebind this Ia. + // + IaCb = &Instance->IaCb; + + if (Instance->Config == NULL || IaCb->Ia == NULL) { + return; + } + + if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) { + + IaCb->LeaseTime++; + + if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) { + // + // Exceed t2, send rebind packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, TRUE); + + } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) { + + // + // Exceed t1, send renew packet to extend the Ia lease. + // + Dhcp6SendRenewRebindMsg (Instance, FALSE); + } + } + + // + // 3. In any situation when a client may have moved to a new link, the + // client MUST initiate a Confirm/Reply message exchange. + // + if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) { + Dhcp6SendConfirmMsg (Instance); + } + + return; + + ON_CLOSE: + + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew || + TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm + ) { + // + // The failure of renew/Confirm will still switch to the bound state. + // + if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) || + (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) { + ASSERT (Instance->IaCb.Ia); + Instance->IaCb.Ia->State = Dhcp6Bound; + } + // + // The failure of info-request will return no response. + // + if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) { + Instance->UdpSts = EFI_NO_RESPONSE; + } + Dhcp6DequeueRetry ( + Instance, + TxCb->Xid, + TRUE + ); + } else { + // + // The failure of the others will terminate current state machine if timeout. + // + Dhcp6CleanupSession (Instance, EFI_NO_RESPONSE); + } +} -- cgit v1.1