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