/*++ | |
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 */ |