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