]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
1. Fix a bug in PXE driver that the PXE boot do not restart if a new boot option...
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Io.c
CommitLineData
772db4bb 1/** @file\r
3e8c18da 2 EFI DHCP protocol implementation.\r
3 \r
75dce340 4Copyright (c) 2006 - 2012, 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
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
396 INTN Class;\r
397\r
398 DhcpSb->ClientAddr = EFI_NTOHL (DhcpSb->Selected->Dhcp4.Header.YourAddr);\r
399\r
400 if (DhcpSb->Para != NULL) {\r
401 DhcpSb->Netmask = DhcpSb->Para->NetMask;\r
402 DhcpSb->ServerAddr = DhcpSb->Para->ServerId;\r
403 }\r
404\r
405 if (DhcpSb->Netmask == 0) {\r
406 Class = NetGetIpClass (DhcpSb->ClientAddr);\r
2a86ff1c 407 DhcpSb->Netmask = gIp4AllMasks[Class << 3];\r
772db4bb 408 }\r
409\r
410 if (DhcpSb->LeaseIoPort != NULL) {\r
b45b45b2 411 UdpIoFreeIo (DhcpSb->LeaseIoPort);\r
772db4bb 412 }\r
413\r
414 //\r
415 // Create a UDP/IP child to provide ARP service for the Leased IP,\r
416 // and transmit unicast packet with it as source address. Don't\r
417 // start receive on this port, the queued packet will be timeout.\r
418 //\r
b45b45b2 419 DhcpSb->LeaseIoPort = UdpIoCreateIo (\r
772db4bb 420 DhcpSb->Controller,\r
421 DhcpSb->Image,\r
422 DhcpConfigLeaseIoPort,\r
b45b45b2 423 UDP_IO_UDP4_VERSION,\r
772db4bb 424 DhcpSb\r
425 );\r
426\r
427 if (DhcpSb->LeaseIoPort == NULL) {\r
428 return EFI_OUT_OF_RESOURCES;\r
429 }\r
430\r
431 if (!DHCP_IS_BOOTP (DhcpSb->Para)) {\r
432 DhcpComputeLease (DhcpSb, DhcpSb->Para);\r
433 }\r
434\r
435 return DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);\r
436}\r
437\r
438\r
439/**\r
440 Clean up the DHCP related states, IoStatus isn't reset.\r
441\r
442 @param DhcpSb The DHCP instance service.\r
443\r
772db4bb 444**/\r
445VOID\r
446DhcpCleanLease (\r
447 IN DHCP_SERVICE *DhcpSb\r
448 )\r
449{\r
450 DhcpSb->DhcpState = Dhcp4Init;\r
451 DhcpSb->Xid = DhcpSb->Xid + 1;\r
452 DhcpSb->ClientAddr = 0;\r
c5bcc2e2 453 DhcpSb->Netmask = 0;\r
772db4bb 454 DhcpSb->ServerAddr = 0;\r
455\r
456 if (DhcpSb->LastOffer != NULL) {\r
a4df47f1 457 FreePool (DhcpSb->LastOffer);\r
772db4bb 458 DhcpSb->LastOffer = NULL;\r
459 }\r
460\r
461 if (DhcpSb->Selected != NULL) {\r
a4df47f1 462 FreePool (DhcpSb->Selected);\r
772db4bb 463 DhcpSb->Selected = NULL;\r
464 }\r
465\r
466 if (DhcpSb->Para != NULL) {\r
a4df47f1 467 FreePool (DhcpSb->Para);\r
772db4bb 468 DhcpSb->Para = NULL;\r
469 }\r
470\r
471 DhcpSb->Lease = 0;\r
472 DhcpSb->T1 = 0;\r
473 DhcpSb->T2 = 0;\r
474 DhcpSb->ExtraRefresh = FALSE;\r
475\r
476 if (DhcpSb->LeaseIoPort != NULL) {\r
b45b45b2 477 UdpIoFreeIo (DhcpSb->LeaseIoPort);\r
772db4bb 478 DhcpSb->LeaseIoPort = NULL;\r
479 }\r
480\r
481 if (DhcpSb->LastPacket != NULL) {\r
434ce3fe 482 FreePool (DhcpSb->LastPacket);\r
772db4bb 483 DhcpSb->LastPacket = NULL;\r
484 }\r
485\r
486 DhcpSb->PacketToLive = 0;\r
434ce3fe 487 DhcpSb->LastTimeout = 0;\r
772db4bb 488 DhcpSb->CurRetry = 0;\r
489 DhcpSb->MaxRetries = 0;\r
772db4bb 490 DhcpSb->LeaseLife = 0;\r
235eaa63 491\r
492 //\r
493 // Clean active config data.\r
494 //\r
495 DhcpCleanConfigure (&DhcpSb->ActiveConfig);\r
772db4bb 496}\r
497\r
498\r
499/**\r
500 Select a offer among all the offers collected. If the offer selected is\r
501 of BOOTP, the lease is recorded and user notified. If the offer is of\r
502 DHCP, it will request the offer from the server.\r
503\r
3e8c18da 504 @param[in] DhcpSb The DHCP service instance.\r
772db4bb 505\r
506 @retval EFI_SUCCESS One of the offer is selected.\r
507\r
508**/\r
772db4bb 509EFI_STATUS\r
510DhcpChooseOffer (\r
511 IN DHCP_SERVICE *DhcpSb\r
512 )\r
513{\r
514 EFI_DHCP4_PACKET *Selected;\r
515 EFI_DHCP4_PACKET *NewPacket;\r
c4a62a12 516 EFI_DHCP4_PACKET *TempPacket;\r
772db4bb 517 EFI_STATUS Status;\r
518\r
519 ASSERT (DhcpSb->LastOffer != NULL);\r
520\r
772db4bb 521 //\r
522 // User will cache previous offers if he wants to select\r
523 // from multiple offers. If user provides an invalid packet,\r
524 // use the last offer, otherwise use the provided packet.\r
525 //\r
526 NewPacket = NULL;\r
527 Status = DhcpCallUser (DhcpSb, Dhcp4SelectOffer, DhcpSb->LastOffer, &NewPacket);\r
528\r
529 if (EFI_ERROR (Status)) {\r
530 return Status;\r
531 }\r
532\r
533 Selected = DhcpSb->LastOffer;\r
534\r
c4a62a12 535 if ((NewPacket != NULL) && !EFI_ERROR (DhcpValidateOptions (NewPacket, NULL))) {\r
e48e37fc 536 TempPacket = (EFI_DHCP4_PACKET *) AllocatePool (NewPacket->Size);\r
c4a62a12 537 if (TempPacket != NULL) {\r
e48e37fc 538 CopyMem (TempPacket, NewPacket, NewPacket->Size);\r
766c7483 539 FreePool (Selected);\r
c4a62a12 540 Selected = TempPacket;\r
772db4bb 541 }\r
542 }\r
543\r
544 DhcpSb->Selected = Selected;\r
545 DhcpSb->LastOffer = NULL;\r
546 DhcpSb->Para = NULL;\r
547 DhcpValidateOptions (Selected, &DhcpSb->Para);\r
548\r
549 //\r
550 // A bootp offer has been selected, save the lease status,\r
551 // enter bound state then notify the user.\r
552 //\r
553 if (DHCP_IS_BOOTP (DhcpSb->Para)) {\r
554 Status = DhcpLeaseAcquired (DhcpSb);\r
555\r
556 if (EFI_ERROR (Status)) {\r
557 return Status;\r
558 }\r
559\r
560 DhcpSb->IoStatus = EFI_SUCCESS;\r
561 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);\r
562 return EFI_SUCCESS;\r
563 }\r
564\r
565 //\r
566 // Send a DHCP requests\r
567 //\r
568 Status = DhcpSetState (DhcpSb, Dhcp4Requesting, TRUE);\r
569\r
570 if (EFI_ERROR (Status)) {\r
571 return Status;\r
572 }\r
573\r
574 return DhcpSendMessage (DhcpSb, Selected, DhcpSb->Para, DHCP_MSG_REQUEST, NULL);\r
575}\r
576\r
577\r
578/**\r
579 Terminate the current address acquire. All the allocated resources\r
580 are released. Be careful when calling this function. A rule related\r
581 to this is: only call DhcpEndSession at the highest level, such as\r
582 DhcpInput, DhcpOnTimerTick...At the other level, just return error.\r
583\r
3e8c18da 584 @param[in] DhcpSb The DHCP service instance\r
585 @param[in] Status The result of the DHCP process.\r
772db4bb 586\r
587**/\r
772db4bb 588VOID\r
589DhcpEndSession (\r
590 IN DHCP_SERVICE *DhcpSb,\r
591 IN EFI_STATUS Status\r
592 )\r
593{\r
594 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
595 DhcpCallUser (DhcpSb, Dhcp4AddressLost, NULL, NULL);\r
596 } else {\r
597 DhcpCallUser (DhcpSb, Dhcp4Fail, NULL, NULL);\r
598 }\r
599\r
600 DhcpCleanLease (DhcpSb);\r
601\r
602 DhcpSb->IoStatus = Status;\r
603 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_ALL);\r
604}\r
605\r
606\r
607/**\r
608 Handle packets in DHCP select state.\r
609\r
3e8c18da 610 @param[in] DhcpSb The DHCP service instance\r
611 @param[in] Packet The DHCP packet received\r
612 @param[in] Para The DHCP parameter extracted from the packet. That\r
613 is, all the option value that we care.\r
772db4bb 614\r
615 @retval EFI_SUCCESS The packet is successfully processed.\r
616 @retval Others Some error occured.\r
617\r
618**/\r
772db4bb 619EFI_STATUS\r
620DhcpHandleSelect (\r
621 IN DHCP_SERVICE *DhcpSb,\r
622 IN EFI_DHCP4_PACKET *Packet,\r
623 IN DHCP_PARAMETER *Para\r
624 )\r
625{\r
772db4bb 626 EFI_STATUS Status;\r
627\r
628 Status = EFI_SUCCESS;\r
629\r
630 //\r
631 // First validate the message:\r
632 // 1. the offer is a unicast\r
633 // 2. if it is a DHCP message, it must contains a server ID.\r
634 // Don't return a error for these two case otherwise the session is ended.\r
635 //\r
772db4bb 636 if (!DHCP_IS_BOOTP (Para) &&\r
f9204641 637 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))\r
638 ) {\r
772db4bb 639 goto ON_EXIT;\r
640 }\r
641\r
642 //\r
643 // Call the user's callback. The action according to the return is as:\r
644 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now\r
645 // 2. EFI_NOT_READY: wait for more offers\r
646 // 3. EFI_ABORTED: abort the address acquiring.\r
647 //\r
648 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);\r
649\r
650 if (Status == EFI_SUCCESS) {\r
651 if (DhcpSb->LastOffer != NULL) {\r
a4df47f1 652 FreePool (DhcpSb->LastOffer);\r
772db4bb 653 }\r
654\r
655 DhcpSb->LastOffer = Packet;\r
656\r
657 return DhcpChooseOffer (DhcpSb);\r
658\r
659 } else if (Status == EFI_NOT_READY) {\r
660 if (DhcpSb->LastOffer != NULL) {\r
a4df47f1 661 FreePool (DhcpSb->LastOffer);\r
772db4bb 662 }\r
663\r
664 DhcpSb->LastOffer = Packet;\r
665\r
666 } else if (Status == EFI_ABORTED) {\r
667 //\r
668 // DhcpInput will end the session upon error return. Remember\r
669 // only to call DhcpEndSession at the top level call.\r
670 //\r
671 goto ON_EXIT;\r
672 }\r
673\r
674 return EFI_SUCCESS;\r
675\r
676ON_EXIT:\r
766c7483 677 FreePool (Packet);\r
772db4bb 678 return Status;\r
679}\r
680\r
681\r
682/**\r
683 Handle packets in DHCP request state.\r
684\r
3e8c18da 685 @param[in] DhcpSb The DHCP service instance\r
686 @param[in] Packet The DHCP packet received\r
687 @param[in] Para The DHCP parameter extracted from the packet. That\r
688 is, all the option value that we care.\r
772db4bb 689\r
690 @retval EFI_SUCCESS The packet is successfully processed.\r
691 @retval Others Some error occured.\r
692\r
693**/\r
772db4bb 694EFI_STATUS\r
695DhcpHandleRequest (\r
696 IN DHCP_SERVICE *DhcpSb,\r
697 IN EFI_DHCP4_PACKET *Packet,\r
698 IN DHCP_PARAMETER *Para\r
699 )\r
700{\r
701 EFI_DHCP4_HEADER *Head;\r
702 EFI_DHCP4_HEADER *Selected;\r
703 EFI_STATUS Status;\r
704 UINT8 *Message;\r
705\r
706 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
707\r
708 Head = &Packet->Dhcp4.Header;\r
709 Selected = &DhcpSb->Selected->Dhcp4.Header;\r
710\r
711 //\r
712 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.\r
713 //\r
714 if (DHCP_IS_BOOTP (Para) ||\r
f9204641 715 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
716 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
717 ) {\r
772db4bb 718\r
719 Status = EFI_SUCCESS;\r
720 goto ON_EXIT;\r
721 }\r
722\r
723 //\r
724 // Received a NAK, end the session no matter what the user returns\r
725 //\r
726 Status = EFI_DEVICE_ERROR;\r
727\r
728 if (Para->DhcpType == DHCP_MSG_NAK) {\r
729 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
730 goto ON_EXIT;\r
731 }\r
732\r
733 //\r
734 // Check whether the ACK matches the selected offer\r
735 //\r
736 Message = NULL;\r
737\r
84b5c78e 738 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
67a58d0f 739 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";\r
772db4bb 740 goto REJECT;\r
741 }\r
742\r
743 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
744\r
745 if (EFI_ERROR (Status)) {\r
67a58d0f 746 Message = (UINT8 *) "Lease is denied upon received ACK";\r
772db4bb 747 goto REJECT;\r
748 }\r
749\r
750 //\r
751 // Record the lease, transit to BOUND state, then notify the user\r
752 //\r
753 Status = DhcpLeaseAcquired (DhcpSb);\r
754\r
755 if (EFI_ERROR (Status)) {\r
67a58d0f 756 Message = (UINT8 *) "Lease is denied upon entering bound";\r
772db4bb 757 goto REJECT;\r
758 }\r
759\r
760 DhcpSb->IoStatus = EFI_SUCCESS;\r
761 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
762\r
766c7483 763 FreePool (Packet);\r
772db4bb 764 return EFI_SUCCESS;\r
765\r
766REJECT:\r
767 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);\r
768\r
769ON_EXIT:\r
766c7483 770 FreePool (Packet);\r
772db4bb 771 return Status;\r
772}\r
773\r
774\r
775/**\r
776 Handle packets in DHCP renew/rebound state.\r
777\r
3e8c18da 778 @param[in] DhcpSb The DHCP service instance\r
779 @param[in] Packet The DHCP packet received\r
780 @param[in] Para The DHCP parameter extracted from the packet. That\r
781 is, all the option value that we care.\r
772db4bb 782\r
783 @retval EFI_SUCCESS The packet is successfully processed.\r
784 @retval Others Some error occured.\r
785\r
786**/\r
772db4bb 787EFI_STATUS\r
788DhcpHandleRenewRebind (\r
789 IN DHCP_SERVICE *DhcpSb,\r
790 IN EFI_DHCP4_PACKET *Packet,\r
791 IN DHCP_PARAMETER *Para\r
792 )\r
793{\r
794 EFI_DHCP4_HEADER *Head;\r
795 EFI_DHCP4_HEADER *Selected;\r
796 EFI_STATUS Status;\r
797\r
798 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
799\r
800 Head = &Packet->Dhcp4.Header;\r
801 Selected = &DhcpSb->Selected->Dhcp4.Header;\r
802\r
803 //\r
804 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
805 //\r
806 if (DHCP_IS_BOOTP (Para) ||\r
f9204641 807 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
808 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
809 ) {\r
772db4bb 810\r
811 Status = EFI_SUCCESS;\r
812 goto ON_EXIT;\r
813 }\r
814\r
815 //\r
816 // Received a NAK, ignore the user's return then terminate the process\r
817 //\r
818 Status = EFI_DEVICE_ERROR;\r
819\r
820 if (Para->DhcpType == DHCP_MSG_NAK) {\r
821 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
822 goto ON_EXIT;\r
823 }\r
824\r
825 //\r
826 // The lease is different from the selected. Don't send a DECLINE\r
827 // since it isn't existed in the client's FSM.\r
828 //\r
84b5c78e 829 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
772db4bb 830 goto ON_EXIT;\r
831 }\r
832\r
833 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
834\r
835 if (EFI_ERROR (Status)) {\r
836 goto ON_EXIT;\r
837 }\r
838\r
839 //\r
840 // Record the lease, start timer for T1 and T2,\r
841 //\r
842 DhcpComputeLease (DhcpSb, Para);\r
843 DhcpSb->LeaseLife = 0;\r
844 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);\r
845\r
7bce0c5a 846 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 847 DhcpSb->ExtraRefresh = FALSE;\r
848\r
849 DhcpSb->IoStatus = EFI_SUCCESS;\r
850 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
851 }\r
852\r
853ON_EXIT:\r
766c7483 854 FreePool (Packet);\r
772db4bb 855 return Status;\r
856}\r
857\r
858\r
859/**\r
860 Handle packets in DHCP reboot state.\r
861\r
3e8c18da 862 @param[in] DhcpSb The DHCP service instance\r
863 @param[in] Packet The DHCP packet received\r
864 @param[in] Para The DHCP parameter extracted from the packet. That\r
865 is, all the option value that we care.\r
772db4bb 866\r
867 @retval EFI_SUCCESS The packet is successfully processed.\r
868 @retval Others Some error occured.\r
869\r
870**/\r
772db4bb 871EFI_STATUS\r
872DhcpHandleReboot (\r
873 IN DHCP_SERVICE *DhcpSb,\r
874 IN EFI_DHCP4_PACKET *Packet,\r
875 IN DHCP_PARAMETER *Para\r
876 )\r
877{\r
878 EFI_DHCP4_HEADER *Head;\r
879 EFI_STATUS Status;\r
880\r
881 Head = &Packet->Dhcp4.Header;\r
882\r
883 //\r
884 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
885 //\r
886 if (DHCP_IS_BOOTP (Para) ||\r
f9204641 887 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))\r
888 ) {\r
772db4bb 889\r
890 Status = EFI_SUCCESS;\r
891 goto ON_EXIT;\r
892 }\r
893\r
894 //\r
895 // If a NAK is received, transit to INIT and try again.\r
896 //\r
897 if (Para->DhcpType == DHCP_MSG_NAK) {\r
898 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
899\r
900 DhcpSb->ClientAddr = 0;\r
901 DhcpSb->DhcpState = Dhcp4Init;\r
902\r
903 Status = DhcpInitRequest (DhcpSb);\r
904 goto ON_EXIT;\r
905 }\r
906\r
907 //\r
908 // Check whether the ACK matches the selected offer\r
909 //\r
910 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {\r
911 Status = EFI_DEVICE_ERROR;\r
912 goto ON_EXIT;\r
913 }\r
914\r
915 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
916 if (EFI_ERROR (Status)) {\r
917 goto ON_EXIT;\r
918 }\r
919\r
920 //\r
921 // OK, get the parameter from server, record the lease\r
922 //\r
a4df47f1 923 DhcpSb->Para = AllocateCopyPool (sizeof (DHCP_PARAMETER), Para);\r
772db4bb 924 if (DhcpSb->Para == NULL) {\r
925 Status = EFI_OUT_OF_RESOURCES;\r
926 goto ON_EXIT;\r
927 }\r
928\r
929 DhcpSb->Selected = Packet;\r
772db4bb 930 Status = DhcpLeaseAcquired (DhcpSb);\r
772db4bb 931 if (EFI_ERROR (Status)) {\r
932 return Status;\r
933 }\r
934\r
935 DhcpSb->IoStatus = EFI_SUCCESS;\r
936 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
937 return EFI_SUCCESS;\r
938\r
939ON_EXIT:\r
766c7483 940 FreePool (Packet);\r
772db4bb 941 return Status;\r
942}\r
943\r
944\r
945/**\r
f9204641 946 Handle the received DHCP packets. This function drives the DHCP\r
772db4bb 947 state machine.\r
948\r
949 @param UdpPacket The UDP packets received.\r
b45b45b2 950 @param EndPoint The local/remote UDP access point\r
772db4bb 951 @param IoStatus The status of the UDP receive\r
952 @param Context The opaque parameter to the function.\r
953\r
772db4bb 954**/\r
955VOID\r
e798cd87 956EFIAPI\r
772db4bb 957DhcpInput (\r
958 NET_BUF *UdpPacket,\r
b45b45b2 959 UDP_END_POINT *EndPoint,\r
772db4bb 960 EFI_STATUS IoStatus,\r
961 VOID *Context\r
962 )\r
963{\r
964 DHCP_SERVICE *DhcpSb;\r
965 EFI_DHCP4_HEADER *Head;\r
966 EFI_DHCP4_PACKET *Packet;\r
967 DHCP_PARAMETER *Para;\r
968 EFI_STATUS Status;\r
969 UINT32 Len;\r
970\r
971 Packet = NULL;\r
972 DhcpSb = (DHCP_SERVICE *) Context;\r
973\r
974 //\r
75dce340 975 // Don't restart receive if error occurs or DHCP is destroyed.\r
772db4bb 976 //\r
977 if (EFI_ERROR (IoStatus)) {\r
978 return ;\r
75dce340 979 } else if (DhcpSb->ServiceState == DHCP_DESTROY) {\r
772db4bb 980 NetbufFree (UdpPacket);\r
981 return ;\r
982 }\r
983\r
984 ASSERT (UdpPacket != NULL);\r
985\r
986 if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
987 goto RESTART;\r
988 }\r
989\r
990 //\r
991 // Validate the packet received\r
992 //\r
993 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {\r
994 goto RESTART;\r
995 }\r
996\r
997 //\r
998 // Copy the DHCP message to a continuous memory block\r
999 //\r
1000 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);\r
e48e37fc 1001 Packet = (EFI_DHCP4_PACKET *) AllocatePool (Len);\r
772db4bb 1002\r
1003 if (Packet == NULL) {\r
1004 goto RESTART;\r
1005 }\r
1006\r
1007 Packet->Size = Len;\r
1008 Head = &Packet->Dhcp4.Header;\r
1009 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);\r
1010\r
1011 if (Packet->Length != UdpPacket->TotalSize) {\r
1012 goto RESTART;\r
1013 }\r
1014\r
1015 //\r
1016 // Is this packet the answer to our packet?\r
1017 //\r
1018 if ((Head->OpCode != BOOTP_REPLY) ||\r
1019 (NTOHL (Head->Xid) != DhcpSb->Xid) ||\r
982a9eae 1020 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {\r
772db4bb 1021 goto RESTART;\r
1022 }\r
1023\r
1024 //\r
1025 // Validate the options and retrieve the interested options\r
1026 //\r
1027 Para = NULL;\r
1028 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&\r
1029 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&\r
1030 EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {\r
1031\r
1032 goto RESTART;\r
1033 }\r
1034\r
1035 //\r
1036 // Call the handler for each state. The handler should return\r
1037 // EFI_SUCCESS if the process can go on no matter whether the\r
1038 // packet is ignored or not. If the return is EFI_ERROR, the\r
1039 // session will be terminated. Packet's ownership is handled\r
1040 // over to the handlers. If operation succeeds, the handler\r
1041 // must notify the user. It isn't necessary to do if EFI_ERROR\r
1042 // is returned because the DhcpEndSession will notify the user.\r
1043 //\r
1044 Status = EFI_SUCCESS;\r
1045\r
1046 switch (DhcpSb->DhcpState) {\r
1047 case Dhcp4Selecting:\r
1048 Status = DhcpHandleSelect (DhcpSb, Packet, Para);\r
1049 break;\r
1050\r
1051 case Dhcp4Requesting:\r
1052 Status = DhcpHandleRequest (DhcpSb, Packet, Para);\r
1053 break;\r
1054\r
1055 case Dhcp4InitReboot:\r
1056 case Dhcp4Init:\r
1057 case Dhcp4Bound:\r
1058 //\r
1059 // Ignore the packet in INITREBOOT, INIT and BOUND states\r
1060 //\r
766c7483 1061 FreePool (Packet);\r
772db4bb 1062 Status = EFI_SUCCESS;\r
1063 break;\r
1064\r
1065 case Dhcp4Renewing:\r
1066 case Dhcp4Rebinding:\r
1067 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);\r
1068 break;\r
1069\r
1070 case Dhcp4Rebooting:\r
1071 Status = DhcpHandleReboot (DhcpSb, Packet, Para);\r
1072 break;\r
1073 }\r
1074\r
1075 if (Para != NULL) {\r
a4df47f1 1076 FreePool (Para);\r
772db4bb 1077 }\r
1078\r
1079 Packet = NULL;\r
1080\r
1081 if (EFI_ERROR (Status)) {\r
1082 NetbufFree (UdpPacket);\r
c5bcc2e2 1083 UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
772db4bb 1084 DhcpEndSession (DhcpSb, Status);\r
1085 return ;\r
1086 }\r
1087\r
1088RESTART:\r
1089 NetbufFree (UdpPacket);\r
1090\r
1091 if (Packet != NULL) {\r
766c7483 1092 FreePool (Packet);\r
772db4bb 1093 }\r
1094\r
1095 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
1096\r
1097 if (EFI_ERROR (Status)) {\r
1098 DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);\r
1099 }\r
1100}\r
1101\r
1102\r
1103/**\r
1104 Release the packet.\r
1105\r
3e8c18da 1106 @param[in] Arg The packet to release\r
772db4bb 1107\r
1108**/\r
1109VOID\r
e798cd87 1110EFIAPI\r
772db4bb 1111DhcpReleasePacket (\r
1112 IN VOID *Arg\r
1113 )\r
1114{\r
766c7483 1115 FreePool (Arg);\r
772db4bb 1116}\r
1117\r
1118\r
1119/**\r
1120 Release the net buffer when packet is sent.\r
1121\r
1122 @param UdpPacket The UDP packets received.\r
b45b45b2 1123 @param EndPoint The local/remote UDP access point\r
772db4bb 1124 @param IoStatus The status of the UDP receive\r
1125 @param Context The opaque parameter to the function.\r
1126\r
772db4bb 1127**/\r
1128VOID\r
e798cd87 1129EFIAPI\r
772db4bb 1130DhcpOnPacketSent (\r
1131 NET_BUF *Packet,\r
b45b45b2 1132 UDP_END_POINT *EndPoint,\r
772db4bb 1133 EFI_STATUS IoStatus,\r
1134 VOID *Context\r
1135 )\r
1136{\r
1137 NetbufFree (Packet);\r
1138}\r
1139\r
1140\r
1141\r
1142/**\r
1143 Build and transmit a DHCP message according to the current states.\r
1144 This function implement the Table 5. of RFC 2131. Always transits\r
1145 the state (as defined in Figure 5. of the same RFC) before sending\r
1146 a DHCP message. The table is adjusted accordingly.\r
1147\r
3e8c18da 1148 @param[in] DhcpSb The DHCP service instance\r
1149 @param[in] Seed The seed packet which the new packet is based on\r
1150 @param[in] Para The DHCP parameter of the Seed packet\r
1151 @param[in] Type The message type to send\r
1152 @param[in] Msg The human readable message to include in the packet\r
1153 sent.\r
772db4bb 1154\r
1155 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet\r
1156 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP\r
1157 @retval EFI_SUCCESS The message is sent\r
f9204641 1158 @retval other Other error occurs\r
772db4bb 1159\r
1160**/\r
1161EFI_STATUS\r
1162DhcpSendMessage (\r
1163 IN DHCP_SERVICE *DhcpSb,\r
1164 IN EFI_DHCP4_PACKET *Seed,\r
1165 IN DHCP_PARAMETER *Para,\r
1166 IN UINT8 Type,\r
1167 IN UINT8 *Msg\r
1168 )\r
1169{\r
1170 EFI_DHCP4_CONFIG_DATA *Config;\r
1171 EFI_DHCP4_PACKET *Packet;\r
1172 EFI_DHCP4_PACKET *NewPacket;\r
1173 EFI_DHCP4_HEADER *Head;\r
1174 EFI_DHCP4_HEADER *SeedHead;\r
b45b45b2 1175 UDP_IO *UdpIo;\r
1176 UDP_END_POINT EndPoint;\r
772db4bb 1177 NET_BUF *Wrap;\r
1178 NET_FRAGMENT Frag;\r
1179 EFI_STATUS Status;\r
1180 IP4_ADDR IpAddr;\r
1181 UINT8 *Buf;\r
1182 UINT16 MaxMsg;\r
1183 UINT32 Len;\r
1184 UINT32 Index;\r
1185\r
1186 //\r
1187 // Allocate a big enough memory block to hold the DHCP packet\r
1188 //\r
1189 Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;\r
1190\r
1191 if (Msg != NULL) {\r
687a2e5f 1192 Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);\r
772db4bb 1193 }\r
1194\r
e48e37fc 1195 Packet = AllocatePool (Len);\r
772db4bb 1196\r
1197 if (Packet == NULL) {\r
1198 return EFI_OUT_OF_RESOURCES;\r
1199 }\r
1200\r
1201 Packet->Size = Len;\r
1202 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);\r
1203\r
1204 //\r
1205 // Fill in the DHCP header fields\r
1206 //\r
1207 Config = &DhcpSb->ActiveConfig;\r
1208 SeedHead = NULL;\r
1209\r
1210 if (Seed != NULL) {\r
1211 SeedHead = &Seed->Dhcp4.Header;\r
1212 }\r
1213\r
1214 Head = &Packet->Dhcp4.Header;\r
e48e37fc 1215 ZeroMem (Head, sizeof (EFI_DHCP4_HEADER));\r
772db4bb 1216\r
1217 Head->OpCode = BOOTP_REQUEST;\r
1218 Head->HwType = DhcpSb->HwType;\r
1219 Head->HwAddrLen = DhcpSb->HwLen;\r
1220 Head->Xid = HTONL (DhcpSb->Xid);\r
1221 Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.\r
1222\r
1223 EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);\r
e48e37fc 1224 CopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);\r
772db4bb 1225\r
842d83d6 1226 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) {\r
1227 Head->Seconds = 0;\r
1228 } else if ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting)) {\r
1229 //\r
1230 // Use the same value as the original DHCPDISCOVER message.\r
1231 //\r
1232 Head->Seconds = DhcpSb->LastPacket->Dhcp4.Header.Seconds;\r
1233 } else {\r
1234 SetElapsedTime(&Head->Seconds, DhcpSb->ActiveChild);\r
1235 }\r
1236\r
772db4bb 1237 //\r
1238 // Append the DHCP message type\r
1239 //\r
1240 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;\r
1241 Buf = Packet->Dhcp4.Option;\r
1242 Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);\r
1243\r
1244 //\r
1245 // Append the serverid option if necessary:\r
1246 // 1. DHCP decline message\r
1247 // 2. DHCP release message\r
1248 // 3. DHCP request to confirm one lease.\r
1249 //\r
1250 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||\r
f9204641 1251 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))\r
1252 ) {\r
772db4bb 1253\r
1254 ASSERT ((Para != NULL) && (Para->ServerId != 0));\r
1255\r
1256 IpAddr = HTONL (Para->ServerId);\r
1257 Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);\r
1258 }\r
1259\r
1260 //\r
1261 // Append the requested IP option if necessary:\r
1262 // 1. DHCP request to use the previously allocated address\r
1263 // 2. DHCP request to confirm one lease\r
1264 // 3. DHCP decline to decline one lease\r
1265 //\r
1266 IpAddr = 0;\r
1267\r
1268 if (Type == DHCP_MSG_REQUEST) {\r
1269 if (DhcpSb->DhcpState == Dhcp4Rebooting) {\r
1270 IpAddr = EFI_IP4 (Config->ClientAddress);\r
1271\r
1272 } else if (DhcpSb->DhcpState == Dhcp4Requesting) {\r
1273 ASSERT (SeedHead != NULL);\r
1274 IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
1275 }\r
1276\r
1277 } else if (Type == DHCP_MSG_DECLINE) {\r
1278 ASSERT (SeedHead != NULL);\r
1279 IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
1280 }\r
1281\r
1282 if (IpAddr != 0) {\r
1283 Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);\r
1284 }\r
1285\r
1286 //\r
1287 // Append the Max Message Length option if it isn't a DECLINE\r
1288 // or RELEASE to direct the server use large messages instead of\r
1289 // override the BOOTFILE and SERVER fields in the message head.\r
1290 //\r
1291 if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {\r
1292 MaxMsg = HTONS (0xFF00);\r
1293 Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);\r
1294 }\r
1295\r
1296 //\r
1297 // Append the user's message if it isn't NULL\r
1298 //\r
1299 if (Msg != NULL) {\r
36ee91ca 1300 Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);\r
772db4bb 1301 Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);\r
1302 }\r
1303\r
1304 //\r
1305 // Append the user configured options\r
1306 //\r
1307 if (DhcpSb->UserOptionLen != 0) {\r
1308 for (Index = 0; Index < Config->OptionCount; Index++) {\r
1309 //\r
1310 // We can't use any option other than the client ID from user\r
1311 // if it is a DHCP decline or DHCP release .\r
1312 //\r
1313 if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&\r
1314 (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {\r
1315 continue;\r
1316 }\r
1317\r
1318 Buf = DhcpAppendOption (\r
1319 Buf,\r
1320 Config->OptionList[Index]->OpCode,\r
1321 Config->OptionList[Index]->Length,\r
1322 Config->OptionList[Index]->Data\r
1323 );\r
1324 }\r
1325 }\r
1326\r
1327 *(Buf++) = DHCP_TAG_EOP;\r
1328 Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);\r
1329\r
1330 //\r
1331 // OK, the message is built, call the user to override it.\r
1332 //\r
1333 Status = EFI_SUCCESS;\r
1334 NewPacket = NULL;\r
1335\r
1336 if (Type == DHCP_MSG_DISCOVER) {\r
1337 Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);\r
1338\r
1339 } else if (Type == DHCP_MSG_REQUEST) {\r
1340 Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);\r
1341\r
1342 } else if (Type == DHCP_MSG_DECLINE) {\r
1343 Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);\r
1344 }\r
1345\r
1346 if (EFI_ERROR (Status)) {\r
766c7483 1347 FreePool (Packet);\r
772db4bb 1348 return Status;\r
1349 }\r
1350\r
1351 if (NewPacket != NULL) {\r
766c7483 1352 FreePool (Packet);\r
772db4bb 1353 Packet = NewPacket;\r
1354 }\r
1355\r
982a9eae 1356 //\r
1357 // Save the Client Address will be sent out\r
1358 //\r
f9204641 1359 CopyMem (\r
1360 &DhcpSb->ClientAddressSendOut[0],\r
1361 &Packet->Dhcp4.Header.ClientHwAddr[0],\r
1362 Packet->Dhcp4.Header.HwAddrLen\r
1363 );\r
982a9eae 1364\r
1365\r
772db4bb 1366 //\r
1367 // Wrap it into a netbuf then send it.\r
1368 //\r
1369 Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;\r
1370 Frag.Len = Packet->Length;\r
1371 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);\r
1372\r
1373 if (Wrap == NULL) {\r
766c7483 1374 FreePool (Packet);\r
772db4bb 1375 return EFI_OUT_OF_RESOURCES;\r
1376 }\r
1377\r
1378 //\r
1379 // Save it as the last sent packet for retransmission\r
1380 //\r
1381 if (DhcpSb->LastPacket != NULL) {\r
434ce3fe 1382 FreePool (DhcpSb->LastPacket);\r
772db4bb 1383 }\r
1384\r
434ce3fe 1385 DhcpSb->LastPacket = Packet;\r
772db4bb 1386 DhcpSetTransmitTimer (DhcpSb);\r
1387\r
1388 //\r
1389 // Broadcast the message, unless we know the server address.\r
1390 // Use the lease UdpIo port to send the unicast packet.\r
1391 //\r
b45b45b2 1392 EndPoint.RemoteAddr.Addr[0] = 0xffffffff;\r
1393 EndPoint.LocalAddr.Addr[0] = 0;\r
1394 EndPoint.RemotePort = DHCP_SERVER_PORT;\r
1395 EndPoint.LocalPort = DHCP_CLIENT_PORT;\r
1396 UdpIo = DhcpSb->UdpIo;\r
772db4bb 1397\r
1398 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {\r
b45b45b2 1399 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;\r
1400 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;\r
1401 UdpIo = DhcpSb->LeaseIoPort;\r
772db4bb 1402 }\r
1403\r
1404 ASSERT (UdpIo != NULL);\r
434ce3fe 1405 NET_GET_REF (Wrap);\r
1406 \r
1407 Status = UdpIoSendDatagram (\r
1408 UdpIo, \r
1409 Wrap, \r
1410 &EndPoint, \r
b45b45b2 1411 NULL, \r
434ce3fe 1412 DhcpOnPacketSent, \r
1413 DhcpSb\r
1414 );\r
772db4bb 1415\r
1416 if (EFI_ERROR (Status)) {\r
434ce3fe 1417 NET_PUT_REF (Wrap);\r
772db4bb 1418 return EFI_ACCESS_DENIED;\r
1419 }\r
1420\r
1421 return EFI_SUCCESS;\r
1422}\r
1423\r
1424\r
1425/**\r
1426 Retransmit a saved packet. Only DISCOVER and REQUEST messages\r
1427 will be retransmitted.\r
1428\r
3e8c18da 1429 @param[in] DhcpSb The DHCP service instance\r
772db4bb 1430\r
1431 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port\r
1432 @retval EFI_SUCCESS The packet is retransmitted.\r
1433\r
1434**/\r
1435EFI_STATUS\r
1436DhcpRetransmit (\r
1437 IN DHCP_SERVICE *DhcpSb\r
1438 )\r
1439{\r
b45b45b2 1440 UDP_IO *UdpIo;\r
1441 UDP_END_POINT EndPoint;\r
434ce3fe 1442 NET_BUF *Wrap;\r
1443 NET_FRAGMENT Frag;\r
772db4bb 1444 EFI_STATUS Status;\r
1445\r
1446 ASSERT (DhcpSb->LastPacket != NULL);\r
1447\r
842d83d6 1448 //\r
1449 // For REQUEST message in Dhcp4Requesting state, do not change the secs fields.\r
1450 //\r
1451 if (DhcpSb->DhcpState != Dhcp4Requesting) {\r
1452 SetElapsedTime(&DhcpSb->LastPacket->Dhcp4.Header.Seconds, DhcpSb->ActiveChild);\r
1453 }\r
434ce3fe 1454\r
1455 //\r
1456 // Wrap it into a netbuf then send it.\r
1457 //\r
1458 Frag.Bulk = (UINT8 *) &DhcpSb->LastPacket->Dhcp4.Header;\r
1459 Frag.Len = DhcpSb->LastPacket->Length;\r
1460 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, DhcpSb->LastPacket);\r
1461\r
1462 if (Wrap == NULL) {\r
1463 return EFI_OUT_OF_RESOURCES;\r
1464 }\r
1465 \r
772db4bb 1466 //\r
1467 // Broadcast the message, unless we know the server address.\r
1468 //\r
b45b45b2 1469 EndPoint.RemotePort = DHCP_SERVER_PORT;\r
1470 EndPoint.LocalPort = DHCP_CLIENT_PORT;\r
1471 EndPoint.RemoteAddr.Addr[0] = 0xffffffff;\r
1472 EndPoint.LocalAddr.Addr[0] = 0;\r
1473 UdpIo = DhcpSb->UdpIo;\r
772db4bb 1474\r
1475 if (DhcpSb->DhcpState == Dhcp4Renewing) {\r
b45b45b2 1476 EndPoint.RemoteAddr.Addr[0] = DhcpSb->ServerAddr;\r
1477 EndPoint.LocalAddr.Addr[0] = DhcpSb->ClientAddr;\r
1478 UdpIo = DhcpSb->LeaseIoPort;\r
772db4bb 1479 }\r
1480\r
1481 ASSERT (UdpIo != NULL);\r
1482\r
434ce3fe 1483 NET_GET_REF (Wrap);\r
772db4bb 1484 Status = UdpIoSendDatagram (\r
1485 UdpIo,\r
434ce3fe 1486 Wrap,\r
772db4bb 1487 &EndPoint,\r
b45b45b2 1488 NULL,\r
772db4bb 1489 DhcpOnPacketSent,\r
1490 DhcpSb\r
1491 );\r
1492\r
1493 if (EFI_ERROR (Status)) {\r
434ce3fe 1494 NET_PUT_REF (Wrap);\r
772db4bb 1495 return EFI_ACCESS_DENIED;\r
1496 }\r
1497\r
1498 return EFI_SUCCESS;\r
1499}\r
1500\r
1501\r
1502/**\r
1503 Each DHCP service has three timer. Two of them are count down timer.\r
1504 One for the packet retransmission. The other is to collect the offers.\r
1505 The third timer increaments the lease life which is compared to T1, T2,\r
1506 and lease to determine the time to renew and rebind the lease.\r
1507 DhcpOnTimerTick will be called once every second.\r
1508\r
3e8c18da 1509 @param[in] Event The timer event\r
1510 @param[in] Context The context, which is the DHCP service instance.\r
772db4bb 1511\r
1512**/\r
1513VOID\r
1514EFIAPI\r
1515DhcpOnTimerTick (\r
1516 IN EFI_EVENT Event,\r
1517 IN VOID *Context\r
1518 )\r
1519{\r
1520 DHCP_SERVICE *DhcpSb;\r
c4a62a12 1521 DHCP_PROTOCOL *Instance;\r
772db4bb 1522 EFI_STATUS Status;\r
982a9eae 1523\r
c4a62a12 1524 DhcpSb = (DHCP_SERVICE *) Context;\r
1525 Instance = DhcpSb->ActiveChild;\r
842d83d6 1526\r
1527 //\r
1528 // 0xffff is the maximum supported value for elapsed time according to RFC.\r
1529 //\r
1530 if (Instance != NULL && Instance->ElaspedTime < 0xffff) {\r
1531 Instance->ElaspedTime++;\r
1532 }\r
e234f7c4 1533 \r
c4a62a12 1534 //\r
e234f7c4 1535 // Check the retransmit timer\r
c4a62a12 1536 //\r
e234f7c4 1537 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {\r
1538\r
c4a62a12 1539 //\r
e234f7c4 1540 // Select offer at each timeout if any offer received.\r
c4a62a12 1541 //\r
e234f7c4 1542 if (DhcpSb->DhcpState == Dhcp4Selecting && DhcpSb->LastOffer != NULL) {\r
772db4bb 1543\r
e234f7c4 1544 Status = DhcpChooseOffer (DhcpSb);\r
c4a62a12 1545\r
e234f7c4 1546 if (EFI_ERROR(Status)) {\r
39669f12 1547 if (DhcpSb->LastOffer != NULL) {\r
1548 FreePool (DhcpSb->LastOffer);\r
1549 DhcpSb->LastOffer = NULL;\r
1550 }\r
e234f7c4 1551 } else {\r
1552 goto ON_EXIT;\r
1553 }\r
c4a62a12 1554 }\r
e234f7c4 1555 \r
772db4bb 1556 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {\r
1557 //\r
1558 // Still has another try\r
1559 //\r
1560 DhcpRetransmit (DhcpSb);\r
1561 DhcpSetTransmitTimer (DhcpSb);\r
1562\r
e234f7c4 1563 } else if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
772db4bb 1564\r
1565 //\r
1566 // Retransmission failed, if the DHCP request is initiated by\r
1567 // user, adjust the current state according to the lease life.\r
1568 // Otherwise do nothing to wait the lease to timeout\r
1569 //\r
7bce0c5a 1570 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 1571 Status = EFI_SUCCESS;\r
1572\r
1573 if (DhcpSb->LeaseLife < DhcpSb->T1) {\r
1574 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);\r
1575\r
1576 } else if (DhcpSb->LeaseLife < DhcpSb->T2) {\r
1577 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);\r
1578\r
1579 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {\r
1580 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);\r
1581\r
1582 } else {\r
1583 goto END_SESSION;\r
1584\r
1585 }\r
1586\r
1587 DhcpSb->IoStatus = EFI_TIMEOUT;\r
1588 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
1589 }\r
e234f7c4 1590 } else {\r
1591 goto END_SESSION;\r
772db4bb 1592 }\r
1593 }\r
e234f7c4 1594 \r
772db4bb 1595 //\r
1596 // If an address has been acquired, check whether need to\r
1597 // refresh or whether it has expired.\r
1598 //\r
1599 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
1600 DhcpSb->LeaseLife++;\r
1601\r
1602 //\r
1603 // Don't timeout the lease, only count the life if user is\r
1604 // requesting extra renew/rebind. Adjust the state after that.\r
1605 //\r
7bce0c5a 1606 if (DhcpSb->ExtraRefresh != 0) {\r
772db4bb 1607 return ;\r
1608 }\r
1609\r
1610 if (DhcpSb->LeaseLife == DhcpSb->Lease) {\r
1611 //\r
1612 // Lease expires, end the session\r
1613 //\r
1614 goto END_SESSION;\r
1615\r
1616 } else if (DhcpSb->LeaseLife == DhcpSb->T2) {\r
1617 //\r
1618 // T2 expires, transit to rebinding then send a REQUEST to any server\r
1619 //\r
1620 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {\r
1621 goto END_SESSION;\r
1622 }\r
1623\r
e1982d4c 1624 if (Instance != NULL) {\r
1625 Instance->ElaspedTime= 0;\r
1626 } \r
842d83d6 1627 \r
772db4bb 1628 Status = DhcpSendMessage (\r
1629 DhcpSb,\r
1630 DhcpSb->Selected,\r
1631 DhcpSb->Para,\r
1632 DHCP_MSG_REQUEST,\r
1633 NULL\r
1634 );\r
1635\r
1636 if (EFI_ERROR (Status)) {\r
1637 goto END_SESSION;\r
1638 }\r
1639\r
1640 } else if (DhcpSb->LeaseLife == DhcpSb->T1) {\r
1641 //\r
1642 // T1 expires, transit to renewing, then send a REQUEST to the server\r
1643 //\r
1644 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {\r
1645 goto END_SESSION;\r
1646 }\r
1647\r
e1982d4c 1648 if (Instance != NULL) {\r
1649 Instance->ElaspedTime= 0;\r
1650 } \r
842d83d6 1651\r
772db4bb 1652 Status = DhcpSendMessage (\r
1653 DhcpSb,\r
1654 DhcpSb->Selected,\r
1655 DhcpSb->Para,\r
1656 DHCP_MSG_REQUEST,\r
1657 NULL\r
1658 );\r
1659\r
1660 if (EFI_ERROR (Status)) {\r
1661 goto END_SESSION;\r
1662 }\r
1663 }\r
1664 }\r
1665\r
e234f7c4 1666ON_EXIT:\r
c4a62a12 1667 if ((Instance != NULL) && (Instance->Token != NULL)) {\r
1668 Instance->Timeout--;\r
1669 if (Instance->Timeout == 0) {\r
1670 PxeDhcpDone (Instance);\r
1671 }\r
1672 }\r
1673\r
772db4bb 1674 return ;\r
1675\r
1676END_SESSION:\r
1677 DhcpEndSession (DhcpSb, EFI_TIMEOUT);\r
1678\r
1679 return ;\r
1680}\r