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