dhcp4io.c

来自「EFI BIOS是Intel提出的下一代的BIOS标准。这里上传的Edk源代码是」· C语言 代码 · 共 1,756 行 · 第 1/3 页

C
1,756
字号
/*++

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:

  Dhcp4Io.c

Abstract:

  EFI DHCP protocol implementation

--*/

#include "Dhcp4Impl.h"

UINT32  mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };

EFI_STATUS
DhcpInitRequest (
  IN DHCP_SERVICE           *DhcpSb
  )
/*++

Routine Description:

  Send an initial DISCOVER or REQUEST message according to the
  DHCP service's current state.

Arguments:

  DhcpSb  - The DHCP service instance

Returns:

  EFI_SUCCESS - The request has been sent

--*/
{
  EFI_STATUS                Status;

  ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));

  if (DhcpSb->DhcpState == Dhcp4Init) {
    DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
    Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
    
    if (EFI_ERROR (Status)) {
      DhcpSb->DhcpState = Dhcp4Init;
      return Status;
    }

    DhcpSb->WaitOffer = DHCP_WAIT_OFFER;
  } else {
    DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
    Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);

    if (EFI_ERROR (Status)) {
      DhcpSb->DhcpState = Dhcp4InitReboot;
      return Status;
    }
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
DhcpCallUser (
  IN  DHCP_SERVICE          *DhcpSb,
  IN  EFI_DHCP4_EVENT       Event,
  IN  EFI_DHCP4_PACKET      *Packet,      OPTIONAL
  OUT EFI_DHCP4_PACKET      **NewPacket   OPTIONAL
  )
/*++

Routine Description:

  Call user provided callback function, and return the value the 
  function returns. If the user doesn't provide a callback, a 
  proper return value is selected to let the caller continue the
  normal process.

Arguments:

  DhcpSb    - The DHCP service instance
  Event     - The event as defined in the spec
  Packet    - The current packet trigger the event
  NewPacket - The user's return new packet

Returns:

  EFI_NOT_READY - Direct the caller to continue collecting the offer.
  EFI_SUCCESS   - The user function returns success.
  EFI_ABORTED   - The user function ask it to abort.

--*/
{
  EFI_DHCP4_CONFIG_DATA     *Config;
  EFI_STATUS                Status;

  if (NewPacket != NULL) {
    *NewPacket = NULL;
  }
  
  //
  // If user doesn't provide the call back function, return the value
  // that directs the client to continue the normal process.
  // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
  // the offers and select a offer, EFI_NOT_READY tells the client to
  // collect more offers.
  //
  Config = &DhcpSb->ActiveConfig;
  
  if (Config->Dhcp4Callback == NULL) {
    if (Event == Dhcp4RcvdOffer) {
      return EFI_NOT_READY;
    }

    return EFI_SUCCESS;
  }

  Status = Config->Dhcp4Callback (
                     &DhcpSb->ActiveChild->Dhcp4Protocol,
                     Config->CallbackContext,
                     DhcpSb->DhcpState,
                     Event,
                     Packet,
                     NewPacket
                     );

  //
  // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
  // and EFI_ABORTED. If it returns values other than those, assume
  // it to be EFI_ABORTED.
  //
  if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
    return Status;
  }

  return EFI_ABORTED;
}

VOID
DhcpNotifyUser (
  IN DHCP_SERVICE           *DhcpSb,
  IN INTN                   Which
  )
/*++

Routine Description:

  Notify the user about the operation result.

Arguments:

  DhcpSb  - DHCP service instance
  Which   - which notify function to signal

Returns:

  None

--*/
{
  DHCP_PROTOCOL             *Child;

  if ((Child = DhcpSb->ActiveChild) == NULL) {
    return ;
  }

  if ((Child->CompletionEvent != NULL) && 
     ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))) {
     
    gBS->SignalEvent (Child->CompletionEvent);
    Child->CompletionEvent = NULL;
  }

  if ((Child->RenewRebindEvent != NULL) && 
     ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))) {
     
    gBS->SignalEvent (Child->RenewRebindEvent);
    Child->RenewRebindEvent = NULL;
  }
}


EFI_STATUS
DhcpSetState (
  IN DHCP_SERVICE           *DhcpSb,
  IN INTN                   State,
  IN BOOLEAN                CallUser
  )
