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