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