git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@3492 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			785 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			785 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| 
 | |
| Copyright (c) 2004, 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:
 | |
|   PxeDhcp4InitSelect.c
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
| 
 | |
| **/
 | |
| 
 | |
| 
 | |
| #include "PxeDhcp4.h"
 | |
| 
 | |
| #define DebugPrint(x)
 | |
| //
 | |
| // #define DebugPrint(x) Aprint x
 | |
| //
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| /**
 | |
| 
 | |
| **/
 | |
| STATIC
 | |
| INTN
 | |
| offer_verify (
 | |
|   IN PXE_DHCP4_PRIVATE_DATA *Private,
 | |
|   IN DHCP4_PACKET           *tx_pkt,
 | |
|   IN DHCP4_PACKET           *rx_pkt,
 | |
|   IN UINTN                  rx_pkt_size
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS    EfiStatus;
 | |
|   DHCP4_PACKET  *tmp;
 | |
|   DHCP4_OP      *msg_type_op;
 | |
|   DHCP4_OP      *srvid_op;
 | |
|   UINT32        magik;
 | |
| 
 | |
|   //
 | |
|   // Verify parameters.  Touch unused parameters to keep
 | |
|   // compiler happy.
 | |
|   //
 | |
|   ASSERT (Private);
 | |
|   ASSERT (rx_pkt);
 | |
| 
 | |
|   if (Private == NULL || rx_pkt == NULL) {
 | |
|     return -2;
 | |
|   }
 | |
| 
 | |
|   tx_pkt      = tx_pkt;
 | |
|   rx_pkt_size = rx_pkt_size;
 | |
| 
 | |
|   //
 | |
|   // This may be a BOOTP Reply or DHCP Offer packet.
 | |
|   // If there is no DHCP magik number, assume that
 | |
|   // this is a BOOTP Reply packet.
 | |
|   //
 | |
|   magik = htonl (DHCP4_MAGIK_NUMBER);
 | |
| 
 | |
|   while (!CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
 | |
|     //
 | |
|     // If there is no DHCP message type option, assume
 | |
|     // this is a BOOTP reply packet and cache it.
 | |
|     //
 | |
|     EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
 | |
| 
 | |
|     if (EFI_ERROR (EfiStatus)) {
 | |
|       break;
 | |
|     }
 | |
|     //
 | |
|     // If there is a DHCP message type option, it must be a
 | |
|     // DHCP offer packet
 | |
|     //
 | |
|     if (msg_type_op->len != 1) {
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|     if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
 | |
|       return -1;
 | |
|     }
 | |
|     //
 | |
|     // There must be a server identifier option.
 | |
|     //
 | |
|     EfiStatus = find_opt (
 | |
|                   rx_pkt,
 | |
|                   DHCP4_SERVER_IDENTIFIER,
 | |
|                   0,
 | |
|                   &srvid_op
 | |
|                   );
 | |
| 
 | |
|     if (EFI_ERROR (EfiStatus)) {
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|     if (srvid_op->len != 4) {
 | |
|       return -1;
 | |
|     }
 | |
|     //
 | |
|     // Good DHCP offer packet.
 | |
|     //
 | |
|     break;
 | |
|   }
 | |
|   //
 | |
|   // Good DHCP (or BOOTP) packet.  Cache it!
 | |
|   //
 | |
|   EfiStatus = gBS->AllocatePool (
 | |
|                     EfiBootServicesData,
 | |
|                     (Private->offers + 1) * sizeof (DHCP4_PACKET),
 | |
|                     (VOID **) &tmp
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return -2;
 | |
|   }
 | |
| 
 | |
|   ASSERT (tmp);
 | |
| 
 | |
|   if (Private->offers != 0) {
 | |
|     CopyMem (
 | |
|       tmp,
 | |
|       Private->offer_list,
 | |
|       Private->offers * sizeof (DHCP4_PACKET)
 | |
|       );
 | |
| 
 | |
|     gBS->FreePool (Private->offer_list);
 | |
|   }
 | |
| 
 | |
|   CopyMem (&tmp[Private->offers++], rx_pkt, sizeof (DHCP4_PACKET));
 | |
| 
 | |
|   Private->offer_list = tmp;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| 
 | |
| /**
 | |
| 
 | |
| **/
 | |
| STATIC
 | |
| INTN
 | |
| acknak_verify (
 | |
|   IN PXE_DHCP4_PRIVATE_DATA *Private,
 | |
|   IN DHCP4_PACKET           *tx_pkt,
 | |
|   IN DHCP4_PACKET           *rx_pkt,
 | |
|   IN UINTN                  rx_pkt_size
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS  EfiStatus;
 | |
|   DHCP4_OP    *msg_type_op;
 | |
|   DHCP4_OP    *srvid_op;
 | |
|   DHCP4_OP    *renew_op;
 | |
|   DHCP4_OP    *rebind_op;
 | |
|   DHCP4_OP    *lease_time_op;
 | |
|   UINT32      magik;
 | |
| 
 | |
|   //
 | |
|   // Verify parameters.  Touch unused parameters to
 | |
|   // keep compiler happy.
 | |
|   //
 | |
|   ASSERT (Private);
 | |
|   ASSERT (rx_pkt);
 | |
| 
 | |
|   if (Private == NULL || rx_pkt == NULL) {
 | |
|     return -2;
 | |
|   }
 | |
| 
 | |
|   tx_pkt      = tx_pkt;
 | |
|   rx_pkt_size = rx_pkt_size;
 | |
| 
 | |
|   //
 | |
|   // This must be a DHCP Ack message.
 | |
|   //
 | |
|   magik = htonl (DHCP4_MAGIK_NUMBER);
 | |
| 
 | |
|   if (CompareMem (&rx_pkt->dhcp4.magik, &magik, 4)) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   EfiStatus = find_opt (rx_pkt, DHCP4_MESSAGE_TYPE, 0, &msg_type_op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   if (msg_type_op->len != 1) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   if (msg_type_op->data[0] != DHCP4_MESSAGE_TYPE_ACK) {
 | |
|     return -1;
 | |
|   }
 | |
|   //
 | |
|   // There must be a server identifier.
 | |
|   //
 | |
|   EfiStatus = find_opt (rx_pkt, DHCP4_SERVER_IDENTIFIER, 0, &srvid_op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     return -1;
 | |
|   }
 | |
| 
 | |
|   if (srvid_op->len != 4) {
 | |
|     return -1;
 | |
|   }
 | |
|   //
 | |
|   // There should be a renewal time.
 | |
|   // If there is not, we will default to the 7/8 of the rebinding time.
 | |
|   //
 | |
|   EfiStatus = find_opt (rx_pkt, DHCP4_RENEWAL_TIME, 0, &renew_op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     renew_op = NULL;
 | |
|   } else if (renew_op->len != 4) {
 | |
|     renew_op = NULL;
 | |
|   }
 | |
|   //
 | |
|   // There should be a rebinding time.
 | |
|   // If there is not, we will default to 7/8 of the lease time.
 | |
|   //
 | |
|   EfiStatus = find_opt (rx_pkt, DHCP4_REBINDING_TIME, 0, &rebind_op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     rebind_op = NULL;
 | |
|   } else if (rebind_op->len != 4) {
 | |
|     rebind_op = NULL;
 | |
|   }
 | |
|   //
 | |
|   // There should be a lease time.
 | |
|   // If there is not, we will default to one week.
 | |
|   //
 | |
|   EfiStatus = find_opt (rx_pkt, DHCP4_LEASE_TIME, 0, &lease_time_op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     lease_time_op = NULL;
 | |
|   } else if (lease_time_op->len != 4) {
 | |
|     lease_time_op = NULL;
 | |
|   }
 | |
|   //
 | |
|   // Packet looks good.  Double check the renew, rebind and lease times.
 | |
|   //
 | |
|   CopyMem (&Private->ServerIp, srvid_op->data, 4);
 | |
| 
 | |
|   if (renew_op != NULL) {
 | |
|     CopyMem (&Private->RenewTime, renew_op->data, 4);
 | |
|     Private->RenewTime = htonl (Private->RenewTime);
 | |
|   } else {
 | |
|     Private->RenewTime = 0;
 | |
|   }
 | |
| 
 | |
|   if (rebind_op != NULL) {
 | |
|     CopyMem (&Private->RebindTime, rebind_op->data, 4);
 | |
|     Private->RebindTime = htonl (Private->RebindTime);
 | |
|   } else {
 | |
|     Private->RebindTime = 0;
 | |
|   }
 | |
| 
 | |
|   if (lease_time_op != NULL) {
 | |
|     CopyMem (&Private->LeaseTime, lease_time_op->data, 4);
 | |
|     Private->LeaseTime = htonl (Private->LeaseTime);
 | |
|   } else {
 | |
|     Private->LeaseTime = 0;
 | |
|   }
 | |
| 
 | |
|   if (Private->LeaseTime < 60) {
 | |
|     Private->LeaseTime = 7 * 86400;
 | |
|   }
 | |
| 
 | |
|   if (Private->RebindTime < 52 || Private->RebindTime >= Private->LeaseTime) {
 | |
|     Private->RebindTime = Private->LeaseTime / 2 + Private->LeaseTime / 4 + Private->LeaseTime / 8;
 | |
|   }
 | |
| 
 | |
|   if (Private->RenewTime < 45 || Private->RenewTime >= Private->RebindTime) {
 | |
|     Private->RenewTime = Private->RebindTime / 2 + Private->RebindTime / 4 + Private->RebindTime / 8;
 | |
|   }
 | |
| 
 | |
|   return 1;
 | |
| }
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| PxeDhcp4Init (
 | |
|   IN EFI_PXE_DHCP4_PROTOCOL *This,
 | |
|   IN UINTN                  seconds_timeout,
 | |
|   OUT UINTN                 *Offers,
 | |
|   OUT DHCP4_PACKET          **OfferList
 | |
|   )
 | |
| {
 | |
|   PXE_DHCP4_PRIVATE_DATA  *Private;
 | |
|   DHCP4_PACKET            offer;
 | |
|   EFI_IP_ADDRESS          bcast_ip;
 | |
|   EFI_STATUS              EfiStatus;
 | |
| 
 | |
|   //
 | |
|   // Verify parameters and protocol state.
 | |
|   //
 | |
|   if (This == NULL ||
 | |
|       seconds_timeout < DHCP4_MIN_SECONDS ||
 | |
|       seconds_timeout > DHCP4_MAX_SECONDS ||
 | |
|       Offers == NULL ||
 | |
|       OfferList == NULL
 | |
|       ) {
 | |
|     //
 | |
|     // Return parameters are not initialized when
 | |
|     // parameters are invalid!
 | |
|     //
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   *Offers     = 0;
 | |
|   *OfferList  = NULL;
 | |
| 
 | |
|   //
 | |
|   // Check protocol state.
 | |
|   //
 | |
|   if (This->Data == NULL) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (!This->Data->SetupCompleted) {
 | |
|     return EFI_NOT_READY;
 | |
|   }
 | |
| 
 | |
| #if 0
 | |
|   if (!is_good_discover (&This->Data->Discover)) {
 | |
|     //
 | |
|     // %%TBD - check discover packet fields
 | |
|     //
 | |
|   }
 | |
| #endif
 | |
|   //
 | |
|   // Get pointer to our instance data.
 | |
|   //
 | |
|   Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
 | |
| 
 | |
|   if (Private == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (Private->PxeBc == NULL) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // Setup variables...
 | |
|   //
 | |
|   Private->offers     = 0;
 | |
|   Private->offer_list = NULL;
 | |
| 
 | |
|   EfiStatus = gBS->HandleProtocol (
 | |
|                     Private->Handle,
 | |
|                     &gEfiPxeDhcp4CallbackProtocolGuid,
 | |
|                     (VOID *) &Private->callback
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     Private->callback = NULL;
 | |
|   }
 | |
| 
 | |
|   Private->function = EFI_PXE_DHCP4_FUNCTION_INIT;
 | |
| 
 | |
|   //
 | |
|   // Increment the transaction ID.
 | |
|   //
 | |
|   {
 | |
|     UINT32  xid;
 | |
| 
 | |
|     CopyMem (&xid, &This->Data->Discover.dhcp4.xid, sizeof (UINT32));
 | |
| 
 | |
|     xid = htonl (htonl (xid) + 1);
 | |
| 
 | |
|     CopyMem (&This->Data->Discover.dhcp4.xid, &xid, sizeof (UINT32));
 | |
|   }
 | |
|   //
 | |
|   // Transmit discover and wait for offers...
 | |
|   //
 | |
|   SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
 | |
| 
 | |
|   EfiStatus = tx_rx_udp (
 | |
|                 Private,
 | |
|                 &bcast_ip,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 &This->Data->Discover,
 | |
|                 &offer,
 | |
|                 &offer_verify,
 | |
|                 seconds_timeout
 | |
|                 );
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     if (Private->offer_list) {
 | |
|       gBS->FreePool (Private->offer_list);
 | |
|     }
 | |
| 
 | |
|     Private->offers     = 0;
 | |
|     Private->offer_list = NULL;
 | |
|     Private->callback   = NULL;
 | |
| 
 | |
|     DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
 | |
|     return EfiStatus;
 | |
|   }
 | |
| 
 | |
|   *Offers                     = Private->offers;
 | |
|   *OfferList                  = Private->offer_list;
 | |
| 
 | |
|   Private->offers             = 0;
 | |
|   Private->offer_list         = NULL;
 | |
|   Private->callback           = NULL;
 | |
| 
 | |
|   This->Data->InitCompleted   = TRUE;
 | |
|   This->Data->SelectCompleted = FALSE;
 | |
|   This->Data->IsBootp         = FALSE;
 | |
|   This->Data->IsAck           = FALSE;
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| PxeDhcp4Select (
 | |
|   IN EFI_PXE_DHCP4_PROTOCOL *This,
 | |
|   IN UINTN                  seconds_timeout,
 | |
|   IN DHCP4_PACKET           *Offer
 | |
|   )
 | |
| {
 | |
|   PXE_DHCP4_PRIVATE_DATA  *Private;
 | |
|   EFI_STATUS              EfiStatus;
 | |
|   DHCP4_PACKET            request;
 | |
|   DHCP4_PACKET            acknak;
 | |
|   EFI_IP_ADDRESS          bcast_ip;
 | |
|   EFI_IP_ADDRESS          zero_ip;
 | |
|   EFI_IP_ADDRESS          local_ip;
 | |
|   DHCP4_OP                *srvid;
 | |
|   DHCP4_OP                *op;
 | |
|   UINT32                  dhcp4_magik;
 | |
|   UINT8                   buf[16];
 | |
|   BOOLEAN                 is_bootp;
 | |
| 
 | |
|   //
 | |
|   // Verify parameters.
 | |
|   //
 | |
|   if (This == NULL || seconds_timeout < DHCP4_MIN_SECONDS || seconds_timeout > DHCP4_MAX_SECONDS || Offer == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   //
 | |
|   // Check protocol state.
 | |
|   //
 | |
|   if (This->Data == NULL) {
 | |
|     return EFI_NOT_STARTED;
 | |
|   }
 | |
| 
 | |
|   if (!This->Data->SetupCompleted) {
 | |
|     return EFI_NOT_READY;
 | |
|   }
 | |
|   //
 | |
|   // Get pointer to instance data.
 | |
|   //
 | |
|   Private = PXE_DHCP4_PRIVATE_DATA_FROM_THIS (This);
 | |
| 
 | |
|   if (Private == NULL) {
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (Private->PxeBc == NULL) {
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
| #if 0
 | |
|   if (!is_good_discover (&This->Data->Discover)) {
 | |
|     //
 | |
|     // %%TBD - check discover packet fields
 | |
|     //
 | |
|   }
 | |
| #endif
 | |
|   //
 | |
|   // Setup useful variables...
 | |
|   //
 | |
|   SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
 | |
| 
 | |
|   ZeroMem (&zero_ip, sizeof (EFI_IP_ADDRESS));
 | |
| 
 | |
|   ZeroMem (&local_ip, sizeof (EFI_IP_ADDRESS));
 | |
|   local_ip.v4.Addr[0]         = 127;
 | |
|   local_ip.v4.Addr[3]         = 1;
 | |
| 
 | |
|   This->Data->SelectCompleted = FALSE;
 | |
|   This->Data->IsBootp         = FALSE;
 | |
|   This->Data->IsAck           = FALSE;
 | |
| 
 | |
|   EfiStatus = gBS->HandleProtocol (
 | |
|                     Private->Handle,
 | |
|                     &gEfiPxeDhcp4CallbackProtocolGuid,
 | |
|                     (VOID *) &Private->callback
 | |
|                     );
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     Private->callback = NULL;
 | |
|   }
 | |
| 
 | |
|   Private->function = EFI_PXE_DHCP4_FUNCTION_SELECT;
 | |
| 
 | |
|   //
 | |
|   // Verify offer packet fields.
 | |
|   //
 | |
|   if (Offer->dhcp4.op != BOOTP_REPLY) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (Offer->dhcp4.htype != This->Data->Discover.dhcp4.htype) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (Offer->dhcp4.hlen != This->Data->Discover.dhcp4.hlen) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (CompareMem (&Offer->dhcp4.xid, &This->Data->Discover.dhcp4.xid, 4)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (!CompareMem (&Offer->dhcp4.yiaddr, &bcast_ip, 4)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (!CompareMem (&Offer->dhcp4.yiaddr, &zero_ip, 4)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (!CompareMem (&Offer->dhcp4.yiaddr, &local_ip, 4)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (CompareMem (
 | |
|         &Offer->dhcp4.chaddr,
 | |
|         &This->Data->Discover.dhcp4.chaddr,
 | |
|         16
 | |
|         )) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
|   //
 | |
|   // DHCP option checks
 | |
|   //
 | |
|   dhcp4_magik = htonl (DHCP4_MAGIK_NUMBER);
 | |
|   is_bootp    = TRUE;
 | |
| 
 | |
|   if (!CompareMem (&Offer->dhcp4.magik, &dhcp4_magik, 4)) {
 | |
|     //
 | |
|     // If present, DHCP message type must be offer.
 | |
|     //
 | |
|     EfiStatus = find_opt (Offer, DHCP4_MESSAGE_TYPE, 0, &op);
 | |
| 
 | |
|     if (!EFI_ERROR (EfiStatus)) {
 | |
|       if (op->len != 1 || op->data[0] != DHCP4_MESSAGE_TYPE_OFFER) {
 | |
|         Private->callback = NULL;
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
| 
 | |
|       is_bootp = FALSE;
 | |
|     }
 | |
|     //
 | |
|     // If present, DHCP max message size must be valid.
 | |
|     //
 | |
|     EfiStatus = find_opt (Offer, DHCP4_MAX_MESSAGE_SIZE, 0, &op);
 | |
| 
 | |
|     if (!EFI_ERROR (EfiStatus)) {
 | |
|       if (op->len != 2 || ((op->data[0] << 8) | op->data[1]) < DHCP4_DEFAULT_MAX_MESSAGE_SIZE) {
 | |
|         Private->callback = NULL;
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
|     }
 | |
|     //
 | |
|     // If present, DHCP server identifier must be valid.
 | |
|     //
 | |
|     EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &op);
 | |
| 
 | |
|     if (!EFI_ERROR (EfiStatus)) {
 | |
|       if (op->len != 4 || !CompareMem (op->data, &bcast_ip, 4) || !CompareMem (op->data, &zero_ip, 4)) {
 | |
|         Private->callback = NULL;
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
|     }
 | |
|     //
 | |
|     // If present, DHCP subnet mask must be valid.
 | |
|     //
 | |
|     EfiStatus = find_opt (
 | |
|                   Offer,
 | |
|                   DHCP4_SUBNET_MASK,
 | |
|                   0,
 | |
|                   &op
 | |
|                   );
 | |
| 
 | |
|     if (!EFI_ERROR (EfiStatus)) {
 | |
|       if (op->len != 4) {
 | |
|         Private->callback = NULL;
 | |
|         return EFI_INVALID_PARAMETER;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Early out for BOOTP.
 | |
|   //
 | |
|   This->Data->IsBootp = is_bootp;
 | |
|   if (is_bootp) {
 | |
|     //
 | |
|     // Copy offer packet to instance data.
 | |
|     //
 | |
|     CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
 | |
| 
 | |
|     //
 | |
|     // Copy discover to request and offer to acknak.
 | |
|     //
 | |
|     CopyMem (
 | |
|       &This->Data->Request,
 | |
|       &This->Data->Discover,
 | |
|       sizeof (DHCP4_PACKET)
 | |
|       );
 | |
| 
 | |
|     CopyMem (
 | |
|       &This->Data->AckNak,
 | |
|       &This->Data->Offer,
 | |
|       sizeof (DHCP4_PACKET)
 | |
|       );
 | |
| 
 | |
|     //
 | |
|     // Set state flags.
 | |
|     //
 | |
|     This->Data->SelectCompleted = TRUE;
 | |
|     This->Data->IsAck           = TRUE;
 | |
| 
 | |
|     Private->callback           = NULL;
 | |
|     return EFI_SUCCESS;
 | |
|   }
 | |
|   //
 | |
|   // Copy discover packet contents to request packet.
 | |
|   //
 | |
|   CopyMem (&request, &This->Data->Discover, sizeof (DHCP4_PACKET));
 | |
| 
 | |
|   This->Data->IsAck = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Change DHCP message type from discover to request.
 | |
|   //
 | |
|   EfiStatus = find_opt (&request, DHCP4_MESSAGE_TYPE, 0, &op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus) && EfiStatus != EFI_NOT_FOUND) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (EfiStatus == EFI_NOT_FOUND) {
 | |
|     EfiStatus = find_opt (&request, DHCP4_END, 0, &op);
 | |
| 
 | |
|     if (EFI_ERROR (EfiStatus)) {
 | |
|       Private->callback = NULL;
 | |
|       return EFI_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     op->op      = DHCP4_MESSAGE_TYPE;
 | |
|     op->len     = 1;
 | |
| 
 | |
|     op->data[1] = DHCP4_END;
 | |
|   }
 | |
| 
 | |
|   op->data[0] = DHCP4_MESSAGE_TYPE_REQUEST;
 | |
| 
 | |
|   //
 | |
|   // Copy server identifier option from offer to request.
 | |
|   //
 | |
|   EfiStatus = find_opt (Offer, DHCP4_SERVER_IDENTIFIER, 0, &srvid);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   if (srvid->len != 4) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_INVALID_PARAMETER;
 | |
|   }
 | |
| 
 | |
|   EfiStatus = add_opt (&request, srvid);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
 | |
|     Private->callback = NULL;
 | |
|     return EfiStatus;
 | |
|   }
 | |
|   //
 | |
|   // Add requested IP address option to request packet.
 | |
|   //
 | |
|   op      = (DHCP4_OP *) buf;
 | |
|   op->op  = DHCP4_REQUESTED_IP_ADDRESS;
 | |
|   op->len = 4;
 | |
|   CopyMem (op->data, &Offer->dhcp4.yiaddr, 4);
 | |
| 
 | |
|   EfiStatus = add_opt (&request, op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
 | |
|     Private->callback = NULL;
 | |
|     return EfiStatus;
 | |
|   }
 | |
|   //
 | |
|   // Transimit DHCP request and wait for DHCP ack...
 | |
|   //
 | |
|   SetMem (&bcast_ip, sizeof (EFI_IP_ADDRESS), 0xFF);
 | |
| 
 | |
|   EfiStatus = tx_rx_udp (
 | |
|                 Private,
 | |
|                 &bcast_ip,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 &request,
 | |
|                 &acknak,
 | |
|                 &acknak_verify,
 | |
|                 seconds_timeout
 | |
|                 );
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     DebugPrint (("%a:%d:%r\n", __FILE__, __LINE__, EfiStatus));
 | |
|     Private->callback = NULL;
 | |
|     return EfiStatus;
 | |
|   }
 | |
|   //
 | |
|   // Set Data->IsAck and return.
 | |
|   //
 | |
|   EfiStatus = find_opt (&acknak, DHCP4_MESSAGE_TYPE, 0, &op);
 | |
| 
 | |
|   if (EFI_ERROR (EfiStatus)) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   if (op->len != 1) {
 | |
|     Private->callback = NULL;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
| 
 | |
|   switch (op->data[0]) {
 | |
|   case DHCP4_MESSAGE_TYPE_ACK:
 | |
|     This->Data->IsAck = TRUE;
 | |
|     break;
 | |
| 
 | |
|   case DHCP4_MESSAGE_TYPE_NAK:
 | |
|     This->Data->IsAck = FALSE;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     Private->callback = NULL;
 | |
|     return EFI_DEVICE_ERROR;
 | |
|   }
 | |
|   //
 | |
|   // Copy packets into instance data...
 | |
|   //
 | |
|   CopyMem (&This->Data->Offer, Offer, sizeof (DHCP4_PACKET));
 | |
|   CopyMem (&This->Data->Request, &request, sizeof (DHCP4_PACKET));
 | |
|   CopyMem (&This->Data->AckNak, &acknak, sizeof (DHCP4_PACKET));
 | |
| 
 | |
|   This->Data->SelectCompleted = TRUE;
 | |
| 
 | |
|   Private->callback           = NULL;
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /* eof - PxeDhcp4InitSelect.c */
 |