Home | History | Annotate | Download | only in Dhcp4Dxe
      1 /** @file
      2   EFI DHCP protocol implementation.
      3 
      4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 
     16 #include "Dhcp4Impl.h"
     17 
     18 UINT32  mDhcp4DefaultTimeout[4] = { 4, 8, 16, 32 };
     19 
     20 
     21 /**
     22   Send an initial DISCOVER or REQUEST message according to the
     23   DHCP service's current state.
     24 
     25   @param[in]  DhcpSb                The DHCP service instance
     26 
     27   @retval EFI_SUCCESS           The request has been sent
     28   @retval other                 Some error occurs when sending the request.
     29 
     30 **/
     31 EFI_STATUS
     32 DhcpInitRequest (
     33   IN DHCP_SERVICE           *DhcpSb
     34   )
     35 {
     36   EFI_STATUS                Status;
     37 
     38   ASSERT ((DhcpSb->DhcpState == Dhcp4Init) || (DhcpSb->DhcpState == Dhcp4InitReboot));
     39 
     40   //
     41   // Clear initial time to make sure that elapsed-time is set to 0 for first Discover or REQUEST message.
     42   //
     43   DhcpSb->ActiveChild->ElaspedTime= 0;
     44 
     45   if (DhcpSb->DhcpState == Dhcp4Init) {
     46     DhcpSetState (DhcpSb, Dhcp4Selecting, FALSE);
     47     Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_DISCOVER, NULL);
     48 
     49     if (EFI_ERROR (Status)) {
     50       DhcpSb->DhcpState = Dhcp4Init;
     51       return Status;
     52     }
     53   } else {
     54     DhcpSetState (DhcpSb, Dhcp4Rebooting, FALSE);
     55     Status = DhcpSendMessage (DhcpSb, NULL, NULL, DHCP_MSG_REQUEST, NULL);
     56 
     57     if (EFI_ERROR (Status)) {
     58       DhcpSb->DhcpState = Dhcp4InitReboot;
     59       return Status;
     60     }
     61   }
     62 
     63   return EFI_SUCCESS;
     64 }
     65 
     66 
     67 /**
     68   Call user provided callback function, and return the value the
     69   function returns. If the user doesn't provide a callback, a
     70   proper return value is selected to let the caller continue the
     71   normal process.
     72 
     73   @param[in]  DhcpSb                The DHCP service instance
     74   @param[in]  Event                 The event as defined in the spec
     75   @param[in]  Packet                The current packet trigger the event
     76   @param[out] NewPacket             The user's return new packet
     77 
     78   @retval EFI_NOT_READY         Direct the caller to continue collecting the offer.
     79   @retval EFI_SUCCESS           The user function returns success.
     80   @retval EFI_ABORTED           The user function ask it to abort.
     81 
     82 **/
     83 EFI_STATUS
     84 DhcpCallUser (
     85   IN  DHCP_SERVICE          *DhcpSb,
     86   IN  EFI_DHCP4_EVENT       Event,
     87   IN  EFI_DHCP4_PACKET      *Packet,      OPTIONAL
     88   OUT EFI_DHCP4_PACKET      **NewPacket   OPTIONAL
     89   )
     90 {
     91   EFI_DHCP4_CONFIG_DATA     *Config;
     92   EFI_STATUS                Status;
     93 
     94   if (NewPacket != NULL) {
     95     *NewPacket = NULL;
     96   }
     97 
     98   //
     99   // If user doesn't provide the call back function, return the value
    100   // that directs the client to continue the normal process.
    101   // In Dhcp4Selecting EFI_SUCCESS tells the client to stop collecting
    102   // the offers and select a offer, EFI_NOT_READY tells the client to
    103   // collect more offers.
    104   //
    105   Config = &DhcpSb->ActiveConfig;
    106 
    107   if (Config->Dhcp4Callback == NULL) {
    108     if (Event == Dhcp4RcvdOffer) {
    109       return EFI_NOT_READY;
    110     }
    111 
    112     return EFI_SUCCESS;
    113   }
    114 
    115   Status = Config->Dhcp4Callback (
    116                      &DhcpSb->ActiveChild->Dhcp4Protocol,
    117                      Config->CallbackContext,
    118                      (EFI_DHCP4_STATE) DhcpSb->DhcpState,
    119                      Event,
    120                      Packet,
    121                      NewPacket
    122                      );
    123 
    124   //
    125   // User's callback should only return EFI_SUCCESS, EFI_NOT_READY,
    126   // and EFI_ABORTED. If it returns values other than those, assume
    127   // it to be EFI_ABORTED.
    128   //
    129   if ((Status == EFI_SUCCESS) || (Status == EFI_NOT_READY)) {
    130     return Status;
    131   }
    132 
    133   return EFI_ABORTED;
    134 }
    135 
    136 
    137 /**
    138   Notify the user about the operation result.
    139 
    140   @param  DhcpSb                DHCP service instance
    141   @param  Which                 Which notify function to signal
    142 
    143 **/
    144 VOID
    145 DhcpNotifyUser (
    146   IN DHCP_SERVICE           *DhcpSb,
    147   IN INTN                   Which
    148   )
    149 {
    150   DHCP_PROTOCOL             *Child;
    151 
    152   if ((Child = DhcpSb->ActiveChild) == NULL) {
    153     return ;
    154   }
    155 
    156   if ((Child->CompletionEvent != NULL) &&
    157       ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))
    158       ) {
    159 
    160     gBS->SignalEvent (Child->CompletionEvent);
    161     Child->CompletionEvent = NULL;
    162   }
    163 
    164   if ((Child->RenewRebindEvent != NULL) &&
    165       ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))
    166       ) {
    167 
    168     gBS->SignalEvent (Child->RenewRebindEvent);
    169     Child->RenewRebindEvent = NULL;
    170   }
    171 }
    172 
    173 
    174 
    175 /**
    176   Set the DHCP state. If CallUser is true, it will try to notify
    177   the user before change the state by DhcpNotifyUser. It returns
    178   EFI_ABORTED if the user return EFI_ABORTED, otherwise, it returns
    179   EFI_SUCCESS. If CallUser is FALSE, it isn't necessary to test
    180   the return value of this function.
    181 
    182   @param  DhcpSb                The DHCP service instance
    183   @param  State                 The new DHCP state to change to
    184   @param  CallUser              Whether we need to call user
    185 
    186   @retval EFI_SUCCESS           The state is changed
    187   @retval EFI_ABORTED           The user asks to abort the DHCP process.
    188 
    189 **/
    190 EFI_STATUS
    191 DhcpSetState (
    192   IN OUT DHCP_SERVICE           *DhcpSb,
    193   IN     INTN                   State,
    194   IN     BOOLEAN                CallUser
    195   )
    196 {
    197   EFI_STATUS                Status;
    198 
    199   if (CallUser) {
    200     Status = EFI_SUCCESS;
    201 
    202     if (State == Dhcp4Renewing) {
    203       Status = DhcpCallUser (DhcpSb, Dhcp4EnterRenewing, NULL, NULL);
    204 
    205     } else if (State == Dhcp4Rebinding) {
    206       Status = DhcpCallUser (DhcpSb, Dhcp4EnterRebinding, NULL, NULL);
    207 
    208     } else if (State == Dhcp4Bound) {
    209       Status = DhcpCallUser (DhcpSb, Dhcp4BoundCompleted, NULL, NULL);
    210 
    211     }
    212 
    213     if (EFI_ERROR (Status)) {
    214       return Status;
    215     }
    216   }
    217 
    218   //
    219   // Update the retransmission timer during the state transition.
    220   // This will clear the retry count. This is also why the rule
    221   // first transit the state, then send packets.
    222   //
    223   if (State == Dhcp4Selecting) {
    224     DhcpSb->MaxRetries = DhcpSb->ActiveConfig.DiscoverTryCount;
    225   } else {
    226     DhcpSb->MaxRetries = DhcpSb->ActiveConfig.RequestTryCount;
    227   }
    228 
    229   if (DhcpSb->MaxRetries == 0) {
    230     DhcpSb->MaxRetries = 4;
    231   }
    232 
    233   DhcpSb->CurRetry      = 0;
    234   DhcpSb->PacketToLive  = 0;
    235   DhcpSb->LastTimeout   = 0;
    236   DhcpSb->DhcpState     = State;
    237   return EFI_SUCCESS;
    238 }
    239 
    240 
    241 /**
    242   Set the retransmit timer for the packet. It will select from either
    243   the discover timeouts/request timeouts or the default timeout values.
    244 
    245   @param  DhcpSb                The DHCP service instance.
    246 
    247 **/
    248 VOID
    249 DhcpSetTransmitTimer (
    250   IN OUT DHCP_SERVICE           *DhcpSb
    251   )
    252 {
    253   UINT32                    *Times;
    254 
    255   ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);
    256 
    257   if (DhcpSb->DhcpState == Dhcp4Selecting) {
    258     Times = DhcpSb->ActiveConfig.DiscoverTimeout;
    259   } else {
    260     Times = DhcpSb->ActiveConfig.RequestTimeout;
    261   }
    262 
    263   if (Times == NULL) {
    264     Times = mDhcp4DefaultTimeout;
    265   }
    266 
    267   DhcpSb->PacketToLive = Times[DhcpSb->CurRetry];
    268   DhcpSb->LastTimeout  = DhcpSb->PacketToLive;
    269 
    270   return;
    271 }
    272 
    273 /**
    274   Compute the lease. If the server grants a permanent lease, just
    275   process it as a normal timeout value since the lease will last
    276   more than 100 years.
    277 
    278   @param  DhcpSb                The DHCP service instance
    279   @param  Para                  The DHCP parameter extracted from the server's
    280                                 response.
    281 **/
    282 VOID
    283 DhcpComputeLease (
    284   IN OUT DHCP_SERVICE           *DhcpSb,
    285   IN     DHCP_PARAMETER         *Para
    286   )
    287 {
    288   ASSERT (Para != NULL);
    289 
    290   DhcpSb->Lease = Para->Lease;
    291   DhcpSb->T2    = Para->T2;
    292   DhcpSb->T1    = Para->T1;
    293 
    294   if (DhcpSb->Lease == 0) {
    295     DhcpSb->Lease = DHCP_DEFAULT_LEASE;
    296   }
    297 
    298   if ((DhcpSb->T2 == 0) || (DhcpSb->T2 >= Para->Lease)) {
    299     DhcpSb->T2 = Para->Lease - (Para->Lease >> 3);
    300   }
    301 
    302   if ((DhcpSb->T1 == 0) || (DhcpSb->T1 >= Para->T2)) {
    303     DhcpSb->T1 = DhcpSb->Lease >> 1;
    304   }
    305 }
    306 
    307 
    308 /**
    309   Configure a UDP IO port to use the acquired lease address.
    310   DHCP driver needs this port to unicast packet to the server
    311   such as DHCP release.
    312 
    313   @param[in]  UdpIo                 The UDP IO to configure
    314   @param[in]  Context               Dhcp service instance.
    315 
    316   @retval EFI_SUCCESS           The UDP IO port is successfully configured.
    317   @retval Others                It failed to configure the port.
    318 
    319 **/
    320 EFI_STATUS
    321 EFIAPI
    322 DhcpConfigLeaseIoPort (
    323   IN UDP_IO                 *UdpIo,
    324   IN VOID                   *Context
    325   )
    326 {
    327   EFI_UDP4_CONFIG_DATA      UdpConfigData;
    328   EFI_IPv4_ADDRESS          Subnet;
    329   EFI_IPv4_ADDRESS          Gateway;
    330   DHCP_SERVICE              *DhcpSb;
    331   EFI_STATUS                Status;
    332   IP4_ADDR                  Ip;
    333 
    334   DhcpSb = (DHCP_SERVICE *) Context;
    335 
    336   UdpConfigData.AcceptBroadcast     = FALSE;
    337   UdpConfigData.AcceptPromiscuous   = FALSE;
    338   UdpConfigData.AcceptAnyPort       = FALSE;
    339   UdpConfigData.AllowDuplicatePort  = TRUE;
    340   UdpConfigData.TypeOfService       = 0;
    341   UdpConfigData.TimeToLive          = 64;
    342   UdpConfigData.DoNotFragment       = FALSE;
    343   UdpConfigData.ReceiveTimeout      = 1;
    344   UdpConfigData.TransmitTimeout     = 0;
    345 
    346   UdpConfigData.UseDefaultAddress   = FALSE;
    347   UdpConfigData.StationPort         = DHCP_CLIENT_PORT;
    348   UdpConfigData.RemotePort          = DHCP_SERVER_PORT;
    349 
    350   Ip = HTONL (DhcpSb->ClientAddr);
    351   CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
    352 
    353   Ip = HTONL (DhcpSb->Netmask);
    354   CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
    355 
    356   ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
    357 
    358   Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);
    359 
    360   if (EFI_ERROR (Status)) {
    361     return Status;
    362   }
    363 
    364   //
    365   // Add a default route if received from the server.
    366   //
    367   if ((DhcpSb->Para != NULL) && (DhcpSb->Para->Router != 0)) {
    368     ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));
    369 
    370     Ip = HTONL (DhcpSb->Para->Router);
    371     CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));
    372 
    373     UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);
    374   }
    375 
    376   return EFI_SUCCESS;
    377 }
    378 
    379 
    380 /**
    381   Update the lease states when a new lease is acquired. It will not only
    382   save the acquired the address and lease time, it will also create a UDP
    383   child to provide address resolution for the address.
    384 
    385   @param  DhcpSb                The DHCP service instance
    386 
    387   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources.
    388   @retval EFI_SUCCESS           The lease is recorded.
    389 
    390 **/
    391 EFI_STATUS
    392 DhcpLeaseAcquired (
    393   IN OUT DHCP_SERVICE           *DhcpSb
    394   )
    395 {
    396   INTN                      Class;
    397 
    398   DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
    399 
    400   if (DhcpSb->Para != NULL) {
    401     DhcpSb->Netmask     = DhcpSb->Para->NetMask;
    402     DhcpSb->ServerAddr  = DhcpSb->Para->ServerId;
    403   }
    404 
    405   if (DhcpSb->Netmask == 0) {
    406     Class           = NetGetIpClass (DhcpSb->ClientAddr);
    407     ASSERT (Class < IP4_ADDR_CLASSE);
    408     DhcpSb->Netmask = gIp4AllMasks[Class << 3];
    409   }
    410 
    411   if (DhcpSb->LeaseIoPort != NULL) {
    412     UdpIoFreeIo (DhcpSb->LeaseIoPort);
    413   }
    414 
    415   //
    416   // Create a UDP/IP child to provide ARP service for the Leased IP,
    417   // and transmit unicast packet with it as source address. Don't
    418   // start receive on this port, the queued packet will be timeout.
    419   //
    420   DhcpSb->LeaseIoPort = UdpIoCreateIo (
    421                           DhcpSb->Controller,
    422                           DhcpSb->Image,
    423                           DhcpConfigLeaseIoPort,
    424                           UDP_IO_UDP4_VERSION,
    425                           DhcpSb
    426                           );
    427 
    428   if (DhcpSb->LeaseIoPort == NULL) {
    429     return EFI_OUT_OF_RESOURCES;
    430   }
    431 
    432   if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
    433     DhcpComputeLease (DhcpSb, DhcpSb->Para);
    434   }
    435 
    436   return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
    437 }
    438 
    439 
    440 /**
    441   Clean up the DHCP related states, IoStatus isn't reset.
    442 
    443   @param  DhcpSb                The DHCP instance service.
    444 
    445 **/
    446 VOID
    447 DhcpCleanLease (
    448   IN DHCP_SERVICE           *DhcpSb
    449   )
    450 {
    451   DhcpSb->DhcpState   = Dhcp4Init;
    452   DhcpSb->Xid         = DhcpSb->Xid + 1;
    453   DhcpSb->ClientAddr  = 0;
    454   DhcpSb->Netmask     = 0;
    455   DhcpSb->ServerAddr  = 0;
    456 
    457   if (DhcpSb->LastOffer != NULL) {
    458     FreePool (DhcpSb->LastOffer);
    459     DhcpSb->LastOffer = NULL;
    460   }
    461 
    462   if (DhcpSb->Selected != NULL) {
    463     FreePool (DhcpSb->Selected);
    464     DhcpSb->Selected = NULL;
    465   }
    466 
    467   if (DhcpSb->Para != NULL) {
    468     FreePool (DhcpSb->Para);
    469     DhcpSb->Para = NULL;
    470   }
    471 
    472   DhcpSb->Lease         = 0;
    473   DhcpSb->T1            = 0;
    474   DhcpSb->T2            = 0;
    475   DhcpSb->ExtraRefresh  = FALSE;
    476 
    477   if (DhcpSb->LeaseIoPort != NULL) {
    478     UdpIoFreeIo (DhcpSb->LeaseIoPort);
    479     DhcpSb->LeaseIoPort = NULL;
    480   }
    481 
    482   if (DhcpSb->LastPacket != NULL) {
    483     FreePool (DhcpSb->LastPacket);
    484     DhcpSb->LastPacket = NULL;
    485   }
    486 
    487   DhcpSb->PacketToLive  = 0;
    488   DhcpSb->LastTimeout   = 0;
    489   DhcpSb->CurRetry      = 0;
    490   DhcpSb->MaxRetries    = 0;
    491   DhcpSb->LeaseLife     = 0;
    492 
    493   //
    494   // Clean active config data.
    495   //
    496   DhcpCleanConfigure (&DhcpSb->ActiveConfig);
    497 }
    498 
    499 
    500 /**
    501   Select a offer among all the offers collected. If the offer selected is
    502   of BOOTP, the lease is recorded and user notified. If the offer is of
    503   DHCP, it will request the offer from the server.
    504 
    505   @param[in]  DhcpSb                The DHCP service instance.
    506 
    507   @retval EFI_SUCCESS           One of the offer is selected.
    508 
    509 **/
    510 EFI_STATUS
    511 DhcpChooseOffer (
    512   IN DHCP_SERVICE           *DhcpSb
    513   )
    514 {
    515   EFI_DHCP4_PACKET          *Selected;
    516   EFI_DHCP4_PACKET          *NewPacket;
    517   EFI_DHCP4_PACKET          *TempPacket;
    518   EFI_STATUS                Status;
    519 
    520   ASSERT (DhcpSb->LastOffer != NULL);
    521 
    522   //
    523   // User will cache previous offers if he wants to select
    524   // from multiple offers. If user provides an invalid packet,
    525   // use the last offer, otherwise use the provided packet.
    526   //
    527   NewPacket = NULL;
    528   Status    = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
    529 
    530   if (EFI_ERROR (Status)) {
    531     return Status;
    532   }
    533 
    534   Selected = DhcpSb->LastOffer;
    535 
    536   if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
    537     TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
    538     if (TempPacket != NULL) {
    539       CopyMem (TempPacket, NewPacket, NewPacket->Size);
    540       FreePool (Selected);
    541       Selected = TempPacket;
    542     }
    543   }
    544 
    545   DhcpSb->Selected  = Selected;
    546   DhcpSb->LastOffer = NULL;
    547   DhcpSb->Para      = NULL;
    548   DhcpValidateOptions (Selected, &DhcpSb->Para);
    549 
    550   //
    551   // A bootp offer has been selected, save the lease status,
    552   // enter bound state then notify the user.
    553   //
    554   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
    555     Status = DhcpLeaseAcquired (DhcpSb);
    556 
    557     if (EFI_ERROR (Status)) {
    558       return Status;
    559     }
    560 
    561     DhcpSb->IoStatus = EFI_SUCCESS;
    562     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
    563     return EFI_SUCCESS;
    564   }
    565 
    566   //
    567   // Send a DHCP requests
    568   //
    569   Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
    570 
    571   if (EFI_ERROR (Status)) {
    572     return Status;
    573   }
    574 
    575   return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
    576 }
    577 
    578 
    579 /**
    580   Terminate the current address acquire. All the allocated resources
    581   are released. Be careful when calling this function. A rule related
    582   to this is: only call DhcpEndSession at the highest level, such as
    583   DhcpInput, DhcpOnTimerTick...At the other level, just return error.
    584 
    585   @param[in]  DhcpSb                The DHCP service instance
    586   @param[in]  Status                The result of the DHCP process.
    587 
    588 **/
    589 VOID
    590 DhcpEndSession (
    591   IN DHCP_SERVICE           *DhcpSb,
    592   IN EFI_STATUS             Status
    593   )
    594 {
    595   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
    596     DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
    597   } else {
    598     DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
    599   }
    600 
    601   DhcpCleanLease (DhcpSb);
    602 
    603   DhcpSb->IoStatus = Status;
    604   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
    605 }
    606 
    607 
    608 /**
    609   Handle packets in DHCP select state.
    610 
    611   @param[in]  DhcpSb                The DHCP service instance
    612   @param[in]  Packet                The DHCP packet received
    613   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    614                                     is, all the option value that we care.
    615 
    616   @retval EFI_SUCCESS           The packet is successfully processed.
    617   @retval Others                Some error occured.
    618 
    619 **/
    620 EFI_STATUS
    621 DhcpHandleSelect (
    622   IN DHCP_SERVICE           *DhcpSb,
    623   IN EFI_DHCP4_PACKET       *Packet,
    624   IN DHCP_PARAMETER         *Para
    625   )
    626 {
    627   EFI_STATUS                Status;
    628 
    629   Status = EFI_SUCCESS;
    630 
    631   //
    632   // First validate the message:
    633   // 1. the offer is a unicast
    634   // 2. if it is a DHCP message, it must contains a server ID.
    635   // Don't return a error for these two case otherwise the session is ended.
    636   //
    637   if (!DHCP_IS_BOOTP (Para) &&
    638       ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
    639       ) {
    640     goto ON_EXIT;
    641   }
    642 
    643   //
    644   // Call the user's callback. The action according to the return is as:
    645   // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
    646   // 2. EFI_NOT_READY: wait for more offers
    647   // 3. EFI_ABORTED: abort the address acquiring.
    648   //
    649   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
    650 
    651   if (Status == EFI_SUCCESS) {
    652     if (DhcpSb->LastOffer != NULL) {
    653       FreePool (DhcpSb->LastOffer);
    654     }
    655 
    656     DhcpSb->LastOffer = Packet;
    657 
    658     return DhcpChooseOffer (DhcpSb);
    659 
    660   } else if (Status == EFI_NOT_READY) {
    661     if (DhcpSb->LastOffer != NULL) {
    662       FreePool (DhcpSb->LastOffer);
    663     }
    664 
    665     DhcpSb->LastOffer = Packet;
    666 
    667   } else if (Status == EFI_ABORTED) {
    668     //
    669     // DhcpInput will end the session upon error return. Remember
    670     // only to call DhcpEndSession at the top level call.
    671     //
    672     goto ON_EXIT;
    673   }
    674 
    675   return EFI_SUCCESS;
    676 
    677 ON_EXIT:
    678   FreePool (Packet);
    679   return Status;
    680 }
    681 
    682 
    683 /**
    684   Handle packets in DHCP request state.
    685 
    686   @param[in]  DhcpSb                The DHCP service instance
    687   @param[in]  Packet                The DHCP packet received
    688   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    689                                     is, all the option value that we care.
    690 
    691   @retval EFI_SUCCESS           The packet is successfully processed.
    692   @retval Others                Some error occured.
    693 
    694 **/
    695 EFI_STATUS
    696 DhcpHandleRequest (
    697   IN DHCP_SERVICE           *DhcpSb,
    698   IN EFI_DHCP4_PACKET       *Packet,
    699   IN DHCP_PARAMETER         *Para
    700   )
    701 {
    702   EFI_DHCP4_HEADER          *Head;
    703   EFI_DHCP4_HEADER          *Selected;
    704   EFI_STATUS                Status;
    705   UINT8                     *Message;
    706 
    707   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
    708 
    709   Head      = &Packet->Dhcp4.Header;
    710   Selected  = &DhcpSb->Selected->Dhcp4.Header;
    711 
    712   //
    713   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
    714   //
    715   if (DHCP_IS_BOOTP (Para) ||
    716       (Para->ServerId != DhcpSb->Para->ServerId) ||
    717       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    718       ) {
    719 
    720     Status = EFI_SUCCESS;
    721     goto ON_EXIT;
    722   }
    723 
    724   //
    725   // Received a NAK, end the session no matter what the user returns
    726   //
    727   Status = EFI_DEVICE_ERROR;
    728 
    729   if (Para->DhcpType == DHCP_MSG_NAK) {
    730     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    731     goto ON_EXIT;
    732   }
    733 
    734   //
    735   // Check whether the ACK matches the selected offer
    736   //
    737   Message = NULL;
    738 
    739   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
    740     Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
    741     goto REJECT;
    742   }
    743 
    744   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    745 
    746   if (EFI_ERROR (Status)) {
    747     Message = (UINT8 *) "Lease is denied upon received ACK";
    748     goto REJECT;
    749   }
    750 
    751   //
    752   // Record the lease, transit to BOUND state, then notify the user
    753   //
    754   Status = DhcpLeaseAcquired (DhcpSb);
    755 
    756   if (EFI_ERROR (Status)) {
    757     Message = (UINT8 *) "Lease is denied upon entering bound";
    758     goto REJECT;
    759   }
    760 
    761   DhcpSb->IoStatus = EFI_SUCCESS;
    762   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
    763 
    764   FreePool (Packet);
    765   return EFI_SUCCESS;
    766 
    767 REJECT:
    768   DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
    769 
    770 ON_EXIT:
    771   FreePool (Packet);
    772   return Status;
    773 }
    774 
    775 
    776 /**
    777   Handle packets in DHCP renew/rebound state.
    778 
    779   @param[in]  DhcpSb                The DHCP service instance
    780   @param[in]  Packet                The DHCP packet received
    781   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    782                                     is, all the option value that we care.
    783 
    784   @retval EFI_SUCCESS           The packet is successfully processed.
    785   @retval Others                Some error occured.
    786 
    787 **/
    788 EFI_STATUS
    789 DhcpHandleRenewRebind (
    790   IN DHCP_SERVICE           *DhcpSb,
    791   IN EFI_DHCP4_PACKET       *Packet,
    792   IN DHCP_PARAMETER         *Para
    793   )
    794 {
    795   EFI_DHCP4_HEADER          *Head;
    796   EFI_DHCP4_HEADER          *Selected;
    797   EFI_STATUS                Status;
    798 
    799   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
    800 
    801   Head      = &Packet->Dhcp4.Header;
    802   Selected  = &DhcpSb->Selected->Dhcp4.Header;
    803 
    804   //
    805   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
    806   //
    807   if (DHCP_IS_BOOTP (Para) ||
    808       (Para->ServerId != DhcpSb->Para->ServerId) ||
    809       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    810       ) {
    811 
    812     Status = EFI_SUCCESS;
    813     goto ON_EXIT;
    814   }
    815 
    816   //
    817   // Received a NAK, ignore the user's return then terminate the process
    818   //
    819   Status = EFI_DEVICE_ERROR;
    820 
    821   if (Para->DhcpType == DHCP_MSG_NAK) {
    822     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    823     goto ON_EXIT;
    824   }
    825 
    826   //
    827   // The lease is different from the selected. Don't send a DECLINE
    828   // since it isn't existed in the client's FSM.
    829   //
    830   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
    831     goto ON_EXIT;
    832   }
    833 
    834   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    835 
    836   if (EFI_ERROR (Status)) {
    837     goto ON_EXIT;
    838   }
    839 
    840   //
    841   // Record the lease, start timer for T1 and T2,
    842   //
    843   DhcpComputeLease (DhcpSb, Para);
    844   DhcpSb->LeaseLife = 0;
    845   DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
    846 
    847   if (DhcpSb->ExtraRefresh != 0) {
    848     DhcpSb->ExtraRefresh  = FALSE;
    849 
    850     DhcpSb->IoStatus      = EFI_SUCCESS;
    851     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
    852   }
    853 
    854 ON_EXIT:
    855   FreePool (Packet);
    856   return Status;
    857 }
    858 
    859 
    860 /**
    861   Handle packets in DHCP reboot state.
    862 
    863   @param[in]  DhcpSb                The DHCP service instance
    864   @param[in]  Packet                The DHCP packet received
    865   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    866                                     is, all the option value that we care.
    867 
    868   @retval EFI_SUCCESS           The packet is successfully processed.
    869   @retval Others                Some error occured.
    870 
    871 **/
    872 EFI_STATUS
    873 DhcpHandleReboot (
    874   IN DHCP_SERVICE           *DhcpSb,
    875   IN EFI_DHCP4_PACKET       *Packet,
    876   IN DHCP_PARAMETER         *Para
    877   )
    878 {
    879   EFI_DHCP4_HEADER          *Head;
    880   EFI_STATUS                Status;
    881 
    882   Head = &Packet->Dhcp4.Header;
    883 
    884   //
    885   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
    886   //
    887   if (DHCP_IS_BOOTP (Para) ||
    888       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    889       ) {
    890 
    891     Status = EFI_SUCCESS;
    892     goto ON_EXIT;
    893   }
    894 
    895   //
    896   // If a NAK is received, transit to INIT and try again.
    897   //
    898   if (Para->DhcpType == DHCP_MSG_NAK) {
    899     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    900 
    901     DhcpSb->ClientAddr  = 0;
    902     DhcpSb->DhcpState   = Dhcp4Init;
    903 
    904     Status              = DhcpInitRequest (DhcpSb);
    905     goto ON_EXIT;
    906   }
    907 
    908   //
    909   // Check whether the ACK matches the selected offer
    910   //
    911   if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
    912     Status = EFI_DEVICE_ERROR;
    913     goto ON_EXIT;
    914   }
    915 
    916   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    917   if (EFI_ERROR (Status)) {
    918     goto ON_EXIT;
    919   }
    920 
    921   //
    922   // OK, get the parameter from server, record the lease
    923   //
    924   DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
    925   if (DhcpSb->Para == NULL) {
    926     Status = EFI_OUT_OF_RESOURCES;
    927     goto ON_EXIT;
    928   }
    929 
    930   DhcpSb->Selected  = Packet;
    931   Status            = DhcpLeaseAcquired (DhcpSb);
    932   if (EFI_ERROR (Status)) {
    933     return Status;
    934   }
    935 
    936   DhcpSb->IoStatus = EFI_SUCCESS;
    937   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
    938   return EFI_SUCCESS;
    939 
    940 ON_EXIT:
    941   FreePool (Packet);
    942   return Status;
    943 }
    944 
    945 
    946 /**
    947   Handle the received DHCP packets. This function drives the DHCP
    948   state machine.
    949 
    950   @param  UdpPacket             The UDP packets received.
    951   @param  EndPoint              The local/remote UDP access point
    952   @param  IoStatus              The status of the UDP receive
    953   @param  Context               The opaque parameter to the function.
    954 
    955 **/
    956 VOID
    957 EFIAPI
    958 DhcpInput (
    959   NET_BUF                   *UdpPacket,
    960   UDP_END_POINT             *EndPoint,
    961   EFI_STATUS                IoStatus,
    962   VOID                      *Context
    963   )
    964 {
    965   DHCP_SERVICE              *DhcpSb;
    966   EFI_DHCP4_HEADER          *Head;
    967   EFI_DHCP4_PACKET          *Packet;
    968   DHCP_PARAMETER            *Para;
    969   EFI_STATUS                Status;
    970   UINT32                    Len;
    971 
    972   Packet  = NULL;
    973   DhcpSb  = (DHCP_SERVICE *) Context;
    974 
    975   //
    976   // Don't restart receive if error occurs or DHCP is destroyed.
    977   //
    978   if (EFI_ERROR (IoStatus)) {
    979     return ;
    980   } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
    981     NetbufFree (UdpPacket);
    982     return ;
    983   }
    984 
    985   ASSERT (UdpPacket != NULL);
    986 
    987   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    988     goto RESTART;
    989   }
    990 
    991   //
    992   // Validate the packet received
    993   //
    994   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
    995     goto RESTART;
    996   }
    997 
    998   //
    999   // Copy the DHCP message to a continuous memory block
   1000   //
   1001   Len     = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
   1002   Packet  = (EFI_DHCP4_PACKET *) AllocatePool (Len);
   1003 
   1004   if (Packet == NULL) {
   1005     goto RESTART;
   1006   }
   1007 
   1008   Packet->Size    = Len;
   1009   Head            = &Packet->Dhcp4.Header;
   1010   Packet->Length  = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
   1011 
   1012   if (Packet->Length != UdpPacket->TotalSize) {
   1013     goto RESTART;
   1014   }
   1015 
   1016   //
   1017   // Is this packet the answer to our packet?
   1018   //
   1019   if ((Head->OpCode != BOOTP_REPLY) ||
   1020       (NTOHL (Head->Xid) != DhcpSb->Xid) ||
   1021       (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
   1022     goto RESTART;
   1023   }
   1024 
   1025   //
   1026   // Validate the options and retrieve the interested options
   1027   //
   1028   Para = NULL;
   1029   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
   1030       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
   1031       EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
   1032 
   1033     goto RESTART;
   1034   }
   1035 
   1036   //
   1037   // Call the handler for each state. The handler should return
   1038   // EFI_SUCCESS if the process can go on no matter whether the
   1039   // packet is ignored or not. If the return is EFI_ERROR, the
   1040   // session will be terminated. Packet's ownership is handled
   1041   // over to the handlers. If operation succeeds, the handler
   1042   // must notify the user. It isn't necessary to do if EFI_ERROR
   1043   // is returned because the DhcpEndSession will notify the user.
   1044   //
   1045   Status = EFI_SUCCESS;
   1046 
   1047   switch (DhcpSb->DhcpState) {
   1048   case Dhcp4Selecting:
   1049     Status = DhcpHandleSelect (DhcpSb, Packet, Para);
   1050     break;
   1051 
   1052   case Dhcp4Requesting:
   1053     Status = DhcpHandleRequest (DhcpSb, Packet, Para);
   1054     break;
   1055 
   1056   case Dhcp4InitReboot:
   1057   case Dhcp4Init:
   1058   case Dhcp4Bound:
   1059     //
   1060     // Ignore the packet in INITREBOOT, INIT and BOUND states
   1061     //
   1062     FreePool (Packet);
   1063     Status = EFI_SUCCESS;
   1064     break;
   1065 
   1066   case Dhcp4Renewing:
   1067   case Dhcp4Rebinding:
   1068     Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
   1069     break;
   1070 
   1071   case Dhcp4Rebooting:
   1072     Status = DhcpHandleReboot (DhcpSb, Packet, Para);
   1073     break;
   1074   }
   1075 
   1076   if (Para != NULL) {
   1077     FreePool (Para);
   1078   }
   1079 
   1080   Packet = NULL;
   1081 
   1082   if (EFI_ERROR (Status)) {
   1083     NetbufFree (UdpPacket);
   1084     UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
   1085     DhcpEndSession (DhcpSb, Status);
   1086     return ;
   1087   }
   1088 
   1089 RESTART:
   1090   NetbufFree (UdpPacket);
   1091 
   1092   if (Packet != NULL) {
   1093     FreePool (Packet);
   1094   }
   1095 
   1096   Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
   1097 
   1098   if (EFI_ERROR (Status)) {
   1099     DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
   1100   }
   1101 }
   1102 
   1103 
   1104 /**
   1105   Release the packet.
   1106 
   1107   @param[in]  Arg                   The packet to release
   1108 
   1109 **/
   1110 VOID
   1111 EFIAPI
   1112 DhcpReleasePacket (
   1113   IN VOID                   *Arg
   1114   )
   1115 {
   1116   FreePool (Arg);
   1117 }
   1118 
   1119 
   1120 /**
   1121   Release the net buffer when packet is sent.
   1122 
   1123   @param  UdpPacket             The UDP packets received.
   1124   @param  EndPoint              The local/remote UDP access point
   1125   @param  IoStatus              The status of the UDP receive
   1126   @param  Context               The opaque parameter to the function.
   1127 
   1128 **/
   1129 VOID
   1130 EFIAPI
   1131 DhcpOnPacketSent (
   1132   NET_BUF                   *Packet,
   1133   UDP_END_POINT             *EndPoint,
   1134   EFI_STATUS                IoStatus,
   1135   VOID                      *Context
   1136   )
   1137 {
   1138   NetbufFree (Packet);
   1139 }
   1140 
   1141 
   1142 
   1143 /**
   1144   Build and transmit a DHCP message according to the current states.
   1145   This function implement the Table 5. of RFC 2131. Always transits
   1146   the state (as defined in Figure 5. of the same RFC) before sending
   1147   a DHCP message. The table is adjusted accordingly.
   1148 
   1149   @param[in]  DhcpSb                The DHCP service instance
   1150   @param[in]  Seed                  The seed packet which the new packet is based on
   1151   @param[in]  Para                  The DHCP parameter of the Seed packet
   1152   @param[in]  Type                  The message type to send
   1153   @param[in]  Msg                   The human readable message to include in the packet
   1154                                     sent.
   1155 
   1156   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources for the packet
   1157   @retval EFI_ACCESS_DENIED     Failed to transmit the packet through UDP
   1158   @retval EFI_SUCCESS           The message is sent
   1159   @retval other                 Other error occurs
   1160 
   1161 **/
   1162 EFI_STATUS
   1163 DhcpSendMessage (
   1164   IN DHCP_SERVICE           *DhcpSb,
   1165   IN EFI_DHCP4_PACKET       *Seed,
   1166   IN DHCP_PARAMETER         *Para,
   1167   IN UINT8                  Type,
   1168   IN UINT8                  *Msg
   1169   )
   1170 {
   1171   EFI_DHCP4_CONFIG_DATA     *Config;
   1172   EFI_DHCP4_PACKET          *Packet;
   1173   EFI_DHCP4_PACKET          *NewPacket;
   1174   EFI_DHCP4_HEADER          *Head;
   1175   EFI_DHCP4_HEADER          *SeedHead;
   1176   UDP_IO                    *UdpIo;
   1177   UDP_END_POINT             EndPoint;
   1178   NET_BUF                   *Wrap;
   1179   NET_FRAGMENT              Frag;
   1180   EFI_STATUS                Status;
   1181   IP4_ADDR                  IpAddr;
   1182   UINT8                     *Buf;
   1183   UINT16                    MaxMsg;
   1184   UINT32                    Len;
   1185   UINT32                    Index;
   1186 
   1187   //
   1188   // Allocate a big enough memory block to hold the DHCP packet
   1189   //
   1190   Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
   1191 
   1192   if (Msg != NULL) {
   1193     Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
   1194   }
   1195 
   1196   Packet = AllocatePool (Len);
   1197 
   1198   if (Packet == NULL) {
   1199     return EFI_OUT_OF_RESOURCES;
   1200   }
   1201 
   1202   Packet->Size    = Len;
   1203   Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
   1204 
   1205   //
   1206   // Fill in the DHCP header fields
   1207   //
   1208   Config    = &DhcpSb->ActiveConfig;
   1209   SeedHead  = NULL;
   1210 
   1211   if (Seed != NULL) {
   1212     SeedHead = &Seed->Dhcp4.Header;
   1213   }
   1214 
   1215   Head = &Packet->Dhcp4.Header;
   1216   ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
   1217 
   1218   Head->OpCode       = BOOTP_REQUEST;
   1219   Head->HwType       = DhcpSb->HwType;
   1220   Head->HwAddrLen    = DhcpSb->HwLen;
   1221   Head->Xid          = HTONL (DhcpSb->Xid);
   1222   Head->Reserved     = HTONS (0x8000);  //Server, broadcast the message please.
   1223 
   1224   EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
   1225   CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
   1226 
   1227   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
   1228     Head->Seconds = 0;
   1229   } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
   1230     //
   1231     // Use the same value as the original DHCPDISCOVER message.
   1232     //
   1233     Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
   1234   } else {
   1235     SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);
   1236   }
   1237 
   1238   //
   1239   // Append the DHCP message type
   1240   //
   1241   Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
   1242   Buf                 = Packet->Dhcp4.Option;
   1243   Buf                 = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);
   1244 
   1245   //
   1246   // Append the serverid option if necessary:
   1247   //   1. DHCP decline message
   1248   //   2. DHCP release message
   1249   //   3. DHCP request to confirm one lease.
   1250   //
   1251   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
   1252       ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
   1253       ) {
   1254 
   1255     ASSERT ((Para != NULL) && (Para->ServerId != 0));
   1256 
   1257     IpAddr  = HTONL (Para->ServerId);
   1258     Buf     = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
   1259   }
   1260 
   1261   //
   1262   // Append the requested IP option if necessary:
   1263   //   1. DHCP request to use the previously allocated address
   1264   //   2. DHCP request to confirm one lease
   1265   //   3. DHCP decline to decline one lease
   1266   //
   1267   IpAddr = 0;
   1268 
   1269   if (Type == DHCP_MSG_REQUEST) {
   1270     if (DhcpSb->DhcpState == Dhcp4Rebooting) {
   1271       IpAddr = EFI_IP4 (Config->ClientAddress);
   1272 
   1273     } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
   1274       ASSERT (SeedHead != NULL);
   1275       IpAddr = EFI_IP4 (SeedHead->YourAddr);
   1276     }
   1277 
   1278   } else if (Type == DHCP_MSG_DECLINE) {
   1279     ASSERT (SeedHead != NULL);
   1280     IpAddr = EFI_IP4 (SeedHead->YourAddr);
   1281   }
   1282 
   1283   if (IpAddr != 0) {
   1284     Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
   1285   }
   1286 
   1287   //
   1288   // Append the Max Message Length option if it isn't a DECLINE
   1289   // or RELEASE to direct the server use large messages instead of
   1290   // override the BOOTFILE and SERVER fields in the message head.
   1291   //
   1292   if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
   1293     MaxMsg  = HTONS (0xFF00);
   1294     Buf     = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
   1295   }
   1296 
   1297   //
   1298   // Append the user's message if it isn't NULL
   1299   //
   1300   if (Msg != NULL) {
   1301     Len     = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
   1302     Buf     = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);
   1303   }
   1304 
   1305   //
   1306   // Append the user configured options
   1307   //
   1308   if (DhcpSb->UserOptionLen != 0) {
   1309     for (Index = 0; Index < Config->OptionCount; Index++) {
   1310       //
   1311       // We can't use any option other than the client ID from user
   1312       // if it is a DHCP decline or DHCP release .
   1313       //
   1314       if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
   1315           (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {
   1316         continue;
   1317       }
   1318 
   1319       Buf = DhcpAppendOption (
   1320               Buf,
   1321               Config->OptionList[Index]->OpCode,
   1322               Config->OptionList[Index]->Length,
   1323               Config->OptionList[Index]->Data
   1324               );
   1325     }
   1326   }
   1327 
   1328   *(Buf++) = DHCP_TAG_EOP;
   1329   Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
   1330 
   1331   //
   1332   // OK, the message is built, call the user to override it.
   1333   //
   1334   Status    = EFI_SUCCESS;
   1335   NewPacket = NULL;
   1336 
   1337   if (Type == DHCP_MSG_DISCOVER) {
   1338     Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
   1339 
   1340   } else if (Type == DHCP_MSG_REQUEST) {
   1341     Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
   1342 
   1343   } else if (Type == DHCP_MSG_DECLINE) {
   1344     Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
   1345   }
   1346 
   1347   if (EFI_ERROR (Status)) {
   1348     FreePool (Packet);
   1349     return Status;
   1350   }
   1351 
   1352   if (NewPacket != NULL) {
   1353     FreePool (Packet);
   1354     Packet = NewPacket;
   1355   }
   1356 
   1357   //
   1358   // Save the Client Address will be sent out
   1359   //
   1360   CopyMem (
   1361     &DhcpSb->ClientAddressSendOut[0],
   1362     &Packet->Dhcp4.Header.ClientHwAddr[0],
   1363     Packet->Dhcp4.Header.HwAddrLen
   1364     );
   1365 
   1366 
   1367   //
   1368   // Wrap it into a netbuf then send it.
   1369   //
   1370   Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
   1371   Frag.Len  = Packet->Length;
   1372   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
   1373 
   1374   if (Wrap == NULL) {
   1375     FreePool (Packet);
   1376     return EFI_OUT_OF_RESOURCES;
   1377   }
   1378 
   1379   //
   1380   // Save it as the last sent packet for retransmission
   1381   //
   1382   if (DhcpSb->LastPacket != NULL) {
   1383     FreePool (DhcpSb->LastPacket);
   1384   }
   1385 
   1386   DhcpSb->LastPacket = Packet;
   1387   DhcpSetTransmitTimer (DhcpSb);
   1388 
   1389   //
   1390   // Broadcast the message, unless we know the server address.
   1391   // Use the lease UdpIo port to send the unicast packet.
   1392   //
   1393   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
   1394   EndPoint.LocalAddr.Addr[0]  = 0;
   1395   EndPoint.RemotePort         = DHCP_SERVER_PORT;
   1396   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
   1397   UdpIo                       = DhcpSb->UdpIo;
   1398 
   1399   if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
   1400     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
   1401     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
   1402     UdpIo                       = DhcpSb->LeaseIoPort;
   1403   }
   1404 
   1405   ASSERT (UdpIo != NULL);
   1406   NET_GET_REF (Wrap);
   1407 
   1408   Status = UdpIoSendDatagram (
   1409              UdpIo,
   1410              Wrap,
   1411              &EndPoint,
   1412              NULL,
   1413              DhcpOnPacketSent,
   1414              DhcpSb
   1415              );
   1416 
   1417   if (EFI_ERROR (Status)) {
   1418     NET_PUT_REF (Wrap);
   1419     return EFI_ACCESS_DENIED;
   1420   }
   1421 
   1422   return EFI_SUCCESS;
   1423 }
   1424 
   1425 
   1426 /**
   1427   Retransmit a saved packet. Only DISCOVER and REQUEST messages
   1428   will be retransmitted.
   1429 
   1430   @param[in]  DhcpSb                The DHCP service instance
   1431 
   1432   @retval EFI_ACCESS_DENIED     Failed to transmit packet through UDP port
   1433   @retval EFI_SUCCESS           The packet is retransmitted.
   1434 
   1435 **/
   1436 EFI_STATUS
   1437 DhcpRetransmit (
   1438   IN DHCP_SERVICE           *DhcpSb
   1439   )
   1440 {
   1441   UDP_IO                    *UdpIo;
   1442   UDP_END_POINT             EndPoint;
   1443   NET_BUF                   *Wrap;
   1444   NET_FRAGMENT              Frag;
   1445   EFI_STATUS                Status;
   1446 
   1447   ASSERT (DhcpSb->LastPacket != NULL);
   1448 
   1449   //
   1450   // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
   1451   //
   1452   if (DhcpSb->DhcpState != Dhcp4Requesting) {
   1453     SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
   1454   }
   1455 
   1456   //
   1457   // Wrap it into a netbuf then send it.
   1458   //
   1459   Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
   1460   Frag.Len  = DhcpSb->LastPacket->Length;
   1461   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);
   1462 
   1463   if (Wrap == NULL) {
   1464     return EFI_OUT_OF_RESOURCES;
   1465   }
   1466 
   1467   //
   1468   // Broadcast the message, unless we know the server address.
   1469   //
   1470   EndPoint.RemotePort         = DHCP_SERVER_PORT;
   1471   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
   1472   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
   1473   EndPoint.LocalAddr.Addr[0]  = 0;
   1474   UdpIo                       = DhcpSb->UdpIo;
   1475 
   1476   if (DhcpSb->DhcpState == Dhcp4Renewing) {
   1477     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
   1478     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
   1479     UdpIo                       = DhcpSb->LeaseIoPort;
   1480   }
   1481 
   1482   ASSERT (UdpIo != NULL);
   1483 
   1484   NET_GET_REF (Wrap);
   1485   Status = UdpIoSendDatagram (
   1486              UdpIo,
   1487              Wrap,
   1488              &EndPoint,
   1489              NULL,
   1490              DhcpOnPacketSent,
   1491              DhcpSb
   1492              );
   1493 
   1494   if (EFI_ERROR (Status)) {
   1495     NET_PUT_REF (Wrap);
   1496     return EFI_ACCESS_DENIED;
   1497   }
   1498 
   1499   return EFI_SUCCESS;
   1500 }
   1501 
   1502 
   1503 /**
   1504   Each DHCP service has three timer. Two of them are count down timer.
   1505   One for the packet retransmission. The other is to collect the offers.
   1506   The third timer increaments the lease life which is compared to T1, T2,
   1507   and lease to determine the time to renew and rebind the lease.
   1508   DhcpOnTimerTick will be called once every second.
   1509 
   1510   @param[in]  Event                 The timer event
   1511   @param[in]  Context               The context, which is the DHCP service instance.
   1512 
   1513 **/
   1514 VOID
   1515 EFIAPI
   1516 DhcpOnTimerTick (
   1517   IN EFI_EVENT              Event,
   1518   IN VOID                   *Context
   1519   )
   1520 {
   1521   LIST_ENTRY                *Entry;
   1522   LIST_ENTRY                *Next;
   1523   DHCP_SERVICE              *DhcpSb;
   1524   DHCP_PROTOCOL             *Instance;
   1525   EFI_STATUS                Status;
   1526 
   1527   DhcpSb   = (DHCP_SERVICE *) Context;
   1528   Instance = DhcpSb->ActiveChild;
   1529 
   1530   //
   1531   // 0xffff is the maximum supported value for elapsed time according to RFC.
   1532   //
   1533   if (Instance != NULL && Instance->ElaspedTime < 0xffff) {
   1534     Instance->ElaspedTime++;
   1535   }
   1536 
   1537   //
   1538   // Check the retransmit timer
   1539   //
   1540   if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
   1541 
   1542     //
   1543     // Select offer at each timeout if any offer received.
   1544     //
   1545     if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
   1546 
   1547       Status = DhcpChooseOffer (DhcpSb);
   1548 
   1549       if (EFI_ERROR(Status)) {
   1550         if (DhcpSb->LastOffer != NULL) {
   1551           FreePool (DhcpSb->LastOffer);
   1552           DhcpSb->LastOffer = NULL;
   1553         }
   1554       } else {
   1555         goto ON_EXIT;
   1556       }
   1557     }
   1558 
   1559     if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
   1560       //
   1561       // Still has another try
   1562       //
   1563       DhcpRetransmit (DhcpSb);
   1564       DhcpSetTransmitTimer (DhcpSb);
   1565 
   1566     } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
   1567 
   1568       //
   1569       // Retransmission failed, if the DHCP request is initiated by
   1570       // user, adjust the current state according to the lease life.
   1571       // Otherwise do nothing to wait the lease to timeout
   1572       //
   1573       if (DhcpSb->ExtraRefresh != 0) {
   1574         Status = EFI_SUCCESS;
   1575 
   1576         if (DhcpSb->LeaseLife < DhcpSb->T1) {
   1577           Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
   1578 
   1579         } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
   1580           Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
   1581 
   1582         } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
   1583           Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
   1584 
   1585         } else {
   1586           goto END_SESSION;
   1587 
   1588         }
   1589 
   1590         DhcpSb->IoStatus = EFI_TIMEOUT;
   1591         DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
   1592       }
   1593     } else {
   1594       goto END_SESSION;
   1595     }
   1596   }
   1597 
   1598   //
   1599   // If an address has been acquired, check whether need to
   1600   // refresh or whether it has expired.
   1601   //
   1602   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
   1603     DhcpSb->LeaseLife++;
   1604 
   1605     //
   1606     // Don't timeout the lease, only count the life if user is
   1607     // requesting extra renew/rebind. Adjust the state after that.
   1608     //
   1609     if (DhcpSb->ExtraRefresh != 0) {
   1610       return ;
   1611     }
   1612 
   1613     if (DhcpSb->LeaseLife == DhcpSb->Lease) {
   1614       //
   1615       // Lease expires, end the session
   1616       //
   1617       goto END_SESSION;
   1618 
   1619     } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
   1620       //
   1621       // T2 expires, transit to rebinding then send a REQUEST to any server
   1622       //
   1623       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
   1624         goto END_SESSION;
   1625       }
   1626 
   1627       if (Instance != NULL) {
   1628         Instance->ElaspedTime= 0;
   1629       }
   1630 
   1631       Status = DhcpSendMessage (
   1632                  DhcpSb,
   1633                  DhcpSb->Selected,
   1634                  DhcpSb->Para,
   1635                  DHCP_MSG_REQUEST,
   1636                  NULL
   1637                  );
   1638 
   1639       if (EFI_ERROR (Status)) {
   1640         goto END_SESSION;
   1641       }
   1642 
   1643     } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
   1644       //
   1645       // T1 expires, transit to renewing, then send a REQUEST to the server
   1646       //
   1647       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
   1648         goto END_SESSION;
   1649       }
   1650 
   1651       if (Instance != NULL) {
   1652         Instance->ElaspedTime= 0;
   1653       }
   1654 
   1655       Status = DhcpSendMessage (
   1656                  DhcpSb,
   1657                  DhcpSb->Selected,
   1658                  DhcpSb->Para,
   1659                  DHCP_MSG_REQUEST,
   1660                  NULL
   1661                  );
   1662 
   1663       if (EFI_ERROR (Status)) {
   1664         goto END_SESSION;
   1665       }
   1666     }
   1667   }
   1668 
   1669 ON_EXIT:
   1670   //
   1671   // Iterate through all the DhcpSb Children.
   1672   //
   1673   NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
   1674     Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
   1675 
   1676     if ((Instance != NULL) && (Instance->Token != NULL)) {
   1677       Instance->Timeout--;
   1678       if (Instance->Timeout == 0) {
   1679         PxeDhcpDone (Instance);
   1680       }
   1681     }
   1682   }
   1683 
   1684   return ;
   1685 
   1686 END_SESSION:
   1687   DhcpEndSession (DhcpSb, EFI_TIMEOUT);
   1688 
   1689   return ;
   1690 }
   1691