diff options
author | hhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-11-01 06:13:54 +0000 |
---|---|---|
committer | hhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524> | 2010-11-01 06:13:54 +0000 |
commit | a3bcde70e6dc69000f85cc5deee98101d2ae200a (patch) | |
tree | 693709a5293f80b320931693b34b2d6983cfcf4b /NetworkPkg/Dhcp6Dxe/Dhcp6Io.c | |
parent | 12873d57666d0beff41959a1fb8f9062016f0983 (diff) | |
download | edk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.zip edk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.gz edk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.bz2 |
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
Diffstat (limited to 'NetworkPkg/Dhcp6Dxe/Dhcp6Io.c')
-rw-r--r-- | NetworkPkg/Dhcp6Dxe/Dhcp6Io.c | 2965 |
1 files changed, 2965 insertions, 0 deletions
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.<BR>
+
+ 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);
+ }
+}
|