mirror of https://github.com/acidanthera/audk.git
1062 lines
26 KiB
C
1062 lines
26 KiB
C
/** @file
|
|
|
|
Copyright (c) 2004 - 2005, 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;
|
|
}
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/**
|
|
|
|
@return EFI_SUCCESS := Option was found
|
|
@return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
|
|
@return EFI_INVALID_PARAMETER := OpCode == DHCP4_PAD
|
|
@return EFI_INVALID_PARAMETER := OpCode == DHCP4_END && Skip != 0
|
|
@return EFI_INVALID_PARAMETER := DHCP magik number in Packet is not valid
|
|
@return EFI_NOT_FOUND := op-code was not found in packet
|
|
@return EFI_INVALID_PARAMETER := If present, DHCP_MAX_MESSAGE_SIZE option
|
|
@return does not have a valid value.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
find_opt (
|
|
IN DHCP4_PACKET *Packet,
|
|
IN UINT8 OpCode,
|
|
IN UINTN Skip,
|
|
OUT DHCP4_OP **OpPtr
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/**
|
|
|
|
@return EFI_INVALID_PARAMETER := Packet == NULL || OpPtr == NULL
|
|
@return EFI_INVALID_PARAMETER := OpPtr->op == DHCP4_PAD || OpPtr->op == DHCP4_END
|
|
@return EFI_INVALID_PARAMETER := DHCP magik number in DHCP packet is not valid
|
|
@return EFI_INVALID_PARAMETER := If DHCP_MAX_MESSAGE_SIZE option is present and
|
|
@return is not valid
|
|
@return EFI_INVALID_PARAMETER := If DHCP_OPTION_OVERLOAD option is present and
|
|
@return is not valid
|
|
@return EFI_DEVICE_ERROR := Cannot determine end of packet
|
|
@return EFI_BUFFER_TOO_SMALL := Not enough room in packet to add option
|
|
@return EFI_SUCCESS := Option added to DHCP packet
|
|
|
|
**/
|
|
EFI_STATUS
|
|
add_opt (
|
|
IN DHCP4_PACKET *Packet,
|
|
IN DHCP4_OP *OpPtr
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/**
|
|
|
|
@return EFI_INVALID_PARAMETER := Private == NULL || Private->PxeBc == NULL
|
|
@return EFI_INVALID_PARAMETER := Only one of StationIp and SubnetMask is given
|
|
@return EFI_SUCCESS := UDP stack is ready
|
|
@return other := Error from PxeBc->SetIpFilter() or PxeBc->SetStationIp()
|
|
|
|
**/
|
|
EFI_STATUS
|
|
start_udp (
|
|
IN PXE_DHCP4_PRIVATE_DATA *Private,
|
|
IN OPTIONAL EFI_IP_ADDRESS *StationIp,
|
|
IN OPTIONAL EFI_IP_ADDRESS *SubnetMask
|
|
)
|
|
{
|
|
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
|
|
)
|
|
{
|
|
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 (
|
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
|
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 (
|
|
EVT_TIMER | EVT_NOTIFY_SIGNAL,
|
|
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;
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/**
|
|
|
|
@return EFI_INVALID_PARAMETER := Private == NULL || dest_ip == NULL ||
|
|
@return buffer == NULL || BufferSize < 300 || Private->PxeBc == NULL
|
|
@return EFI_SUCCESS := Buffer was transmitted
|
|
@return other := Return from PxeBc->UdpWrite()
|
|
|
|
**/
|
|
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
|
|
)
|
|
{
|
|
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
|
|
);
|
|
}
|
|
|
|
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
|
|
|
|
/**
|
|
|
|
@return EFI_INVALID_PARAMETER :=
|
|
@return EFI_SUCCESS := Packet received
|
|
@return other := Return from PxeBc->UdpRead()
|
|
|
|
**/
|
|
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
|
|
)
|
|
{
|
|
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)...
|
|
//
|
|
|
|
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 */
|