]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Impl.c
enhanced UefiPxeBcDxe to support
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Dhcp4Dxe / Dhcp4Impl.c
1 /** @file
2
3 Copyright (c) 2006 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 Dhcp4Impl.c
15
16 Abstract:
17
18 This file implement the EFI_DHCP4_PROTOCOL interface.
19
20
21 **/
22
23
24 #include "Dhcp4Impl.h"
25
26
27 /**
28 Get the current operation parameter and lease for the network interface.
29
30 @param This The DHCP protocol instance
31 @param Dhcp4ModeData The variable to save the DHCP mode data.
32
33 @retval EFI_INVALID_PARAMETER The parameter is invalid
34 @retval EFI_SUCCESS The Dhcp4ModeData is updated with the current
35 operation parameter.
36
37 **/
38 STATIC
39 EFI_STATUS
40 EFIAPI
41 EfiDhcp4GetModeData (
42 IN EFI_DHCP4_PROTOCOL *This,
43 OUT EFI_DHCP4_MODE_DATA *Dhcp4ModeData
44 )
45 {
46 DHCP_PROTOCOL *Instance;
47 DHCP_SERVICE *DhcpSb;
48 DHCP_PARAMETER *Para;
49 EFI_TPL OldTpl;
50 IP4_ADDR Ip;
51
52 //
53 // First validate the parameters.
54 //
55 if ((This == NULL) || (Dhcp4ModeData == NULL)) {
56 return EFI_INVALID_PARAMETER;
57 }
58
59 Instance = DHCP_INSTANCE_FROM_THIS (This);
60
61 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
62 DhcpSb = Instance->Service;
63
64 //
65 // Caller can use GetModeData to retrieve current DHCP states
66 // no matter whether it is the active child or not.
67 //
68 Dhcp4ModeData->State = (EFI_DHCP4_STATE) DhcpSb->DhcpState;
69 CopyMem (&Dhcp4ModeData->ConfigData, &DhcpSb->ActiveConfig, sizeof (Dhcp4ModeData->ConfigData));
70 CopyMem (&Dhcp4ModeData->ClientMacAddress, &DhcpSb->Mac, sizeof (Dhcp4ModeData->ClientMacAddress));
71
72 Ip = HTONL (DhcpSb->ClientAddr);
73 CopyMem (&Dhcp4ModeData->ClientAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
74
75 Ip = HTONL (DhcpSb->Netmask);
76 CopyMem (&Dhcp4ModeData->SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
77
78 Ip = HTONL (DhcpSb->ServerAddr);
79 CopyMem (&Dhcp4ModeData->ServerAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
80
81 Para = DhcpSb->Para;
82
83 if (Para != NULL) {
84 Ip = HTONL (Para->Router);
85 CopyMem (&Dhcp4ModeData->RouterAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
86 Dhcp4ModeData->LeaseTime = Para->Lease;
87 } else {
88 ZeroMem (&Dhcp4ModeData->RouterAddress, sizeof (EFI_IPv4_ADDRESS));
89 Dhcp4ModeData->LeaseTime = 0xffffffff;
90 }
91
92 Dhcp4ModeData->ReplyPacket = DhcpSb->Selected;
93
94 gBS->RestoreTPL (OldTpl);
95 return EFI_SUCCESS;
96 }
97
98
99 /**
100 Free the resource related to the configure parameters.
101 DHCP driver will make a copy of the user's configure
102 such as the time out value.
103
104 @param Config The DHCP configure data
105
106 @return None
107
108 **/
109 VOID
110 DhcpCleanConfigure (
111 IN EFI_DHCP4_CONFIG_DATA *Config
112 )
113 {
114 UINT32 Index;
115
116 if (Config->DiscoverTimeout != NULL) {
117 gBS->FreePool (Config->DiscoverTimeout);
118 }
119
120 if (Config->RequestTimeout != NULL) {
121 gBS->FreePool (Config->RequestTimeout);
122 }
123
124 if (Config->OptionList != NULL) {
125 for (Index = 0; Index < Config->OptionCount; Index++) {
126 if (Config->OptionList[Index] != NULL) {
127 gBS->FreePool (Config->OptionList[Index]);
128 }
129 }
130
131 gBS->FreePool (Config->OptionList);
132 }
133
134 ZeroMem (Config, sizeof (EFI_DHCP4_CONFIG_DATA));
135 }
136
137
138 /**
139 Allocate memory for configure parameter such as timeout value for Dst,
140 then copy the configure parameter from Src to Dst.
141
142 @param Dst The destination DHCP configure data.
143 @param Src The source DHCP configure data.
144
145 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
146 @retval EFI_SUCCESS The configure is copied.
147
148 **/
149 EFI_STATUS
150 DhcpCopyConfigure (
151 IN EFI_DHCP4_CONFIG_DATA *Dst,
152 IN EFI_DHCP4_CONFIG_DATA *Src
153 )
154 {
155 EFI_DHCP4_PACKET_OPTION **DstOptions;
156 EFI_DHCP4_PACKET_OPTION **SrcOptions;
157 INTN Len;
158 UINT32 Index;
159
160 CopyMem (Dst, Src, sizeof (*Dst));
161 Dst->DiscoverTimeout = NULL;
162 Dst->RequestTimeout = NULL;
163 Dst->OptionList = NULL;
164
165 //
166 // Allocate a memory then copy DiscoverTimeout to it
167 //
168 if (Src->DiscoverTimeout != NULL) {
169 Len = Src->DiscoverTryCount * sizeof (UINT32);
170 Dst->DiscoverTimeout = AllocatePool (Len);
171
172 if (Dst->DiscoverTimeout == NULL) {
173 return EFI_OUT_OF_RESOURCES;
174 }
175
176 for (Index = 0; Index < Src->DiscoverTryCount; Index++) {
177 Dst->DiscoverTimeout[Index] = MAX (Src->DiscoverTimeout[Index], 1);
178 }
179 }
180
181 //
182 // Allocate a memory then copy RequestTimeout to it
183 //
184 if (Src->RequestTimeout != NULL) {
185 Len = Src->RequestTryCount * sizeof (UINT32);
186 Dst->RequestTimeout = AllocatePool (Len);
187
188 if (Dst->RequestTimeout == NULL) {
189 goto ON_ERROR;
190 }
191
192 for (Index = 0; Index < Src->RequestTryCount; Index++) {
193 Dst->RequestTimeout[Index] = MAX (Src->RequestTimeout[Index], 1);
194 }
195 }
196
197 //
198 // Allocate an array of dhcp option point, then allocate memory
199 // for each option and copy the source option to it
200 //
201 if (Src->OptionList != NULL) {
202 Len = Src->OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *);
203 Dst->OptionList = AllocateZeroPool (Len);
204
205 if (Dst->OptionList == NULL) {
206 goto ON_ERROR;
207 }
208
209 DstOptions = Dst->OptionList;
210 SrcOptions = Src->OptionList;
211
212 for (Index = 0; Index < Src->OptionCount; Index++) {
213 Len = sizeof (EFI_DHCP4_PACKET_OPTION) + MAX (SrcOptions[Index]->Length - 1, 0);
214
215 DstOptions[Index] = AllocatePool (Len);
216
217 if (DstOptions[Index] == NULL) {
218 goto ON_ERROR;
219 }
220
221 CopyMem (DstOptions[Index], SrcOptions[Index], Len);
222 }
223 }
224
225 return EFI_SUCCESS;
226
227 ON_ERROR:
228 DhcpCleanConfigure (Dst);
229 return EFI_OUT_OF_RESOURCES;
230 }
231
232
233 /**
234 Give up the control of the DHCP service to let other child
235 resume. Don't change the service's DHCP state and the Client
236 address and option list configure as required by RFC2131.
237
238 @param DhcpSb The DHCP service instance.
239
240 @return None
241
242 **/
243 VOID
244 DhcpYieldControl (
245 IN DHCP_SERVICE *DhcpSb
246 )
247 {
248 EFI_DHCP4_CONFIG_DATA *Config;
249
250 Config = &DhcpSb->ActiveConfig;
251
252 DhcpSb->ServiceState = DHCP_UNCONFIGED;
253 DhcpSb->ActiveChild = NULL;
254
255 if (Config->DiscoverTimeout != NULL) {
256 gBS->FreePool (Config->DiscoverTimeout);
257
258 Config->DiscoverTryCount = 0;
259 Config->DiscoverTimeout = NULL;
260 }
261
262 if (Config->RequestTimeout != NULL) {
263 gBS->FreePool (Config->RequestTimeout);
264
265 Config->RequestTryCount = 0;
266 Config->RequestTimeout = NULL;
267 }
268
269 Config->Dhcp4Callback = NULL;
270 Config->CallbackContext = NULL;
271 }
272
273
274 /**
275 Configure the DHCP protocol instance and its underlying DHCP service
276 for operation. If Dhcp4CfgData is NULL and the child is currently
277 controlling the DHCP service, release the control.
278
279 @param This The DHCP protocol instance
280 @param Dhcp4CfgData The DHCP configure data.
281
282 @retval EFI_INVALID_PARAMETER The parameters are invalid.
283 @retval EFI_ACCESS_DENIED The service isn't in one of configurable states,
284 or there is already an active child.
285 @retval EFI_OUT_OF_RESOURCE Failed to allocate some resources.
286 @retval EFI_SUCCESS The child is configured.
287
288 **/
289 STATIC
290 EFI_STATUS
291 EFIAPI
292 EfiDhcp4Configure (
293 IN EFI_DHCP4_PROTOCOL *This,
294 IN EFI_DHCP4_CONFIG_DATA *Dhcp4CfgData OPTIONAL
295 )
296 {
297 EFI_DHCP4_CONFIG_DATA *Config;
298 DHCP_PROTOCOL *Instance;
299 DHCP_SERVICE *DhcpSb;
300 EFI_STATUS Status;
301 EFI_TPL OldTpl;
302 UINT32 Index;
303 IP4_ADDR Ip;
304
305 //
306 // First validate the parameters
307 //
308 if (This == NULL) {
309 return EFI_INVALID_PARAMETER;
310 }
311
312 if (Dhcp4CfgData != NULL) {
313 if (Dhcp4CfgData->DiscoverTryCount && (Dhcp4CfgData->DiscoverTimeout == NULL)) {
314 return EFI_INVALID_PARAMETER;
315 }
316
317 if (Dhcp4CfgData->RequestTryCount && (Dhcp4CfgData->RequestTimeout == NULL)) {
318 return EFI_INVALID_PARAMETER;
319 }
320
321 if (Dhcp4CfgData->OptionCount && (Dhcp4CfgData->OptionList == NULL)) {
322 return EFI_INVALID_PARAMETER;
323 }
324
325 CopyMem (&Ip, &Dhcp4CfgData->ClientAddress, sizeof (IP4_ADDR));
326
327 if ((Ip != 0) && !Ip4IsUnicast (NTOHL (Ip), 0)) {
328
329 return EFI_INVALID_PARAMETER;
330 }
331 }
332
333 Instance = DHCP_INSTANCE_FROM_THIS (This);
334
335 if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
336 return EFI_INVALID_PARAMETER;
337 }
338
339 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
340
341 DhcpSb = Instance->Service;
342 Config = &DhcpSb->ActiveConfig;
343
344 Status = EFI_ACCESS_DENIED;
345
346 if ((DhcpSb->DhcpState != Dhcp4Stopped) &&
347 (DhcpSb->DhcpState != Dhcp4Init) &&
348 (DhcpSb->DhcpState != Dhcp4InitReboot) &&
349 (DhcpSb->DhcpState != Dhcp4Bound)) {
350
351 goto ON_EXIT;
352 }
353
354 if ((DhcpSb->ActiveChild != NULL) && (DhcpSb->ActiveChild != Instance)) {
355 goto ON_EXIT;
356 }
357
358 if (Dhcp4CfgData != NULL) {
359 Status = EFI_OUT_OF_RESOURCES;
360 DhcpCleanConfigure (Config);
361
362 if (EFI_ERROR (DhcpCopyConfigure (Config, Dhcp4CfgData))) {
363 goto ON_EXIT;
364 }
365
366 DhcpSb->UserOptionLen = 0;
367
368 for (Index = 0; Index < Dhcp4CfgData->OptionCount; Index++) {
369 DhcpSb->UserOptionLen += Dhcp4CfgData->OptionList[Index]->Length + 2;
370 }
371
372 DhcpSb->ActiveChild = Instance;
373
374 if (DhcpSb->DhcpState == Dhcp4Stopped) {
375 DhcpSb->ClientAddr = EFI_NTOHL (Dhcp4CfgData->ClientAddress);
376
377 if (DhcpSb->ClientAddr != 0) {
378 DhcpSb->DhcpState = Dhcp4InitReboot;
379 } else {
380 DhcpSb->DhcpState = Dhcp4Init;
381 }
382 }
383
384 DhcpSb->ServiceState = DHCP_CONFIGED;
385 Status = EFI_SUCCESS;
386
387 } else if (DhcpSb->ActiveChild == Instance) {
388 Status = EFI_SUCCESS;
389 DhcpYieldControl (DhcpSb);
390 }
391
392 ON_EXIT:
393 gBS->RestoreTPL (OldTpl);
394 return Status;
395 }
396
397
398 /**
399 Start the DHCP process.
400
401 @param This The DHCP protocol instance
402 @param CompletionEvent The event to signal is address is acquired.
403
404 @retval EFI_INVALID_PARAMETER The parameters are invalid.
405 @retval EFI_NOT_STARTED The protocol hasn't been configured.
406 @retval EFI_ALREADY_STARTED The DHCP process has already been started.
407 @retval EFI_SUCCESS The DHCP process is started.
408
409 **/
410 STATIC
411 EFI_STATUS
412 EFIAPI
413 EfiDhcp4Start (
414 IN EFI_DHCP4_PROTOCOL *This,
415 IN EFI_EVENT CompletionEvent OPTIONAL
416 )
417 {
418 DHCP_PROTOCOL *Instance;
419 DHCP_SERVICE *DhcpSb;
420 EFI_STATUS Status;
421 EFI_TPL OldTpl;
422
423 //
424 // First validate the parameters
425 //
426 if (This == NULL) {
427 return EFI_INVALID_PARAMETER;
428 }
429
430 Instance = DHCP_INSTANCE_FROM_THIS (This);
431
432 if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
433 return EFI_INVALID_PARAMETER;
434 }
435
436 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
437 DhcpSb = Instance->Service;
438
439 if (DhcpSb->DhcpState == Dhcp4Stopped) {
440 Status = EFI_NOT_STARTED;
441 goto ON_ERROR;
442 }
443
444 if ((DhcpSb->DhcpState != Dhcp4Init) && (DhcpSb->DhcpState != Dhcp4InitReboot)) {
445 Status = EFI_ALREADY_STARTED;
446 goto ON_ERROR;
447 }
448
449 DhcpSb->IoStatus = EFI_ALREADY_STARTED;
450
451 if (EFI_ERROR (Status = DhcpInitRequest (DhcpSb))) {
452 goto ON_ERROR;
453 }
454
455 //
456 // Start/Restart the receiving.
457 //
458 Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
459
460 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
461 goto ON_ERROR;
462 }
463
464 Instance->CompletionEvent = CompletionEvent;
465
466 //
467 // Restore the TPL now, don't call poll function at TPL_CALLBACK.
468 //
469 gBS->RestoreTPL (OldTpl);
470
471 if (CompletionEvent == NULL) {
472 while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
473 DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);
474 }
475
476 return DhcpSb->IoStatus;
477 }
478
479 return EFI_SUCCESS;
480
481 ON_ERROR:
482 gBS->RestoreTPL (OldTpl);
483 return Status;
484 }
485
486
487 /**
488 Request an extra manual renew/rebind.
489
490 @param This The DHCP protocol instance
491 @param RebindRequest TRUE if request a rebind, otherwise renew it
492 @param CompletionEvent Event to signal when complete
493
494 @retval EFI_INVALID_PARAMETER The parameters are invalid
495 @retval EFI_NOT_STARTED The DHCP protocol hasn't been started.
496 @retval EFI_ACCESS_DENIED The DHCP protocol isn't in Bound state.
497 @retval EFI_SUCCESS The DHCP is renewed/rebound.
498
499 **/
500 STATIC
501 EFI_STATUS
502 EFIAPI
503 EfiDhcp4RenewRebind (
504 IN EFI_DHCP4_PROTOCOL *This,
505 IN BOOLEAN RebindRequest,
506 IN EFI_EVENT CompletionEvent OPTIONAL
507 )
508 {
509 DHCP_PROTOCOL *Instance;
510 DHCP_SERVICE *DhcpSb;
511 EFI_STATUS Status;
512 EFI_TPL OldTpl;
513
514 //
515 // First validate the parameters
516 //
517 if (This == NULL) {
518 return EFI_INVALID_PARAMETER;
519 }
520
521 Instance = DHCP_INSTANCE_FROM_THIS (This);
522
523 if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
524 return EFI_INVALID_PARAMETER;
525 }
526
527 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
528 DhcpSb = Instance->Service;
529
530 if (DhcpSb->DhcpState == Dhcp4Stopped) {
531 Status = EFI_NOT_STARTED;
532 goto ON_ERROR;
533 }
534
535 if (DhcpSb->DhcpState != Dhcp4Bound) {
536 Status = EFI_ACCESS_DENIED;
537 goto ON_ERROR;
538 }
539
540 if (DHCP_IS_BOOTP (DhcpSb->Para)) {
541 return EFI_SUCCESS;
542 }
543
544 //
545 // Transit the states then send a extra DHCP request
546 //
547 if (!RebindRequest) {
548 DhcpSetState (DhcpSb, Dhcp4Renewing, FALSE);
549 } else {
550 DhcpSetState (DhcpSb, Dhcp4Rebinding, FALSE);
551 }
552
553 Status = DhcpSendMessage (
554 DhcpSb,
555 DhcpSb->Selected,
556 DhcpSb->Para,
557 DHCP_MSG_REQUEST,
558 (UINT8 *) "Extra renew/rebind by the application"
559 );
560
561 if (EFI_ERROR (Status)) {
562 DhcpSetState (DhcpSb, Dhcp4Bound, FALSE);
563 goto ON_ERROR;
564 }
565
566 DhcpSb->ExtraRefresh = TRUE;
567 DhcpSb->IoStatus = EFI_ALREADY_STARTED;
568 Instance->RenewRebindEvent = CompletionEvent;
569
570 gBS->RestoreTPL (OldTpl);
571
572 if (CompletionEvent == NULL) {
573 while (DhcpSb->IoStatus == EFI_ALREADY_STARTED) {
574 DhcpSb->UdpIo->Udp->Poll (DhcpSb->UdpIo->Udp);
575 }
576
577 return DhcpSb->IoStatus;
578 }
579
580 return EFI_SUCCESS;
581
582 ON_ERROR:
583 gBS->RestoreTPL (OldTpl);
584 return Status;
585 }
586
587
588 /**
589 Release the current acquired lease.
590
591 @param This The DHCP protocol instance
592
593 @retval EFI_INVALID_PARAMETER The parameter is invalid
594 @retval EFI_DEVICE_ERROR Failed to transmit the DHCP release packet
595 @retval EFI_ACCESS_DENIED The DHCP service isn't in one of the connected
596 state.
597 @retval EFI_SUCCESS The lease is released.
598
599 **/
600 STATIC
601 EFI_STATUS
602 EFIAPI
603 EfiDhcp4Release (
604 IN EFI_DHCP4_PROTOCOL *This
605 )
606 {
607 DHCP_PROTOCOL *Instance;
608 DHCP_SERVICE *DhcpSb;
609 EFI_STATUS Status;
610 EFI_TPL OldTpl;
611
612 //
613 // First validate the parameters
614 //
615 if (This == NULL) {
616 return EFI_INVALID_PARAMETER;
617 }
618
619 Instance = DHCP_INSTANCE_FROM_THIS (This);
620
621 if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
622 return EFI_INVALID_PARAMETER;
623 }
624
625 Status = EFI_SUCCESS;
626 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
627 DhcpSb = Instance->Service;
628
629 if ((DhcpSb->DhcpState != Dhcp4InitReboot) && (DhcpSb->DhcpState != Dhcp4Bound)) {
630 Status = EFI_ACCESS_DENIED;
631 goto ON_EXIT;
632 }
633
634 if (!DHCP_IS_BOOTP (DhcpSb->Para) && (DhcpSb->DhcpState == Dhcp4Bound)) {
635 Status = DhcpSendMessage (
636 DhcpSb,
637 DhcpSb->Selected,
638 DhcpSb->Para,
639 DHCP_MSG_RELEASE,
640 NULL
641 );
642
643 if (EFI_ERROR (Status)) {
644 Status = EFI_DEVICE_ERROR;
645 goto ON_EXIT;
646 }
647 }
648
649 DhcpCleanLease (DhcpSb);
650
651 ON_EXIT:
652 gBS->RestoreTPL (OldTpl);
653 return Status;
654 }
655
656
657 /**
658 Stop the current DHCP process. After this, other DHCP child
659 can gain control of the service, configure and use it.
660
661 @param This The DHCP protocol instance
662
663 @retval EFI_INVALID_PARAMETER The parameter is invalid.
664 @retval EFI_SUCCESS The DHCP process is stopped.
665
666 **/
667 STATIC
668 EFI_STATUS
669 EFIAPI
670 EfiDhcp4Stop (
671 IN EFI_DHCP4_PROTOCOL *This
672 )
673 {
674 DHCP_PROTOCOL *Instance;
675 DHCP_SERVICE *DhcpSb;
676 EFI_TPL OldTpl;
677
678 //
679 // First validate the parameters
680 //
681 if (This == NULL) {
682 return EFI_INVALID_PARAMETER;
683 }
684
685 Instance = DHCP_INSTANCE_FROM_THIS (This);
686
687 if (Instance->Signature != DHCP_PROTOCOL_SIGNATURE) {
688 return EFI_INVALID_PARAMETER;
689 }
690
691 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
692 DhcpSb = Instance->Service;
693
694 DhcpCleanLease (DhcpSb);
695
696 DhcpSb->DhcpState = Dhcp4Stopped;
697 DhcpSb->ServiceState = DHCP_UNCONFIGED;
698
699 gBS->RestoreTPL (OldTpl);
700 return EFI_SUCCESS;
701 }
702
703
704 /**
705 Build a new DHCP packet from the seed packet. Options may be deleted or
706 appended. The caller should free the NewPacket when finished using it.
707
708 @param This The DHCP protocol instance.
709 @param SeedPacket The seed packet to start with
710 @param DeleteCount The number of options to delete
711 @param DeleteList The options to delete from the packet
712 @param AppendCount The number of options to append
713 @param AppendList The options to append to the packet
714 @param NewPacket The new packet, allocated and built by this
715 function.
716
717 @retval EFI_INVALID_PARAMETER The parameters are invalid.
718 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
719 @retval EFI_SUCCESS The packet is build.
720
721 **/
722 STATIC
723 EFI_STATUS
724 EFIAPI
725 EfiDhcp4Build (
726 IN EFI_DHCP4_PROTOCOL *This,
727 IN EFI_DHCP4_PACKET *SeedPacket,
728 IN UINT32 DeleteCount,
729 IN UINT8 *DeleteList OPTIONAL,
730 IN UINT32 AppendCount,
731 IN EFI_DHCP4_PACKET_OPTION *AppendList[] OPTIONAL,
732 OUT EFI_DHCP4_PACKET **NewPacket
733 )
734 {
735 //
736 // First validate the parameters
737 //
738 if ((This == NULL) || (NewPacket == NULL)) {
739 return EFI_INVALID_PARAMETER;
740 }
741
742 if ((SeedPacket == NULL) || (SeedPacket->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
743 EFI_ERROR (DhcpValidateOptions (SeedPacket, NULL))) {
744
745 return EFI_INVALID_PARAMETER;
746 }
747
748 if (((DeleteCount == 0) && (AppendCount == 0)) ||
749 ((DeleteCount != 0) && (DeleteList == NULL)) ||
750 ((AppendCount != 0) && (AppendList == NULL))) {
751
752 return EFI_INVALID_PARAMETER;
753 }
754
755 return DhcpBuild (
756 SeedPacket,
757 DeleteCount,
758 DeleteList,
759 AppendCount,
760 AppendList,
761 NewPacket
762 );
763 }
764
765 STATIC
766 EFI_STATUS
767 Dhcp4InstanceConfigUdpIo (
768 IN UDP_IO_PORT *UdpIo,
769 IN VOID *Context
770 )
771 {
772 DHCP_PROTOCOL *Instance;
773 DHCP_SERVICE *DhcpSb;
774 EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
775 EFI_UDP4_CONFIG_DATA UdpConfigData;
776 IP4_ADDR Ip;
777
778 Instance = (DHCP_PROTOCOL *) Context;
779 DhcpSb = Instance->Service;
780 Token = Instance->Token;
781
782 ZeroMem (&UdpConfigData, sizeof (EFI_UDP4_CONFIG_DATA));
783
784 UdpConfigData.AcceptBroadcast = TRUE;
785 UdpConfigData.AllowDuplicatePort = TRUE;
786 UdpConfigData.TimeToLive = 64;
787 UdpConfigData.DoNotFragment = TRUE;
788
789 Ip = HTONL (DhcpSb->ClientAddr);
790 CopyMem (&UdpConfigData.StationAddress, &Ip, sizeof (EFI_IPv4_ADDRESS));
791
792 Ip = HTONL (DhcpSb->Netmask);
793 CopyMem (&UdpConfigData.SubnetMask, &Ip, sizeof (EFI_IPv4_ADDRESS));
794
795 if ((Token->ListenPointCount == 0) || (Token->ListenPoints[0].ListenPort == 0)) {
796 UdpConfigData.StationPort = DHCP_CLIENT_PORT;
797 } else {
798 UdpConfigData.StationPort = Token->ListenPoints[0].ListenPort;
799 }
800
801 return UdpIo->Udp->Configure (UdpIo->Udp, &UdpConfigData);
802 }
803
804 STATIC
805 EFI_STATUS
806 Dhcp4InstanceCreateUdpIo (
807 IN DHCP_PROTOCOL *Instance
808 )
809 {
810 DHCP_SERVICE *DhcpSb;
811
812 ASSERT (Instance->Token != NULL);
813
814 DhcpSb = Instance->Service;
815 Instance->UdpIo = UdpIoCreatePort (DhcpSb->Controller, DhcpSb->Image, Dhcp4InstanceConfigUdpIo, Instance);
816 if (Instance->UdpIo == NULL) {
817 return EFI_OUT_OF_RESOURCES;
818 } else {
819 return EFI_SUCCESS;
820 }
821 }
822
823 STATIC
824 VOID
825 DhcpDummyExtFree (
826 IN VOID *Arg
827 )
828 /*++
829
830 Routine Description:
831
832 Release the packet.
833
834 Arguments:
835
836 Arg - The packet to release
837
838 Returns:
839
840 None
841
842 --*/
843 {
844 }
845
846 VOID
847 PxeDhcpInput (
848 NET_BUF *UdpPacket,
849 UDP_POINTS *Points,
850 EFI_STATUS IoStatus,
851 VOID *Context
852 )
853 {
854 DHCP_PROTOCOL *Instance;
855 DHCP_SERVICE *DhcpSb;
856 EFI_DHCP4_HEADER *Head;
857 NET_BUF *Wrap;
858 EFI_DHCP4_PACKET *Packet;
859 EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
860 UINT32 Len;
861 EFI_STATUS Status;
862
863 Wrap = NULL;
864 Instance = (DHCP_PROTOCOL *) Context;
865 Token = Instance->Token;
866 DhcpSb = Instance->Service;
867
868 //
869 // Don't restart receive if error occurs or DHCP is destoried.
870 //
871 if (EFI_ERROR (IoStatus)) {
872 return ;
873 }
874
875 ASSERT (UdpPacket != NULL);
876
877 //
878 // Validate the packet received
879 //
880 if (UdpPacket->TotalSize < sizeof (EFI_DHCP4_HEADER)) {
881 goto RESTART;
882 }
883
884 //
885 // Copy the DHCP message to a continuous memory block, make the buffer size
886 // of the EFI_DHCP4_PACKET a multiple of 4-byte.
887 //
888 Len = NET_ROUNDUP (sizeof (EFI_DHCP4_PACKET) + UdpPacket->TotalSize - sizeof (EFI_DHCP4_HEADER), 4);
889 Wrap = NetbufAlloc (Len);
890
891 if (Wrap == NULL) {
892 goto RESTART;
893 }
894
895 Packet = (EFI_DHCP4_PACKET *) NetbufAllocSpace (Wrap, Len, NET_BUF_TAIL);
896 Packet->Size = Len;
897 Head = &Packet->Dhcp4.Header;
898 Packet->Length = NetbufCopy (UdpPacket, 0, UdpPacket->TotalSize, (UINT8 *) Head);
899
900 if (Packet->Length != UdpPacket->TotalSize) {
901 goto RESTART;
902 }
903
904 //
905 // Is this packet the answer to our packet?
906 //
907 if ((Head->OpCode != BOOTP_REPLY) ||
908 (Head->Xid != Token->Packet->Dhcp4.Header.Xid) ||
909 (CompareMem (DhcpSb->ClientAddressSendOut, Head->ClientHwAddr, Head->HwAddrLen) != 0)) {
910 goto RESTART;
911 }
912
913 //
914 // Validate the options and retrieve the interested options
915 //
916 if ((Packet->Length > sizeof (EFI_DHCP4_HEADER) + sizeof (UINT32)) &&
917 (Packet->Dhcp4.Magik == DHCP_OPTION_MAGIC) &&
918 EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
919
920 goto RESTART;
921 }
922
923 //
924 // Keep this packet in the ResponseQueue.
925 //
926 NET_GET_REF (Wrap);
927 NetbufQueAppend (&Instance->ResponseQueue, Wrap);
928
929 RESTART:
930
931 NetbufFree (UdpPacket);
932
933 if (Wrap != NULL) {
934 NetbufFree (Wrap);
935 }
936
937 Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
938 if (EFI_ERROR (Status)) {
939 PxeDhcpDone (Instance);
940 }
941 }
942
943 VOID
944 PxeDhcpDone (
945 IN DHCP_PROTOCOL *Instance
946 )
947 {
948 EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token;
949
950 Token = Instance->Token;
951
952 Token->ResponseCount = Instance->ResponseQueue.BufNum;
953 if (Token->ResponseCount != 0) {
954 Token->ResponseList = (EFI_DHCP4_PACKET *) AllocatePool (Instance->ResponseQueue.BufSize);
955 if (Token->ResponseList == NULL) {
956 Token->Status = EFI_OUT_OF_RESOURCES;
957 goto SIGNAL_USER;
958 }
959
960 //
961 // Copy the recieved DHCP responses.
962 //
963 NetbufQueCopy (&Instance->ResponseQueue, 0, Instance->ResponseQueue.BufSize, (UINT8 *) Token->ResponseList);
964 Token->Status = EFI_SUCCESS;
965 } else {
966 Token->ResponseList = NULL;
967 Token->Status = EFI_TIMEOUT;
968 }
969
970 SIGNAL_USER:
971 //
972 // Clean the resources dedicated for this transmit receive transaction.
973 //
974 NetbufQueFlush (&Instance->ResponseQueue);
975 UdpIoCleanPort (Instance->UdpIo);
976 UdpIoFreePort (Instance->UdpIo);
977 Instance->UdpIo = NULL;
978 Instance->Token = NULL;
979
980 if (Token->CompletionEvent != NULL) {
981 gBS->SignalEvent (Token->CompletionEvent);
982 }
983 }
984
985
986 /**
987 Transmits a DHCP formatted packet and optionally waits for responses.
988
989 @param This Pointer to the EFI_DHCP4_PROTOCOL instance.
990 @param Token Pointer to the EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN structure.
991
992 @retval EFI_SUCCESS The packet was successfully queued for transmission.
993 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
994 @retval EFI_NOT_READY The previous call to this function has not finished yet. Try to call
995 this function after collection process completes.
996 @retval EFI_NO_MAPPING The default station address is not available yet.
997 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
998 @retval Others Some other unexpected error occurred.
999
1000 **/
1001 STATIC
1002 EFI_STATUS
1003 EFIAPI
1004 EfiDhcp4TransmitReceive (
1005 IN EFI_DHCP4_PROTOCOL *This,
1006 IN EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN *Token
1007 )
1008 {
1009 DHCP_PROTOCOL *Instance;
1010 EFI_TPL OldTpl;
1011 EFI_STATUS Status;
1012 NET_FRAGMENT Frag;
1013 NET_BUF *Wrap;
1014 UDP_POINTS EndPoint;
1015 IP4_ADDR Ip;
1016 DHCP_SERVICE *DhcpSb;
1017 IP4_ADDR Gateway;
1018 IP4_ADDR SubnetMask;
1019
1020 if ((This == NULL) || (Token == NULL) || (Token->Packet == NULL)) {
1021 return EFI_INVALID_PARAMETER;
1022 }
1023
1024 Instance = DHCP_INSTANCE_FROM_THIS (This);
1025 DhcpSb = Instance->Service;
1026
1027 if (Instance->Token != NULL) {
1028 //
1029 // The previous call to TransmitReceive is not finished.
1030 //
1031 return EFI_NOT_READY;
1032 }
1033
1034 if ((Token->Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1035 (NTOHL (Token->Packet->Dhcp4.Header.Xid) == Instance->Service->Xid) ||
1036 (Token->TimeoutValue == 0) ||
1037 ((Token->ListenPointCount != 0) && (Token->ListenPoints == NULL)) ||
1038 EFI_ERROR (DhcpValidateOptions (Token->Packet, NULL)) ||
1039 EFI_IP4_EQUAL (&Token->RemoteAddress, &mZeroIp4Addr)) {
1040 //
1041 // The DHCP packet isn't well-formed, the Transaction ID is already used
1042 // , the timeout value is zero, the ListenPoint is invalid,
1043 // or the RemoteAddress is zero.
1044 //
1045 return EFI_INVALID_PARAMETER;
1046 }
1047
1048 if (DhcpSb->ClientAddr == 0) {
1049
1050 return EFI_NO_MAPPING;
1051 }
1052
1053 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1054
1055 //
1056 // Save the token and the timeout value.
1057 //
1058 Instance->Token = Token;
1059 Instance->Timeout = Token->TimeoutValue;
1060
1061 //
1062 // Create a UDP IO for this transmit receive transaction.
1063 //
1064 Status = Dhcp4InstanceCreateUdpIo (Instance);
1065 if (EFI_ERROR (Status)) {
1066 goto ON_ERROR;
1067 }
1068
1069 //
1070 // Save the Client Address is sent out
1071 //
1072 CopyMem (&DhcpSb->ClientAddressSendOut[0], &Token->Packet->Dhcp4.Header.ClientHwAddr[0], Token->Packet->Dhcp4.Header.HwAddrLen);
1073
1074 //
1075 // Wrap the DHCP packet into a net buffer.
1076 //
1077 Frag.Bulk = (UINT8 *) &Token->Packet->Dhcp4;
1078 Frag.Len = Token->Packet->Length;
1079 Wrap = NetbufFromExt (&Frag, 1, 0, 0, DhcpDummyExtFree, NULL);
1080 if (Wrap == NULL) {
1081 Status = EFI_OUT_OF_RESOURCES;
1082 goto ON_ERROR;
1083 }
1084
1085 //
1086 // Set the local address and local port.
1087 //
1088 EndPoint.LocalAddr = 0;
1089 EndPoint.LocalPort = 0;
1090
1091 //
1092 // Set the destination address and destination port.
1093 //
1094 CopyMem (&Ip, &Token->RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
1095 EndPoint.RemoteAddr = NTOHL (Ip);
1096
1097 if (Token->RemotePort == 0) {
1098 EndPoint.RemotePort = DHCP_SERVER_PORT;
1099 } else {
1100 EndPoint.RemotePort = Token->RemotePort;
1101 }
1102
1103 //
1104 // Get the gateway.
1105 //
1106 SubnetMask = DhcpSb->Netmask;
1107 Gateway = 0;
1108 if (!IP4_NET_EQUAL (DhcpSb->ClientAddr, EndPoint.RemoteAddr, SubnetMask)) {
1109 CopyMem (&Gateway, &Token->GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
1110 Gateway = NTOHL (Gateway);
1111 }
1112
1113 //
1114 // Transmit the DHCP packet.
1115 //
1116 Status = UdpIoSendDatagram (Instance->UdpIo, Wrap, &EndPoint, Gateway, DhcpOnPacketSent, NULL);
1117 if (EFI_ERROR (Status)) {
1118 NetbufFree (Wrap);
1119 goto ON_ERROR;
1120 }
1121
1122 //
1123 // Start to receive the DHCP response.
1124 //
1125 Status = UdpIoRecvDatagram (Instance->UdpIo, PxeDhcpInput, Instance, 0);
1126 if (EFI_ERROR (Status)) {
1127 goto ON_ERROR;
1128 }
1129
1130 ON_ERROR:
1131
1132 if (EFI_ERROR (Status) && (Instance->UdpIo != NULL)) {
1133 UdpIoCleanPort (Instance->UdpIo);
1134 UdpIoFreePort (Instance->UdpIo);
1135 Instance->UdpIo = NULL;
1136 Instance->Token = NULL;
1137 }
1138
1139 gBS->RestoreTPL (OldTpl);
1140
1141 if (!EFI_ERROR (Status) && (Token->CompletionEvent == NULL)) {
1142 //
1143 // Keep polling until timeout if no error happens and the CompletionEvent
1144 // is NULL.
1145 //
1146 while (Instance->Timeout != 0) {
1147 Instance->UdpIo->Udp->Poll (Instance->UdpIo->Udp);
1148 }
1149 }
1150
1151 return Status;
1152 }
1153
1154
1155 /**
1156 Callback function for DhcpIterateOptions. This callback sets the
1157 EFI_DHCP4_PACKET_OPTION array in the DHCP_PARSE_CONTEXT to point
1158 the individual DHCP option in the packet.
1159
1160 @param Tag The DHCP option type
1161 @param Len length of the DHCP option data
1162 @param Data The DHCP option data
1163 @param Context The context, to pass several parameters in.
1164
1165 @retval EFI_SUCCESS It always returns EFI_SUCCESS
1166
1167 **/
1168 STATIC
1169 EFI_STATUS
1170 Dhcp4ParseCheckOption (
1171 IN UINT8 Tag,
1172 IN UINT8 Len,
1173 IN UINT8 *Data,
1174 IN VOID *Context
1175 )
1176 {
1177 DHCP_PARSE_CONTEXT *Parse;
1178
1179 Parse = (DHCP_PARSE_CONTEXT *) Context;
1180 Parse->Index++;
1181
1182 if (Parse->Index <= Parse->OptionCount) {
1183 //
1184 // Use _CR to get the memory position of EFI_DHCP4_PACKET_OPTION for
1185 // the EFI_DHCP4_PACKET_OPTION->Data because DhcpIterateOptions only
1186 // pass in the point to option data.
1187 //
1188 Parse->Option[Parse->Index - 1] = _CR (Data, EFI_DHCP4_PACKET_OPTION, Data);
1189 }
1190
1191 return EFI_SUCCESS;
1192 }
1193
1194
1195 /**
1196 Parse the DHCP options in the Packet into the PacketOptionList.
1197 User should allocate this array of EFI_DHCP4_PACKET_OPTION points.
1198
1199 @param This The DHCP protocol instance
1200 @param Packet The DHCP packet to parse
1201 @param OptionCount On input, the size of the PacketOptionList; On
1202 output, the actual number of options processed.
1203 @param PacketOptionList The array of EFI_DHCP4_PACKET_OPTION points
1204
1205 @retval EFI_INVALID_PARAMETER The parameters are invalid.
1206 @retval EFI_BUFFER_TOO_SMALL A bigger array of points is needed.
1207 @retval EFI_SUCCESS The options are parsed.
1208
1209 **/
1210 STATIC
1211 EFI_STATUS
1212 EFIAPI
1213 EfiDhcp4Parse (
1214 IN EFI_DHCP4_PROTOCOL *This,
1215 IN EFI_DHCP4_PACKET *Packet,
1216 IN OUT UINT32 *OptionCount,
1217 OUT EFI_DHCP4_PACKET_OPTION *PacketOptionList[] OPTIONAL
1218 )
1219 {
1220 DHCP_PARSE_CONTEXT Context;
1221 EFI_STATUS Status;
1222
1223 //
1224 // First validate the parameters
1225 //
1226 if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
1227 return EFI_INVALID_PARAMETER;
1228 }
1229
1230 if ((Packet->Size < Packet->Length + 2 * sizeof (UINT32)) ||
1231 (Packet->Dhcp4.Magik != DHCP_OPTION_MAGIC) ||
1232 EFI_ERROR (DhcpValidateOptions (Packet, NULL))) {
1233
1234 return EFI_INVALID_PARAMETER;
1235 }
1236
1237 if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
1238 return EFI_BUFFER_TOO_SMALL;
1239 }
1240
1241 ZeroMem (PacketOptionList, *OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
1242
1243 Context.Option = PacketOptionList;
1244 Context.OptionCount = *OptionCount;
1245 Context.Index = 0;
1246
1247 Status = DhcpIterateOptions (Packet, Dhcp4ParseCheckOption, &Context);
1248
1249 if (EFI_ERROR (Status)) {
1250 return Status;
1251 }
1252
1253 *OptionCount = Context.Index;
1254
1255 if (Context.Index > Context.OptionCount) {
1256 return EFI_BUFFER_TOO_SMALL;
1257 }
1258
1259 return EFI_SUCCESS;
1260 }
1261
1262 EFI_DHCP4_PROTOCOL mDhcp4ProtocolTemplate = {
1263 EfiDhcp4GetModeData,
1264 EfiDhcp4Configure,
1265 EfiDhcp4Start,
1266 EfiDhcp4RenewRebind,
1267 EfiDhcp4Release,
1268 EfiDhcp4Stop,
1269 EfiDhcp4Build,
1270 EfiDhcp4TransmitReceive,
1271 EfiDhcp4Parse
1272 };
1273