blob: 50c991372df3eb108ff14ea9683534b76a27b356 [file] [log] [blame]
/*++
Copyright (c) 2006, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
support.c
Abstract:
Miscellaneous support routines for PxeDhcp4 protocol.
--*/
#include "PxeDhcp4.h"
#define DebugPrint(x)
//
// #define DebugPrint(x) Aprint x
//
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
UINT16
htons (
UINTN n
)
{
return (UINT16) ((n >> 8) | (n << 8));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
UINT32
htonl (
UINTN n
)
{
return (UINT32) ((n >> 24) | ((n >> 8) & 0xFF00) | ((n & 0xFF00) << 8) | (n << 24));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
EFIAPI
timeout_notify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
ASSERT (Context);
if (Context != NULL) {
((PXE_DHCP4_PRIVATE_DATA *) Context)->TimeoutOccurred = TRUE;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
EFIAPI
periodic_notify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
ASSERT (Context);
if (Context != NULL) {
((PXE_DHCP4_PRIVATE_DATA *) Context)->PeriodicOccurred = TRUE;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
find_opt (
IN DHCP4_PACKET *Packet,
IN UINT8 OpCode,
IN UINTN Skip,
OUT DHCP4_OP **OpPtr
)
/*++
Routine description:
Locate option inside DHCP packet.
Parameters:
Packet := Pointer to DHCP packet structure.
OpCode := Option op-code to find.
Skip := Number of found op-codes to skip.
OpPtr := Pointer to found op-code pointer.
Returns:
EFI_SUCCESS := Option was found
EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD
EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0
EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid
EFI_NOT_FOUND := op-code was not found in packet
EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option
does not have a valid value.
--*/
{
UINTN msg_size;
UINTN buf_len;
UINTN n;
UINT8 *buf;
UINT8 *end_ptr;
UINT8 overload;
//
// Verify parameters.
//
if (Packet == NULL || OpPtr == NULL || OpCode == DHCP4_PAD || (OpCode == DHCP4_END && Skip != 0)) {
return EFI_INVALID_PARAMETER;
}
if (Packet->dhcp4.magik != htonl (DHCP4_MAGIK_NUMBER)) {
return EFI_INVALID_PARAMETER;
}
//
// Initialize search variables.
//
*OpPtr = NULL;
msg_size = DHCP4_MAX_PACKET_SIZE - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
overload = 0;
end_ptr = NULL;
buf = Packet->dhcp4.options;
buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);
//
// Start searching for requested option.
//
for (n = 0;;) {
//
// If match is found, decrement skip count and return
// when desired match is found.
//
if (buf[n] == OpCode) {
*OpPtr = (DHCP4_OP *) &buf[n];
if (Skip-- == 0) {
return EFI_SUCCESS;
}
}
//
// Skip past current option. Check for option overload
// and message size options since these will affect the
// amount of data to be searched.
//
switch (buf[n]) {
case DHCP4_PAD:
//
// Remember the first pad byte of a group. This
// could be the end of a badly formed packet.
//
if (end_ptr == NULL) {
end_ptr = &buf[n];
}
++n;
break;
case DHCP4_END:
//
// If we reach the end we are done.
//
end_ptr = NULL;
return EFI_NOT_FOUND;
case DHCP4_OPTION_OVERLOAD:
//
// Remember the option overload value since it
// could cause the search to continue into
// the fname and sname fields.
//
end_ptr = NULL;
if (buf[n + 1] == 1) {
overload = buf[n + 2];
}
n += 2 + buf[n + 1];
break;
case DHCP4_MAX_MESSAGE_SIZE:
//
// Remember the message size value since it could
// change the amount of option buffer to search.
//
end_ptr = NULL;
if (buf[n + 1] == 2 && buf == Packet->dhcp4.options) {
msg_size = ((buf[n + 2] << 8) | buf[n + 3]) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
if (msg_size < 328) {
return EFI_INVALID_PARAMETER;
}
buf_len = msg_size - (Packet->dhcp4.options - Packet->raw);
if (n + 2 + buf[n + 1] > buf_len) {
return EFI_INVALID_PARAMETER;
}
}
/* fall thru */
default:
end_ptr = NULL;
n += 2 + buf[n + 1];
}
//
// Keep searching until the end of the buffer is reached.
//
if (n < buf_len) {
continue;
}
//
// Reached end of current buffer. Check if we are supposed
// to search the fname and sname buffers.
//
if (buf == Packet->dhcp4.options &&
(overload == DHCP4_OVERLOAD_FNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)
) {
buf = Packet->dhcp4.fname;
buf_len = 128;
n = 0;
continue;
}
if (buf != Packet->dhcp4.sname && (overload == DHCP4_OVERLOAD_SNAME || overload == DHCP4_OVERLOAD_FNAME_AND_SNAME)) {
buf = Packet->dhcp4.sname;
buf_len = 64;
n = 0;
continue;
}
//
// End of last buffer reached. If this was a search
// for the end of the options, go back to the start
// of the current pad block.
//
if (OpCode == DHCP4_END && end_ptr != NULL) {
*OpPtr = (DHCP4_OP *) end_ptr;
return EFI_SUCCESS;
}
return EFI_NOT_FOUND;
}
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
add_opt (
IN DHCP4_PACKET *Packet,
IN DHCP4_OP *OpPtr
)
/*++
Routine description:
Add option to DHCP packet.
Parameters:
Packet := Pointer to DHCP packet structure.
OpPtr := Pointer to DHCP option.
Returns:
EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END
EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid
EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and
is not valid
EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and
is not valid
EFI_DEVICE_ERROR := Cannot determine end of packet
EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option
EFI_SUCCESS := Option added to DHCP packet
--*/
{
EFI_STATUS efi_status;
DHCP4_OP *msg_size_op;
DHCP4_OP *overload_op;
DHCP4_OP *op;
UINTN msg_size;
UINTN buf_len;
UINT32 magik;
UINT8 *buf;
//
// Verify parameters.
//
ASSERT (Packet);
ASSERT (OpPtr);
if (Packet == NULL || OpPtr == NULL) {
return EFI_INVALID_PARAMETER;
}
switch (OpPtr->op) {
case DHCP4_PAD:
case DHCP4_END:
//
// No adding PAD or END.
//
return EFI_INVALID_PARAMETER;
}
//
// Check the DHCP magik number.
//
CopyMem (&magik, &Packet->dhcp4.magik, 4);
if (magik != htonl (DHCP4_MAGIK_NUMBER)) {
return EFI_INVALID_PARAMETER;
}
//
// Find the DHCP message size option.
//
msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;
efi_status = find_opt (
Packet,
DHCP4_MAX_MESSAGE_SIZE,
0,
&msg_size_op
);
if (EFI_ERROR (efi_status)) {
if (efi_status != EFI_NOT_FOUND) {
DebugPrint (
("%s:%d:%r\n",
__FILE__,
__LINE__,
efi_status)
);
return efi_status;
}
msg_size_op = NULL;
} else {
CopyMem (&msg_size, msg_size_op->data, 2);
msg_size = htons (msg_size);
if (msg_size < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {
return EFI_INVALID_PARAMETER;
}
}
//
// Find the DHCP option overload option.
//
efi_status = find_opt (
Packet,
DHCP4_OPTION_OVERLOAD,
0,
&overload_op
);
if (EFI_ERROR (efi_status)) {
if (efi_status != EFI_NOT_FOUND) {
DebugPrint (
("%s:%d:%r\n",
__FILE__,
__LINE__,
efi_status)
);
return efi_status;
}
overload_op = NULL;
} else {
if (overload_op->len != 1) {
return EFI_INVALID_PARAMETER;
}
switch (overload_op->data[0]) {
case 1:
case 2:
case 3:
break;
default:
return EFI_INVALID_PARAMETER;
}
}
//
// Find the end of the packet.
//
efi_status = find_opt (Packet, DHCP4_END, 0, &op);
if (EFI_ERROR (efi_status)) {
return EFI_INVALID_PARAMETER;
}
//
// Find which buffer the end is in.
//
if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.options)) {
buf_len = (msg_size - ((UINT8 *) &Packet->dhcp4.options - (UINT8 *) &Packet->raw)) - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE);
} else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.fname)) {
buf_len = 128;
} else if ((UINTN) op >= (UINTN) (buf = Packet->dhcp4.sname)) {
buf_len = 64;
} else {
return EFI_DEVICE_ERROR;
}
//
// Add option to current buffer if there is no overlow.
//
if ((UINTN) ((&op->op - buf) + 3 + op->len) < buf_len) {
CopyMem (op, OpPtr, OpPtr->len + 2);
op->data[op->len] = DHCP4_END;
return EFI_SUCCESS;
}
//
// Error if there is no space for option.
//
return EFI_BUFFER_TOO_SMALL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
start_udp (
IN PXE_DHCP4_PRIVATE_DATA *Private,
IN OPTIONAL EFI_IP_ADDRESS *StationIp,
IN OPTIONAL EFI_IP_ADDRESS *SubnetMask
)
/*++
Routine description:
Setup PXE BaseCode UDP stack.
Parameters:
Private := Pointer to PxeDhcp4 private data.
StationIp := Pointer to IP address or NULL if not known.
SubnetMask := Pointer to subnet mask or NULL if not known.
Returns:
EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL
EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given
EFI_SUCCESS := UDP stack is ready
other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp()
--*/
{
EFI_PXE_BASE_CODE_IP_FILTER bcast_filter;
EFI_STATUS efi_status;
//
//
//
ASSERT (Private);
ASSERT (Private->PxeBc);
if (Private == NULL) {
return EFI_INVALID_PARAMETER;
}
if (Private->PxeBc == NULL) {
return EFI_INVALID_PARAMETER;
}
if (StationIp != NULL && SubnetMask == NULL) {
return EFI_INVALID_PARAMETER;
}
if (StationIp == NULL && SubnetMask != NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Setup broadcast receive filter...
//
ZeroMem (&bcast_filter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
bcast_filter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST;
bcast_filter.IpCnt = 0;
efi_status = Private->PxeBc->SetIpFilter (
Private->PxeBc,
&bcast_filter
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
return efi_status;
}
//
// Configure station IP address and subnet mask...
//
efi_status = Private->PxeBc->SetStationIp (
Private->PxeBc,
StationIp,
SubnetMask
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
}
return efi_status;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
stop_udp (
IN PXE_DHCP4_PRIVATE_DATA *Private
)
{
//
//
//
ASSERT (Private);
ASSERT (Private->PxeBc);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
start_receive_events (
IN PXE_DHCP4_PRIVATE_DATA *Private,
IN UINTN SecondsTimeout
)
/*++
Routine description:
Create periodic and timeout receive events.
Parameters:
Private := Pointer to PxeDhcp4 private data.
SecondsTimeout := Number of seconds to wait before timeout.
Returns:
--*/
{
EFI_STATUS efi_status;
UINTN random;
//
//
//
ASSERT (Private);
ASSERT (SecondsTimeout);
if (Private == NULL || SecondsTimeout == 0) {
return EFI_INVALID_PARAMETER;
}
//
// Need a bettern randomizer...
// For now adjust the timeout value by the least significant
// digit in the MAC address.
//
random = 0;
if (Private->PxeDhcp4.Data != NULL) {
if (Private->PxeDhcp4.Data->Discover.dhcp4.hlen != 0 && Private->PxeDhcp4.Data->Discover.dhcp4.hlen <= 16) {
random = 0xFFF & Private->PxeDhcp4.Data->Discover.dhcp4.chaddr[Private->PxeDhcp4.Data->Discover.dhcp4.hlen - 1];
}
}
//
// Setup timeout event and start timer.
//
efi_status = gBS->CreateEvent (
EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
EFI_TPL_NOTIFY,
&timeout_notify,
Private,
&Private->TimeoutEvent
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
return efi_status;
}
efi_status = gBS->SetTimer (
Private->TimeoutEvent,
TimerRelative,
SecondsTimeout * 10000000 + random
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
gBS->CloseEvent (Private->TimeoutEvent);
return efi_status;
}
Private->TimeoutOccurred = FALSE;
//
// Setup periodic event for callbacks
//
efi_status = gBS->CreateEvent (
EFI_EVENT_TIMER | EFI_EVENT_NOTIFY_SIGNAL,
EFI_TPL_NOTIFY,
&periodic_notify,
Private,
&Private->PeriodicEvent
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
gBS->CloseEvent (Private->TimeoutEvent);
return efi_status;
}
efi_status = gBS->SetTimer (
Private->PeriodicEvent,
TimerPeriodic,
1000000
); /* 1/10th second */
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
gBS->CloseEvent (Private->TimeoutEvent);
gBS->CloseEvent (Private->PeriodicEvent);
return efi_status;
}
Private->PeriodicOccurred = FALSE;
return EFI_SUCCESS;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
VOID
stop_receive_events (
IN PXE_DHCP4_PRIVATE_DATA *Private
)
{
//
//
//
ASSERT (Private);
if (Private == NULL) {
return ;
}
//
//
//
gBS->CloseEvent (Private->TimeoutEvent);
Private->TimeoutOccurred = FALSE;
//
//
//
gBS->CloseEvent (Private->PeriodicEvent);
Private->PeriodicOccurred = FALSE;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
tx_udp (
IN PXE_DHCP4_PRIVATE_DATA *Private,
IN EFI_IP_ADDRESS *dest_ip,
IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
IN EFI_IP_ADDRESS *src_ip,
IN VOID *buffer,
IN UINTN BufferSize
)
/*++
Routine description:
Transmit DHCP packet.
Parameters:
Private := Pointer to PxeDhcp4 private data
dest_ip := Pointer to destination IP address
gateway_ip := Pointer to gateway IP address or NULL
src_ip := Pointer to source IP address or NULL
buffer := Pointer to buffer to transmit
BufferSize := Size of buffer in bytes
Returns:
EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL ||
buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL
EFI_SUCCESS := Buffer was transmitted
other := Return from PxeBc->UdpWrite()
--*/
{
EFI_PXE_BASE_CODE_UDP_PORT dest_port;
EFI_PXE_BASE_CODE_UDP_PORT src_port;
EFI_IP_ADDRESS zero_ip;
//
//
//
ASSERT (Private);
ASSERT (dest_ip);
ASSERT (buffer);
ASSERT (BufferSize >= 300);
if (Private == NULL || dest_ip == NULL || buffer == NULL || BufferSize < 300) {
return EFI_INVALID_PARAMETER;
}
ASSERT (Private->PxeBc);
if (Private->PxeBc == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Transmit DHCP discover packet...
//
ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
if (src_ip == NULL) {
src_ip = &zero_ip;
}
dest_port = DHCP4_SERVER_PORT;
src_port = DHCP4_CLIENT_PORT;
return Private->PxeBc->UdpWrite (
Private->PxeBc,
EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT,
dest_ip,
&dest_port,
gateway_ip,
src_ip,
&src_port,
NULL,
NULL,
&BufferSize,
buffer
);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
rx_udp (
IN PXE_DHCP4_PRIVATE_DATA *Private,
OUT VOID *buffer,
IN OUT UINTN *BufferSize,
IN OUT EFI_IP_ADDRESS *dest_ip,
IN OUT EFI_IP_ADDRESS *src_ip,
IN UINT16 op_flags
)
/*++
Routine description:
Receive DHCP packet.
Parameters:
Private := Pointer to PxeDhcp4 private data
buffer := Pointer to buffer to receive DHCP packet
BufferSize := Pointer to buffer size in bytes
dest_ip := Pointer to destination IP address
src_ip := Pointer to source IP address
op_flags := UDP receive operation flags
Returns:
EFI_INVALID_PARAMETER :=
EFI_SUCCESS := Packet received
other := Return from PxeBc->UdpRead()
--*/
{
EFI_PXE_BASE_CODE_UDP_PORT dest_port;
EFI_PXE_BASE_CODE_UDP_PORT src_port;
//
//
//
ASSERT (Private);
ASSERT (buffer);
ASSERT (dest_ip);
ASSERT (src_ip);
if (Private == NULL || buffer == NULL || dest_ip == NULL || src_ip == NULL || BufferSize == NULL) {
return EFI_INVALID_PARAMETER;
}
ASSERT (Private->PxeBc);
if (Private->PxeBc == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Check for packet
//
*BufferSize = sizeof (DHCP4_PACKET);
dest_port = DHCP4_CLIENT_PORT;
src_port = DHCP4_SERVER_PORT;
return Private->PxeBc->UdpRead (
Private->PxeBc,
op_flags,
dest_ip,
&dest_port,
src_ip,
&src_port,
NULL,
NULL,
BufferSize,
buffer
);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
EFI_STATUS
tx_rx_udp (
IN PXE_DHCP4_PRIVATE_DATA *Private,
IN OUT EFI_IP_ADDRESS *ServerIp,
IN OPTIONAL EFI_IP_ADDRESS *gateway_ip,
IN OPTIONAL EFI_IP_ADDRESS *client_ip,
IN OPTIONAL EFI_IP_ADDRESS *SubnetMask,
IN DHCP4_PACKET *tx_pkt,
OUT DHCP4_PACKET *rx_pkt,
IN INTN (*rx_vfy)(
IN PXE_DHCP4_PRIVATE_DATA *Private,
IN DHCP4_PACKET *tx_pkt,
IN DHCP4_PACKET *rx_pkt,
IN UINTN rx_pkt_size
),
IN UINTN SecondsTimeout
)
/*++
Routine description:
Transmit DHCP packet and wait for replies.
Parameters:
Private := Pointer to PxeDhcp4 private data
ServerIp := Pointer to server IP address
gateway_ip := Pointer to gateway IP address or NULL
client_ip := Pointer to client IP address or NULL
SubnetMask := Pointer to subnet mask or NULL
tx_pkt := Pointer to DHCP packet to transmit
rx_pkt := Pointer to DHCP packet receive buffer
rx_vfy := Pointer to DHCP packet receive verification routine
SecondsTimeout := Number of seconds until timeout
Returns:
EFI_INVALID_PARAMETER := Private == NULL || ServerIp == NULL ||
tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL || Private->PxeBc == NULL
EFI_ABORTED := Receive aborted
EFI_TIMEOUT := No packets received
EFI_SUCCESS := Packet(s) received
other := Returns from other PxeDhcp4 support routines
--*/
{
EFI_PXE_DHCP4_CALLBACK_STATUS CallbackStatus;
EFI_IP_ADDRESS dest_ip;
EFI_IP_ADDRESS src_ip;
EFI_STATUS efi_status;
DHCP4_OP *msg_size_op;
UINTN pkt_size;
UINTN n;
UINT16 msg_size;
UINT16 op_flags;
BOOLEAN done_flag;
BOOLEAN got_packet;
//
// Bad programmer check...
//
ASSERT (Private);
ASSERT (ServerIp);
ASSERT (tx_pkt);
ASSERT (rx_pkt);
ASSERT (rx_vfy);
if (Private == NULL || ServerIp == NULL || tx_pkt == NULL || rx_pkt == NULL || rx_vfy == NULL) {
return EFI_INVALID_PARAMETER;
}
ASSERT (Private->PxeBc);
if (Private->PxeBc == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Enable UDP...
//
efi_status = start_udp (Private, client_ip, SubnetMask);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
return efi_status;
}
//
// Get length of transmit packet...
//
msg_size = DHCP4_DEFAULT_MAX_MESSAGE_SIZE;
efi_status = find_opt (
tx_pkt,
DHCP4_MAX_MESSAGE_SIZE,
0,
&msg_size_op
);
if (!EFI_ERROR (efi_status)) {
CopyMem (&msg_size, msg_size_op->data, 2);
if ((msg_size = htons (msg_size)) < 328) {
msg_size = 328;
}
}
//
// Transmit packet...
//
efi_status = tx_udp (
Private,
ServerIp,
gateway_ip,
client_ip,
tx_pkt,
msg_size - (DHCP4_UDP_HEADER_SIZE + DHCP4_IP_HEADER_SIZE)
);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
stop_udp (Private);
return efi_status;
}
//
// Enable periodic and timeout events...
//
efi_status = start_receive_events (Private, SecondsTimeout);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
stop_udp (Private);
return efi_status;
}
//
// Wait for packet(s)...
//
#if 0
if (!client_ip) {
Aprint ("client_ip == NULL ");
} else {
Aprint (
"client_ip == %d.%d.%d.%d ",
client_ip->v4.Addr[0],
client_ip->v4.Addr[1],
client_ip->v4.Addr[2],
client_ip->v4.Addr[3]
);
}
if (!ServerIp) {
Aprint ("ServerIp == NULL\n");
} else {
Aprint (
"ServerIp == %d.%d.%d.%d\n",
ServerIp->v4.Addr[0],
ServerIp->v4.Addr[1],
ServerIp->v4.Addr[2],
ServerIp->v4.Addr[3]
);
}
#endif
done_flag = FALSE;
got_packet = FALSE;
while (!done_flag) {
//
// Check for timeout event...
//
if (Private->TimeoutOccurred) {
efi_status = EFI_SUCCESS;
break;
}
//
// Check for periodic event...
//
if (Private->PeriodicOccurred && Private->callback != NULL) {
CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE;
if (Private->callback->Callback != NULL) {
CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, 0, NULL);
}
switch (CallbackStatus) {
case EFI_PXE_DHCP4_CALLBACK_STATUS_CONTINUE:
break;
case EFI_PXE_DHCP4_CALLBACK_STATUS_ABORT:
default:
stop_receive_events (Private);
stop_udp (Private);
return EFI_ABORTED;
}
Private->PeriodicOccurred = FALSE;
}
//
// Check for packet...
//
if (client_ip == NULL) {
SetMem (&dest_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
} else {
CopyMem (&dest_ip, client_ip, sizeof (EFI_IP_ADDRESS));
}
SetMem (&src_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
if (CompareMem (&src_ip, &ServerIp, sizeof (EFI_IP_ADDRESS))) {
ZeroMem (&src_ip, sizeof (EFI_IP_ADDRESS));
op_flags = EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP;
} else {
op_flags = 0;
}
efi_status = rx_udp (
Private,
rx_pkt,
&pkt_size,
&dest_ip,
&src_ip,
op_flags
);
if (efi_status == EFI_TIMEOUT) {
efi_status = EFI_SUCCESS;
continue;
}
if (EFI_ERROR (efi_status)) {
break;
}
//
// Some basic packet sanity checks..
//
if (pkt_size < 300) {
continue;
}
if (rx_pkt->dhcp4.op != BOOTP_REPLY) {
continue;
}
if (tx_pkt->dhcp4.htype != rx_pkt->dhcp4.htype) {
continue;
}
if ((n = tx_pkt->dhcp4.hlen) != rx_pkt->dhcp4.hlen) {
continue;
}
if (CompareMem (&tx_pkt->dhcp4.xid, &rx_pkt->dhcp4.xid, 4)) {
continue;
}
if (n != 0) {
if (n >= 16) {
n = 16;
}
if (CompareMem (tx_pkt->dhcp4.chaddr, rx_pkt->dhcp4.chaddr, n)) {
continue;
}
}
//
// Internal callback packet verification...
//
switch ((*rx_vfy) (Private, tx_pkt, rx_pkt, pkt_size)) {
case -2: /* ignore and stop */
stop_receive_events (Private);
stop_udp (Private);
return EFI_ABORTED;
case -1: /* ignore and wait */
continue;
case 0: /* accept and wait */
break;
case 1: /* accept and stop */
done_flag = TRUE;
break;
default:
ASSERT (0);
}
//
// External callback packet verification...
//
CallbackStatus = EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE;
if (Private->callback != NULL) {
if (Private->callback->Callback != NULL) {
CallbackStatus = (Private->callback->Callback) (&Private->PxeDhcp4, Private->function, (UINT32) pkt_size, rx_pkt);
}
}
switch (CallbackStatus) {
case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_CONTINUE:
continue;
case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_ABORT:
done_flag = TRUE;
break;
case EFI_PXE_DHCP4_CALLBACK_STATUS_IGNORE_ABORT:
stop_receive_events (Private);
stop_udp (Private);
return EFI_ABORTED;
case EFI_PXE_DHCP4_CALLBACK_STATUS_KEEP_CONTINUE:
default:
break;
}
//
// We did! We did get a packet!
//
got_packet = TRUE;
}
//
//
//
stop_receive_events (Private);
stop_udp (Private);
if (EFI_ERROR (efi_status)) {
DebugPrint (("%s:%d:%r\n", __FILE__, __LINE__, efi_status));
return efi_status;
}
if (got_packet) {
return EFI_SUCCESS;
} else {
return EFI_TIMEOUT;
}
}
/* eof - support.c */