git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1087 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1087 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)...
 | |
|   //
 | |
| #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 */
 |