]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Io.c
Fix a conformance issue in gBS->CreateEvent() & gBS->CreateEventEx():
[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
772db4bb 642 EFI_STATUS Status;\r
643\r
644 Status = EFI_SUCCESS;\r
645\r
646 //\r
647 // First validate the message:\r
648 // 1. the offer is a unicast\r
649 // 2. if it is a DHCP message, it must contains a server ID.\r
650 // Don't return a error for these two case otherwise the session is ended.\r
651 //\r
772db4bb 652 if (!DHCP_IS_BOOTP (Para) &&\r
653 ((Para->DhcpType != DHCP_MSG_OFFER) || (Para->ServerId == 0))) {\r
654 goto ON_EXIT;\r
655 }\r
656\r
657 //\r
658 // Call the user's callback. The action according to the return is as:\r
659 // 1. EFI_SUCESS: stop waiting for more offers, select the offer now\r
660 // 2. EFI_NOT_READY: wait for more offers\r
661 // 3. EFI_ABORTED: abort the address acquiring.\r
662 //\r
663 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdOffer, Packet, NULL);\r
664\r
665 if (Status == EFI_SUCCESS) {\r
666 if (DhcpSb->LastOffer != NULL) {\r
667 NetFreePool (DhcpSb->LastOffer);\r
668 }\r
669\r
670 DhcpSb->LastOffer = Packet;\r
671\r
672 return DhcpChooseOffer (DhcpSb);\r
673\r
674 } else if (Status == EFI_NOT_READY) {\r
675 if (DhcpSb->LastOffer != NULL) {\r
676 NetFreePool (DhcpSb->LastOffer);\r
677 }\r
678\r
679 DhcpSb->LastOffer = Packet;\r
680\r
681 } else if (Status == EFI_ABORTED) {\r
682 //\r
683 // DhcpInput will end the session upon error return. Remember\r
684 // only to call DhcpEndSession at the top level call.\r
685 //\r
686 goto ON_EXIT;\r
687 }\r
688\r
689 return EFI_SUCCESS;\r
690\r
691ON_EXIT:\r
692 NetFreePool (Packet);\r
693 return Status;\r
694}\r
695\r
696\r
697/**\r
698 Handle packets in DHCP request state.\r
699\r
700 @param DhcpSb The DHCP service instance\r
701 @param Packet The DHCP packet received\r
702 @param Para The DHCP parameter extracted from the packet. That\r
703 is, all the option value that we care.\r
704\r
705 @retval EFI_SUCCESS The packet is successfully processed.\r
706 @retval Others Some error occured.\r
707\r
708**/\r
709STATIC\r
710EFI_STATUS\r
711DhcpHandleRequest (\r
712 IN DHCP_SERVICE *DhcpSb,\r
713 IN EFI_DHCP4_PACKET *Packet,\r
714 IN DHCP_PARAMETER *Para\r
715 )\r
716{\r
717 EFI_DHCP4_HEADER *Head;\r
718 EFI_DHCP4_HEADER *Selected;\r
719 EFI_STATUS Status;\r
720 UINT8 *Message;\r
721\r
722 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
723\r
724 Head = &Packet->Dhcp4.Header;\r
725 Selected = &DhcpSb->Selected->Dhcp4.Header;\r
726\r
727 //\r
728 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK.\r
729 //\r
730 if (DHCP_IS_BOOTP (Para) ||\r
731 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
732 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
733\r
734 Status = EFI_SUCCESS;\r
735 goto ON_EXIT;\r
736 }\r
737\r
738 //\r
739 // Received a NAK, end the session no matter what the user returns\r
740 //\r
741 Status = EFI_DEVICE_ERROR;\r
742\r
743 if (Para->DhcpType == DHCP_MSG_NAK) {\r
744 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
745 goto ON_EXIT;\r
746 }\r
747\r
748 //\r
749 // Check whether the ACK matches the selected offer\r
750 //\r
751 Message = NULL;\r
752\r
84b5c78e 753 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
67a58d0f 754 Message = (UINT8 *) "Lease confirmed isn't the same as that in the offer";\r
772db4bb 755 goto REJECT;\r
756 }\r
757\r
758 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
759\r
760 if (EFI_ERROR (Status)) {\r
67a58d0f 761 Message = (UINT8 *) "Lease is denied upon received ACK";\r
772db4bb 762 goto REJECT;\r
763 }\r
764\r
765 //\r
766 // Record the lease, transit to BOUND state, then notify the user\r
767 //\r
768 Status = DhcpLeaseAcquired (DhcpSb);\r
769\r
770 if (EFI_ERROR (Status)) {\r
67a58d0f 771 Message = (UINT8 *) "Lease is denied upon entering bound";\r
772db4bb 772 goto REJECT;\r
773 }\r
774\r
775 DhcpSb->IoStatus = EFI_SUCCESS;\r
776 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
777\r
778 NetFreePool (Packet);\r
779 return EFI_SUCCESS;\r
780\r
781REJECT:\r
782 DhcpSendMessage (DhcpSb, DhcpSb->Selected, DhcpSb->Para, DHCP_MSG_DECLINE, Message);\r
783\r
784ON_EXIT:\r
785 NetFreePool (Packet);\r
786 return Status;\r
787}\r
788\r
789\r
790/**\r
791 Handle packets in DHCP renew/rebound state.\r
792\r
793 @param DhcpSb The DHCP service instance\r
794 @param Packet The DHCP packet received\r
795 @param Para The DHCP parameter extracted from the packet. That\r
796 is, all the option value that we care.\r
797\r
798 @retval EFI_SUCCESS The packet is successfully processed.\r
799 @retval Others Some error occured.\r
800\r
801**/\r
802STATIC\r
803EFI_STATUS\r
804DhcpHandleRenewRebind (\r
805 IN DHCP_SERVICE *DhcpSb,\r
806 IN EFI_DHCP4_PACKET *Packet,\r
807 IN DHCP_PARAMETER *Para\r
808 )\r
809{\r
810 EFI_DHCP4_HEADER *Head;\r
811 EFI_DHCP4_HEADER *Selected;\r
812 EFI_STATUS Status;\r
813\r
814 ASSERT (!DHCP_IS_BOOTP (DhcpSb->Para));\r
815\r
816 Head = &Packet->Dhcp4.Header;\r
817 Selected = &DhcpSb->Selected->Dhcp4.Header;\r
818\r
819 //\r
820 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
821 //\r
822 if (DHCP_IS_BOOTP (Para) ||\r
823 (Para->ServerId != DhcpSb->Para->ServerId) ||\r
824 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
825\r
826 Status = EFI_SUCCESS;\r
827 goto ON_EXIT;\r
828 }\r
829\r
830 //\r
831 // Received a NAK, ignore the user's return then terminate the process\r
832 //\r
833 Status = EFI_DEVICE_ERROR;\r
834\r
835 if (Para->DhcpType == DHCP_MSG_NAK) {\r
836 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
837 goto ON_EXIT;\r
838 }\r
839\r
840 //\r
841 // The lease is different from the selected. Don't send a DECLINE\r
842 // since it isn't existed in the client's FSM.\r
843 //\r
84b5c78e 844 if (!EFI_IP4_EQUAL (&Head->YourAddr, &Selected->YourAddr)) {\r
772db4bb 845 goto ON_EXIT;\r
846 }\r
847\r
848 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
849\r
850 if (EFI_ERROR (Status)) {\r
851 goto ON_EXIT;\r
852 }\r
853\r
854 //\r
855 // Record the lease, start timer for T1 and T2,\r
856 //\r
857 DhcpComputeLease (DhcpSb, Para);\r
858 DhcpSb->LeaseLife = 0;\r
859 DhcpSetState (DhcpSb, Dhcp4Bound, TRUE);\r
860\r
861 if (DhcpSb->ExtraRefresh) {\r
862 DhcpSb->ExtraRefresh = FALSE;\r
863\r
864 DhcpSb->IoStatus = EFI_SUCCESS;\r
865 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
866 }\r
867\r
868ON_EXIT:\r
869 NetFreePool (Packet);\r
870 return Status;\r
871}\r
872\r
873\r
874/**\r
875 Handle packets in DHCP reboot state.\r
876\r
877 @param DhcpSb The DHCP service instance\r
878 @param Packet The DHCP packet received\r
879 @param Para The DHCP parameter extracted from the packet. That\r
880 is, all the option value that we care.\r
881\r
882 @retval EFI_SUCCESS The packet is successfully processed.\r
883 @retval Others Some error occured.\r
884\r
885**/\r
886STATIC\r
887EFI_STATUS\r
888DhcpHandleReboot (\r
889 IN DHCP_SERVICE *DhcpSb,\r
890 IN EFI_DHCP4_PACKET *Packet,\r
891 IN DHCP_PARAMETER *Para\r
892 )\r
893{\r
894 EFI_DHCP4_HEADER *Head;\r
895 EFI_STATUS Status;\r
896\r
897 Head = &Packet->Dhcp4.Header;\r
898\r
899 //\r
900 // Ignore the BOOTP message and DHCP messages other than DHCP ACK/NACK\r
901 //\r
902 if (DHCP_IS_BOOTP (Para) ||\r
903 ((Para->DhcpType != DHCP_MSG_ACK) && (Para->DhcpType != DHCP_MSG_NAK))) {\r
904\r
905 Status = EFI_SUCCESS;\r
906 goto ON_EXIT;\r
907 }\r
908\r
909 //\r
910 // If a NAK is received, transit to INIT and try again.\r
911 //\r
912 if (Para->DhcpType == DHCP_MSG_NAK) {\r
913 DhcpCallUser (DhcpSb, Dhcp4RcvdNak, Packet, NULL);\r
914\r
915 DhcpSb->ClientAddr = 0;\r
916 DhcpSb->DhcpState = Dhcp4Init;\r
917\r
918 Status = DhcpInitRequest (DhcpSb);\r
919 goto ON_EXIT;\r
920 }\r
921\r
922 //\r
923 // Check whether the ACK matches the selected offer\r
924 //\r
925 if (EFI_NTOHL (Head->YourAddr) != DhcpSb->ClientAddr) {\r
926 Status = EFI_DEVICE_ERROR;\r
927 goto ON_EXIT;\r
928 }\r
929\r
930 Status = DhcpCallUser (DhcpSb, Dhcp4RcvdAck, Packet, NULL);\r
931 if (EFI_ERROR (Status)) {\r
932 goto ON_EXIT;\r
933 }\r
934\r
935 //\r
936 // OK, get the parameter from server, record the lease\r
937 //\r
938 DhcpSb->Para = NetAllocatePool (sizeof (DHCP_PARAMETER));\r
939\r
940 if (DhcpSb->Para == NULL) {\r
941 Status = EFI_OUT_OF_RESOURCES;\r
942 goto ON_EXIT;\r
943 }\r
944\r
945 DhcpSb->Selected = Packet;\r
687a2e5f 946 CopyMem (DhcpSb->Para, Para, sizeof (*DhcpSb->Para));\r
772db4bb 947\r
948 Status = DhcpLeaseAcquired (DhcpSb);\r
949\r
950 if (EFI_ERROR (Status)) {\r
951 return Status;\r
952 }\r
953\r
954 DhcpSb->IoStatus = EFI_SUCCESS;\r
955 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_COMPLETION);\r
956 return EFI_SUCCESS;\r
957\r
958ON_EXIT:\r
959 NetFreePool (Packet);\r
960 return Status;\r
961}\r
962\r
963\r
964/**\r
965 Handle the received DHCP packets. This function drivers the DHCP\r
966 state machine.\r
967\r
968 @param UdpPacket The UDP packets received.\r
969 @param Points The local/remote UDP access points\r
970 @param IoStatus The status of the UDP receive\r
971 @param Context The opaque parameter to the function.\r
972\r
973 @return None\r
974\r
975**/\r
976VOID\r
977DhcpInput (\r
978 NET_BUF *UdpPacket,\r
979 UDP_POINTS *Points,\r
980 EFI_STATUS IoStatus,\r
981 VOID *Context\r
982 )\r
983{\r
984 DHCP_SERVICE *DhcpSb;\r
985 EFI_DHCP4_HEADER *Head;\r
986 EFI_DHCP4_PACKET *Packet;\r
987 DHCP_PARAMETER *Para;\r
988 EFI_STATUS Status;\r
989 UINT32 Len;\r
990\r
991 Packet = NULL;\r
992 DhcpSb = (DHCP_SERVICE *) Context;\r
993\r
994 //\r
995 // Don't restart receive if error occurs or DHCP is destoried.\r
996 //\r
997 if (EFI_ERROR (IoStatus)) {\r
998 return ;\r
999 } else if (DhcpSb->ServiceState == DHCP_DESTORY) {\r
1000 NetbufFree (UdpPacket);\r
1001 return ;\r
1002 }\r
1003\r
1004 ASSERT (UdpPacket != NULL);\r
1005\r
1006 if (DhcpSb->DhcpState == Dhcp4Stopped) {\r
1007 goto RESTART;\r
1008 }\r
1009\r
1010 //\r
1011 // Validate the packet received\r
1012 //\r
1013 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {\r
1014 goto RESTART;\r
1015 }\r
1016\r
1017 //\r
1018 // Copy the DHCP message to a continuous memory block\r
1019 //\r
1020 Len = sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER);\r
1021 Packet = (EFI_DHCP4_PACKET *) NetAllocatePool (Len);\r
1022\r
1023 if (Packet == NULL) {\r
1024 goto RESTART;\r
1025 }\r
1026\r
1027 Packet->Size = Len;\r
1028 Head = &Packet->Dhcp4.Header;\r
1029 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);\r
1030\r
1031 if (Packet->Length != UdpPacket->TotalSize) {\r
1032 goto RESTART;\r
1033 }\r
1034\r
1035 //\r
1036 // Is this packet the answer to our packet?\r
1037 //\r
1038 if ((Head->OpCode != BOOTP_REPLY) ||\r
1039 (NTOHL (Head->Xid) != DhcpSb->Xid) ||\r
1040 !NET_MAC_EQUAL (&DhcpSb->Mac, Head->ClientHwAddr, DhcpSb->HwLen)) {\r
1041 goto RESTART;\r
1042 }\r
1043\r
1044 //\r
1045 // Validate the options and retrieve the interested options\r
1046 //\r
1047 Para = NULL;\r
1048 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&\r
1049 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&\r
1050 EFI_ERROR (DhcpValidateOptions (Packet, &Para))) {\r
1051\r
1052 goto RESTART;\r
1053 }\r
1054\r
1055 //\r
1056 // Call the handler for each state. The handler should return\r
1057 // EFI_SUCCESS if the process can go on no matter whether the\r
1058 // packet is ignored or not. If the return is EFI_ERROR, the\r
1059 // session will be terminated. Packet's ownership is handled\r
1060 // over to the handlers. If operation succeeds, the handler\r
1061 // must notify the user. It isn't necessary to do if EFI_ERROR\r
1062 // is returned because the DhcpEndSession will notify the user.\r
1063 //\r
1064 Status = EFI_SUCCESS;\r
1065\r
1066 switch (DhcpSb->DhcpState) {\r
1067 case Dhcp4Selecting:\r
1068 Status = DhcpHandleSelect (DhcpSb, Packet, Para);\r
1069 break;\r
1070\r
1071 case Dhcp4Requesting:\r
1072 Status = DhcpHandleRequest (DhcpSb, Packet, Para);\r
1073 break;\r
1074\r
1075 case Dhcp4InitReboot:\r
1076 case Dhcp4Init:\r
1077 case Dhcp4Bound:\r
1078 //\r
1079 // Ignore the packet in INITREBOOT, INIT and BOUND states\r
1080 //\r
1081 NetFreePool (Packet);\r
1082 Status = EFI_SUCCESS;\r
1083 break;\r
1084\r
1085 case Dhcp4Renewing:\r
1086 case Dhcp4Rebinding:\r
1087 Status = DhcpHandleRenewRebind (DhcpSb, Packet, Para);\r
1088 break;\r
1089\r
1090 case Dhcp4Rebooting:\r
1091 Status = DhcpHandleReboot (DhcpSb, Packet, Para);\r
1092 break;\r
1093 }\r
1094\r
1095 if (Para != NULL) {\r
1096 NetFreePool (Para);\r
1097 }\r
1098\r
1099 Packet = NULL;\r
1100\r
1101 if (EFI_ERROR (Status)) {\r
1102 NetbufFree (UdpPacket);\r
1103 DhcpEndSession (DhcpSb, Status);\r
1104 return ;\r
1105 }\r
1106\r
1107RESTART:\r
1108 NetbufFree (UdpPacket);\r
1109\r
1110 if (Packet != NULL) {\r
1111 NetFreePool (Packet);\r
1112 }\r
1113\r
1114 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);\r
1115\r
1116 if (EFI_ERROR (Status)) {\r
1117 DhcpEndSession (DhcpSb, EFI_DEVICE_ERROR);\r
1118 }\r
1119}\r
1120\r
1121\r
1122/**\r
1123 Release the packet.\r
1124\r
1125 @param Arg The packet to release\r
1126\r
1127 @return None\r
1128\r
1129**/\r
1130VOID\r
1131DhcpReleasePacket (\r
1132 IN VOID *Arg\r
1133 )\r
1134{\r
1135 NetFreePool (Arg);\r
1136}\r
1137\r
1138\r
1139/**\r
1140 Release the net buffer when packet is sent.\r
1141\r
1142 @param UdpPacket The UDP packets received.\r
1143 @param Points The local/remote UDP access points\r
1144 @param IoStatus The status of the UDP receive\r
1145 @param Context The opaque parameter to the function.\r
1146\r
1147 @return None\r
1148\r
1149**/\r
1150VOID\r
1151DhcpOnPacketSent (\r
1152 NET_BUF *Packet,\r
1153 UDP_POINTS *Points,\r
1154 EFI_STATUS IoStatus,\r
1155 VOID *Context\r
1156 )\r
1157{\r
1158 NetbufFree (Packet);\r
1159}\r
1160\r
1161\r
1162\r
1163/**\r
1164 Build and transmit a DHCP message according to the current states.\r
1165 This function implement the Table 5. of RFC 2131. Always transits\r
1166 the state (as defined in Figure 5. of the same RFC) before sending\r
1167 a DHCP message. The table is adjusted accordingly.\r
1168\r
1169 @param DhcpSb The DHCP service instance\r
1170 @param Seed The seed packet which the new packet is based on\r
1171 @param Para The DHCP parameter of the Seed packet\r
1172 @param Type The message type to send\r
1173 @param Msg The human readable message to include in the packet\r
1174 sent.\r
1175\r
1176 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources for the packet\r
1177 @retval EFI_ACCESS_DENIED Failed to transmit the packet through UDP\r
1178 @retval EFI_SUCCESS The message is sent\r
1179\r
1180**/\r
1181EFI_STATUS\r
1182DhcpSendMessage (\r
1183 IN DHCP_SERVICE *DhcpSb,\r
1184 IN EFI_DHCP4_PACKET *Seed,\r
1185 IN DHCP_PARAMETER *Para,\r
1186 IN UINT8 Type,\r
1187 IN UINT8 *Msg\r
1188 )\r
1189{\r
1190 EFI_DHCP4_CONFIG_DATA *Config;\r
1191 EFI_DHCP4_PACKET *Packet;\r
1192 EFI_DHCP4_PACKET *NewPacket;\r
1193 EFI_DHCP4_HEADER *Head;\r
1194 EFI_DHCP4_HEADER *SeedHead;\r
1195 UDP_IO_PORT *UdpIo;\r
1196 UDP_POINTS EndPoint;\r
1197 NET_BUF *Wrap;\r
1198 NET_FRAGMENT Frag;\r
1199 EFI_STATUS Status;\r
1200 IP4_ADDR IpAddr;\r
1201 UINT8 *Buf;\r
1202 UINT16 MaxMsg;\r
1203 UINT32 Len;\r
1204 UINT32 Index;\r
1205\r
1206 //\r
1207 // Allocate a big enough memory block to hold the DHCP packet\r
1208 //\r
1209 Len = sizeof (EFI_DHCP4_PACKET) + 128 + DhcpSb->UserOptionLen;\r
1210\r
1211 if (Msg != NULL) {\r
687a2e5f 1212 Len += (UINT32)AsciiStrLen ((CHAR8 *) Msg);\r
772db4bb 1213 }\r
1214\r
1215 Packet = NetAllocatePool (Len);\r
1216\r
1217 if (Packet == NULL) {\r
1218 return EFI_OUT_OF_RESOURCES;\r
1219 }\r
1220\r
1221 Packet->Size = Len;\r
1222 Packet->Length = sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32);\r
1223\r
1224 //\r
1225 // Fill in the DHCP header fields\r
1226 //\r
1227 Config = &DhcpSb->ActiveConfig;\r
1228 SeedHead = NULL;\r
1229\r
1230 if (Seed != NULL) {\r
1231 SeedHead = &Seed->Dhcp4.Header;\r
1232 }\r
1233\r
1234 Head = &Packet->Dhcp4.Header;\r
1235 NetZeroMem (Head, sizeof (EFI_DHCP4_HEADER));\r
1236\r
1237 Head->OpCode = BOOTP_REQUEST;\r
1238 Head->HwType = DhcpSb->HwType;\r
1239 Head->HwAddrLen = DhcpSb->HwLen;\r
1240 Head->Xid = HTONL (DhcpSb->Xid);\r
1241 Head->Reserved = HTONS (0x8000); //Server, broadcast the message please.\r
1242\r
1243 EFI_IP4 (Head->ClientAddr) = HTONL (DhcpSb->ClientAddr);\r
1244 NetCopyMem (Head->ClientHwAddr, DhcpSb->Mac.Addr, DhcpSb->HwLen);\r
1245\r
1246 //\r
1247 // Append the DHCP message type\r
1248 //\r
1249 Packet->Dhcp4.Magik = DHCP_OPTION_MAGIC;\r
1250 Buf = Packet->Dhcp4.Option;\r
1251 Buf = DhcpAppendOption (Buf, DHCP_TAG_TYPE, 1, &Type);\r
1252\r
1253 //\r
1254 // Append the serverid option if necessary:\r
1255 // 1. DHCP decline message\r
1256 // 2. DHCP release message\r
1257 // 3. DHCP request to confirm one lease.\r
1258 //\r
1259 if ((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE) ||\r
1260 ((Type == DHCP_MSG_REQUEST) && (DhcpSb->DhcpState == Dhcp4Requesting))) {\r
1261\r
1262 ASSERT ((Para != NULL) && (Para->ServerId != 0));\r
1263\r
1264 IpAddr = HTONL (Para->ServerId);\r
1265 Buf = DhcpAppendOption (Buf, DHCP_TAG_SERVER_ID, 4, (UINT8 *) &IpAddr);\r
1266 }\r
1267\r
1268 //\r
1269 // Append the requested IP option if necessary:\r
1270 // 1. DHCP request to use the previously allocated address\r
1271 // 2. DHCP request to confirm one lease\r
1272 // 3. DHCP decline to decline one lease\r
1273 //\r
1274 IpAddr = 0;\r
1275\r
1276 if (Type == DHCP_MSG_REQUEST) {\r
1277 if (DhcpSb->DhcpState == Dhcp4Rebooting) {\r
1278 IpAddr = EFI_IP4 (Config->ClientAddress);\r
1279\r
1280 } else if (DhcpSb->DhcpState == Dhcp4Requesting) {\r
1281 ASSERT (SeedHead != NULL);\r
1282 IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
1283 }\r
1284\r
1285 } else if (Type == DHCP_MSG_DECLINE) {\r
1286 ASSERT (SeedHead != NULL);\r
1287 IpAddr = EFI_IP4 (SeedHead->YourAddr);\r
1288 }\r
1289\r
1290 if (IpAddr != 0) {\r
1291 Buf = DhcpAppendOption (Buf, DHCP_TAG_REQUEST_IP, 4, (UINT8 *) &IpAddr);\r
1292 }\r
1293\r
1294 //\r
1295 // Append the Max Message Length option if it isn't a DECLINE\r
1296 // or RELEASE to direct the server use large messages instead of\r
1297 // override the BOOTFILE and SERVER fields in the message head.\r
1298 //\r
1299 if ((Type != DHCP_MSG_DECLINE) && (Type != DHCP_MSG_RELEASE)) {\r
1300 MaxMsg = HTONS (0xFF00);\r
1301 Buf = DhcpAppendOption (Buf, DHCP_TAG_MAXMSG, 2, (UINT8 *) &MaxMsg);\r
1302 }\r
1303\r
1304 //\r
1305 // Append the user's message if it isn't NULL\r
1306 //\r
1307 if (Msg != NULL) {\r
36ee91ca 1308 Len = MIN ((UINT32) AsciiStrLen ((CHAR8 *) Msg), 255);\r
772db4bb 1309 Buf = DhcpAppendOption (Buf, DHCP_TAG_MESSAGE, (UINT16) Len, Msg);\r
1310 }\r
1311\r
1312 //\r
1313 // Append the user configured options\r
1314 //\r
1315 if (DhcpSb->UserOptionLen != 0) {\r
1316 for (Index = 0; Index < Config->OptionCount; Index++) {\r
1317 //\r
1318 // We can't use any option other than the client ID from user\r
1319 // if it is a DHCP decline or DHCP release .\r
1320 //\r
1321 if (((Type == DHCP_MSG_DECLINE) || (Type == DHCP_MSG_RELEASE)) &&\r
1322 (Config->OptionList[Index]->OpCode != DHCP_TAG_CLIENT_ID)) {\r
1323 continue;\r
1324 }\r
1325\r
1326 Buf = DhcpAppendOption (\r
1327 Buf,\r
1328 Config->OptionList[Index]->OpCode,\r
1329 Config->OptionList[Index]->Length,\r
1330 Config->OptionList[Index]->Data\r
1331 );\r
1332 }\r
1333 }\r
1334\r
1335 *(Buf++) = DHCP_TAG_EOP;\r
1336 Packet->Length += (UINT32) (Buf - Packet->Dhcp4.Option);\r
1337\r
1338 //\r
1339 // OK, the message is built, call the user to override it.\r
1340 //\r
1341 Status = EFI_SUCCESS;\r
1342 NewPacket = NULL;\r
1343\r
1344 if (Type == DHCP_MSG_DISCOVER) {\r
1345 Status = DhcpCallUser (DhcpSb, Dhcp4SendDiscover, Packet, &NewPacket);\r
1346\r
1347 } else if (Type == DHCP_MSG_REQUEST) {\r
1348 Status = DhcpCallUser (DhcpSb, Dhcp4SendRequest, Packet, &NewPacket);\r
1349\r
1350 } else if (Type == DHCP_MSG_DECLINE) {\r
1351 Status = DhcpCallUser (DhcpSb, Dhcp4SendDecline, Packet, &NewPacket);\r
1352 }\r
1353\r
1354 if (EFI_ERROR (Status)) {\r
1355 NetFreePool (Packet);\r
1356 return Status;\r
1357 }\r
1358\r
1359 if (NewPacket != NULL) {\r
1360 NetFreePool (Packet);\r
1361 Packet = NewPacket;\r
1362 }\r
1363\r
1364 //\r
1365 // Wrap it into a netbuf then send it.\r
1366 //\r
1367 Frag.Bulk = (UINT8 *) &Packet->Dhcp4.Header;\r
1368 Frag.Len = Packet->Length;\r
1369 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpReleasePacket, Packet);\r
1370\r
1371 if (Wrap == NULL) {\r
1372 NetFreePool (Packet);\r
1373 return EFI_OUT_OF_RESOURCES;\r
1374 }\r
1375\r
1376 //\r
1377 // Save it as the last sent packet for retransmission\r
1378 //\r
1379 if (DhcpSb->LastPacket != NULL) {\r
1380 NetbufFree (DhcpSb->LastPacket);\r
1381 }\r
1382\r
1383 NET_GET_REF (Wrap);\r
1384 DhcpSb->LastPacket = Wrap;\r
1385 DhcpSetTransmitTimer (DhcpSb);\r
1386\r
1387 //\r
1388 // Broadcast the message, unless we know the server address.\r
1389 // Use the lease UdpIo port to send the unicast packet.\r
1390 //\r
1391 EndPoint.RemoteAddr = 0xffffffff;\r
1392 EndPoint.LocalAddr = 0;\r
1393 EndPoint.RemotePort = DHCP_SERVER_PORT;\r
1394 EndPoint.LocalPort = DHCP_CLIENT_PORT;\r
1395 UdpIo = DhcpSb->UdpIo;\r
1396\r
1397 if ((DhcpSb->DhcpState == Dhcp4Renewing) || (Type == DHCP_MSG_RELEASE)) {\r
1398 EndPoint.RemoteAddr = DhcpSb->ServerAddr;\r
1399 EndPoint.LocalAddr = DhcpSb->ClientAddr;\r
1400 UdpIo = DhcpSb->LeaseIoPort;\r
1401 }\r
1402\r
1403 ASSERT (UdpIo != NULL);\r
1404 Status = UdpIoSendDatagram (UdpIo, Wrap, &EndPoint, 0, DhcpOnPacketSent, DhcpSb);\r
1405\r
1406 if (EFI_ERROR (Status)) {\r
1407 NetbufFree (Wrap);\r
1408 return EFI_ACCESS_DENIED;\r
1409 }\r
1410\r
1411 return EFI_SUCCESS;\r
1412}\r
1413\r
1414\r
1415/**\r
1416 Retransmit a saved packet. Only DISCOVER and REQUEST messages\r
1417 will be retransmitted.\r
1418\r
1419 @param DhcpSb The DHCP service instance\r
1420\r
1421 @retval EFI_ACCESS_DENIED Failed to transmit packet through UDP port\r
1422 @retval EFI_SUCCESS The packet is retransmitted.\r
1423\r
1424**/\r
1425EFI_STATUS\r
1426DhcpRetransmit (\r
1427 IN DHCP_SERVICE *DhcpSb\r
1428 )\r
1429{\r
1430 UDP_IO_PORT *UdpIo;\r
1431 UDP_POINTS EndPoint;\r
1432 EFI_STATUS Status;\r
1433\r
1434 ASSERT (DhcpSb->LastPacket != NULL);\r
1435\r
1436 //\r
1437 // Broadcast the message, unless we know the server address.\r
1438 //\r
1439 EndPoint.RemotePort = DHCP_SERVER_PORT;\r
1440 EndPoint.LocalPort = DHCP_CLIENT_PORT;\r
1441 EndPoint.RemoteAddr = 0xffffffff;\r
1442 EndPoint.LocalAddr = 0;\r
1443 UdpIo = DhcpSb->UdpIo;\r
1444\r
1445 if (DhcpSb->DhcpState == Dhcp4Renewing) {\r
1446 EndPoint.RemoteAddr = DhcpSb->ServerAddr;\r
1447 EndPoint.LocalAddr = DhcpSb->ClientAddr;\r
1448 UdpIo = DhcpSb->LeaseIoPort;\r
1449 }\r
1450\r
1451 ASSERT (UdpIo != NULL);\r
1452\r
1453 NET_GET_REF (DhcpSb->LastPacket);\r
1454 Status = UdpIoSendDatagram (\r
1455 UdpIo,\r
1456 DhcpSb->LastPacket,\r
1457 &EndPoint,\r
1458 0,\r
1459 DhcpOnPacketSent,\r
1460 DhcpSb\r
1461 );\r
1462\r
1463 if (EFI_ERROR (Status)) {\r
1464 NET_PUT_REF (DhcpSb->LastPacket);\r
1465 return EFI_ACCESS_DENIED;\r
1466 }\r
1467\r
1468 return EFI_SUCCESS;\r
1469}\r
1470\r
1471\r
1472/**\r
1473 Each DHCP service has three timer. Two of them are count down timer.\r
1474 One for the packet retransmission. The other is to collect the offers.\r
1475 The third timer increaments the lease life which is compared to T1, T2,\r
1476 and lease to determine the time to renew and rebind the lease.\r
1477 DhcpOnTimerTick will be called once every second.\r
1478\r
1479 @param Event The timer event\r
1480 @param Context The context, which is the DHCP service instance.\r
1481\r
1482 @return None\r
1483\r
1484**/\r
1485VOID\r
1486EFIAPI\r
1487DhcpOnTimerTick (\r
1488 IN EFI_EVENT Event,\r
1489 IN VOID *Context\r
1490 )\r
1491{\r
1492 DHCP_SERVICE *DhcpSb;\r
c4a62a12 1493 DHCP_PROTOCOL *Instance;\r
772db4bb 1494 EFI_STATUS Status;\r
c4a62a12 1495 \r
1496 DhcpSb = (DHCP_SERVICE *) Context;\r
1497 Instance = DhcpSb->ActiveChild;\r
772db4bb 1498\r
c4a62a12 1499 //\r
1500 // Check the time to wait offer\r
1501 //\r
1502 if ((DhcpSb->WaitOffer > 0) && (--DhcpSb->WaitOffer == 0)) {\r
1503 //\r
1504 // OK, offer collection finished, select a offer\r
1505 //\r
1506 ASSERT (DhcpSb->DhcpState == Dhcp4Selecting);\r
772db4bb 1507\r
c4a62a12 1508 if (DhcpSb->LastOffer == NULL) {\r
1509 goto END_SESSION;\r
1510 }\r
1511\r
1512 if (EFI_ERROR (DhcpChooseOffer (DhcpSb))) {\r
1513 goto END_SESSION;\r
1514 }\r
1515 }\r
1516 \r
772db4bb 1517 //\r
c4a62a12 1518 // Check the retransmit timer\r
772db4bb 1519 //\r
1520 if ((DhcpSb->PacketToLive > 0) && (--DhcpSb->PacketToLive == 0)) {\r
1521\r
1522 if (++DhcpSb->CurRetry < DhcpSb->MaxRetries) {\r
1523 //\r
1524 // Still has another try\r
1525 //\r
1526 DhcpRetransmit (DhcpSb);\r
1527 DhcpSetTransmitTimer (DhcpSb);\r
1528\r
1529 } else {\r
1530 if (!DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
1531 goto END_SESSION;\r
1532 }\r
1533\r
1534 //\r
1535 // Retransmission failed, if the DHCP request is initiated by\r
1536 // user, adjust the current state according to the lease life.\r
1537 // Otherwise do nothing to wait the lease to timeout\r
1538 //\r
1539 if (DhcpSb->ExtraRefresh) {\r
1540 Status = EFI_SUCCESS;\r
1541\r
1542 if (DhcpSb->LeaseLife < DhcpSb->T1) {\r
1543 Status = DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);\r
1544\r
1545 } else if (DhcpSb->LeaseLife < DhcpSb->T2) {\r
1546 Status = DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);\r
1547\r
1548 } else if (DhcpSb->LeaseLife < DhcpSb->Lease) {\r
1549 Status = DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);\r
1550\r
1551 } else {\r
1552 goto END_SESSION;\r
1553\r
1554 }\r
1555\r
1556 DhcpSb->IoStatus = EFI_TIMEOUT;\r
1557 DhcpNotifyUser (DhcpSb, DHCP_NOTIFY_RENEWREBIND);\r
1558 }\r
1559 }\r
1560 }\r
c4a62a12 1561 \r
772db4bb 1562 //\r
1563 // If an address has been acquired, check whether need to\r
1564 // refresh or whether it has expired.\r
1565 //\r
1566 if (DHCP_CONNECTED (DhcpSb->DhcpState)) {\r
1567 DhcpSb->LeaseLife++;\r
1568\r
1569 //\r
1570 // Don't timeout the lease, only count the life if user is\r
1571 // requesting extra renew/rebind. Adjust the state after that.\r
1572 //\r
1573 if (DhcpSb->ExtraRefresh) {\r
1574 return ;\r
1575 }\r
1576\r
1577 if (DhcpSb->LeaseLife == DhcpSb->Lease) {\r
1578 //\r
1579 // Lease expires, end the session\r
1580 //\r
1581 goto END_SESSION;\r
1582\r
1583 } else if (DhcpSb->LeaseLife == DhcpSb->T2) {\r
1584 //\r
1585 // T2 expires, transit to rebinding then send a REQUEST to any server\r
1586 //\r
1587 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Rebinding, TRUE))) {\r
1588 goto END_SESSION;\r
1589 }\r
1590\r
1591 Status = DhcpSendMessage (\r
1592 DhcpSb,\r
1593 DhcpSb->Selected,\r
1594 DhcpSb->Para,\r
1595 DHCP_MSG_REQUEST,\r
1596 NULL\r
1597 );\r
1598\r
1599 if (EFI_ERROR (Status)) {\r
1600 goto END_SESSION;\r
1601 }\r
1602\r
1603 } else if (DhcpSb->LeaseLife == DhcpSb->T1) {\r
1604 //\r
1605 // T1 expires, transit to renewing, then send a REQUEST to the server\r
1606 //\r
1607 if (EFI_ERROR (DhcpSetState (DhcpSb, Dhcp4Renewing, TRUE))) {\r
1608 goto END_SESSION;\r
1609 }\r
1610\r
1611 Status = DhcpSendMessage (\r
1612 DhcpSb,\r
1613 DhcpSb->Selected,\r
1614 DhcpSb->Para,\r
1615 DHCP_MSG_REQUEST,\r
1616 NULL\r
1617 );\r
1618\r
1619 if (EFI_ERROR (Status)) {\r
1620 goto END_SESSION;\r
1621 }\r
1622 }\r
1623 }\r
1624\r
c4a62a12 1625 //\r
1626 //\r
1627 //\r
1628 if ((Instance != NULL) && (Instance->Token != NULL)) {\r
1629 Instance->Timeout--;\r
1630 if (Instance->Timeout == 0) {\r
1631 PxeDhcpDone (Instance);\r
1632 }\r
1633 }\r
1634\r
772db4bb 1635 return ;\r
1636\r
1637END_SESSION:\r
1638 DhcpEndSession (DhcpSb, EFI_TIMEOUT);\r
1639\r
1640 return ;\r
1641}\r