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