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