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