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