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