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