]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Io.c
CommitLineData
772db4bb 1/** @file\r
3e8c18da 2 EFI DHCP protocol implementation.\r
d1102dba 3\r
71096201 4Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
9d510e61 5SPDX-License-Identifier: BSD-2-Clause-Patent\r
772db4bb 6\r
772db4bb 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
3e8c18da 19 @param[in] DhcpSb The DHCP service instance\r
772db4bb 20\r
21 @retval EFI_SUCCESS The request has been sent\r
f9204641 22 @retval other Some error occurs when sending the request.\r
772db4bb 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
842d83d6 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
d1102dba 38\r
772db4bb 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
772db4bb 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
3e8c18da 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
772db4bb 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
772db4bb 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
687a2e5f 112 (EFI_DHCP4_STATE) DhcpSb->DhcpState,\r
772db4bb 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
f9204641 135 @param Which Which notify function to signal\r
772db4bb 136\r
772db4bb 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
f9204641 151 ((Which == DHCP_NOTIFY_COMPLETION) || (Which == DHCP_NOTIFY_ALL))\r
152 ) {\r
772db4bb 153\r
154 gBS->SignalEvent (Child->CompletionEvent);\r
155 Child->CompletionEvent = NULL;\r
156 }\r
157\r
158 if ((Child->RenewRebindEvent != NULL) &&\r
f9204641 159 ((Which == DHCP_NOTIFY_RENEWREBIND) || (Which == DHCP_NOTIFY_ALL))\r
160 ) {\r
772db4bb 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
f9204641 186 IN OUT DHCP_SERVICE *DhcpSb,\r
187 IN INTN State,\r
188 IN BOOLEAN CallUser\r
772db4bb 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
c4a62a12 217 if (State == Dhcp4Selecting) {\r
772db4bb 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
434ce3fe 229 DhcpSb->LastTimeout = 0;\r
772db4bb 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
772db4bb 241**/\r
772db4bb 242VOID\r
243DhcpSetTransmitTimer (\r
f9204641 244 IN OUT DHCP_SERVICE *DhcpSb\r
772db4bb 245 )\r
246{\r
247 UINT32 *Times;\r
248\r
249 ASSERT (DhcpSb->MaxRetries > DhcpSb->CurRetry);\r
250\r
c4a62a12 251 if (DhcpSb->DhcpState == Dhcp4Selecting) {\r
772db4bb 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
434ce3fe 262 DhcpSb->LastTimeout = DhcpSb->PacketToLive;\r
772db4bb 263\r
e234f7c4 264 return;\r
c4a62a12 265}\r
772db4bb 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
235eaa63 274 response.\r
772db4bb 275**/\r
772db4bb 276VOID\r
277DhcpComputeLease (\r
f9204641 278 IN OUT DHCP_SERVICE *DhcpSb,\r
279 IN DHCP_PARAMETER *Para\r
772db4bb 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
b45b45b2 307 @param[in] UdpIo The UDP IO to configure\r
3e8c18da 308 @param[in] Context Dhcp service instance.\r
772db4bb 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
e798cd87 315EFIAPI\r
772db4bb 316DhcpConfigLeaseIoPort (\r
b45b45b2 317 IN UDP_IO *UdpIo,\r
772db4bb 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
e48e37fc 345 CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
772db4bb 346\r
347 Ip = HTONL (DhcpSb->Netmask);\r
e48e37fc 348 CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
772db4bb 349\r
e48e37fc 350 ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));\r
772db4bb 351\r
b45b45b2 352 Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);\r
772db4bb 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
e48e37fc 362 ZeroMem (&Subnet, sizeof (EFI_IPv4_ADDRESS));\r
772db4bb 363\r
364 Ip = HTONL (DhcpSb->Para->Router);\r
e48e37fc 365 CopyMem (&Gateway, &Ip, sizeof (EFI_IPv4_ADDRESS));\r
772db4bb 366\r
b45b45b2 367 UdpIo->Protocol.Udp4->Routes (UdpIo->Protocol.Udp4, FALSE, &Subnet, &Subnet, &Gateway);\r
772db4bb 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
772db4bb 385EFI_STATUS\r
386DhcpLeaseAcquired (\r
f9204641 387 IN OUT DHCP_SERVICE *DhcpSb\r
772db4bb 388 )\r
389{\r
772db4bb 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
01b5ac88 398 return EFI_ABORTED;\r
772db4bb 399 }\r
400\r
401 if (DhcpSb->LeaseIoPort != NULL) {\r
b45b45b2 402 UdpIoFreeIo (DhcpSb->LeaseIoPort);\r
772db4bb 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
b45b45b2 410 DhcpSb->LeaseIoPort = UdpIoCreateIo (\r
772db4bb 411 DhcpSb->Controller,\r
412 DhcpSb->Image,\r
413 DhcpConfigLeaseIoPort,\r
b45b45b2 414 UDP_IO_UDP4_VERSION,\r
772db4bb 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
772db4bb 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
c5bcc2e2 444 DhcpSb->Netmask = 0;\r
772db4bb 445 DhcpSb->ServerAddr = 0;\r
446\r
447 if (DhcpSb->LastOffer != NULL) {\r
a4df47f1 448 FreePool (DhcpSb->LastOffer);\r
772db4bb 449 DhcpSb->LastOffer = NULL;\r
450 }\r
451\r
452 if (DhcpSb->Selected != NULL) {\r
a4df47f1 453 FreePool (DhcpSb->Selected);\r
772db4bb 454 DhcpSb->Selected = NULL;\r
455 }\r
456\r
457 if (DhcpSb->Para != NULL) {\r
a4df47f1 458 FreePool (DhcpSb->Para);\r
772db4bb 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
b45b45b2 468 UdpIoFreeIo (DhcpSb->LeaseIoPort);\r
772db4bb 469 DhcpSb->LeaseIoPort = NULL;\r
470 }\r
471\r
472 if (DhcpSb->LastPacket != NULL) {\r
434ce3fe 473 FreePool (DhcpSb->LastPacket);\r
772db4bb 474 DhcpSb->LastPacket = NULL;\r
475 }\r
476\r
477 DhcpSb->PacketToLive = 0;\r
434ce3fe 478 DhcpSb->LastTimeout = 0;\r
772db4bb 479 DhcpSb->CurRetry = 0;\r
480 DhcpSb->MaxRetries = 0;\r
772db4bb 481 DhcpSb->LeaseLife = 0;\r
235eaa63 482\r
483 //\r
484 // Clean active config data.\r
485 //\r
486 DhcpCleanConfigure (&DhcpSb->ActiveConfig);\r
772db4bb 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
3e8c18da 495 @param[in] DhcpSb The DHCP service instance.\r
772db4bb 496\r
497 @retval EFI_SUCCESS One of the offer is selected.\r
498\r
499**/\r
772db4bb 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
c4a62a12 507 EFI_DHCP4_PACKET *TempPacket;\r
772db4bb 508 EFI_STATUS Status;\r
509\r
510 ASSERT (DhcpSb->LastOffer != NULL);\r
511\r
772db4bb 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
c4a62a12 526 if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {\r
e48e37fc 527 TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);\r
c4a62a12 528 if (TempPacket != NULL) {\r
e48e37fc 529 CopyMem (TempPacket, NewPacket, NewPacket->Size);\r
766c7483 530 FreePool (Selected);\r
c4a62a12 531 Selected = TempPacket;\r
772db4bb 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
3e8c18da 575 @param[in] DhcpSb The DHCP service instance\r
576 @param[in] Status The result of the DHCP process.\r
772db4bb 577\r
578**/\r
772db4bb 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
3e8c18da 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
772db4bb 605\r
606 @retval EFI_SUCCESS The packet is successfully processed.\r
607 @retval Others Some error occured.\r
608\r
609**/\r
772db4bb 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
772db4bb 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
772db4bb 627 if (!DHCP_IS_BOOTP (Para) &&\r
f9204641 628 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))\r
629 ) {\r
772db4bb 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_SUCESS: 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
a4df47f1 643 FreePool (DhcpSb->LastOffer);\r
772db4bb 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
a4df47f1 652 FreePool (DhcpSb->LastOffer);\r
772db4bb 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
766c7483 668 FreePool (Packet);\r
772db4bb 669 return Status;\r
670}\r
671\r
672\r
673/**\r
674 Handle packets in DHCP request state.\r
675\r
3e8c18da 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
772db4bb 680\r
681 @retval EFI_SUCCESS The packet is successfully processed.\r
682 @retval Others Some error occured.\r
683\r
684**/\r
772db4bb 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
f9204641 706 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
707 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
708 ) {\r
772db4bb 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
84b5c78e 729 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
67a58d0f 730 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";\r
772db4bb 731 goto REJECT;\r
732 }\r
733\r
734 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
735\r
736 if (EFI_ERROR (Status)) {\r
67a58d0f 737 Message = (UINT8 *) "Lease is denied upon received ACK";\r
772db4bb 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
67a58d0f 747 Message = (UINT8 *) "Lease is denied upon entering bound";\r
772db4bb 748 goto REJECT;\r
749 }\r
750\r
751 DhcpSb->IoStatus = EFI_SUCCESS;\r
752 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
753\r
766c7483 754 FreePool (Packet);\r
772db4bb 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
766c7483 761 FreePool (Packet);\r
772db4bb 762 return Status;\r
763}\r
764\r
765\r
766/**\r
767 Handle packets in DHCP renew/rebound state.\r
768\r
3e8c18da 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
772db4bb 773\r
774 @retval EFI_SUCCESS The packet is successfully processed.\r
775 @retval Others Some error occured.\r
776\r
777**/\r
772db4bb 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
f9204641 798 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
799 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
800 ) {\r
772db4bb 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
84b5c78e 820 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
772db4bb 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
7bce0c5a 837 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 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
766c7483 845 FreePool (Packet);\r
772db4bb 846 return Status;\r
847}\r
848\r
849\r
850/**\r
851 Handle packets in DHCP reboot state.\r
852\r
3e8c18da 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
772db4bb 857\r
858 @retval EFI_SUCCESS The packet is successfully processed.\r
859 @retval Others Some error occured.\r
860\r
861**/\r
772db4bb 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
f9204641 878 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
879 ) {\r
772db4bb 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
a4df47f1 914 DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);\r
772db4bb 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
772db4bb 921 Status = DhcpLeaseAcquired (DhcpSb);\r
772db4bb 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
766c7483 931 FreePool (Packet);\r
772db4bb 932 return Status;\r
933}\r
934\r
935\r
936/**\r
f9204641 937 Handle the received DHCP packets. This function drives the DHCP\r
772db4bb 938 state machine.\r
939\r
940 @param UdpPacket The UDP packets received.\r
b45b45b2 941 @param EndPoint The local/remote UDP access point\r
772db4bb 942 @param IoStatus The status of the UDP receive\r
943 @param Context The opaque parameter to the function.\r
944\r
772db4bb 945**/\r
946VOID\r
e798cd87 947EFIAPI\r
772db4bb 948DhcpInput (\r
949 NET_BUF *UdpPacket,\r
b45b45b2 950 UDP_END_POINT *EndPoint,\r
772db4bb 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
75dce340 966 // Don't restart receive if error occurs or DHCP is destroyed.\r
772db4bb 967 //\r
968 if (EFI_ERROR (IoStatus)) {\r
969 return ;\r
75dce340 970 } else if (DhcpSb->ServiceState == DHCP_DESTROY) {\r
772db4bb 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
e48e37fc 992 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);\r
772db4bb 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
982a9eae 1011 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {\r
772db4bb 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
766c7483 1052 FreePool (Packet);\r
772db4bb 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
a4df47f1 1067 FreePool (Para);\r
772db4bb 1068 }\r
1069\r
1070 Packet = NULL;\r
1071\r
1072 if (EFI_ERROR (Status)) {\r
1073 NetbufFree (UdpPacket);\r
c5bcc2e2 1074 UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
772db4bb 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
766c7483 1083 FreePool (Packet);\r
772db4bb 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
772db4bb 1093/**\r
1094 Release the net buffer when packet is sent.\r
1095\r
1096 @param UdpPacket The UDP packets received.\r
b45b45b2 1097 @param EndPoint The local/remote UDP access point\r
772db4bb 1098 @param IoStatus The status of the UDP receive\r
1099 @param Context The opaque parameter to the function.\r
1100\r
772db4bb 1101**/\r
1102VOID\r
e798cd87 1103EFIAPI\r
772db4bb 1104DhcpOnPacketSent (\r
1105 NET_BUF *Packet,\r
b45b45b2 1106 UDP_END_POINT *EndPoint,\r
772db4bb 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
3e8c18da 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
772db4bb 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
f9204641 1132 @retval other Other error occurs\r
772db4bb 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
b45b45b2 1149 UDP_IO *UdpIo;\r
1150 UDP_END_POINT EndPoint;\r
772db4bb 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
687a2e5f 1166 Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);\r
772db4bb 1167 }\r
1168\r
e48e37fc 1169 Packet = AllocatePool (Len);\r
772db4bb 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
e48e37fc 1189 ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));\r
772db4bb 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
e48e37fc 1198 CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);\r
772db4bb 1199\r
842d83d6 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
772db4bb 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
ac6c3d90 1216 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MSG_TYPE, 1, &Type);\r
772db4bb 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
f9204641 1225 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))\r
1226 ) {\r
772db4bb 1227\r
1228 ASSERT ((Para != NULL) && (Para->ServerId != 0));\r
1229\r
1230 IpAddr = HTONL (Para->ServerId);\r
ac6c3d90 1231 Buf = DhcpAppendOption (Buf, DHCP4_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);\r
772db4bb 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
ac6c3d90 1257 Buf = DhcpAppendOption (Buf, DHCP4_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);\r
772db4bb 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
ac6c3d90 1267 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);\r
772db4bb 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
36ee91ca 1274 Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);\r
ac6c3d90 1275 Buf = DhcpAppendOption (Buf, DHCP4_TAG_MESSAGE, (UINT16) Len, Msg);\r
772db4bb 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
ac6c3d90 1288 (Config->OptionList[Index]->OpCode != DHCP4_TAG_CLIENT_ID)) {\r
772db4bb 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
ac6c3d90 1301 *(Buf++) = DHCP4_TAG_EOP;\r
772db4bb 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
766c7483 1321 FreePool (Packet);\r
772db4bb 1322 return Status;\r
1323 }\r
1324\r
1325 if (NewPacket != NULL) {\r
766c7483 1326 FreePool (Packet);\r
772db4bb 1327 Packet = NewPacket;\r
1328 }\r
1329\r
982a9eae 1330 //\r
1331 // Save the Client Address will be sent out\r
1332 //\r
f9204641 1333 CopyMem (\r
1334 &DhcpSb->ClientAddressSendOut[0],\r
1335 &Packet->Dhcp4.Header.ClientHwAddr[0],\r
1336 Packet->Dhcp4.Header.HwAddrLen\r
1337 );\r
982a9eae 1338\r
772db4bb 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
479a3b60 1344 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);\r
772db4bb 1345\r
1346 if (Wrap == NULL) {\r
766c7483 1347 FreePool (Packet);\r
772db4bb 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
434ce3fe 1355 FreePool (DhcpSb->LastPacket);\r
772db4bb 1356 }\r
1357\r
434ce3fe 1358 DhcpSb->LastPacket = Packet;\r
772db4bb 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
b45b45b2 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
772db4bb 1370\r
1371 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {\r
b45b45b2 1372 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;\r
1373 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;\r
1374 UdpIo = DhcpSb->LeaseIoPort;\r
772db4bb 1375 }\r
1376\r
1377 ASSERT (UdpIo != NULL);\r
d1102dba 1378\r
434ce3fe 1379 Status = UdpIoSendDatagram (\r
d1102dba
LG
1380 UdpIo,\r
1381 Wrap,\r
1382 &EndPoint,\r
1383 NULL,\r
1384 DhcpOnPacketSent,\r
434ce3fe 1385 DhcpSb\r
1386 );\r
772db4bb 1387\r
1388 if (EFI_ERROR (Status)) {\r
479a3b60 1389 NetbufFree (Wrap);\r
772db4bb 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
3e8c18da 1401 @param[in] DhcpSb The DHCP service instance\r
772db4bb 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
b45b45b2 1412 UDP_IO *UdpIo;\r
1413 UDP_END_POINT EndPoint;\r
434ce3fe 1414 NET_BUF *Wrap;\r
1415 NET_FRAGMENT Frag;\r
772db4bb 1416 EFI_STATUS Status;\r
1417\r
1418 ASSERT (DhcpSb->LastPacket != NULL);\r
1419\r
842d83d6 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
434ce3fe 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
479a3b60 1432 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);\r
434ce3fe 1433\r
1434 if (Wrap == NULL) {\r
1435 return EFI_OUT_OF_RESOURCES;\r
1436 }\r
479a3b60 1437\r
772db4bb 1438 //\r
1439 // Broadcast the message, unless we know the server address.\r
1440 //\r
b45b45b2 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
772db4bb 1446\r
1447 if (DhcpSb->DhcpState == Dhcp4Renewing) {\r
b45b45b2 1448 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;\r
1449 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;\r
1450 UdpIo = DhcpSb->LeaseIoPort;\r
772db4bb 1451 }\r
1452\r
1453 ASSERT (UdpIo != NULL);\r
1454\r
772db4bb 1455 Status = UdpIoSendDatagram (\r
1456 UdpIo,\r
434ce3fe 1457 Wrap,\r
772db4bb 1458 &EndPoint,\r
b45b45b2 1459 NULL,\r
772db4bb 1460 DhcpOnPacketSent,\r
1461 DhcpSb\r
1462 );\r
1463\r
1464 if (EFI_ERROR (Status)) {\r
479a3b60 1465 NetbufFree (Wrap);\r
772db4bb 1466 return EFI_ACCESS_DENIED;\r
1467 }\r
1468\r
1469 return EFI_SUCCESS;\r
1470}\r
1471\r
1472\r
1473/**\r
1474 Each DHCP service has three timer. Two of them are count down timer.\r
1475 One for the packet retransmission. The other is to collect the offers.\r
1476 The third timer increaments the lease life which is compared to T1, T2,\r
1477 and lease to determine the time to renew and rebind the lease.\r
1478 DhcpOnTimerTick will be called once every second.\r
1479\r
3e8c18da 1480 @param[in] Event The timer event\r
1481 @param[in] Context The context, which is the DHCP service instance.\r
772db4bb 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
48bd6530
JW
1491 LIST_ENTRY *Entry;\r
1492 LIST_ENTRY *Next;\r
772db4bb 1493 DHCP_SERVICE *DhcpSb;\r
c4a62a12 1494 DHCP_PROTOCOL *Instance;\r
772db4bb 1495 EFI_STATUS Status;\r
982a9eae 1496\r
c4a62a12 1497 DhcpSb = (DHCP_SERVICE *) Context;\r
1498 Instance = DhcpSb->ActiveChild;\r
842d83d6 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
d1102dba 1506\r
c4a62a12 1507 //\r
e234f7c4 1508 // Check the retransmit timer\r
c4a62a12 1509 //\r
e234f7c4 1510 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {\r
1511\r
c4a62a12 1512 //\r
e234f7c4 1513 // Select offer at each timeout if any offer received.\r
c4a62a12 1514 //\r
e234f7c4 1515 if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {\r
772db4bb 1516\r
e234f7c4 1517 Status = DhcpChooseOffer (DhcpSb);\r
c4a62a12 1518\r
e234f7c4 1519 if (EFI_ERROR(Status)) {\r
39669f12 1520 if (DhcpSb->LastOffer != NULL) {\r
1521 FreePool (DhcpSb->LastOffer);\r
1522 DhcpSb->LastOffer = NULL;\r
1523 }\r
e234f7c4 1524 } else {\r
1525 goto ON_EXIT;\r
1526 }\r
c4a62a12 1527 }\r
d1102dba 1528\r
772db4bb 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
e234f7c4 1536 } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
772db4bb 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
7bce0c5a 1543 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 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
e234f7c4 1563 } else {\r
1564 goto END_SESSION;\r
772db4bb 1565 }\r
1566 }\r
d1102dba 1567\r
772db4bb 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
7bce0c5a 1579 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 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
e1982d4c 1597 if (Instance != NULL) {\r
1598 Instance->ElaspedTime= 0;\r
d1102dba
LG
1599 }\r
1600\r
772db4bb 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
e1982d4c 1621 if (Instance != NULL) {\r
1622 Instance->ElaspedTime= 0;\r
d1102dba 1623 }\r
842d83d6 1624\r
772db4bb 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
e234f7c4 1639ON_EXIT:\r
48bd6530
JW
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
71096201
JW
1645 Instance->Timeout--;\r
1646 if (Instance->Timeout == 0 && Instance->Token != NULL) {\r
1647 PxeDhcpDone (Instance);\r
c4a62a12 1648 }\r
1649 }\r
1650\r
772db4bb 1651 return ;\r
1652\r
1653END_SESSION:\r
1654 DhcpEndSession (DhcpSb, EFI_TIMEOUT);\r
1655\r
1656 return ;\r
1657}\r