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