]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Dhcp6Dxe/Dhcp6Impl.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / NetworkPkg / Dhcp6Dxe / Dhcp6Impl.c
1 /** @file
2 This EFI_DHCP6_PROTOCOL interface implementation.
3
4 Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "Dhcp6Impl.h"
11
12 //
13 // Well-known multi-cast address defined in section-24.1 of rfc-3315
14 //
15 // ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
16 //
17 EFI_IPv6_ADDRESS mAllDhcpRelayAndServersAddress = {
18 { 0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2 }
19 };
20
21 EFI_DHCP6_PROTOCOL gDhcp6ProtocolTemplate = {
22 EfiDhcp6GetModeData,
23 EfiDhcp6Configure,
24 EfiDhcp6Start,
25 EfiDhcp6InfoRequest,
26 EfiDhcp6RenewRebind,
27 EfiDhcp6Decline,
28 EfiDhcp6Release,
29 EfiDhcp6Stop,
30 EfiDhcp6Parse
31 };
32
33 /**
34 Starts the DHCPv6 standard S.A.R.R. process.
35
36 The Start() function starts the DHCPv6 standard process. This function can
37 be called only when the state of Dhcp6 instance is in the Dhcp6Init state.
38 If the DHCP process completes successfully, the state of the Dhcp6 instance
39 will be transferred through Dhcp6Selecting and Dhcp6Requesting to the
40 Dhcp6Bound state.
41 Refer to rfc-3315 for precise state transitions during this process. At the
42 time when each event occurs in this process, the callback function that was set
43 by EFI_DHCP6_PROTOCOL.Configure() will be called, and the user can take this
44 opportunity to control the process.
45
46 @param[in] This The pointer to Dhcp6 protocol.
47
48 @retval EFI_SUCCESS The DHCPv6 standard process has started, or it has
49 completed when CompletionEvent is NULL.
50 @retval EFI_ACCESS_DENIED The EFI DHCPv6 Child instance hasn't been configured.
51 @retval EFI_INVALID_PARAMETER This is NULL.
52 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
53 @retval EFI_TIMEOUT The DHCPv6 configuration process failed because no
54 response was received from the server within the
55 specified timeout value.
56 @retval EFI_ABORTED The user aborted the DHCPv6 process.
57 @retval EFI_ALREADY_STARTED Some other Dhcp6 instance already started the DHCPv6
58 standard process.
59 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
60 @retval EFI_NO_MEDIA There was a media error.
61
62 **/
63 EFI_STATUS
64 EFIAPI
65 EfiDhcp6Start (
66 IN EFI_DHCP6_PROTOCOL *This
67 )
68 {
69 EFI_STATUS Status;
70 EFI_TPL OldTpl;
71 DHCP6_INSTANCE *Instance;
72 DHCP6_SERVICE *Service;
73 EFI_STATUS MediaStatus;
74
75 if (This == NULL) {
76 return EFI_INVALID_PARAMETER;
77 }
78
79 Instance = DHCP6_INSTANCE_FROM_THIS (This);
80 Service = Instance->Service;
81
82 //
83 // The instance hasn't been configured.
84 //
85 if (Instance->Config == NULL) {
86 return EFI_ACCESS_DENIED;
87 }
88
89 ASSERT (Instance->IaCb.Ia != NULL);
90
91 //
92 // The instance has already been started.
93 //
94 if (Instance->IaCb.Ia->State != Dhcp6Init) {
95 return EFI_ALREADY_STARTED;
96 }
97
98 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
99
100 //
101 // Check Media Status.
102 //
103 MediaStatus = EFI_SUCCESS;
104 NetLibDetectMediaWaitTimeout (Service->Controller, DHCP_CHECK_MEDIA_WAITING_TIME, &MediaStatus);
105 if (MediaStatus != EFI_SUCCESS) {
106 Status = EFI_NO_MEDIA;
107 goto ON_ERROR;
108 }
109
110 Instance->UdpSts = EFI_ALREADY_STARTED;
111
112 //
113 // Send the solicit message to start S.A.R.R process.
114 //
115 Status = Dhcp6SendSolicitMsg (Instance);
116
117 if (EFI_ERROR (Status)) {
118 goto ON_ERROR;
119 }
120
121 //
122 // Register receive callback for the stateful exchange process.
123 //
124 Status = UdpIoRecvDatagram (
125 Service->UdpIo,
126 Dhcp6ReceivePacket,
127 Service,
128 0
129 );
130
131 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
132 goto ON_ERROR;
133 }
134
135 gBS->RestoreTPL (OldTpl);
136
137 //
138 // Poll udp out of the net tpl if synchronous call.
139 //
140 if (Instance->Config->IaInfoEvent == NULL) {
141 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
142 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
143 }
144
145 return Instance->UdpSts;
146 }
147
148 return EFI_SUCCESS;
149
150 ON_ERROR:
151
152 gBS->RestoreTPL (OldTpl);
153 return Status;
154 }
155
156 /**
157 Stops the DHCPv6 standard S.A.R.R. process.
158
159 The Stop() function is used to stop the DHCPv6 standard process. After this
160 function is called successfully, the state of Dhcp6 instance is transferred
161 into Dhcp6Init. EFI_DHCP6_PROTOCOL.Configure() needs to be called
162 before DHCPv6 standard process can be started again. This function can be
163 called when the Dhcp6 instance is in any state.
164
165 @param[in] This The pointer to the Dhcp6 protocol.
166
167 @retval EFI_SUCCESS The Dhcp6 instance is now in the Dhcp6Init state.
168 @retval EFI_INVALID_PARAMETER This is NULL.
169
170 **/
171 EFI_STATUS
172 EFIAPI
173 EfiDhcp6Stop (
174 IN EFI_DHCP6_PROTOCOL *This
175 )
176 {
177 EFI_TPL OldTpl;
178 EFI_STATUS Status;
179 EFI_UDP6_PROTOCOL *Udp6;
180 DHCP6_INSTANCE *Instance;
181 DHCP6_SERVICE *Service;
182
183 if (This == NULL) {
184 return EFI_INVALID_PARAMETER;
185 }
186
187 Instance = DHCP6_INSTANCE_FROM_THIS (This);
188 Service = Instance->Service;
189 Udp6 = Service->UdpIo->Protocol.Udp6;
190 Status = EFI_SUCCESS;
191
192 //
193 // The instance hasn't been configured.
194 //
195 if (Instance->Config == NULL) {
196 return Status;
197 }
198
199 ASSERT (Instance->IaCb.Ia != NULL);
200
201 //
202 // No valid REPLY message received yet, cleanup this instance directly.
203 //
204 if ((Instance->IaCb.Ia->State == Dhcp6Init) ||
205 (Instance->IaCb.Ia->State == Dhcp6Selecting) ||
206 (Instance->IaCb.Ia->State == Dhcp6Requesting)
207 )
208 {
209 goto ON_EXIT;
210 }
211
212 //
213 // Release the current ready Ia.
214 //
215 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
216
217 Instance->UdpSts = EFI_ALREADY_STARTED;
218 Status = Dhcp6SendReleaseMsg (Instance, Instance->IaCb.Ia);
219 gBS->RestoreTPL (OldTpl);
220 if (EFI_ERROR (Status)) {
221 goto ON_EXIT;
222 }
223
224 //
225 // Poll udp out of the net tpl if synchronous call.
226 //
227 if (Instance->Config->IaInfoEvent == NULL) {
228 ASSERT (Udp6 != NULL);
229 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
230 Udp6->Poll (Udp6);
231 }
232
233 Status = Instance->UdpSts;
234 }
235
236 ON_EXIT:
237 //
238 // Clean up the session data for the released Ia.
239 //
240 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
241 Dhcp6CleanupSession (Instance, EFI_SUCCESS);
242 gBS->RestoreTPL (OldTpl);
243
244 return Status;
245 }
246
247 /**
248 Returns the current operating mode data for the Dhcp6 instance.
249
250 The GetModeData() function returns the current operating mode and
251 cached data packet for the Dhcp6 instance.
252
253 @param[in] This The pointer to the Dhcp6 protocol.
254 @param[out] Dhcp6ModeData The pointer to the Dhcp6 mode data.
255 @param[out] Dhcp6ConfigData The pointer to the Dhcp6 configure data.
256
257 @retval EFI_SUCCESS The mode data was returned.
258 @retval EFI_INVALID_PARAMETER This is NULL.
259 @retval EFI_ACCESS_DENIED The EFI DHCPv6 Protocol instance was not
260 configured.
261 **/
262 EFI_STATUS
263 EFIAPI
264 EfiDhcp6GetModeData (
265 IN EFI_DHCP6_PROTOCOL *This,
266 OUT EFI_DHCP6_MODE_DATA *Dhcp6ModeData OPTIONAL,
267 OUT EFI_DHCP6_CONFIG_DATA *Dhcp6ConfigData OPTIONAL
268 )
269 {
270 EFI_TPL OldTpl;
271 EFI_DHCP6_IA *Ia;
272 DHCP6_INSTANCE *Instance;
273 DHCP6_SERVICE *Service;
274 UINT32 IaSize;
275 UINT32 IdSize;
276
277 if ((This == NULL) || ((Dhcp6ModeData == NULL) && (Dhcp6ConfigData == NULL))) {
278 return EFI_INVALID_PARAMETER;
279 }
280
281 Instance = DHCP6_INSTANCE_FROM_THIS (This);
282 Service = Instance->Service;
283
284 if ((Instance->Config == NULL) && (Dhcp6ConfigData != NULL)) {
285 return EFI_ACCESS_DENIED;
286 }
287
288 ASSERT (Service->ClientId != NULL);
289
290 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
291
292 //
293 // User needs a copy of instance config data.
294 //
295 if (Dhcp6ConfigData != NULL) {
296 ZeroMem (Dhcp6ConfigData, sizeof (EFI_DHCP6_CONFIG_DATA));
297 //
298 // Duplicate config data, including all reference buffers.
299 //
300 if (EFI_ERROR (Dhcp6CopyConfigData (Dhcp6ConfigData, Instance->Config))) {
301 goto ON_ERROR;
302 }
303 }
304
305 //
306 // User need a copy of instance mode data.
307 //
308 if (Dhcp6ModeData != NULL) {
309 ZeroMem (Dhcp6ModeData, sizeof (EFI_DHCP6_MODE_DATA));
310 //
311 // Duplicate a copy of EFI_DHCP6_DUID for client Id.
312 //
313 IdSize = Service->ClientId->Length + sizeof (Service->ClientId->Length);
314
315 Dhcp6ModeData->ClientId = AllocateZeroPool (IdSize);
316 if (Dhcp6ModeData->ClientId == NULL) {
317 goto ON_ERROR;
318 }
319
320 CopyMem (
321 Dhcp6ModeData->ClientId,
322 Service->ClientId,
323 IdSize
324 );
325
326 Ia = Instance->IaCb.Ia;
327 if (Ia != NULL) {
328 //
329 // Duplicate a copy of EFI_DHCP6_IA for configured Ia.
330 //
331 IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount -1) * sizeof (EFI_DHCP6_IA_ADDRESS);
332
333 Dhcp6ModeData->Ia = AllocateZeroPool (IaSize);
334 if (Dhcp6ModeData->Ia == NULL) {
335 goto ON_ERROR;
336 }
337
338 CopyMem (
339 Dhcp6ModeData->Ia,
340 Ia,
341 IaSize
342 );
343
344 //
345 // Duplicate a copy of reply packet if has.
346 //
347 if (Ia->ReplyPacket != NULL) {
348 Dhcp6ModeData->Ia->ReplyPacket = AllocateZeroPool (Ia->ReplyPacket->Size);
349 if (Dhcp6ModeData->Ia->ReplyPacket == NULL) {
350 goto ON_ERROR;
351 }
352
353 CopyMem (
354 Dhcp6ModeData->Ia->ReplyPacket,
355 Ia->ReplyPacket,
356 Ia->ReplyPacket->Size
357 );
358 }
359 }
360 }
361
362 gBS->RestoreTPL (OldTpl);
363
364 return EFI_SUCCESS;
365
366 ON_ERROR:
367
368 if (Dhcp6ConfigData != NULL) {
369 Dhcp6CleanupConfigData (Dhcp6ConfigData);
370 }
371
372 if (Dhcp6ModeData != NULL) {
373 Dhcp6CleanupModeData (Dhcp6ModeData);
374 }
375
376 gBS->RestoreTPL (OldTpl);
377
378 return EFI_OUT_OF_RESOURCES;
379 }
380
381 /**
382 Initializes, changes, or resets the operational settings for the Dhcp6 instance.
383
384 The Configure() function is used to initialize or clean up the configuration
385 data of the Dhcp6 instance:
386 - When Dhcp6CfgData is not NULL and Configure() is called successfully, the
387 configuration data will be initialized in the Dhcp6 instance, and the state
388 of the configured IA will be transferred into Dhcp6Init.
389 - When Dhcp6CfgData is NULL and Configure() is called successfully, the
390 configuration data will be cleaned up and no IA will be associated with
391 the Dhcp6 instance.
392 To update the configuration data for an Dhcp6 instance, the original data
393 must be cleaned up before setting the new configuration data.
394
395 @param[in] This The pointer to the Dhcp6 protocol
396 @param[in] Dhcp6CfgData The pointer to the EFI_DHCP6_CONFIG_DATA.
397
398 @retval EFI_SUCCESS The Dhcp6 is configured successfully with the
399 Dhcp6Init state, or cleaned up the original
400 configuration setting.
401 @retval EFI_ACCESS_DENIED The Dhcp6 instance was already configured.
402 The Dhcp6 instance has already started the
403 DHCPv6 S.A.R.R when Dhcp6CfgData is NULL.
404 @retval EFI_INVALID_PARAMETER Some of the parameter is invalid.
405 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
406 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
407
408 **/
409 EFI_STATUS
410 EFIAPI
411 EfiDhcp6Configure (
412 IN EFI_DHCP6_PROTOCOL *This,
413 IN EFI_DHCP6_CONFIG_DATA *Dhcp6CfgData OPTIONAL
414 )
415 {
416 EFI_TPL OldTpl;
417 EFI_STATUS Status;
418 LIST_ENTRY *Entry;
419 DHCP6_INSTANCE *Other;
420 DHCP6_INSTANCE *Instance;
421 DHCP6_SERVICE *Service;
422 UINTN Index;
423
424 if (This == NULL) {
425 return EFI_INVALID_PARAMETER;
426 }
427
428 Instance = DHCP6_INSTANCE_FROM_THIS (This);
429 Service = Instance->Service;
430
431 //
432 // Check the parameter of configure data.
433 //
434 if (Dhcp6CfgData != NULL) {
435 if ((Dhcp6CfgData->OptionCount > 0) && (Dhcp6CfgData->OptionList == NULL)) {
436 return EFI_INVALID_PARAMETER;
437 }
438
439 if (Dhcp6CfgData->OptionList != NULL) {
440 for (Index = 0; Index < Dhcp6CfgData->OptionCount; Index++) {
441 if ((Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptClientId) ||
442 (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptRapidCommit) ||
443 (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptReconfigureAccept) ||
444 (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIana) ||
445 (Dhcp6CfgData->OptionList[Index]->OpCode == Dhcp6OptIata)
446 )
447 {
448 return EFI_INVALID_PARAMETER;
449 }
450 }
451 }
452
453 if ((Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_NA) &&
454 (Dhcp6CfgData->IaDescriptor.Type != EFI_DHCP6_IA_TYPE_TA)
455 )
456 {
457 return EFI_INVALID_PARAMETER;
458 }
459
460 if ((Dhcp6CfgData->IaInfoEvent == NULL) && (Dhcp6CfgData->SolicitRetransmission == NULL)) {
461 return EFI_INVALID_PARAMETER;
462 }
463
464 if ((Dhcp6CfgData->SolicitRetransmission != NULL) &&
465 (Dhcp6CfgData->SolicitRetransmission->Mrc == 0) &&
466 (Dhcp6CfgData->SolicitRetransmission->Mrd == 0)
467 )
468 {
469 return EFI_INVALID_PARAMETER;
470 }
471
472 //
473 // Make sure the (IaId, IaType) is unique over all the instances.
474 //
475 NET_LIST_FOR_EACH (Entry, &Service->Child) {
476 Other = NET_LIST_USER_STRUCT (Entry, DHCP6_INSTANCE, Link);
477 if ((Other->IaCb.Ia != NULL) &&
478 (Other->IaCb.Ia->Descriptor.Type == Dhcp6CfgData->IaDescriptor.Type) &&
479 (Other->IaCb.Ia->Descriptor.IaId == Dhcp6CfgData->IaDescriptor.IaId)
480 )
481 {
482 return EFI_INVALID_PARAMETER;
483 }
484 }
485 }
486
487 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
488
489 if (Dhcp6CfgData != NULL) {
490 //
491 // It's not allowed to configure one instance twice without configure null.
492 //
493 if (Instance->Config != NULL) {
494 gBS->RestoreTPL (OldTpl);
495 return EFI_ACCESS_DENIED;
496 }
497
498 //
499 // Duplicate config data including all reference buffers.
500 //
501 Instance->Config = AllocateZeroPool (sizeof (EFI_DHCP6_CONFIG_DATA));
502 if (Instance->Config == NULL) {
503 gBS->RestoreTPL (OldTpl);
504 return EFI_OUT_OF_RESOURCES;
505 }
506
507 Status = Dhcp6CopyConfigData (Instance->Config, Dhcp6CfgData);
508 if (EFI_ERROR (Status)) {
509 FreePool (Instance->Config);
510 gBS->RestoreTPL (OldTpl);
511 return EFI_OUT_OF_RESOURCES;
512 }
513
514 //
515 // Initialize the Ia descriptor from the config data, and leave the other
516 // fields of the Ia as default value 0.
517 //
518 Instance->IaCb.Ia = AllocateZeroPool (sizeof (EFI_DHCP6_IA));
519 if (Instance->IaCb.Ia == NULL) {
520 Dhcp6CleanupConfigData (Instance->Config);
521 FreePool (Instance->Config);
522 gBS->RestoreTPL (OldTpl);
523 return EFI_OUT_OF_RESOURCES;
524 }
525
526 CopyMem (
527 &Instance->IaCb.Ia->Descriptor,
528 &Dhcp6CfgData->IaDescriptor,
529 sizeof (EFI_DHCP6_IA_DESCRIPTOR)
530 );
531 } else {
532 if (Instance->Config == NULL) {
533 ASSERT (Instance->IaCb.Ia == NULL);
534 gBS->RestoreTPL (OldTpl);
535 return EFI_SUCCESS;
536 }
537
538 //
539 // It's not allowed to configure a started instance as null.
540 //
541 if (Instance->IaCb.Ia->State != Dhcp6Init) {
542 gBS->RestoreTPL (OldTpl);
543 return EFI_ACCESS_DENIED;
544 }
545
546 Dhcp6CleanupConfigData (Instance->Config);
547 FreePool (Instance->Config);
548 Instance->Config = NULL;
549
550 FreePool (Instance->IaCb.Ia);
551 Instance->IaCb.Ia = NULL;
552 }
553
554 gBS->RestoreTPL (OldTpl);
555
556 return EFI_SUCCESS;
557 }
558
559 /**
560 Request configuration information without the assignment of any
561 Ia addresses of the client.
562
563 The InfoRequest() function is used to request configuration information
564 without the assignment of any IPv6 address of the client. The client sends
565 out an Information Request packet to obtain the required configuration
566 information, and DHCPv6 server responds with a Reply packet containing
567 the information for the client. The received Reply packet will be passed
568 to the user by ReplyCallback function. If the user returns EFI_NOT_READY from
569 ReplyCallback, the Dhcp6 instance will continue to receive other Reply
570 packets unless timeout according to the Retransmission parameter.
571 Otherwise, the Information Request exchange process will be finished
572 successfully if user returns EFI_SUCCESS from ReplyCallback.
573
574 @param[in] This The pointer to the Dhcp6 protocol.
575 @param[in] SendClientId If TRUE, the DHCPv6 protocol instance will build Client
576 Identifier option and include it into Information Request
577 packet. Otherwise, Client Identifier option will not be included.
578 @param[in] OptionRequest The pointer to the buffer of option request options.
579 @param[in] OptionCount The option number in the OptionList.
580 @param[in] OptionList The list of appended options.
581 @param[in] Retransmission The pointer to the retransmission of the message.
582 @param[in] TimeoutEvent The event of timeout.
583 @param[in] ReplyCallback The callback function when the reply was received.
584 @param[in] CallbackContext The pointer to the parameter passed to the callback.
585
586 @retval EFI_SUCCESS The DHCPv6 information request exchange process
587 completed when TimeoutEvent is NULL. Information
588 Request packet has been sent to DHCPv6 server when
589 TimeoutEvent is not NULL.
590 @retval EFI_NO_RESPONSE The DHCPv6 information request exchange process failed
591 because of no response, or not all requested-options
592 are responded by DHCPv6 servers when Timeout happened.
593 @retval EFI_ABORTED The DHCPv6 information request exchange process was aborted
594 by user.
595 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
596 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
597 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
598
599 **/
600 EFI_STATUS
601 EFIAPI
602 EfiDhcp6InfoRequest (
603 IN EFI_DHCP6_PROTOCOL *This,
604 IN BOOLEAN SendClientId,
605 IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
606 IN UINT32 OptionCount,
607 IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
608 IN EFI_DHCP6_RETRANSMISSION *Retransmission,
609 IN EFI_EVENT TimeoutEvent OPTIONAL,
610 IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
611 IN VOID *CallbackContext OPTIONAL
612 )
613 {
614 EFI_STATUS Status;
615 DHCP6_INSTANCE *Instance;
616 DHCP6_SERVICE *Service;
617 UINTN Index;
618 EFI_EVENT Timer;
619 EFI_STATUS TimerStatus;
620 UINTN GetMappingTimeOut;
621
622 if ((This == NULL) || (OptionRequest == NULL) || (Retransmission == NULL) || (ReplyCallback == NULL)) {
623 return EFI_INVALID_PARAMETER;
624 }
625
626 if ((Retransmission != NULL) && (Retransmission->Mrc == 0) && (Retransmission->Mrd == 0)) {
627 return EFI_INVALID_PARAMETER;
628 }
629
630 if ((OptionCount > 0) && (OptionList == NULL)) {
631 return EFI_INVALID_PARAMETER;
632 }
633
634 if (OptionList != NULL) {
635 for (Index = 0; Index < OptionCount; Index++) {
636 if ((OptionList[Index]->OpCode == Dhcp6OptClientId) || (OptionList[Index]->OpCode == Dhcp6OptRequestOption)) {
637 return EFI_INVALID_PARAMETER;
638 }
639 }
640 }
641
642 Instance = DHCP6_INSTANCE_FROM_THIS (This);
643 Service = Instance->Service;
644
645 Status = Dhcp6StartInfoRequest (
646 Instance,
647 SendClientId,
648 OptionRequest,
649 OptionCount,
650 OptionList,
651 Retransmission,
652 TimeoutEvent,
653 ReplyCallback,
654 CallbackContext
655 );
656 if (Status == EFI_NO_MAPPING) {
657 //
658 // The link local address is not ready, wait for some time and restart
659 // the DHCP6 information request process.
660 //
661 Status = Dhcp6GetMappingTimeOut (Service->Ip6Cfg, &GetMappingTimeOut);
662 if (EFI_ERROR (Status)) {
663 return Status;
664 }
665
666 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
667 if (EFI_ERROR (Status)) {
668 return Status;
669 }
670
671 //
672 // Start the timer, wait for link local address DAD to finish.
673 //
674 Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
675 if (EFI_ERROR (Status)) {
676 gBS->CloseEvent (Timer);
677 return Status;
678 }
679
680 do {
681 TimerStatus = gBS->CheckEvent (Timer);
682 if (!EFI_ERROR (TimerStatus)) {
683 Status = Dhcp6StartInfoRequest (
684 Instance,
685 SendClientId,
686 OptionRequest,
687 OptionCount,
688 OptionList,
689 Retransmission,
690 TimeoutEvent,
691 ReplyCallback,
692 CallbackContext
693 );
694 }
695 } while (TimerStatus == EFI_NOT_READY);
696
697 gBS->CloseEvent (Timer);
698 }
699
700 if (EFI_ERROR (Status)) {
701 return Status;
702 }
703
704 //
705 // Poll udp out of the net tpl if synchronous call.
706 //
707 if (TimeoutEvent == NULL) {
708 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
709 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
710 }
711
712 return Instance->UdpSts;
713 }
714
715 return EFI_SUCCESS;
716 }
717
718 /**
719 Manually extend the valid and preferred lifetimes for the IPv6 addresses
720 of the configured IA and update other configuration parameters by sending a
721 Renew or Rebind packet.
722
723 The RenewRebind() function is used to manually extend the valid and preferred
724 lifetimes for the IPv6 addresses of the configured IA, and update other
725 configuration parameters by sending Renew or Rebind packet.
726 - When RebindRequest is FALSE and the state of the configured IA is Dhcp6Bound,
727 it sends Renew packet to the previously DHCPv6 server and transfer the
728 state of the configured IA to Dhcp6Renewing. If valid Reply packet received,
729 the state transfers to Dhcp6Bound and the valid and preferred timer restarts.
730 If fails, the state transfers to Dhcp6Bound, but the timer continues.
731 - When RebindRequest is TRUE and the state of the configured IA is Dhcp6Bound,
732 it will send a Rebind packet. If valid Reply packet is received, the state transfers
733 to Dhcp6Bound and the valid and preferred timer restarts. If it fails, the state
734 transfers to Dhcp6Init, and the IA can't be used.
735
736 @param[in] This The pointer to the Dhcp6 protocol.
737 @param[in] RebindRequest If TRUE, Rebind packet will be sent and enter Dhcp6Rebinding state.
738 Otherwise, Renew packet will be sent and enter Dhcp6Renewing state.
739
740 @retval EFI_SUCCESS The DHCPv6 renew/rebind exchange process has
741 completed and at least one IPv6 address of the
742 configured IA has been bound again when
743 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL.
744 The EFI DHCPv6 Protocol instance has sent Renew
745 or Rebind packet when
746 EFI_DHCP6_CONFIG_DATA.IaInfoEvent is not NULL.
747 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
748 state of the configured IA is not in Dhcp6Bound.
749 @retval EFI_ALREADY_STARTED The state of the configured IA has already entered
750 Dhcp6Renewing when RebindRequest is FALSE.
751 The state of the configured IA has already entered
752 Dhcp6Rebinding when RebindRequest is TRUE.
753 @retval EFI_ABORTED The DHCPv6 renew/rebind exchange process aborted
754 by the user.
755 @retval EFI_NO_RESPONSE The DHCPv6 renew/rebind exchange process failed
756 because of no response.
757 @retval EFI_NO_MAPPING No IPv6 address has been bound to the configured
758 IA after the DHCPv6 renew/rebind exchange process.
759 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
760 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
761
762 **/
763 EFI_STATUS
764 EFIAPI
765 EfiDhcp6RenewRebind (
766 IN EFI_DHCP6_PROTOCOL *This,
767 IN BOOLEAN RebindRequest
768 )
769 {
770 EFI_STATUS Status;
771 EFI_TPL OldTpl;
772 DHCP6_INSTANCE *Instance;
773 DHCP6_SERVICE *Service;
774
775 if (This == NULL) {
776 return EFI_INVALID_PARAMETER;
777 }
778
779 Instance = DHCP6_INSTANCE_FROM_THIS (This);
780 Service = Instance->Service;
781
782 //
783 // The instance hasn't been configured.
784 //
785 if (Instance->Config == NULL) {
786 return EFI_ACCESS_DENIED;
787 }
788
789 ASSERT (Instance->IaCb.Ia != NULL);
790
791 //
792 // The instance has already entered renewing or rebinding state.
793 //
794 if (((Instance->IaCb.Ia->State == Dhcp6Rebinding) && RebindRequest) ||
795 ((Instance->IaCb.Ia->State == Dhcp6Renewing) && !RebindRequest)
796 )
797 {
798 return EFI_ALREADY_STARTED;
799 }
800
801 if (Instance->IaCb.Ia->State != Dhcp6Bound) {
802 return EFI_ACCESS_DENIED;
803 }
804
805 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
806 Instance->UdpSts = EFI_ALREADY_STARTED;
807
808 //
809 // Send renew/rebind message to start exchange process.
810 //
811 Status = Dhcp6SendRenewRebindMsg (Instance, RebindRequest);
812
813 if (EFI_ERROR (Status)) {
814 goto ON_ERROR;
815 }
816
817 //
818 // Register receive callback for the stateful exchange process.
819 //
820 Status = UdpIoRecvDatagram (
821 Service->UdpIo,
822 Dhcp6ReceivePacket,
823 Service,
824 0
825 );
826
827 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
828 goto ON_ERROR;
829 }
830
831 gBS->RestoreTPL (OldTpl);
832
833 //
834 // Poll udp out of the net tpl if synchronous call.
835 //
836 if (Instance->Config->IaInfoEvent == NULL) {
837 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
838 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
839 }
840
841 return Instance->UdpSts;
842 }
843
844 return EFI_SUCCESS;
845
846 ON_ERROR:
847
848 gBS->RestoreTPL (OldTpl);
849 return Status;
850 }
851
852 /**
853 Inform that one or more addresses assigned by a server are already
854 in use by another node.
855
856 The Decline() function is used to manually decline the assignment of
857 IPv6 addresses, which have been already used by another node. If all
858 IPv6 addresses of the configured IA are declined through this function,
859 the state of the IA will switch through Dhcp6Declining to Dhcp6Init.
860 Otherwise, the state of the IA will restore to Dhcp6Bound after the
861 declining process. The Decline() can only be called when the IA is in
862 Dhcp6Bound state. If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL,
863 this function is a blocking operation. It will return after the
864 declining process finishes, or aborted by user.
865
866 @param[in] This The pointer to EFI_DHCP6_PROTOCOL.
867 @param[in] AddressCount The number of declining addresses.
868 @param[in] Addresses The pointer to the buffer stored the declining
869 addresses.
870
871 @retval EFI_SUCCESS The DHCPv6 decline exchange process completed
872 when EFI_DHCP6_CONFIG_DATA.IaInfoEvent was NULL.
873 The Dhcp6 instance sent Decline packet when
874 EFI_DHCP6_CONFIG_DATA.IaInfoEvent was not NULL.
875 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
876 state of the configured IA is not in Dhcp6Bound.
877 @retval EFI_ABORTED The DHCPv6 decline exchange process aborted by user.
878 @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
879 the configured IA for this instance.
880 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
881 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
882
883 **/
884 EFI_STATUS
885 EFIAPI
886 EfiDhcp6Decline (
887 IN EFI_DHCP6_PROTOCOL *This,
888 IN UINT32 AddressCount,
889 IN EFI_IPv6_ADDRESS *Addresses
890 )
891 {
892 EFI_STATUS Status;
893 EFI_TPL OldTpl;
894 EFI_DHCP6_IA *DecIa;
895 DHCP6_INSTANCE *Instance;
896 DHCP6_SERVICE *Service;
897
898 if ((This == NULL) || (AddressCount == 0) || (Addresses == NULL)) {
899 return EFI_INVALID_PARAMETER;
900 }
901
902 Instance = DHCP6_INSTANCE_FROM_THIS (This);
903 Service = Instance->Service;
904
905 //
906 // The instance hasn't been configured.
907 //
908 if (Instance->Config == NULL) {
909 return EFI_ACCESS_DENIED;
910 }
911
912 ASSERT (Instance->IaCb.Ia != NULL);
913
914 if (Instance->IaCb.Ia->State != Dhcp6Bound) {
915 return EFI_ACCESS_DENIED;
916 }
917
918 //
919 // Check whether all the declined addresses belongs to the configured Ia.
920 //
921 Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
922
923 if (EFI_ERROR (Status)) {
924 return Status;
925 }
926
927 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
928 Instance->UdpSts = EFI_ALREADY_STARTED;
929
930 //
931 // Deprive of all the declined addresses from the configured Ia, and create a
932 // DeclineIa used to create decline message.
933 //
934 DecIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
935
936 if (DecIa == NULL) {
937 Status = EFI_OUT_OF_RESOURCES;
938 goto ON_ERROR;
939 }
940
941 //
942 // Send the decline message to start exchange process.
943 //
944 Status = Dhcp6SendDeclineMsg (Instance, DecIa);
945
946 if (EFI_ERROR (Status)) {
947 goto ON_ERROR;
948 }
949
950 //
951 // Register receive callback for the stateful exchange process.
952 //
953 Status = UdpIoRecvDatagram (
954 Service->UdpIo,
955 Dhcp6ReceivePacket,
956 Service,
957 0
958 );
959
960 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
961 goto ON_ERROR;
962 }
963
964 FreePool (DecIa);
965 gBS->RestoreTPL (OldTpl);
966
967 //
968 // Poll udp out of the net tpl if synchronous call.
969 //
970 if (Instance->Config->IaInfoEvent == NULL) {
971 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
972 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
973 }
974
975 return Instance->UdpSts;
976 }
977
978 return EFI_SUCCESS;
979
980 ON_ERROR:
981
982 if (DecIa != NULL) {
983 FreePool (DecIa);
984 }
985
986 gBS->RestoreTPL (OldTpl);
987
988 return Status;
989 }
990
991 /**
992 Release one or more addresses associated with the configured Ia
993 for current instance.
994
995 The Release() function is used to manually release one or more
996 IPv6 addresses. If AddressCount is zero, it will release all IPv6
997 addresses of the configured IA. If all IPv6 addresses of the IA are
998 released through this function, the state of the IA will switch
999 through Dhcp6Releasing to Dhcp6Init, otherwise, the state of the
1000 IA will restore to Dhcp6Bound after the releasing process.
1001 The Release() can only be called when the IA is in Dhcp6Bound state.
1002 If the EFI_DHCP6_CONFIG_DATA.IaInfoEvent is NULL, the function is
1003 a blocking operation. It will return after the releasing process
1004 finishes, or is aborted by user.
1005
1006 @param[in] This The pointer to the Dhcp6 protocol.
1007 @param[in] AddressCount The number of releasing addresses.
1008 @param[in] Addresses The pointer to the buffer stored the releasing
1009 addresses.
1010
1011 @retval EFI_SUCCESS The DHCPv6 release exchange process
1012 completed when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
1013 was NULL. The Dhcp6 instance was sent Release
1014 packet when EFI_DHCP6_CONFIG_DATA.IaInfoEvent
1015 was not NULL.
1016 @retval EFI_ACCESS_DENIED The Dhcp6 instance hasn't been configured, or the
1017 state of the configured IA is not in Dhcp6Bound.
1018 @retval EFI_ABORTED The DHCPv6 release exchange process aborted by user.
1019 @retval EFI_NOT_FOUND Any specified IPv6 address is not correlated with
1020 the configured IA for this instance.
1021 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1022 @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
1023
1024 **/
1025 EFI_STATUS
1026 EFIAPI
1027 EfiDhcp6Release (
1028 IN EFI_DHCP6_PROTOCOL *This,
1029 IN UINT32 AddressCount,
1030 IN EFI_IPv6_ADDRESS *Addresses
1031 )
1032 {
1033 EFI_STATUS Status;
1034 EFI_TPL OldTpl;
1035 EFI_DHCP6_IA *RelIa;
1036 DHCP6_INSTANCE *Instance;
1037 DHCP6_SERVICE *Service;
1038
1039 if ((This == NULL) || ((AddressCount != 0) && (Addresses == NULL))) {
1040 return EFI_INVALID_PARAMETER;
1041 }
1042
1043 Instance = DHCP6_INSTANCE_FROM_THIS (This);
1044 Service = Instance->Service;
1045
1046 //
1047 // The instance hasn't been configured.
1048 //
1049 if (Instance->Config == NULL) {
1050 return EFI_ACCESS_DENIED;
1051 }
1052
1053 ASSERT (Instance->IaCb.Ia != NULL);
1054
1055 if (Instance->IaCb.Ia->State != Dhcp6Bound) {
1056 return EFI_ACCESS_DENIED;
1057 }
1058
1059 //
1060 // Check whether all the released addresses belongs to the configured Ia.
1061 //
1062 Status = Dhcp6CheckAddress (Instance->IaCb.Ia, AddressCount, Addresses);
1063
1064 if (EFI_ERROR (Status)) {
1065 return Status;
1066 }
1067
1068 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1069 Instance->UdpSts = EFI_ALREADY_STARTED;
1070
1071 //
1072 // Deprive of all the released addresses from the configured Ia, and create a
1073 // ReleaseIa used to create release message.
1074 //
1075 RelIa = Dhcp6DepriveAddress (Instance->IaCb.Ia, AddressCount, Addresses);
1076
1077 if (RelIa == NULL) {
1078 Status = EFI_OUT_OF_RESOURCES;
1079 goto ON_ERROR;
1080 }
1081
1082 //
1083 // Send the release message to start exchange process.
1084 //
1085 Status = Dhcp6SendReleaseMsg (Instance, RelIa);
1086
1087 if (EFI_ERROR (Status)) {
1088 goto ON_ERROR;
1089 }
1090
1091 //
1092 // Register receive callback for the stateful exchange process.
1093 //
1094 Status = UdpIoRecvDatagram (
1095 Service->UdpIo,
1096 Dhcp6ReceivePacket,
1097 Service,
1098 0
1099 );
1100
1101 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
1102 goto ON_ERROR;
1103 }
1104
1105 FreePool (RelIa);
1106 gBS->RestoreTPL (OldTpl);
1107
1108 //
1109 // Poll udp out of the net tpl if synchronous call.
1110 //
1111 if (Instance->Config->IaInfoEvent == NULL) {
1112 while (Instance->UdpSts == EFI_ALREADY_STARTED) {
1113 Service->UdpIo->Protocol.Udp6->Poll (Service->UdpIo->Protocol.Udp6);
1114 }
1115
1116 return Instance->UdpSts;
1117 }
1118
1119 return EFI_SUCCESS;
1120
1121 ON_ERROR:
1122
1123 if (RelIa != NULL) {
1124 FreePool (RelIa);
1125 }
1126
1127 gBS->RestoreTPL (OldTpl);
1128
1129 return Status;
1130 }
1131
1132 /**
1133 Parse the option data in the Dhcp6 packet.
1134
1135 The Parse() function is used to retrieve the option list in the DHCPv6 packet.
1136
1137 @param[in] This The pointer to the Dhcp6 protocol.
1138 @param[in] Packet The pointer to the Dhcp6 packet.
1139 @param[in, out] OptionCount The number of option in the packet.
1140 @param[out] PacketOptionList The array of pointers to each option in the packet.
1141
1142 @retval EFI_SUCCESS The packet was successfully parsed.
1143 @retval EFI_INVALID_PARAMETER Some parameter is NULL.
1144 @retval EFI_BUFFER_TOO_SMALL *OptionCount is smaller than the number of options
1145 that were found in the Packet.
1146
1147 **/
1148 EFI_STATUS
1149 EFIAPI
1150 EfiDhcp6Parse (
1151 IN EFI_DHCP6_PROTOCOL *This,
1152 IN EFI_DHCP6_PACKET *Packet,
1153 IN OUT UINT32 *OptionCount,
1154 OUT EFI_DHCP6_PACKET_OPTION *PacketOptionList[] OPTIONAL
1155 )
1156 {
1157 UINT32 OptCnt;
1158 UINT32 OptLen;
1159 UINT16 DataLen;
1160 UINT8 *Start;
1161 UINT8 *End;
1162
1163 if ((This == NULL) || (Packet == NULL) || (OptionCount == NULL)) {
1164 return EFI_INVALID_PARAMETER;
1165 }
1166
1167 if ((*OptionCount != 0) && (PacketOptionList == NULL)) {
1168 return EFI_INVALID_PARAMETER;
1169 }
1170
1171 if ((Packet->Length > Packet->Size) || (Packet->Length < sizeof (EFI_DHCP6_HEADER))) {
1172 return EFI_INVALID_PARAMETER;
1173 }
1174
1175 //
1176 // The format of Dhcp6 option:
1177 //
1178 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1179 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1180 // | option-code | option-len (option data) |
1181 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1182 // | option-data |
1183 // | (option-len octets) |
1184 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1185 //
1186
1187 OptCnt = 0;
1188 OptLen = Packet->Length - sizeof (EFI_DHCP6_HEADER);
1189 Start = Packet->Dhcp6.Option;
1190 End = Start + OptLen;
1191
1192 //
1193 // Calculate the number of option in the packet.
1194 //
1195 while (Start < End) {
1196 DataLen = ((EFI_DHCP6_PACKET_OPTION *)Start)->OpLen;
1197 Start += (NTOHS (DataLen) + 4);
1198 OptCnt++;
1199 }
1200
1201 //
1202 // It will return buffer too small if pass-in option count is smaller than the
1203 // actual count of options in the packet.
1204 //
1205 if (OptCnt > *OptionCount) {
1206 *OptionCount = OptCnt;
1207 return EFI_BUFFER_TOO_SMALL;
1208 }
1209
1210 ZeroMem (
1211 PacketOptionList,
1212 (*OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *))
1213 );
1214
1215 OptCnt = 0;
1216 Start = Packet->Dhcp6.Option;
1217
1218 while (Start < End) {
1219 PacketOptionList[OptCnt] = (EFI_DHCP6_PACKET_OPTION *)Start;
1220 DataLen = ((EFI_DHCP6_PACKET_OPTION *)Start)->OpLen;
1221 Start += (NTOHS (DataLen) + 4);
1222 OptCnt++;
1223 }
1224
1225 return EFI_SUCCESS;
1226 }