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