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