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