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