Home | History | Annotate | Download | only in Dhcp4Dxe
      1 /** @file
      2   EFI DHCP protocol implementation.
      3 
      4 Copyright (c) 2006 - 2016, 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   DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);
    397 
    398   if (DhcpSb->Para != NULL) {
    399     DhcpSb->Netmask     = DhcpSb->Para->NetMask;
    400     DhcpSb->ServerAddr  = DhcpSb->Para->ServerId;
    401   }
    402 
    403   if (DhcpSb->Netmask == 0) {
    404     return EFI_ABORTED;
    405   }
    406 
    407   if (DhcpSb->LeaseIoPort != NULL) {
    408     UdpIoFreeIo (DhcpSb->LeaseIoPort);
    409   }
    410 
    411   //
    412   // Create a UDP/IP child to provide ARP service for the Leased IP,
    413   // and transmit unicast packet with it as source address. Don't
    414   // start receive on this port, the queued packet will be timeout.
    415   //
    416   DhcpSb->LeaseIoPort = UdpIoCreateIo (
    417                           DhcpSb->Controller,
    418                           DhcpSb->Image,
    419                           DhcpConfigLeaseIoPort,
    420                           UDP_IO_UDP4_VERSION,
    421                           DhcpSb
    422                           );
    423 
    424   if (DhcpSb->LeaseIoPort == NULL) {
    425     return EFI_OUT_OF_RESOURCES;
    426   }
    427 
    428   if (!DHCP_IS_BOOTP (DhcpSb->Para)) {
    429     DhcpComputeLease (DhcpSb, DhcpSb->Para);
    430   }
    431 
    432   return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
    433 }
    434 
    435 
    436 /**
    437   Clean up the DHCP related states, IoStatus isn't reset.
    438 
    439   @param  DhcpSb                The DHCP instance service.
    440 
    441 **/
    442 VOID
    443 DhcpCleanLease (
    444   IN DHCP_SERVICE           *DhcpSb
    445   )
    446 {
    447   DhcpSb->DhcpState   = Dhcp4Init;
    448   DhcpSb->Xid         = DhcpSb->Xid + 1;
    449   DhcpSb->ClientAddr  = 0;
    450   DhcpSb->Netmask     = 0;
    451   DhcpSb->ServerAddr  = 0;
    452 
    453   if (DhcpSb->LastOffer != NULL) {
    454     FreePool (DhcpSb->LastOffer);
    455     DhcpSb->LastOffer = NULL;
    456   }
    457 
    458   if (DhcpSb->Selected != NULL) {
    459     FreePool (DhcpSb->Selected);
    460     DhcpSb->Selected = NULL;
    461   }
    462 
    463   if (DhcpSb->Para != NULL) {
    464     FreePool (DhcpSb->Para);
    465     DhcpSb->Para = NULL;
    466   }
    467 
    468   DhcpSb->Lease         = 0;
    469   DhcpSb->T1            = 0;
    470   DhcpSb->T2            = 0;
    471   DhcpSb->ExtraRefresh  = FALSE;
    472 
    473   if (DhcpSb->LeaseIoPort != NULL) {
    474     UdpIoFreeIo (DhcpSb->LeaseIoPort);
    475     DhcpSb->LeaseIoPort = NULL;
    476   }
    477 
    478   if (DhcpSb->LastPacket != NULL) {
    479     FreePool (DhcpSb->LastPacket);
    480     DhcpSb->LastPacket = NULL;
    481   }
    482 
    483   DhcpSb->PacketToLive  = 0;
    484   DhcpSb->LastTimeout   = 0;
    485   DhcpSb->CurRetry      = 0;
    486   DhcpSb->MaxRetries    = 0;
    487   DhcpSb->LeaseLife     = 0;
    488 
    489   //
    490   // Clean active config data.
    491   //
    492   DhcpCleanConfigure (&DhcpSb->ActiveConfig);
    493 }
    494 
    495 
    496 /**
    497   Select a offer among all the offers collected. If the offer selected is
    498   of BOOTP, the lease is recorded and user notified. If the offer is of
    499   DHCP, it will request the offer from the server.
    500 
    501   @param[in]  DhcpSb                The DHCP service instance.
    502 
    503   @retval EFI_SUCCESS           One of the offer is selected.
    504 
    505 **/
    506 EFI_STATUS
    507 DhcpChooseOffer (
    508   IN DHCP_SERVICE           *DhcpSb
    509   )
    510 {
    511   EFI_DHCP4_PACKET          *Selected;
    512   EFI_DHCP4_PACKET          *NewPacket;
    513   EFI_DHCP4_PACKET          *TempPacket;
    514   EFI_STATUS                Status;
    515 
    516   ASSERT (DhcpSb->LastOffer != NULL);
    517 
    518   //
    519   // User will cache previous offers if he wants to select
    520   // from multiple offers. If user provides an invalid packet,
    521   // use the last offer, otherwise use the provided packet.
    522   //
    523   NewPacket = NULL;
    524   Status    = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);
    525 
    526   if (EFI_ERROR (Status)) {
    527     return Status;
    528   }
    529 
    530   Selected = DhcpSb->LastOffer;
    531 
    532   if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {
    533     TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);
    534     if (TempPacket != NULL) {
    535       CopyMem (TempPacket, NewPacket, NewPacket->Size);
    536       FreePool (Selected);
    537       Selected = TempPacket;
    538     }
    539   }
    540 
    541   DhcpSb->Selected  = Selected;
    542   DhcpSb->LastOffer = NULL;
    543   DhcpSb->Para      = NULL;
    544   DhcpValidateOptions (Selected, &DhcpSb->Para);
    545 
    546   //
    547   // A bootp offer has been selected, save the lease status,
    548   // enter bound state then notify the user.
    549   //
    550   if (DHCP_IS_BOOTP (DhcpSb->Para)) {
    551     Status = DhcpLeaseAcquired (DhcpSb);
    552 
    553     if (EFI_ERROR (Status)) {
    554       return Status;
    555     }
    556 
    557     DhcpSb->IoStatus = EFI_SUCCESS;
    558     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
    559     return EFI_SUCCESS;
    560   }
    561 
    562   //
    563   // Send a DHCP requests
    564   //
    565   Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);
    566 
    567   if (EFI_ERROR (Status)) {
    568     return Status;
    569   }
    570 
    571   return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);
    572 }
    573 
    574 
    575 /**
    576   Terminate the current address acquire. All the allocated resources
    577   are released. Be careful when calling this function. A rule related
    578   to this is: only call DhcpEndSession at the highest level, such as
    579   DhcpInput, DhcpOnTimerTick...At the other level, just return error.
    580 
    581   @param[in]  DhcpSb                The DHCP service instance
    582   @param[in]  Status                The result of the DHCP process.
    583 
    584 **/
    585 VOID
    586 DhcpEndSession (
    587   IN DHCP_SERVICE           *DhcpSb,
    588   IN EFI_STATUS             Status
    589   )
    590 {
    591   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
    592     DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);
    593   } else {
    594     DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);
    595   }
    596 
    597   DhcpCleanLease (DhcpSb);
    598 
    599   DhcpSb->IoStatus = Status;
    600   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);
    601 }
    602 
    603 
    604 /**
    605   Handle packets in DHCP select state.
    606 
    607   @param[in]  DhcpSb                The DHCP service instance
    608   @param[in]  Packet                The DHCP packet received
    609   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    610                                     is, all the option value that we care.
    611 
    612   @retval EFI_SUCCESS           The packet is successfully processed.
    613   @retval Others                Some error occured.
    614 
    615 **/
    616 EFI_STATUS
    617 DhcpHandleSelect (
    618   IN DHCP_SERVICE           *DhcpSb,
    619   IN EFI_DHCP4_PACKET       *Packet,
    620   IN DHCP_PARAMETER         *Para
    621   )
    622 {
    623   EFI_STATUS                Status;
    624 
    625   Status = EFI_SUCCESS;
    626 
    627   //
    628   // First validate the message:
    629   // 1. the offer is a unicast
    630   // 2. if it is a DHCP message, it must contains a server ID.
    631   // Don't return a error for these two case otherwise the session is ended.
    632   //
    633   if (!DHCP_IS_BOOTP (Para) &&
    634       ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))
    635       ) {
    636     goto ON_EXIT;
    637   }
    638 
    639   //
    640   // Call the user's callback. The action according to the return is as:
    641   // 1. EFI_SUCESS: stop waiting for more offers, select the offer now
    642   // 2. EFI_NOT_READY: wait for more offers
    643   // 3. EFI_ABORTED: abort the address acquiring.
    644   //
    645   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);
    646 
    647   if (Status == EFI_SUCCESS) {
    648     if (DhcpSb->LastOffer != NULL) {
    649       FreePool (DhcpSb->LastOffer);
    650     }
    651 
    652     DhcpSb->LastOffer = Packet;
    653 
    654     return DhcpChooseOffer (DhcpSb);
    655 
    656   } else if (Status == EFI_NOT_READY) {
    657     if (DhcpSb->LastOffer != NULL) {
    658       FreePool (DhcpSb->LastOffer);
    659     }
    660 
    661     DhcpSb->LastOffer = Packet;
    662 
    663   } else if (Status == EFI_ABORTED) {
    664     //
    665     // DhcpInput will end the session upon error return. Remember
    666     // only to call DhcpEndSession at the top level call.
    667     //
    668     goto ON_EXIT;
    669   }
    670 
    671   return EFI_SUCCESS;
    672 
    673 ON_EXIT:
    674   FreePool (Packet);
    675   return Status;
    676 }
    677 
    678 
    679 /**
    680   Handle packets in DHCP request state.
    681 
    682   @param[in]  DhcpSb                The DHCP service instance
    683   @param[in]  Packet                The DHCP packet received
    684   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    685                                     is, all the option value that we care.
    686 
    687   @retval EFI_SUCCESS           The packet is successfully processed.
    688   @retval Others                Some error occured.
    689 
    690 **/
    691 EFI_STATUS
    692 DhcpHandleRequest (
    693   IN DHCP_SERVICE           *DhcpSb,
    694   IN EFI_DHCP4_PACKET       *Packet,
    695   IN DHCP_PARAMETER         *Para
    696   )
    697 {
    698   EFI_DHCP4_HEADER          *Head;
    699   EFI_DHCP4_HEADER          *Selected;
    700   EFI_STATUS                Status;
    701   UINT8                     *Message;
    702 
    703   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
    704 
    705   Head      = &Packet->Dhcp4.Header;
    706   Selected  = &DhcpSb->Selected->Dhcp4.Header;
    707 
    708   //
    709   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.
    710   //
    711   if (DHCP_IS_BOOTP (Para) ||
    712       (Para->ServerId != DhcpSb->Para->ServerId) ||
    713       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    714       ) {
    715 
    716     Status = EFI_SUCCESS;
    717     goto ON_EXIT;
    718   }
    719 
    720   //
    721   // Received a NAK, end the session no matter what the user returns
    722   //
    723   Status = EFI_DEVICE_ERROR;
    724 
    725   if (Para->DhcpType == DHCP_MSG_NAK) {
    726     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    727     goto ON_EXIT;
    728   }
    729 
    730   //
    731   // Check whether the ACK matches the selected offer
    732   //
    733   Message = NULL;
    734 
    735   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
    736     Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";
    737     goto REJECT;
    738   }
    739 
    740   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    741 
    742   if (EFI_ERROR (Status)) {
    743     Message = (UINT8 *) "Lease is denied upon received ACK";
    744     goto REJECT;
    745   }
    746 
    747   //
    748   // Record the lease, transit to BOUND state, then notify the user
    749   //
    750   Status = DhcpLeaseAcquired (DhcpSb);
    751 
    752   if (EFI_ERROR (Status)) {
    753     Message = (UINT8 *) "Lease is denied upon entering bound";
    754     goto REJECT;
    755   }
    756 
    757   DhcpSb->IoStatus = EFI_SUCCESS;
    758   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
    759 
    760   FreePool (Packet);
    761   return EFI_SUCCESS;
    762 
    763 REJECT:
    764   DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);
    765 
    766 ON_EXIT:
    767   FreePool (Packet);
    768   return Status;
    769 }
    770 
    771 
    772 /**
    773   Handle packets in DHCP renew/rebound state.
    774 
    775   @param[in]  DhcpSb                The DHCP service instance
    776   @param[in]  Packet                The DHCP packet received
    777   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    778                                     is, all the option value that we care.
    779 
    780   @retval EFI_SUCCESS           The packet is successfully processed.
    781   @retval Others                Some error occured.
    782 
    783 **/
    784 EFI_STATUS
    785 DhcpHandleRenewRebind (
    786   IN DHCP_SERVICE           *DhcpSb,
    787   IN EFI_DHCP4_PACKET       *Packet,
    788   IN DHCP_PARAMETER         *Para
    789   )
    790 {
    791   EFI_DHCP4_HEADER          *Head;
    792   EFI_DHCP4_HEADER          *Selected;
    793   EFI_STATUS                Status;
    794 
    795   ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));
    796 
    797   Head      = &Packet->Dhcp4.Header;
    798   Selected  = &DhcpSb->Selected->Dhcp4.Header;
    799 
    800   //
    801   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
    802   //
    803   if (DHCP_IS_BOOTP (Para) ||
    804       (Para->ServerId != DhcpSb->Para->ServerId) ||
    805       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    806       ) {
    807 
    808     Status = EFI_SUCCESS;
    809     goto ON_EXIT;
    810   }
    811 
    812   //
    813   // Received a NAK, ignore the user's return then terminate the process
    814   //
    815   Status = EFI_DEVICE_ERROR;
    816 
    817   if (Para->DhcpType == DHCP_MSG_NAK) {
    818     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    819     goto ON_EXIT;
    820   }
    821 
    822   //
    823   // The lease is different from the selected. Don't send a DECLINE
    824   // since it isn't existed in the client's FSM.
    825   //
    826   if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {
    827     goto ON_EXIT;
    828   }
    829 
    830   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    831 
    832   if (EFI_ERROR (Status)) {
    833     goto ON_EXIT;
    834   }
    835 
    836   //
    837   // Record the lease, start timer for T1 and T2,
    838   //
    839   DhcpComputeLease (DhcpSb, Para);
    840   DhcpSb->LeaseLife = 0;
    841   DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);
    842 
    843   if (DhcpSb->ExtraRefresh != 0) {
    844     DhcpSb->ExtraRefresh  = FALSE;
    845 
    846     DhcpSb->IoStatus      = EFI_SUCCESS;
    847     DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
    848   }
    849 
    850 ON_EXIT:
    851   FreePool (Packet);
    852   return Status;
    853 }
    854 
    855 
    856 /**
    857   Handle packets in DHCP reboot state.
    858 
    859   @param[in]  DhcpSb                The DHCP service instance
    860   @param[in]  Packet                The DHCP packet received
    861   @param[in]  Para                  The DHCP parameter extracted from the packet. That
    862                                     is, all the option value that we care.
    863 
    864   @retval EFI_SUCCESS           The packet is successfully processed.
    865   @retval Others                Some error occured.
    866 
    867 **/
    868 EFI_STATUS
    869 DhcpHandleReboot (
    870   IN DHCP_SERVICE           *DhcpSb,
    871   IN EFI_DHCP4_PACKET       *Packet,
    872   IN DHCP_PARAMETER         *Para
    873   )
    874 {
    875   EFI_DHCP4_HEADER          *Head;
    876   EFI_STATUS                Status;
    877 
    878   Head = &Packet->Dhcp4.Header;
    879 
    880   //
    881   // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK
    882   //
    883   if (DHCP_IS_BOOTP (Para) ||
    884       ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))
    885       ) {
    886 
    887     Status = EFI_SUCCESS;
    888     goto ON_EXIT;
    889   }
    890 
    891   //
    892   // If a NAK is received, transit to INIT and try again.
    893   //
    894   if (Para->DhcpType == DHCP_MSG_NAK) {
    895     DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);
    896 
    897     DhcpSb->ClientAddr  = 0;
    898     DhcpSb->DhcpState   = Dhcp4Init;
    899 
    900     Status              = DhcpInitRequest (DhcpSb);
    901     goto ON_EXIT;
    902   }
    903 
    904   //
    905   // Check whether the ACK matches the selected offer
    906   //
    907   if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {
    908     Status = EFI_DEVICE_ERROR;
    909     goto ON_EXIT;
    910   }
    911 
    912   Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);
    913   if (EFI_ERROR (Status)) {
    914     goto ON_EXIT;
    915   }
    916 
    917   //
    918   // OK, get the parameter from server, record the lease
    919   //
    920   DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);
    921   if (DhcpSb->Para == NULL) {
    922     Status = EFI_OUT_OF_RESOURCES;
    923     goto ON_EXIT;
    924   }
    925 
    926   DhcpSb->Selected  = Packet;
    927   Status            = DhcpLeaseAcquired (DhcpSb);
    928   if (EFI_ERROR (Status)) {
    929     return Status;
    930   }
    931 
    932   DhcpSb->IoStatus = EFI_SUCCESS;
    933   DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);
    934   return EFI_SUCCESS;
    935 
    936 ON_EXIT:
    937   FreePool (Packet);
    938   return Status;
    939 }
    940 
    941 
    942 /**
    943   Handle the received DHCP packets. This function drives the DHCP
    944   state machine.
    945 
    946   @param  UdpPacket             The UDP packets received.
    947   @param  EndPoint              The local/remote UDP access point
    948   @param  IoStatus              The status of the UDP receive
    949   @param  Context               The opaque parameter to the function.
    950 
    951 **/
    952 VOID
    953 EFIAPI
    954 DhcpInput (
    955   NET_BUF                   *UdpPacket,
    956   UDP_END_POINT             *EndPoint,
    957   EFI_STATUS                IoStatus,
    958   VOID                      *Context
    959   )
    960 {
    961   DHCP_SERVICE              *DhcpSb;
    962   EFI_DHCP4_HEADER          *Head;
    963   EFI_DHCP4_PACKET          *Packet;
    964   DHCP_PARAMETER            *Para;
    965   EFI_STATUS                Status;
    966   UINT32                    Len;
    967 
    968   Packet  = NULL;
    969   DhcpSb  = (DHCP_SERVICE *) Context;
    970 
    971   //
    972   // Don't restart receive if error occurs or DHCP is destroyed.
    973   //
    974   if (EFI_ERROR (IoStatus)) {
    975     return ;
    976   } else if (DhcpSb->ServiceState == DHCP_DESTROY) {
    977     NetbufFree (UdpPacket);
    978     return ;
    979   }
    980 
    981   ASSERT (UdpPacket != NULL);
    982 
    983   if (DhcpSb->DhcpState == Dhcp4Stopped) {
    984     goto RESTART;
    985   }
    986 
    987   //
    988   // Validate the packet received
    989   //
    990   if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
    991     goto RESTART;
    992   }
    993 
    994   //
    995   // Copy the DHCP message to a continuous memory block
    996   //
    997   Len     = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);
    998   Packet  = (EFI_DHCP4_PACKET *) AllocatePool (Len);
    999 
   1000   if (Packet == NULL) {
   1001     goto RESTART;
   1002   }
   1003 
   1004   Packet->Size    = Len;
   1005   Head            = &Packet->Dhcp4.Header;
   1006   Packet->Length  = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
   1007 
   1008   if (Packet->Length != UdpPacket->TotalSize) {
   1009     goto RESTART;
   1010   }
   1011 
   1012   //
   1013   // Is this packet the answer to our packet?
   1014   //
   1015   if ((Head->OpCode != BOOTP_REPLY) ||
   1016       (NTOHL (Head->Xid) != DhcpSb->Xid) ||
   1017       (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
   1018     goto RESTART;
   1019   }
   1020 
   1021   //
   1022   // Validate the options and retrieve the interested options
   1023   //
   1024   Para = NULL;
   1025   if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
   1026       (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
   1027       EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {
   1028 
   1029     goto RESTART;
   1030   }
   1031 
   1032   //
   1033   // Call the handler for each state. The handler should return
   1034   // EFI_SUCCESS if the process can go on no matter whether the
   1035   // packet is ignored or not. If the return is EFI_ERROR, the
   1036   // session will be terminated. Packet's ownership is handled
   1037   // over to the handlers. If operation succeeds, the handler
   1038   // must notify the user. It isn't necessary to do if EFI_ERROR
   1039   // is returned because the DhcpEndSession will notify the user.
   1040   //
   1041   Status = EFI_SUCCESS;
   1042 
   1043   switch (DhcpSb->DhcpState) {
   1044   case Dhcp4Selecting:
   1045     Status = DhcpHandleSelect (DhcpSb, Packet, Para);
   1046     break;
   1047 
   1048   case Dhcp4Requesting:
   1049     Status = DhcpHandleRequest (DhcpSb, Packet, Para);
   1050     break;
   1051 
   1052   case Dhcp4InitReboot:
   1053   case Dhcp4Init:
   1054   case Dhcp4Bound:
   1055     //
   1056     // Ignore the packet in INITREBOOT, INIT and BOUND states
   1057     //
   1058     FreePool (Packet);
   1059     Status = EFI_SUCCESS;
   1060     break;
   1061 
   1062   case Dhcp4Renewing:
   1063   case Dhcp4Rebinding:
   1064     Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);
   1065     break;
   1066 
   1067   case Dhcp4Rebooting:
   1068     Status = DhcpHandleReboot (DhcpSb, Packet, Para);
   1069     break;
   1070   }
   1071 
   1072   if (Para != NULL) {
   1073     FreePool (Para);
   1074   }
   1075 
   1076   Packet = NULL;
   1077 
   1078   if (EFI_ERROR (Status)) {
   1079     NetbufFree (UdpPacket);
   1080     UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
   1081     DhcpEndSession (DhcpSb, Status);
   1082     return ;
   1083   }
   1084 
   1085 RESTART:
   1086   NetbufFree (UdpPacket);
   1087 
   1088   if (Packet != NULL) {
   1089     FreePool (Packet);
   1090   }
   1091 
   1092   Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
   1093 
   1094   if (EFI_ERROR (Status)) {
   1095     DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);
   1096   }
   1097 }
   1098 
   1099 
   1100 /**
   1101   Release the packet.
   1102 
   1103   @param[in]  Arg                   The packet to release
   1104 
   1105 **/
   1106 VOID
   1107 EFIAPI
   1108 DhcpReleasePacket (
   1109   IN VOID                   *Arg
   1110   )
   1111 {
   1112   FreePool (Arg);
   1113 }
   1114 
   1115 
   1116 /**
   1117   Release the net buffer when packet is sent.
   1118 
   1119   @param  UdpPacket             The UDP packets received.
   1120   @param  EndPoint              The local/remote UDP access point
   1121   @param  IoStatus              The status of the UDP receive
   1122   @param  Context               The opaque parameter to the function.
   1123 
   1124 **/
   1125 VOID
   1126 EFIAPI
   1127 DhcpOnPacketSent (
   1128   NET_BUF                   *Packet,
   1129   UDP_END_POINT             *EndPoint,
   1130   EFI_STATUS                IoStatus,
   1131   VOID                      *Context
   1132   )
   1133 {
   1134   NetbufFree (Packet);
   1135 }
   1136 
   1137 
   1138 
   1139 /**
   1140   Build and transmit a DHCP message according to the current states.
   1141   This function implement the Table 5. of RFC 2131. Always transits
   1142   the state (as defined in Figure 5. of the same RFC) before sending
   1143   a DHCP message. The table is adjusted accordingly.
   1144 
   1145   @param[in]  DhcpSb                The DHCP service instance
   1146   @param[in]  Seed                  The seed packet which the new packet is based on
   1147   @param[in]  Para                  The DHCP parameter of the Seed packet
   1148   @param[in]  Type                  The message type to send
   1149   @param[in]  Msg                   The human readable message to include in the packet
   1150                                     sent.
   1151 
   1152   @retval EFI_OUT_OF_RESOURCES  Failed to allocate resources for the packet
   1153   @retval EFI_ACCESS_DENIED     Failed to transmit the packet through UDP
   1154   @retval EFI_SUCCESS           The message is sent
   1155   @retval other                 Other error occurs
   1156 
   1157 **/
   1158 EFI_STATUS
   1159 DhcpSendMessage (
   1160   IN DHCP_SERVICE           *DhcpSb,
   1161   IN EFI_DHCP4_PACKET       *Seed,
   1162   IN DHCP_PARAMETER         *Para,
   1163   IN UINT8                  Type,
   1164   IN UINT8                  *Msg
   1165   )
   1166 {
   1167   EFI_DHCP4_CONFIG_DATA     *Config;
   1168   EFI_DHCP4_PACKET          *Packet;
   1169   EFI_DHCP4_PACKET          *NewPacket;
   1170   EFI_DHCP4_HEADER          *Head;
   1171   EFI_DHCP4_HEADER          *SeedHead;
   1172   UDP_IO                    *UdpIo;
   1173   UDP_END_POINT             EndPoint;
   1174   NET_BUF                   *Wrap;
   1175   NET_FRAGMENT              Frag;
   1176   EFI_STATUS                Status;
   1177   IP4_ADDR                  IpAddr;
   1178   UINT8                     *Buf;
   1179   UINT16                    MaxMsg;
   1180   UINT32                    Len;
   1181   UINT32                    Index;
   1182 
   1183   //
   1184   // Allocate a big enough memory block to hold the DHCP packet
   1185   //
   1186   Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;
   1187 
   1188   if (Msg != NULL) {
   1189     Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);
   1190   }
   1191 
   1192   Packet = AllocatePool (Len);
   1193 
   1194   if (Packet == NULL) {
   1195     return EFI_OUT_OF_RESOURCES;
   1196   }
   1197 
   1198   Packet->Size    = Len;
   1199   Packet->Length  = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);
   1200 
   1201   //
   1202   // Fill in the DHCP header fields
   1203   //
   1204   Config    = &DhcpSb->ActiveConfig;
   1205   SeedHead  = NULL;
   1206 
   1207   if (Seed != NULL) {
   1208     SeedHead = &Seed->Dhcp4.Header;
   1209   }
   1210 
   1211   Head = &Packet->Dhcp4.Header;
   1212   ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));
   1213 
   1214   Head->OpCode       = BOOTP_REQUEST;
   1215   Head->HwType       = DhcpSb->HwType;
   1216   Head->HwAddrLen    = DhcpSb->HwLen;
   1217   Head->Xid          = HTONL (DhcpSb->Xid);
   1218   Head->Reserved     = HTONS (0x8000);  //Server, broadcast the message please.
   1219 
   1220   EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);
   1221   CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);
   1222 
   1223   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {
   1224     Head->Seconds = 0;
   1225   } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {
   1226     //
   1227     // Use the same value as the original DHCPDISCOVER message.
   1228     //
   1229     Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;
   1230   } else {
   1231     SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);
   1232   }
   1233 
   1234   //
   1235   // Append the DHCP message type
   1236   //
   1237   Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;
   1238   Buf                 = Packet->Dhcp4.Option;
   1239   Buf                 = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);
   1240 
   1241   //
   1242   // Append the serverid option if necessary:
   1243   //   1. DHCP decline message
   1244   //   2. DHCP release message
   1245   //   3. DHCP request to confirm one lease.
   1246   //
   1247   if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||
   1248       ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))
   1249       ) {
   1250 
   1251     ASSERT ((Para != NULL) && (Para->ServerId != 0));
   1252 
   1253     IpAddr  = HTONL (Para->ServerId);
   1254     Buf     = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);
   1255   }
   1256 
   1257   //
   1258   // Append the requested IP option if necessary:
   1259   //   1. DHCP request to use the previously allocated address
   1260   //   2. DHCP request to confirm one lease
   1261   //   3. DHCP decline to decline one lease
   1262   //
   1263   IpAddr = 0;
   1264 
   1265   if (Type == DHCP_MSG_REQUEST) {
   1266     if (DhcpSb->DhcpState == Dhcp4Rebooting) {
   1267       IpAddr = EFI_IP4 (Config->ClientAddress);
   1268 
   1269     } else if (DhcpSb->DhcpState == Dhcp4Requesting) {
   1270       ASSERT (SeedHead != NULL);
   1271       IpAddr = EFI_IP4 (SeedHead->YourAddr);
   1272     }
   1273 
   1274   } else if (Type == DHCP_MSG_DECLINE) {
   1275     ASSERT (SeedHead != NULL);
   1276     IpAddr = EFI_IP4 (SeedHead->YourAddr);
   1277   }
   1278 
   1279   if (IpAddr != 0) {
   1280     Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);
   1281   }
   1282 
   1283   //
   1284   // Append the Max Message Length option if it isn't a DECLINE
   1285   // or RELEASE to direct the server use large messages instead of
   1286   // override the BOOTFILE and SERVER fields in the message head.
   1287   //
   1288   if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {
   1289     MaxMsg  = HTONS (0xFF00);
   1290     Buf     = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);
   1291   }
   1292 
   1293   //
   1294   // Append the user's message if it isn't NULL
   1295   //
   1296   if (Msg != NULL) {
   1297     Len     = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);
   1298     Buf     = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16) Len, Msg);
   1299   }
   1300 
   1301   //
   1302   // Append the user configured options
   1303   //
   1304   if (DhcpSb->UserOptionLen != 0) {
   1305     for (Index = 0; Index < Config->OptionCount; Index++) {
   1306       //
   1307       // We can't use any option other than the client ID from user
   1308       // if it is a DHCP decline or DHCP release .
   1309       //
   1310       if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&
   1311           (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID)) {
   1312         continue;
   1313       }
   1314 
   1315       Buf = DhcpAppendOption (
   1316               Buf,
   1317               Config->OptionList[Index]->OpCode,
   1318               Config->OptionList[Index]->Length,
   1319               Config->OptionList[Index]->Data
   1320               );
   1321     }
   1322   }
   1323 
   1324   *(Buf++) = DHCP4_TAG_EOP;
   1325   Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);
   1326 
   1327   //
   1328   // OK, the message is built, call the user to override it.
   1329   //
   1330   Status    = EFI_SUCCESS;
   1331   NewPacket = NULL;
   1332 
   1333   if (Type == DHCP_MSG_DISCOVER) {
   1334     Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);
   1335 
   1336   } else if (Type == DHCP_MSG_REQUEST) {
   1337     Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);
   1338 
   1339   } else if (Type == DHCP_MSG_DECLINE) {
   1340     Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);
   1341   }
   1342 
   1343   if (EFI_ERROR (Status)) {
   1344     FreePool (Packet);
   1345     return Status;
   1346   }
   1347 
   1348   if (NewPacket != NULL) {
   1349     FreePool (Packet);
   1350     Packet = NewPacket;
   1351   }
   1352 
   1353   //
   1354   // Save the Client Address will be sent out
   1355   //
   1356   CopyMem (
   1357     &DhcpSb->ClientAddressSendOut[0],
   1358     &Packet->Dhcp4.Header.ClientHwAddr[0],
   1359     Packet->Dhcp4.Header.HwAddrLen
   1360     );
   1361 
   1362 
   1363   //
   1364   // Wrap it into a netbuf then send it.
   1365   //
   1366   Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;
   1367   Frag.Len  = Packet->Length;
   1368   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);
   1369 
   1370   if (Wrap == NULL) {
   1371     FreePool (Packet);
   1372     return EFI_OUT_OF_RESOURCES;
   1373   }
   1374 
   1375   //
   1376   // Save it as the last sent packet for retransmission
   1377   //
   1378   if (DhcpSb->LastPacket != NULL) {
   1379     FreePool (DhcpSb->LastPacket);
   1380   }
   1381 
   1382   DhcpSb->LastPacket = Packet;
   1383   DhcpSetTransmitTimer (DhcpSb);
   1384 
   1385   //
   1386   // Broadcast the message, unless we know the server address.
   1387   // Use the lease UdpIo port to send the unicast packet.
   1388   //
   1389   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
   1390   EndPoint.LocalAddr.Addr[0]  = 0;
   1391   EndPoint.RemotePort         = DHCP_SERVER_PORT;
   1392   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
   1393   UdpIo                       = DhcpSb->UdpIo;
   1394 
   1395   if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {
   1396     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
   1397     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
   1398     UdpIo                       = DhcpSb->LeaseIoPort;
   1399   }
   1400 
   1401   ASSERT (UdpIo != NULL);
   1402   NET_GET_REF (Wrap);
   1403 
   1404   Status = UdpIoSendDatagram (
   1405              UdpIo,
   1406              Wrap,
   1407              &EndPoint,
   1408              NULL,
   1409              DhcpOnPacketSent,
   1410              DhcpSb
   1411              );
   1412 
   1413   if (EFI_ERROR (Status)) {
   1414     NET_PUT_REF (Wrap);
   1415     return EFI_ACCESS_DENIED;
   1416   }
   1417 
   1418   return EFI_SUCCESS;
   1419 }
   1420 
   1421 
   1422 /**
   1423   Retransmit a saved packet. Only DISCOVER and REQUEST messages
   1424   will be retransmitted.
   1425 
   1426   @param[in]  DhcpSb                The DHCP service instance
   1427 
   1428   @retval EFI_ACCESS_DENIED     Failed to transmit packet through UDP port
   1429   @retval EFI_SUCCESS           The packet is retransmitted.
   1430 
   1431 **/
   1432 EFI_STATUS
   1433 DhcpRetransmit (
   1434   IN DHCP_SERVICE           *DhcpSb
   1435   )
   1436 {
   1437   UDP_IO                    *UdpIo;
   1438   UDP_END_POINT             EndPoint;
   1439   NET_BUF                   *Wrap;
   1440   NET_FRAGMENT              Frag;
   1441   EFI_STATUS                Status;
   1442 
   1443   ASSERT (DhcpSb->LastPacket != NULL);
   1444 
   1445   //
   1446   // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.
   1447   //
   1448   if (DhcpSb->DhcpState != Dhcp4Requesting) {
   1449     SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);
   1450   }
   1451 
   1452   //
   1453   // Wrap it into a netbuf then send it.
   1454   //
   1455   Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;
   1456   Frag.Len  = DhcpSb->LastPacket->Length;
   1457   Wrap      = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);
   1458 
   1459   if (Wrap == NULL) {
   1460     return EFI_OUT_OF_RESOURCES;
   1461   }
   1462 
   1463   //
   1464   // Broadcast the message, unless we know the server address.
   1465   //
   1466   EndPoint.RemotePort         = DHCP_SERVER_PORT;
   1467   EndPoint.LocalPort          = DHCP_CLIENT_PORT;
   1468   EndPoint.RemoteAddr.Addr[0] = 0xffffffff;
   1469   EndPoint.LocalAddr.Addr[0]  = 0;
   1470   UdpIo                       = DhcpSb->UdpIo;
   1471 
   1472   if (DhcpSb->DhcpState == Dhcp4Renewing) {
   1473     EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;
   1474     EndPoint.LocalAddr.Addr[0]  = DhcpSb->ClientAddr;
   1475     UdpIo                       = DhcpSb->LeaseIoPort;
   1476   }
   1477 
   1478   ASSERT (UdpIo != NULL);
   1479 
   1480   NET_GET_REF (Wrap);
   1481   Status = UdpIoSendDatagram (
   1482              UdpIo,
   1483              Wrap,
   1484              &EndPoint,
   1485              NULL,
   1486              DhcpOnPacketSent,
   1487              DhcpSb
   1488              );
   1489 
   1490   if (EFI_ERROR (Status)) {
   1491     NET_PUT_REF (Wrap);
   1492     return EFI_ACCESS_DENIED;
   1493   }
   1494 
   1495   return EFI_SUCCESS;
   1496 }
   1497 
   1498 
   1499 /**
   1500   Each DHCP service has three timer. Two of them are count down timer.
   1501   One for the packet retransmission. The other is to collect the offers.
   1502   The third timer increaments the lease life which is compared to T1, T2,
   1503   and lease to determine the time to renew and rebind the lease.
   1504   DhcpOnTimerTick will be called once every second.
   1505 
   1506   @param[in]  Event                 The timer event
   1507   @param[in]  Context               The context, which is the DHCP service instance.
   1508 
   1509 **/
   1510 VOID
   1511 EFIAPI
   1512 DhcpOnTimerTick (
   1513   IN EFI_EVENT              Event,
   1514   IN VOID                   *Context
   1515   )
   1516 {
   1517   LIST_ENTRY                *Entry;
   1518   LIST_ENTRY                *Next;
   1519   DHCP_SERVICE              *DhcpSb;
   1520   DHCP_PROTOCOL             *Instance;
   1521   EFI_STATUS                Status;
   1522 
   1523   DhcpSb   = (DHCP_SERVICE *) Context;
   1524   Instance = DhcpSb->ActiveChild;
   1525 
   1526   //
   1527   // 0xffff is the maximum supported value for elapsed time according to RFC.
   1528   //
   1529   if (Instance != NULL && Instance->ElaspedTime < 0xffff) {
   1530     Instance->ElaspedTime++;
   1531   }
   1532 
   1533   //
   1534   // Check the retransmit timer
   1535   //
   1536   if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {
   1537 
   1538     //
   1539     // Select offer at each timeout if any offer received.
   1540     //
   1541     if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {
   1542 
   1543       Status = DhcpChooseOffer (DhcpSb);
   1544 
   1545       if (EFI_ERROR(Status)) {
   1546         if (DhcpSb->LastOffer != NULL) {
   1547           FreePool (DhcpSb->LastOffer);
   1548           DhcpSb->LastOffer = NULL;
   1549         }
   1550       } else {
   1551         goto ON_EXIT;
   1552       }
   1553     }
   1554 
   1555     if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {
   1556       //
   1557       // Still has another try
   1558       //
   1559       DhcpRetransmit (DhcpSb);
   1560       DhcpSetTransmitTimer (DhcpSb);
   1561 
   1562     } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
   1563 
   1564       //
   1565       // Retransmission failed, if the DHCP request is initiated by
   1566       // user, adjust the current state according to the lease life.
   1567       // Otherwise do nothing to wait the lease to timeout
   1568       //
   1569       if (DhcpSb->ExtraRefresh != 0) {
   1570         Status = EFI_SUCCESS;
   1571 
   1572         if (DhcpSb->LeaseLife < DhcpSb->T1) {
   1573           Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
   1574 
   1575         } else if (DhcpSb->LeaseLife < DhcpSb->T2) {
   1576           Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
   1577 
   1578         } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {
   1579           Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
   1580 
   1581         } else {
   1582           goto END_SESSION;
   1583 
   1584         }
   1585 
   1586         DhcpSb->IoStatus = EFI_TIMEOUT;
   1587         DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);
   1588       }
   1589     } else {
   1590       goto END_SESSION;
   1591     }
   1592   }
   1593 
   1594   //
   1595   // If an address has been acquired, check whether need to
   1596   // refresh or whether it has expired.
   1597   //
   1598   if (DHCP_CONNECTED (DhcpSb->DhcpState)) {
   1599     DhcpSb->LeaseLife++;
   1600 
   1601     //
   1602     // Don't timeout the lease, only count the life if user is
   1603     // requesting extra renew/rebind. Adjust the state after that.
   1604     //
   1605     if (DhcpSb->ExtraRefresh != 0) {
   1606       return ;
   1607     }
   1608 
   1609     if (DhcpSb->LeaseLife == DhcpSb->Lease) {
   1610       //
   1611       // Lease expires, end the session
   1612       //
   1613       goto END_SESSION;
   1614 
   1615     } else if (DhcpSb->LeaseLife == DhcpSb->T2) {
   1616       //
   1617       // T2 expires, transit to rebinding then send a REQUEST to any server
   1618       //
   1619       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {
   1620         goto END_SESSION;
   1621       }
   1622 
   1623       if (Instance != NULL) {
   1624         Instance->ElaspedTime= 0;
   1625       }
   1626 
   1627       Status = DhcpSendMessage (
   1628                  DhcpSb,
   1629                  DhcpSb->Selected,
   1630                  DhcpSb->Para,
   1631                  DHCP_MSG_REQUEST,
   1632                  NULL
   1633                  );
   1634 
   1635       if (EFI_ERROR (Status)) {
   1636         goto END_SESSION;
   1637       }
   1638 
   1639     } else if (DhcpSb->LeaseLife == DhcpSb->T1) {
   1640       //
   1641       // T1 expires, transit to renewing, then send a REQUEST to the server
   1642       //
   1643       if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {
   1644         goto END_SESSION;
   1645       }
   1646 
   1647       if (Instance != NULL) {
   1648         Instance->ElaspedTime= 0;
   1649       }
   1650 
   1651       Status = DhcpSendMessage (
   1652                  DhcpSb,
   1653                  DhcpSb->Selected,
   1654                  DhcpSb->Para,
   1655                  DHCP_MSG_REQUEST,
   1656                  NULL
   1657                  );
   1658 
   1659       if (EFI_ERROR (Status)) {
   1660         goto END_SESSION;
   1661       }
   1662     }
   1663   }
   1664 
   1665 ON_EXIT:
   1666   //
   1667   // Iterate through all the DhcpSb Children.
   1668   //
   1669   NET_LIST_FOR_EACH_SAFE (Entry, Next, &DhcpSb->Children) {
   1670     Instance = NET_LIST_USER_STRUCT (Entry, DHCP_PROTOCOL, Link);
   1671 
   1672     if ((Instance != NULL) && (Instance->Token != NULL)) {
   1673       Instance->Timeout--;
   1674       if (Instance->Timeout == 0) {
   1675         PxeDhcpDone (Instance);
   1676       }
   1677     }
   1678   }
   1679 
   1680   return ;
   1681 
   1682 END_SESSION:
   1683   DhcpEndSession (DhcpSb, EFI_TIMEOUT);
   1684 
   1685   return ;
   1686 }
   1687