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