mirror of https://github.com/acidanthera/audk.git
2970 lines
85 KiB
C
2970 lines
85 KiB
C
/** @file
|
|
Dhcp6 internal functions implementation.
|
|
|
|
Copyright (c) 2009 - 2011, 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;
|
|
EFI_STATUS Status;
|
|
|
|
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.
|
|
//
|
|
Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
|
|
if (EFI_ERROR (Status)) {
|
|
TxCb->RetryCnt++;
|
|
}
|
|
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);
|
|
}
|
|
}
|