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