/*++

Routine Description:

  Set the DHCP state. If CallUser is true, it will try to notify 
  the user before change the state by DhcpNotifyUser. It returns 
  EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
  EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test 
  the return value of this function.

Arguments:

  DhcpSb    - The DHCP service instance
  State     - The new DHCP state to change to
  CallUser  - Whether we need to call user

Returns:

  EFI_SUCCESS - The state is changed
  EFI_ABORTED - The user asks to abort the DHCP process.
  
--*/
{
  EFI_STATUS                Status;

  if (CallUser) {
    Status = EFI_SUCCESS;

    if (State == Dhcp4Renewing) {
      Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);

    } else if (State == Dhcp4Rebinding) {
      Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);

    } else if (State == Dhcp4Bound) {
      Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);

    }

    if (EFI_ERROR (Status)) {
      return Status;
    }
  }
  
  //
  // Update the retransmission timer during the state transition.
  // This will clear the retry count. This is also why the rule
  // first transit the state, then send packets.
  //
  if (State == Dhcp4Init) {
    DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
  } else {
    DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
  }

  if (DhcpSb->MaxRetries == 0) {
    DhcpSb->MaxRetries = 4;
  }

  DhcpSb->CurRetry      = 0;
  DhcpSb->PacketToLive  = 0;

  DhcpSb->DhcpState     = State;
  return EFI_SUCCESS;
}

STATIC
VOID
DhcpSetTransmitTimer (
  IN DHCP_SERVICE           *DhcpSb
  )
/*++

Routine Description:

  Set the retransmit timer for the packet. It will select from either 
  the discover timeouts/request timeouts or the default timeout values.
  
Arguments:

  DhcpSb  - The DHCP service instance.

Returns:

  None

--*/
{
  UINT32                    *Times;

  ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);

  if (DhcpSb->DhcpState == Dhcp4Init) {
    Times = DhcpSb->ActiveConfig.DiscoverTimeout;
  } else {
    Times = DhcpSb->ActiveConfig.RequestTimeout;
  }

  if (Times == NULL) {
    Times = mDhcp4DefaultTimeout;
  }

  DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
}

STATIC
VOID
DhcpComputeLease (
  IN DHCP_SERVICE           *DhcpSb,
  IN DHCP_PARAMETER         *Para
  )
/*++

Routine Description:

  Compute the lease. If the server grants a permanent lease, just
  process it as a normal timeout value since the lease will last 
  more than 100 years.

Arguments:

  DhcpSb  - The DHCP service instance
  Para    - The DHCP parameter extracted from the server's response.

Returns:

  None

--*/
{
  ASSERT (Para != NULL);

  DhcpSb->Lease = Para->Lease;
  DhcpSb->T2    = Para->T2;
  DhcpSb->T1    = Para->T1;

  if (DhcpSb->Lease == 0) {
    DhcpSb->Lease = DHCP_DEFAULT_LEASE;
  }

  if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
    DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
  }

  if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
    DhcpSb->T1 = DhcpSb->Lease >> 1;
  }
}

EFI_STATUS
DhcpConfigLeaseIoPort (
  IN UDP_IO_PORT            *UdpIo,
  IN VOID                   *Context
  )
/*++

Routine Description:

  Configure a UDP IO port to use the acquired lease address. 
  DHCP driver needs this port to unicast packet to the server
  such as DHCP release.

Arguments:

  UdpIo   - The UDP IO port to configure
  Context - The opaque parameter to the function.

Returns:

  EFI_SUCCESS - The UDP IO port is successfully configured.
  Others      - It failed to configure the port.

--*/
{
  EFI_UDP4_CONFIG_DATA      UdpConfigData;
  EFI_IPv4_ADDRESS          Subnet;
  EFI_IPv4_ADDRESS          Gateway;
  DHCP_SERVICE              *DhcpSb;
  EFI_STATUS                Status;

  DhcpSb = (DHCP_SERVICE *) Context;
  
  UdpConfigData.AcceptBroadcast     = FALSE;
  UdpConfigData.AcceptPromiscuous   = FALSE;
  UdpConfigData.AcceptAnyPort       = FALSE;
  UdpConfigData.AllowDuplicatePort  = TRUE;
  UdpConfigData.TypeOfService       = 0;
  UdpConfigData.TimeToLive          = 64;
  UdpConfigData.DoNotFragment       = FALSE;
  UdpConfigData.ReceiveTimeout      = 1;
  UdpConfigData.TransmitTimeout     = 0;

  UdpConfigData.UseDefaultAddress   = FALSE;
  UdpConfigData.StationPort         = DHCP_CLIENT_PORT;
  UdpConfigData.RemotePort          = DHCP_SERVER_PORT;

  EFI_IP4 (UdpConfigData.StationAddress)= HTONL (DhcpSb->ClientAddr);
  EFI_IP4 (UdpConfigData.SubnetMask)    = HTONL (DhcpSb->Netmask);
  EFI_IP4 (UdpConfigData.RemoteAddress) = 0;

  Status = UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);
  
  if (EFI_ERROR (Status)) {
    return Status;
  }
  
  //
  // Add a default route if received from the server.
  //
  if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
    EFI_IP4 (Subnet)  = 0;
    EFI_IP4 (Gateway) = HTONL (DhcpSb->Para->Router);
    UdpIo->Udp->Routes (UdpIo->Udp, FALSE, &Subnet, &Subnet, &Gateway);
  }

  return EFI_SUCCESS;
}

