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