/** @file | |
This file implement the EFI_DHCP4_PROTOCOL interface. | |
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Dhcp4Impl.h" | |
/** | |
Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver. | |
The GetModeData() function returns the current operating mode and cached data | |
packet for the EFI DHCPv4 Protocol driver. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure. | |
@retval EFI_SUCCESS The mode data was returned. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4GetModeData ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData | |
); | |
/** | |
Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver. | |
The Configure() function is used to initialize, change, or reset the operational | |
settings of the EFI DHCPv4 Protocol driver for the communication device on which | |
the EFI DHCPv4 Service Binding Protocol is installed. This function can be | |
successfully called only if both of the following are true: | |
* This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init, | |
Dhcp4InitReboot, or Dhcp4Bound states. | |
* No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI | |
DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4 | |
Protocol driver. | |
When this driver is in the Dhcp4Stopped state, it can transfer into one of the | |
following two possible initial states: | |
* Dhcp4Init | |
* Dhcp4InitReboot | |
The driver can transfer into these states by calling Configure() with a non-NULL | |
Dhcp4CfgData. The driver will transfer into the appropriate state based on the | |
supplied client network address in the ClientAddress parameter and DHCP options | |
in the OptionList parameter as described in RFC 2131. | |
When Configure() is called successfully while Dhcp4CfgData is set to NULL, the | |
default configuring data will be reset in the EFI DHCPv4 Protocol driver and | |
the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance | |
wants to make it possible for another instance to configure the EFI DHCPv4 Protocol | |
driver, it must call this function with Dhcp4CfgData set to NULL. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or | |
Dhcp4InitReboot state, if the original state of this driver | |
was Dhcp4Stopped and the value of Dhcp4CfgData was | |
not NULL. Otherwise, the state was left unchanged. | |
@retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the | |
Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state; | |
Or another instance of this EFI DHCPv4 Protocol driver is already | |
in a valid configured state. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Configure ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL | |
); | |
/** | |
Starts the DHCP configuration process. | |
The Start() function starts the DHCP configuration process. This function can | |
be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or | |
Dhcp4InitReboot state. | |
If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol | |
driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the | |
Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL. | |
If the process aborts, either by the user or by some unexpected network error, | |
the state is restored to the Dhcp4Init state. The Start() function can be called | |
again to restart the process. | |
Refer to RFC 2131 for precise state transitions during this process. At the | |
time when each event occurs in this process, the callback function that was set | |
by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this | |
opportunity to control the process. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the | |
EFI DHCPv4 Protocol driver is transferred into the | |
Dhcp4Bound state or when the DHCP process is aborted. | |
EFI_DHCP4_PROTOCOL.GetModeData() can be called to | |
check the completion status. If NULL, | |
EFI_DHCP4_PROTOCOL.Start() will wait until the driver | |
is transferred into the Dhcp4Bound state or the process fails. | |
@retval EFI_SUCCESS The DHCP configuration process has started, or it has completed | |
when CompletionEvent is NULL. | |
@retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped | |
state. EFI_DHCP4_PROTOCOL. Configure() needs to be called. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_TIMEOUT The DHCP configuration process failed because no response was | |
received from the server within the specified timeout value. | |
@retval EFI_ABORTED The user aborted the DHCP process. | |
@retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the | |
DHCP process. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Start ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_EVENT CompletionEvent OPTIONAL | |
); | |
/** | |
Extends the lease time by sending a request packet. | |
The RenewRebind() function is used to manually extend the lease time when the | |
EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has | |
not expired yet. This function will send a request packet to the previously | |
found server (or to any server when RebindRequest is TRUE) and transfer the | |
state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is | |
TRUE). When a response is received, the state is returned to Dhcp4Bound. | |
If no response is received before the try count is exceeded (the RequestTryCount | |
field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that | |
was issued by the previous server expires, the driver will return to the Dhcp4Bound | |
state and the previous configuration is restored. The outgoing and incoming packets | |
can be captured by the EFI_DHCP4_CALLBACK function. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters | |
the Dhcp4Rebinding state. Otherwise, it sends a unicast | |
request packet and enters the Dhcp4Renewing state. | |
@param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase | |
completes or some error occurs. | |
EFI_DHCP4_PROTOCOL.GetModeData() can be called to | |
check the completion status. If NULL, | |
EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait | |
until the DHCP process finishes. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the | |
Dhcp4Renewing state or is back to the Dhcp4Bound state. | |
@retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped | |
state. EFI_DHCP4_PROTOCOL.Configure() needs to | |
be called. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_TIMEOUT There was no response from the server when the try count was | |
exceeded. | |
@retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4RenewRebind ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN BOOLEAN RebindRequest, | |
IN EFI_EVENT CompletionEvent OPTIONAL | |
); | |
/** | |
Releases the current address configuration. | |
The Release() function releases the current configured IP address by doing either | |
of the following: | |
* Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the | |
Dhcp4Bound state | |
* Setting the previously assigned IP address that was provided with the | |
EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in | |
Dhcp4InitReboot state | |
After a successful call to this function, the EFI DHCPv4 Protocol driver returns | |
to the Dhcp4Init state and any subsequent incoming packets will be discarded silently. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Release ( | |
IN EFI_DHCP4_PROTOCOL *This | |
); | |
/** | |
Stops the current address configuration. | |
The Stop() function is used to stop the DHCP configuration process. After this | |
function is called successfully, the EFI DHCPv4 Protocol driver is transferred | |
into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called | |
before DHCP configuration process can be started again. This function can be | |
called when the EFI DHCPv4 Protocol driver is in any state. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Stop ( | |
IN EFI_DHCP4_PROTOCOL *This | |
); | |
/** | |
Builds a DHCP packet, given the options to be appended or deleted or replaced. | |
The Build() function is used to assemble a new packet from the original packet | |
by replacing or deleting existing options or appending new options. This function | |
does not change any state of the EFI DHCPv4 Protocol driver and can be used at | |
any time. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] SeedPacket Initial packet to be used as a base for building new packet. | |
@param[in] DeleteCount Number of opcodes in the DeleteList. | |
@param[in] DeleteList List of opcodes to be deleted from the seed packet. | |
Ignored if DeleteCount is zero. | |
@param[in] AppendCount Number of entries in the OptionList. | |
@param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket. | |
If SeedPacket also contains options in this list, they are | |
replaced by new options (except pad option). Ignored if | |
AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION | |
@param[out] NewPacket Pointer to storage for the pointer to the new allocated packet. | |
Use the EFI Boot Service FreePool() on the resulting pointer | |
when done with the packet. | |
@retval EFI_SUCCESS The new packet was built. | |
@retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Build ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_PACKET *SeedPacket, | |
IN UINT32 DeleteCount, | |
IN UINT8 *DeleteList OPTIONAL, | |
IN UINT32 AppendCount, | |
IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, | |
OUT EFI_DHCP4_PACKET **NewPacket | |
); | |
/** | |
Transmits a DHCP formatted packet and optionally waits for responses. | |
The TransmitReceive() function is used to transmit a DHCP packet and optionally | |
wait for the response from servers. This function does not change the state of | |
the EFI DHCPv4 Protocol driver and thus can be used at any time. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure. | |
@retval EFI_SUCCESS The packet was successfully queued for transmission. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call | |
this function after collection process completes. | |
@retval EFI_NO_MAPPING The default station address is not available yet. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval Others Some other unexpected error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4TransmitReceive ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token | |
); | |
/** | |
Parses the packed DHCP option data. | |
The Parse() function is used to retrieve the option list from a DHCP packet. | |
If *OptionCount isn't zero, and there is enough space for all the DHCP options | |
in the Packet, each element of PacketOptionList is set to point to somewhere in | |
the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported, | |
the caller should reassemble the parsed DHCP options to get the finial result. | |
If *OptionCount is zero or there isn't enough space for all of them, the number | |
of DHCP options in the Packet is returned in OptionCount. | |
@param This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param Packet Pointer to packet to be parsed. | |
@param OptionCount On input, the number of entries in the PacketOptionList. | |
On output, the number of entries that were written into the | |
PacketOptionList. | |
@param PacketOptionList List of packet option entries to be filled in. End option or pad | |
options are not included. | |
@retval EFI_SUCCESS The packet was successfully parsed. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE: | |
1) *OptionCount is smaller than the number of options that | |
were found in the Packet. | |
2) PacketOptionList is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Parse ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_PACKET *Packet, | |
IN OUT UINT32 *OptionCount, | |
OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL | |
); | |
EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = { | |
EfiDhcp4GetModeData, | |
EfiDhcp4Configure, | |
EfiDhcp4Start, | |
EfiDhcp4RenewRebind, | |
EfiDhcp4Release, | |
EfiDhcp4Stop, | |
EfiDhcp4Build, | |
EfiDhcp4TransmitReceive, | |
EfiDhcp4Parse | |
}; | |
/** | |
Returns the current operating mode and cached data packet for the EFI DHCPv4 Protocol driver. | |
The GetModeData() function returns the current operating mode and cached data | |
packet for the EFI DHCPv4 Protocol driver. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[out] Dhcp4ModeData Pointer to storage for the EFI_DHCP4_MODE_DATA structure. | |
@retval EFI_SUCCESS The mode data was returned. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4GetModeData ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
DHCP_PARAMETER *Para; | |
EFI_TPL OldTpl; | |
IP4_ADDR Ip; | |
// | |
// First validate the parameters. | |
// | |
if ((This == NULL) || (Dhcp4ModeData == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
// | |
// Caller can use GetModeData to retrieve current DHCP states | |
// no matter whether it is the active child or not. | |
// | |
Dhcp4ModeData->State = (EFI_DHCP4_STATE)DhcpSb->DhcpState; | |
CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData)); | |
CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress)); | |
Ip = HTONL (DhcpSb->ClientAddr); | |
CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
Ip = HTONL (DhcpSb->Netmask); | |
CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
Ip = HTONL (DhcpSb->ServerAddr); | |
CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
Para = DhcpSb->Para; | |
if (Para != NULL) { | |
Ip = HTONL (Para->Router); | |
CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
Dhcp4ModeData->LeaseTime = Para->Lease; | |
} else { | |
ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS)); | |
Dhcp4ModeData->LeaseTime = 0xffffffff; | |
} | |
Dhcp4ModeData->ReplyPacket = DhcpSb->Selected; | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
/** | |
Free the resource related to the configure parameters. | |
DHCP driver will make a copy of the user's configure | |
such as the time out value. | |
@param Config The DHCP configure data | |
**/ | |
VOID | |
DhcpCleanConfigure ( | |
IN OUT EFI_DHCP4_CONFIG_DATA *Config | |
) | |
{ | |
UINT32 Index; | |
if (Config->DiscoverTimeout != NULL) { | |
FreePool (Config->DiscoverTimeout); | |
} | |
if (Config->RequestTimeout != NULL) { | |
FreePool (Config->RequestTimeout); | |
} | |
if (Config->OptionList != NULL) { | |
for (Index = 0; Index < Config->OptionCount; Index++) { | |
if (Config->OptionList[Index] != NULL) { | |
FreePool (Config->OptionList[Index]); | |
} | |
} | |
FreePool (Config->OptionList); | |
} | |
ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA)); | |
} | |
/** | |
Allocate memory for configure parameter such as timeout value for Dst, | |
then copy the configure parameter from Src to Dst. | |
@param[out] Dst The destination DHCP configure data. | |
@param[in] Src The source DHCP configure data. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
@retval EFI_SUCCESS The configure is copied. | |
**/ | |
EFI_STATUS | |
DhcpCopyConfigure ( | |
OUT EFI_DHCP4_CONFIG_DATA *Dst, | |
IN EFI_DHCP4_CONFIG_DATA *Src | |
) | |
{ | |
EFI_DHCP4_PACKET_OPTION **DstOptions; | |
EFI_DHCP4_PACKET_OPTION **SrcOptions; | |
UINTN Len; | |
UINT32 Index; | |
CopyMem (Dst, Src, sizeof (*Dst)); | |
Dst->DiscoverTimeout = NULL; | |
Dst->RequestTimeout = NULL; | |
Dst->OptionList = NULL; | |
// | |
// Allocate a memory then copy DiscoverTimeout to it | |
// | |
if (Src->DiscoverTimeout != NULL) { | |
Len = Src->DiscoverTryCount * sizeof (UINT32); | |
Dst->DiscoverTimeout = AllocatePool (Len); | |
if (Dst->DiscoverTimeout == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
for (Index = 0; Index < Src->DiscoverTryCount; Index++) { | |
Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1); | |
} | |
} | |
// | |
// Allocate a memory then copy RequestTimeout to it | |
// | |
if (Src->RequestTimeout != NULL) { | |
Len = Src->RequestTryCount * sizeof (UINT32); | |
Dst->RequestTimeout = AllocatePool (Len); | |
if (Dst->RequestTimeout == NULL) { | |
goto ON_ERROR; | |
} | |
for (Index = 0; Index < Src->RequestTryCount; Index++) { | |
Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1); | |
} | |
} | |
// | |
// Allocate an array of dhcp option point, then allocate memory | |
// for each option and copy the source option to it | |
// | |
if (Src->OptionList != NULL) { | |
Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *); | |
Dst->OptionList = AllocateZeroPool (Len); | |
if (Dst->OptionList == NULL) { | |
goto ON_ERROR; | |
} | |
DstOptions = Dst->OptionList; | |
SrcOptions = Src->OptionList; | |
for (Index = 0; Index < Src->OptionCount; Index++) { | |
Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0); | |
DstOptions[Index] = AllocatePool (Len); | |
if (DstOptions[Index] == NULL) { | |
goto ON_ERROR; | |
} | |
CopyMem (DstOptions[Index], SrcOptions[Index], Len); | |
} | |
} | |
return EFI_SUCCESS; | |
ON_ERROR: | |
DhcpCleanConfigure (Dst); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
/** | |
Give up the control of the DHCP service to let other child | |
resume. Don't change the service's DHCP state and the Client | |
address and option list configure as required by RFC2131. | |
@param DhcpSb The DHCP service instance. | |
**/ | |
VOID | |
DhcpYieldControl ( | |
IN DHCP_SERVICE *DhcpSb | |
) | |
{ | |
EFI_DHCP4_CONFIG_DATA *Config; | |
Config = &DhcpSb->ActiveConfig; | |
DhcpSb->ServiceState = DHCP_UNCONFIGED; | |
DhcpSb->ActiveChild = NULL; | |
if (Config->DiscoverTimeout != NULL) { | |
FreePool (Config->DiscoverTimeout); | |
Config->DiscoverTryCount = 0; | |
Config->DiscoverTimeout = NULL; | |
} | |
if (Config->RequestTimeout != NULL) { | |
FreePool (Config->RequestTimeout); | |
Config->RequestTryCount = 0; | |
Config->RequestTimeout = NULL; | |
} | |
Config->Dhcp4Callback = NULL; | |
Config->CallbackContext = NULL; | |
} | |
/** | |
Initializes, changes, or resets the operational settings for the EFI DHCPv4 Protocol driver. | |
The Configure() function is used to initialize, change, or reset the operational | |
settings of the EFI DHCPv4 Protocol driver for the communication device on which | |
the EFI DHCPv4 Service Binding Protocol is installed. This function can be | |
successfully called only if both of the following are true: | |
* This instance of the EFI DHCPv4 Protocol driver is in the Dhcp4Stopped, Dhcp4Init, | |
Dhcp4InitReboot, or Dhcp4Bound states. | |
* No other EFI DHCPv4 Protocol driver instance that is controlled by this EFI | |
DHCPv4 Service Binding Protocol driver instance has configured this EFI DHCPv4 | |
Protocol driver. | |
When this driver is in the Dhcp4Stopped state, it can transfer into one of the | |
following two possible initial states: | |
* Dhcp4Init | |
* Dhcp4InitReboot | |
The driver can transfer into these states by calling Configure() with a non-NULL | |
Dhcp4CfgData. The driver will transfer into the appropriate state based on the | |
supplied client network address in the ClientAddress parameter and DHCP options | |
in the OptionList parameter as described in RFC 2131. | |
When Configure() is called successfully while Dhcp4CfgData is set to NULL, the | |
default configuring data will be reset in the EFI DHCPv4 Protocol driver and | |
the state of the EFI DHCPv4 Protocol driver will not be changed. If one instance | |
wants to make it possible for another instance to configure the EFI DHCPv4 Protocol | |
driver, it must call this function with Dhcp4CfgData set to NULL. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] Dhcp4CfgData Pointer to the EFI_DHCP4_CONFIG_DATA. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init or | |
Dhcp4InitReboot state, if the original state of this driver | |
was Dhcp4Stopped and the value of Dhcp4CfgData was | |
not NULL. Otherwise, the state was left unchanged. | |
@retval EFI_ACCESS_DENIED This instance of the EFI DHCPv4 Protocol driver was not in the | |
Dhcp4Stopped, Dhcp4Init, Dhcp4InitReboot, or Dhcp4Bound state; | |
Or another instance of this EFI DHCPv4 Protocol driver is already | |
in a valid configured state. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Configure ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL | |
) | |
{ | |
EFI_DHCP4_CONFIG_DATA *Config; | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
UINT32 Index; | |
IP4_ADDR Ip; | |
// | |
// First validate the parameters | |
// | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Dhcp4CfgData != NULL) { | |
if ((Dhcp4CfgData->DiscoverTryCount != 0) && (Dhcp4CfgData->DiscoverTimeout == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Dhcp4CfgData->RequestTryCount != 0) && (Dhcp4CfgData->RequestTimeout == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Dhcp4CfgData->OptionCount != 0) && (Dhcp4CfgData->OptionList == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR)); | |
if (IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) { | |
return EFI_INVALID_PARAMETER; | |
} | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
Config = &DhcpSb->ActiveConfig; | |
Status = EFI_ACCESS_DENIED; | |
if ((DhcpSb->DhcpState != Dhcp4Stopped) && | |
(DhcpSb->DhcpState != Dhcp4Init) && | |
(DhcpSb->DhcpState != Dhcp4InitReboot) && | |
(DhcpSb->DhcpState != Dhcp4Bound)) | |
{ | |
goto ON_EXIT; | |
} | |
if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) { | |
goto ON_EXIT; | |
} | |
if (Dhcp4CfgData != NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
DhcpCleanConfigure (Config); | |
if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) { | |
goto ON_EXIT; | |
} | |
DhcpSb->UserOptionLen = 0; | |
for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) { | |
DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2; | |
} | |
DhcpSb->ActiveChild = Instance; | |
if (DhcpSb->DhcpState == Dhcp4Stopped) { | |
DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress); | |
if (DhcpSb->ClientAddr != 0) { | |
DhcpSb->DhcpState = Dhcp4InitReboot; | |
} else { | |
DhcpSb->DhcpState = Dhcp4Init; | |
} | |
} | |
DhcpSb->ServiceState = DHCP_CONFIGED; | |
Status = EFI_SUCCESS; | |
} else if (DhcpSb->ActiveChild == Instance) { | |
Status = EFI_SUCCESS; | |
DhcpYieldControl (DhcpSb); | |
} | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Starts the DHCP configuration process. | |
The Start() function starts the DHCP configuration process. This function can | |
be called only when the EFI DHCPv4 Protocol driver is in the Dhcp4Init or | |
Dhcp4InitReboot state. | |
If the DHCP process completes successfully, the state of the EFI DHCPv4 Protocol | |
driver will be transferred through Dhcp4Selecting and Dhcp4Requesting to the | |
Dhcp4Bound state. The CompletionEvent will then be signaled if it is not NULL. | |
If the process aborts, either by the user or by some unexpected network error, | |
the state is restored to the Dhcp4Init state. The Start() function can be called | |
again to restart the process. | |
Refer to RFC 2131 for precise state transitions during this process. At the | |
time when each event occurs in this process, the callback function that was set | |
by EFI_DHCP4_PROTOCOL.Configure() will be called and the user can take this | |
opportunity to control the process. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] CompletionEvent If not NULL, indicates the event that will be signaled when the | |
EFI DHCPv4 Protocol driver is transferred into the | |
Dhcp4Bound state or when the DHCP process is aborted. | |
EFI_DHCP4_PROTOCOL.GetModeData() can be called to | |
check the completion status. If NULL, | |
EFI_DHCP4_PROTOCOL.Start() will wait until the driver | |
is transferred into the Dhcp4Bound state or the process fails. | |
@retval EFI_SUCCESS The DHCP configuration process has started, or it has completed | |
when CompletionEvent is NULL. | |
@retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped | |
state. EFI_DHCP4_PROTOCOL. Configure() needs to be called. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval EFI_TIMEOUT The DHCP configuration process failed because no response was | |
received from the server within the specified timeout value. | |
@retval EFI_ABORTED The user aborted the DHCP process. | |
@retval EFI_ALREADY_STARTED Some other EFI DHCPv4 Protocol instance already started the | |
DHCP process. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
@retval EFI_NO_MEDIA There was a media error. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Start ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_EVENT CompletionEvent OPTIONAL | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
EFI_STATUS MediaStatus; | |
// | |
// First validate the parameters | |
// | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
if (DhcpSb->DhcpState == Dhcp4Stopped) { | |
Status = EFI_NOT_STARTED; | |
goto ON_ERROR; | |
} | |
if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) { | |
Status = EFI_ALREADY_STARTED; | |
goto ON_ERROR; | |
} | |
// | |
// Check Media Status. | |
// | |
MediaStatus = EFI_SUCCESS; | |
NetLibDetectMediaWaitTimeout (DhcpSb->Controller, DHCP_CHECK_MEDIA_WAITING_TIME, &MediaStatus); | |
if (MediaStatus != EFI_SUCCESS) { | |
Status = EFI_NO_MEDIA; | |
goto ON_ERROR; | |
} | |
DhcpSb->IoStatus = EFI_ALREADY_STARTED; | |
if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) { | |
goto ON_ERROR; | |
} | |
Instance->CompletionEvent = CompletionEvent; | |
// | |
// Restore the TPL now, don't call poll function at TPL_CALLBACK. | |
// | |
gBS->RestoreTPL (OldTpl); | |
if (CompletionEvent == NULL) { | |
while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) { | |
DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4); | |
} | |
return DhcpSb->IoStatus; | |
} | |
return EFI_SUCCESS; | |
ON_ERROR: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Extends the lease time by sending a request packet. | |
The RenewRebind() function is used to manually extend the lease time when the | |
EFI DHCPv4 Protocol driver is in the Dhcp4Bound state and the lease time has | |
not expired yet. This function will send a request packet to the previously | |
found server (or to any server when RebindRequest is TRUE) and transfer the | |
state into the Dhcp4Renewing state (or Dhcp4Rebinding when RebindingRequest is | |
TRUE). When a response is received, the state is returned to Dhcp4Bound. | |
If no response is received before the try count is exceeded (the RequestTryCount | |
field that is specified in EFI_DHCP4_CONFIG_DATA) but before the lease time that | |
was issued by the previous server expires, the driver will return to the Dhcp4Bound | |
state and the previous configuration is restored. The outgoing and incoming packets | |
can be captured by the EFI_DHCP4_CALLBACK function. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] RebindRequest If TRUE, this function broadcasts the request packets and enters | |
the Dhcp4Rebinding state. Otherwise, it sends a unicast | |
request packet and enters the Dhcp4Renewing state. | |
@param[in] CompletionEvent If not NULL, this event is signaled when the renew/rebind phase | |
completes or some error occurs. | |
EFI_DHCP4_PROTOCOL.GetModeData() can be called to | |
check the completion status. If NULL, | |
EFI_DHCP4_PROTOCOL.RenewRebind() will busy-wait | |
until the DHCP process finishes. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the | |
Dhcp4Renewing state or is back to the Dhcp4Bound state. | |
@retval EFI_NOT_STARTED The EFI DHCPv4 Protocol driver is in the Dhcp4Stopped | |
state. EFI_DHCP4_PROTOCOL.Configure() needs to | |
be called. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_TIMEOUT There was no response from the server when the try count was | |
exceeded. | |
@retval EFI_ACCESS_DENIED The driver is not in the Dhcp4Bound state. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4RenewRebind ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN BOOLEAN RebindRequest, | |
IN EFI_EVENT CompletionEvent OPTIONAL | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
// | |
// First validate the parameters | |
// | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
if (DhcpSb->DhcpState == Dhcp4Stopped) { | |
Status = EFI_NOT_STARTED; | |
goto ON_EXIT; | |
} | |
if (DhcpSb->DhcpState != Dhcp4Bound) { | |
Status = EFI_ACCESS_DENIED; | |
goto ON_EXIT; | |
} | |
if (DHCP_IS_BOOTP (DhcpSb->Para)) { | |
Status = EFI_SUCCESS; | |
goto ON_EXIT; | |
} | |
// | |
// Transit the states then send a extra DHCP request | |
// | |
if (!RebindRequest) { | |
DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE); | |
} else { | |
DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE); | |
} | |
// | |
// Clear initial time to make sure that elapsed-time | |
// is set to 0 for first REQUEST in renewal process. | |
// | |
Instance->ElaspedTime = 0; | |
Status = DhcpSendMessage ( | |
DhcpSb, | |
DhcpSb->Selected, | |
DhcpSb->Para, | |
DHCP_MSG_REQUEST, | |
(UINT8 *)"Extra renew/rebind by the application" | |
); | |
if (EFI_ERROR (Status)) { | |
DhcpSetState (DhcpSb, Dhcp4Bound, FALSE); | |
goto ON_EXIT; | |
} | |
DhcpSb->ExtraRefresh = TRUE; | |
DhcpSb->IoStatus = EFI_ALREADY_STARTED; | |
Instance->RenewRebindEvent = CompletionEvent; | |
gBS->RestoreTPL (OldTpl); | |
if (CompletionEvent == NULL) { | |
while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) { | |
DhcpSb->UdpIo->Protocol.Udp4->Poll (DhcpSb->UdpIo->Protocol.Udp4); | |
} | |
return DhcpSb->IoStatus; | |
} | |
return EFI_SUCCESS; | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Releases the current address configuration. | |
The Release() function releases the current configured IP address by doing either | |
of the following: | |
* Sending a DHCPRELEASE packet when the EFI DHCPv4 Protocol driver is in the | |
Dhcp4Bound state | |
* Setting the previously assigned IP address that was provided with the | |
EFI_DHCP4_PROTOCOL.Configure() function to 0.0.0.0 when the driver is in | |
Dhcp4InitReboot state | |
After a successful call to this function, the EFI DHCPv4 Protocol driver returns | |
to the Dhcp4Init state and any subsequent incoming packets will be discarded silently. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Init phase. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
@retval EFI_ACCESS_DENIED The EFI DHCPv4 Protocol driver is not Dhcp4InitReboot state. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Release ( | |
IN EFI_DHCP4_PROTOCOL *This | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
// | |
// First validate the parameters | |
// | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = EFI_SUCCESS; | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) { | |
Status = EFI_ACCESS_DENIED; | |
goto ON_EXIT; | |
} | |
if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) { | |
Status = DhcpSendMessage ( | |
DhcpSb, | |
DhcpSb->Selected, | |
DhcpSb->Para, | |
DHCP_MSG_RELEASE, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
Status = EFI_DEVICE_ERROR; | |
goto ON_EXIT; | |
} | |
} | |
DhcpCleanLease (DhcpSb); | |
ON_EXIT: | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
/** | |
Stops the current address configuration. | |
The Stop() function is used to stop the DHCP configuration process. After this | |
function is called successfully, the EFI DHCPv4 Protocol driver is transferred | |
into the Dhcp4Stopped state. EFI_DHCP4_PROTOCOL.Configure() needs to be called | |
before DHCP configuration process can be started again. This function can be | |
called when the EFI DHCPv4 Protocol driver is in any state. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@retval EFI_SUCCESS The EFI DHCPv4 Protocol driver is now in the Dhcp4Stopped phase. | |
@retval EFI_INVALID_PARAMETER This is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Stop ( | |
IN EFI_DHCP4_PROTOCOL *This | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_TPL OldTpl; | |
// | |
// First validate the parameters | |
// | |
if (This == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
DhcpSb = Instance->Service; | |
DhcpCleanLease (DhcpSb); | |
DhcpSb->DhcpState = Dhcp4Stopped; | |
DhcpSb->ServiceState = DHCP_UNCONFIGED; | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
} | |
/** | |
Builds a DHCP packet, given the options to be appended or deleted or replaced. | |
The Build() function is used to assemble a new packet from the original packet | |
by replacing or deleting existing options or appending new options. This function | |
does not change any state of the EFI DHCPv4 Protocol driver and can be used at | |
any time. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] SeedPacket Initial packet to be used as a base for building new packet. | |
@param[in] DeleteCount Number of opcodes in the DeleteList. | |
@param[in] DeleteList List of opcodes to be deleted from the seed packet. | |
Ignored if DeleteCount is zero. | |
@param[in] AppendCount Number of entries in the OptionList. | |
@param[in] AppendList Pointer to a DHCP option list to be appended to SeedPacket. | |
If SeedPacket also contains options in this list, they are | |
replaced by new options (except pad option). Ignored if | |
AppendCount is zero. Type EFI_DHCP4_PACKET_OPTION | |
@param[out] NewPacket Pointer to storage for the pointer to the new allocated packet. | |
Use the EFI Boot Service FreePool() on the resulting pointer | |
when done with the packet. | |
@retval EFI_SUCCESS The new packet was built. | |
@retval EFI_OUT_OF_RESOURCES Storage for the new packet could not be allocated. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Build ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_PACKET *SeedPacket, | |
IN UINT32 DeleteCount, | |
IN UINT8 *DeleteList OPTIONAL, | |
IN UINT32 AppendCount, | |
IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL, | |
OUT EFI_DHCP4_PACKET **NewPacket | |
) | |
{ | |
// | |
// First validate the parameters | |
// | |
if ((This == NULL) || (NewPacket == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) || | |
EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
if (((DeleteCount == 0) && (AppendCount == 0)) || | |
((DeleteCount != 0) && (DeleteList == NULL)) || | |
((AppendCount != 0) && (AppendList == NULL))) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
return DhcpBuild ( | |
SeedPacket, | |
DeleteCount, | |
DeleteList, | |
AppendCount, | |
AppendList, | |
NewPacket | |
); | |
} | |
/** | |
Callback by UdpIoCreatePort() when creating UdpIo for this Dhcp4 instance. | |
@param[in] UdpIo The UdpIo being created. | |
@param[in] Context Dhcp4 instance. | |
@retval EFI_SUCCESS UdpIo is configured successfully. | |
@retval EFI_INVALID_PARAMETER Class E IP address is not supported or other parameters | |
are not valid. | |
@retval other Other error occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp4InstanceConfigUdpIo ( | |
IN UDP_IO *UdpIo, | |
IN VOID *Context | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
DHCP_SERVICE *DhcpSb; | |
EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token; | |
EFI_UDP4_CONFIG_DATA UdpConfigData; | |
IP4_ADDR ClientAddr; | |
IP4_ADDR Ip; | |
INTN Class; | |
IP4_ADDR SubnetMask; | |
Instance = (DHCP_PROTOCOL *)Context; | |
DhcpSb = Instance->Service; | |
Token = Instance->Token; | |
ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA)); | |
UdpConfigData.AcceptBroadcast = TRUE; | |
UdpConfigData.AllowDuplicatePort = TRUE; | |
UdpConfigData.TimeToLive = 64; | |
UdpConfigData.DoNotFragment = TRUE; | |
ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr); | |
Ip = HTONL (ClientAddr); | |
CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
if (DhcpSb->Netmask == 0) { | |
// | |
// The Dhcp4.TransmitReceive() API should be able to used at any time according to | |
// UEFI spec, while in classless addressing network, the netmask must be explicitly | |
// provided together with the station address. | |
// If the DHCP instance haven't be configured with a valid netmask, we could only | |
// compute it according to the classful addressing rule. | |
// | |
Class = NetGetIpClass (ClientAddr); | |
// | |
// Class E IP address is not supported here! | |
// | |
ASSERT (Class < IP4_ADDR_CLASSE); | |
if (Class >= IP4_ADDR_CLASSE) { | |
return EFI_INVALID_PARAMETER; | |
} | |
SubnetMask = gIp4AllMasks[Class << 3]; | |
} else { | |
SubnetMask = DhcpSb->Netmask; | |
} | |
Ip = HTONL (SubnetMask); | |
CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS)); | |
if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) { | |
UdpConfigData.StationPort = DHCP_CLIENT_PORT; | |
} else { | |
UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort; | |
} | |
return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData); | |
} | |
/** | |
Create UdpIo for this Dhcp4 instance. | |
@param Instance The Dhcp4 instance. | |
@retval EFI_SUCCESS UdpIo is created successfully. | |
@retval EFI_OUT_OF_RESOURCES Fails to create UdpIo because of limited | |
resources or configuration failure. | |
**/ | |
EFI_STATUS | |
Dhcp4InstanceCreateUdpIo ( | |
IN OUT DHCP_PROTOCOL *Instance | |
) | |
{ | |
DHCP_SERVICE *DhcpSb; | |
EFI_STATUS Status; | |
VOID *Udp4; | |
ASSERT (Instance->Token != NULL); | |
DhcpSb = Instance->Service; | |
Instance->UdpIo = UdpIoCreateIo ( | |
DhcpSb->Controller, | |
DhcpSb->Image, | |
Dhcp4InstanceConfigUdpIo, | |
UDP_IO_UDP4_VERSION, | |
Instance | |
); | |
if (Instance->UdpIo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} else { | |
Status = gBS->OpenProtocol ( | |
Instance->UdpIo->UdpHandle, | |
&gEfiUdp4ProtocolGuid, | |
(VOID **)&Udp4, | |
Instance->Service->Image, | |
Instance->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
if (EFI_ERROR (Status)) { | |
UdpIoFreeIo (Instance->UdpIo); | |
Instance->UdpIo = NULL; | |
} | |
return Status; | |
} | |
} | |
/** | |
Callback of Dhcp packet. Does nothing. | |
@param Arg The context. | |
**/ | |
VOID | |
EFIAPI | |
DhcpDummyExtFree ( | |
IN VOID *Arg | |
) | |
{ | |
} | |
/** | |
Callback of UdpIoRecvDatagram() that handles a Dhcp4 packet. | |
Only BOOTP responses will be handled that correspond to the Xid of the request | |
sent out. The packet will be queued to the response queue. | |
@param UdpPacket The Dhcp4 packet. | |
@param EndPoint Udp4 address pair. | |
@param IoStatus Status of the input. | |
@param Context Extra info for the input. | |
**/ | |
VOID | |
EFIAPI | |
PxeDhcpInput ( | |
NET_BUF *UdpPacket, | |
UDP_END_POINT *EndPoint, | |
EFI_STATUS IoStatus, | |
VOID *Context | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
EFI_DHCP4_HEADER *Head; | |
NET_BUF *Wrap; | |
EFI_DHCP4_PACKET *Packet; | |
EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token; | |
UINT32 Len; | |
EFI_STATUS Status; | |
Wrap = NULL; | |
Instance = (DHCP_PROTOCOL *)Context; | |
Token = Instance->Token; | |
// | |
// Don't restart receive if error occurs or DHCP is destroyed. | |
// | |
if (EFI_ERROR (IoStatus)) { | |
return; | |
} | |
ASSERT (UdpPacket != NULL); | |
// | |
// Validate the packet received | |
// | |
if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) { | |
goto RESTART; | |
} | |
// | |
// Copy the DHCP message to a continuous memory block, make the buffer size | |
// of the EFI_DHCP4_PACKET a multiple of 4-byte. | |
// | |
Len = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4); | |
Wrap = NetbufAlloc (Len); | |
if (Wrap == NULL) { | |
goto RESTART; | |
} | |
Packet = (EFI_DHCP4_PACKET *)NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL); | |
ASSERT (Packet != NULL); | |
Packet->Size = Len; | |
Head = &Packet->Dhcp4.Header; | |
Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *)Head); | |
if (Packet->Length != UdpPacket->TotalSize) { | |
goto RESTART; | |
} | |
// | |
// Is this packet the answer to our packet? | |
// | |
if ((Head->OpCode != BOOTP_REPLY) || | |
(Head->Xid != Token->Packet->Dhcp4.Header.Xid) || | |
(CompareMem (&Token->Packet->Dhcp4.Header.ClientHwAddr[0], Head->ClientHwAddr, Head->HwAddrLen) != 0)) | |
{ | |
goto RESTART; | |
} | |
// | |
// Validate the options and retrieve the interested options | |
// | |
if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) && | |
(Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) && | |
EFI_ERROR (DhcpValidateOptions (Packet, NULL))) | |
{ | |
goto RESTART; | |
} | |
// | |
// Keep this packet in the ResponseQueue. | |
// | |
NET_GET_REF (Wrap); | |
NetbufQueAppend (&Instance->ResponseQueue, Wrap); | |
RESTART: | |
NetbufFree (UdpPacket); | |
if (Wrap != NULL) { | |
NetbufFree (Wrap); | |
} | |
Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0); | |
if (EFI_ERROR (Status)) { | |
PxeDhcpDone (Instance); | |
} | |
} | |
/** | |
Complete a Dhcp4 transaction and signal the upper layer. | |
@param Instance Dhcp4 instance. | |
**/ | |
VOID | |
PxeDhcpDone ( | |
IN DHCP_PROTOCOL *Instance | |
) | |
{ | |
EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token; | |
Token = Instance->Token; | |
Token->ResponseCount = Instance->ResponseQueue.BufNum; | |
if (Token->ResponseCount != 0) { | |
Token->ResponseList = (EFI_DHCP4_PACKET *)AllocatePool (Instance->ResponseQueue.BufSize); | |
if (Token->ResponseList == NULL) { | |
Token->Status = EFI_OUT_OF_RESOURCES; | |
goto SIGNAL_USER; | |
} | |
// | |
// Copy the received DHCP responses. | |
// | |
NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *)Token->ResponseList); | |
Token->Status = EFI_SUCCESS; | |
} else { | |
Token->ResponseList = NULL; | |
Token->Status = EFI_TIMEOUT; | |
} | |
SIGNAL_USER: | |
// | |
// Clean up the resources dedicated for this transmit receive transaction. | |
// | |
NetbufQueFlush (&Instance->ResponseQueue); | |
UdpIoCleanIo (Instance->UdpIo); | |
gBS->CloseProtocol ( | |
Instance->UdpIo->UdpHandle, | |
&gEfiUdp4ProtocolGuid, | |
Instance->Service->Image, | |
Instance->Handle | |
); | |
UdpIoFreeIo (Instance->UdpIo); | |
Instance->UdpIo = NULL; | |
Instance->Token = NULL; | |
if (Token->CompletionEvent != NULL) { | |
gBS->SignalEvent (Token->CompletionEvent); | |
} | |
} | |
/** | |
Transmits a DHCP formatted packet and optionally waits for responses. | |
The TransmitReceive() function is used to transmit a DHCP packet and optionally | |
wait for the response from servers. This function does not change the state of | |
the EFI DHCPv4 Protocol driver and thus can be used at any time. | |
@param[in] This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param[in] Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure. | |
@retval EFI_SUCCESS The packet was successfully queued for transmission. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call | |
this function after collection process completes. | |
@retval EFI_NO_MAPPING The default station address is not available yet. | |
@retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated. | |
@retval Others Some other unexpected error occurred. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4TransmitReceive ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token | |
) | |
{ | |
DHCP_PROTOCOL *Instance; | |
EFI_TPL OldTpl; | |
EFI_STATUS Status; | |
NET_FRAGMENT Frag; | |
NET_BUF *Wrap; | |
UDP_END_POINT EndPoint; | |
IP4_ADDR Ip; | |
DHCP_SERVICE *DhcpSb; | |
EFI_IP_ADDRESS Gateway; | |
IP4_ADDR ClientAddr; | |
if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = DHCP_INSTANCE_FROM_THIS (This); | |
DhcpSb = Instance->Service; | |
if (Instance->Token != NULL) { | |
// | |
// The previous call to TransmitReceive is not finished. | |
// | |
return EFI_NOT_READY; | |
} | |
if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) || | |
(NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) || | |
(Token->TimeoutValue == 0) || | |
((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL)) || | |
EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL)) || | |
EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr) | |
) | |
{ | |
// | |
// The DHCP packet isn't well-formed, the Transaction ID is already used, | |
// the timeout value is zero, the ListenPoint is invalid, or the | |
// RemoteAddress is zero. | |
// | |
return EFI_INVALID_PARAMETER; | |
} | |
ClientAddr = EFI_NTOHL (Token->Packet->Dhcp4.Header.ClientAddr); | |
if (ClientAddr == 0) { | |
return EFI_NO_MAPPING; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
// | |
// Save the token and the timeout value. | |
// | |
Instance->Token = Token; | |
Instance->Timeout = Token->TimeoutValue; | |
// | |
// Create a UDP IO for this transmit receive transaction. | |
// | |
Status = Dhcp4InstanceCreateUdpIo (Instance); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Save the Client Address is sent out | |
// | |
CopyMem ( | |
&DhcpSb->ClientAddressSendOut[0], | |
&Token->Packet->Dhcp4.Header.ClientHwAddr[0], | |
Token->Packet->Dhcp4.Header.HwAddrLen | |
); | |
// | |
// Wrap the DHCP packet into a net buffer. | |
// | |
Frag.Bulk = (UINT8 *)&Token->Packet->Dhcp4; | |
Frag.Len = Token->Packet->Length; | |
Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL); | |
if (Wrap == NULL) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto ON_ERROR; | |
} | |
// | |
// Set the local address and local port to ZERO. | |
// | |
ZeroMem (&EndPoint, sizeof (UDP_END_POINT)); | |
// | |
// Set the destination address and destination port. | |
// | |
CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS)); | |
EndPoint.RemoteAddr.Addr[0] = NTOHL (Ip); | |
if (Token->RemotePort == 0) { | |
EndPoint.RemotePort = DHCP_SERVER_PORT; | |
} else { | |
EndPoint.RemotePort = Token->RemotePort; | |
} | |
// | |
// Get the gateway. | |
// | |
ZeroMem (&Gateway, sizeof (Gateway)); | |
if (!IP4_NET_EQUAL (ClientAddr, EndPoint.RemoteAddr.Addr[0], DhcpSb->Netmask)) { | |
CopyMem (&Gateway.v4, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS)); | |
Gateway.Addr[0] = NTOHL (Gateway.Addr[0]); | |
} | |
// | |
// Transmit the DHCP packet. | |
// | |
Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, &Gateway, DhcpOnPacketSent, NULL); | |
if (EFI_ERROR (Status)) { | |
NetbufFree (Wrap); | |
goto ON_ERROR; | |
} | |
// | |
// Start to receive the DHCP response. | |
// | |
Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
ON_ERROR: | |
if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) { | |
UdpIoCleanIo (Instance->UdpIo); | |
gBS->CloseProtocol ( | |
Instance->UdpIo->UdpHandle, | |
&gEfiUdp4ProtocolGuid, | |
Instance->Service->Image, | |
Instance->Handle | |
); | |
UdpIoFreeIo (Instance->UdpIo); | |
Instance->UdpIo = NULL; | |
Instance->Token = NULL; | |
} | |
gBS->RestoreTPL (OldTpl); | |
if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) { | |
// | |
// Keep polling until timeout if no error happens and the CompletionEvent | |
// is NULL. | |
// | |
while (TRUE) { | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
// | |
// Raise TPL to protect the UDPIO in instance, in case that DhcpOnTimerTick | |
// free it when timeout. | |
// | |
if (Instance->Timeout > 0) { | |
Instance->UdpIo->Protocol.Udp4->Poll (Instance->UdpIo->Protocol.Udp4); | |
gBS->RestoreTPL (OldTpl); | |
} else { | |
gBS->RestoreTPL (OldTpl); | |
break; | |
} | |
} | |
} | |
return Status; | |
} | |
/** | |
Callback function for DhcpIterateOptions. This callback sets the | |
EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point | |
the individual DHCP option in the packet. | |
@param[in] Tag The DHCP option type | |
@param[in] Len Length of the DHCP option data | |
@param[in] Data The DHCP option data | |
@param[in] Context The context, to pass several parameters in. | |
@retval EFI_SUCCESS It always returns EFI_SUCCESS | |
**/ | |
EFI_STATUS | |
Dhcp4ParseCheckOption ( | |
IN UINT8 Tag, | |
IN UINT8 Len, | |
IN UINT8 *Data, | |
IN VOID *Context | |
) | |
{ | |
DHCP_PARSE_CONTEXT *Parse; | |
Parse = (DHCP_PARSE_CONTEXT *)Context; | |
Parse->Index++; | |
if (Parse->Index <= Parse->OptionCount) { | |
// | |
// Use BASE_CR to get the memory position of EFI_DHCP4_PACKET_OPTION for | |
// the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only | |
// pass in the point to option data. | |
// | |
Parse->Option[Parse->Index - 1] = BASE_CR (Data, EFI_DHCP4_PACKET_OPTION, Data); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Parses the packed DHCP option data. | |
The Parse() function is used to retrieve the option list from a DHCP packet. | |
If *OptionCount isn't zero, and there is enough space for all the DHCP options | |
in the Packet, each element of PacketOptionList is set to point to somewhere in | |
the Packet->Dhcp4.Option where a new DHCP option begins. If RFC3396 is supported, | |
the caller should reassemble the parsed DHCP options to get the finial result. | |
If *OptionCount is zero or there isn't enough space for all of them, the number | |
of DHCP options in the Packet is returned in OptionCount. | |
@param This Pointer to the EFI_DHCP4_PROTOCOL instance. | |
@param Packet Pointer to packet to be parsed. | |
@param OptionCount On input, the number of entries in the PacketOptionList. | |
On output, the number of entries that were written into the | |
PacketOptionList. | |
@param PacketOptionList List of packet option entries to be filled in. End option or pad | |
options are not included. | |
@retval EFI_SUCCESS The packet was successfully parsed. | |
@retval EFI_INVALID_PARAMETER Some parameter is NULL. | |
@retval EFI_BUFFER_TOO_SMALL One or more of the following conditions is TRUE: | |
1) *OptionCount is smaller than the number of options that | |
were found in the Packet. | |
2) PacketOptionList is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiDhcp4Parse ( | |
IN EFI_DHCP4_PROTOCOL *This, | |
IN EFI_DHCP4_PACKET *Packet, | |
IN OUT UINT32 *OptionCount, | |
OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL | |
) | |
{ | |
DHCP_PARSE_CONTEXT Context; | |
EFI_STATUS Status; | |
// | |
// First validate the parameters | |
// | |
if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) || | |
(Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) || | |
EFI_ERROR (DhcpValidateOptions (Packet, NULL))) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((*OptionCount != 0) && (PacketOptionList == NULL)) { | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *)); | |
Context.Option = PacketOptionList; | |
Context.OptionCount = *OptionCount; | |
Context.Index = 0; | |
Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
*OptionCount = Context.Index; | |
if (Context.Index > Context.OptionCount) { | |
return EFI_BUFFER_TOO_SMALL; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Set the elapsed time based on the given instance and the pointer to the | |
elapsed time option. | |
@param[in] Elapsed The pointer to the position to append. | |
@param[in] Instance The pointer to the Dhcp4 instance. | |
**/ | |
VOID | |
SetElapsedTime ( | |
IN UINT16 *Elapsed, | |
IN DHCP_PROTOCOL *Instance | |
) | |
{ | |
WriteUnaligned16 (Elapsed, HTONS (Instance->ElaspedTime)); | |
} |