STATIC
EFI_STATUS
DhcpLeaseAcquired (
  IN DHCP_SERVICE           *DhcpSb
  )
/*++

Routine Description:

  Update the lease states when a new lease is acquired. It will not only
  save the acquired the address and lease time, it will also create a UDP 
  child to provide address resolution for the address.

Arguments:

  DhcpSb  - The DHCP service instance

Returns:

  EFI_OUT_OF_RESOURCES - Failed to allocate resources.
  EFI_SUCCESS          - The lease is recorded.

--*/
{
  INTN                      Class;

  DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);

  if (DhcpSb->Para != NULL) {
    DhcpSb->Netmask     = DhcpSb->Para->NetMask;
    DhcpSb->ServerAddr  = DhcpSb->Para->ServerId;
  }

  if (DhcpSb->Netmask == 0) {
    Class           = NetGetIpClass (DhcpSb->ClientAddr);
    DhcpSb->Netmask = mIp4AllMasks[Class << 3];
  }

  if (DhcpSb->LeaseIoPort != NULL) {
    UdpIoFreePort (DhcpSb->LeaseIoPort);
  }
  
  //
  // Create a UDP/IP child to provide ARP service for the Leased IP,
  // and transmit unicast packet with it as source address. Don't
  // start receive on this port, the queued packet will be timeout.
  //
  DhcpSb->LeaseIoPort = UdpIoCreatePort (
                          DhcpSb->Controller,
                          DhcpSb->Image,
                          DhcpConfigLeaseIoPort,
                          DhcpSb
                          );

  if (DhcpSb->LeaseIoPort == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
    DhcpComputeLease (DhcpSb, DhcpSb->Para);
  }

  return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
}

VOID
DhcpCleanLease (
  IN DHCP_SERVICE           *DhcpSb
  )
/*++

Routine Description:

  Clean up the DHCP related states, IoStatus isn't reset.

Arguments:

  DhcpSb  - The DHCP instance service.

Returns:

  None

--*/
{
  DhcpSb->DhcpState   = Dhcp4Init;
  DhcpSb->Xid         = DhcpSb->Xid + 1;
  DhcpSb->ClientAddr  = 0;
  DhcpSb->ServerAddr  = 0;

  if (DhcpSb->LastOffer != NULL) {
    NetFreePool (DhcpSb->LastOffer);
    DhcpSb->LastOffer = NULL;
  }

  if (DhcpSb->Selected != NULL) {
    NetFreePool (DhcpSb->Selected);
    DhcpSb->Selected = NULL;
  }

  if (DhcpSb->Para != NULL) {
    NetFreePool (DhcpSb->Para);
    DhcpSb->Para = NULL;
  }

  DhcpSb->Lease         = 0;
  DhcpSb->T1            = 0;
  DhcpSb->T2            = 0;
  DhcpSb->ExtraRefresh  = FALSE;

  if (DhcpSb->LeaseIoPort != NULL) {
    UdpIoFreePort (DhcpSb->LeaseIoPort);
    DhcpSb->LeaseIoPort = NULL;
  }

  if (DhcpSb->LastPacket != NULL) {
    NetbufFree (DhcpSb->LastPacket);
    DhcpSb->LastPacket = NULL;
  }

  DhcpSb->PacketToLive  = 0;
  DhcpSb->CurRetry      = 0;
  DhcpSb->MaxRetries    = 0;
  DhcpSb->WaitOffer     = 0;
  DhcpSb->LeaseLife     = 0;
}

STATIC
EFI_STATUS
DhcpChooseOffer (
  IN DHCP_SERVICE           *DhcpSb
  )
/*++

Routine Description:

  Select a offer among all the offers collected. If the offer selected is
  of BOOTP, the lease is recorded and user notified. If the offer is of 
  DHCP, it will request the offer from the server.

Arguments:

  DhcpSb  - The DHCP service instance.

Returns:

  EFI_SUCCESS - One of the offer is selected.

--*/
{
  EFI_DHCP4_PACKET          *Selected;
  EFI_DHCP4_PACKET          *NewPacket;
  EFI_STATUS                Status;

  ASSERT (DhcpSb->LastOffer != NULL);

  //
  // Stop waiting more offers
  //
  DhcpSb->WaitOffer = 0;

  //
  // User will cache previous offers if he wants to select
  // from multiple offers. If user provides an invalid packet,
  // use the last offer, otherwise use the provided packet.
  //
  NewPacket = NULL;
  Status    = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?