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