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