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