| /** @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)... | |
| // | |
| #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 */ |