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