blob: b4e00079260a2924951b68717290805e3ac22f7b [file] [log] [blame]
Vishal Bhoj82c80712015-12-15 21:13:33 +05301/** @file
2 Dhcp6 internal functions implementation.
3
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php.
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15**/
16
17#include "Dhcp6Impl.h"
18
19
20/**
21 Enqueue the packet into the retry list in case of timeout.
22
23 @param[in] Instance The pointer to the Dhcp6 instance.
24 @param[in] Packet The pointer to the Dhcp6 packet to retry.
25 @param[in] Elapsed The pointer to the elapsed time value in the packet.
26 @param[in] RetryCtl The pointer to the transmission control of the packet.
27 This parameter is optional and may be NULL.
28
29 @retval EFI_SUCCESS Successfully enqueued the packet into the retry list according
30 to its message type.
31 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
32 @retval EFI_DEVICE_ERROR An unexpected message type.
33
34**/
35EFI_STATUS
36Dhcp6EnqueueRetry (
37 IN DHCP6_INSTANCE *Instance,
38 IN EFI_DHCP6_PACKET *Packet,
39 IN UINT16 *Elapsed,
40 IN EFI_DHCP6_RETRANSMISSION *RetryCtl OPTIONAL
41 )
42{
43 DHCP6_TX_CB *TxCb;
44 DHCP6_IA_CB *IaCb;
45
46 ASSERT (Packet != NULL);
47
48 IaCb = &Instance->IaCb;
49 TxCb = AllocateZeroPool (sizeof (DHCP6_TX_CB));
50
51 if (TxCb == NULL) {
52 return EFI_OUT_OF_RESOURCES;
53 }
54
55 //
56 // Save tx packet pointer, and it will be destroyed when reply received.
57 //
58 TxCb->TxPacket = Packet;
59 TxCb->Xid = Packet->Dhcp6.Header.TransactionId;
60
61 //
62 // Save pointer to elapsed-time value so we can update it on retransmits.
63 //
64 TxCb->Elapsed = Elapsed;
65
66 //
67 // Calculate the retransmission according to the the message type.
68 //
69 switch (Packet->Dhcp6.Header.MessageType) {
70 case Dhcp6MsgSolicit:
71 //
72 // Calculate the retransmission threshold value for solicit packet.
73 // Use the default value by rfc-3315 if user doesn't configure.
74 //
75 if (RetryCtl == NULL) {
76 TxCb->RetryCtl.Irt = DHCP6_SOL_IRT;
77 TxCb->RetryCtl.Mrc = DHCP6_SOL_MRC;
78 TxCb->RetryCtl.Mrt = DHCP6_SOL_MRT;
79 TxCb->RetryCtl.Mrd = DHCP6_SOL_MRD;
80 } else {
81 TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_SOL_IRT;
82 TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_SOL_MRC;
83 TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_SOL_MRT;
84 TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_SOL_MRD;
85 }
86
87 TxCb->RetryExp = Dhcp6CalculateExpireTime (
88 TxCb->RetryCtl.Irt,
89 TRUE,
90 FALSE
91 );
92 break;
93
94 case Dhcp6MsgRequest:
95 //
96 // Calculate the retransmission threshold value for request packet.
97 //
98 TxCb->RetryCtl.Irt = DHCP6_REQ_IRT;
99 TxCb->RetryCtl.Mrc = DHCP6_REQ_MRC;
100 TxCb->RetryCtl.Mrt = DHCP6_REQ_MRT;
101 TxCb->RetryCtl.Mrd = DHCP6_REQ_MRD;
102 TxCb->RetryExp = Dhcp6CalculateExpireTime (
103 TxCb->RetryCtl.Irt,
104 TRUE,
105 TRUE
106 );
107 break;
108
109 case Dhcp6MsgConfirm:
110 //
111 // Calculate the retransmission threshold value for confirm packet.
112 //
113 TxCb->RetryCtl.Irt = DHCP6_CNF_IRT;
114 TxCb->RetryCtl.Mrc = DHCP6_CNF_MRC;
115 TxCb->RetryCtl.Mrt = DHCP6_CNF_MRT;
116 TxCb->RetryCtl.Mrd = DHCP6_CNF_MRD;
117 TxCb->RetryExp = Dhcp6CalculateExpireTime (
118 TxCb->RetryCtl.Irt,
119 TRUE,
120 TRUE
121 );
122 break;
123
124 case Dhcp6MsgRenew:
125 //
126 // Calculate the retransmission threshold value for renew packet.
127 //
128 TxCb->RetryCtl.Irt = DHCP6_REB_IRT;
129 TxCb->RetryCtl.Mrc = DHCP6_REB_MRC;
130 TxCb->RetryCtl.Mrt = DHCP6_REB_MRT;
131 TxCb->RetryCtl.Mrd = IaCb->T2 - IaCb->T1;
132 TxCb->RetryExp = Dhcp6CalculateExpireTime (
133 TxCb->RetryCtl.Irt,
134 TRUE,
135 TRUE
136 );
137 break;
138
139 case Dhcp6MsgRebind:
140 //
141 // Calculate the retransmission threshold value for rebind packet.
142 //
143 TxCb->RetryCtl.Irt = DHCP6_REN_IRT;
144 TxCb->RetryCtl.Mrc = DHCP6_REN_MRC;
145 TxCb->RetryCtl.Mrt = DHCP6_REN_MRT;
146 TxCb->RetryCtl.Mrd = IaCb->AllExpireTime - IaCb->T2;
147 TxCb->RetryExp = Dhcp6CalculateExpireTime (
148 TxCb->RetryCtl.Irt,
149 TRUE,
150 TRUE
151 );
152 break;
153
154 case Dhcp6MsgDecline:
155 //
156 // Calculate the retransmission threshold value for decline packet.
157 //
158 TxCb->RetryCtl.Irt = DHCP6_DEC_IRT;
159 TxCb->RetryCtl.Mrc = DHCP6_DEC_MRC;
160 TxCb->RetryCtl.Mrt = DHCP6_DEC_MRT;
161 TxCb->RetryCtl.Mrd = DHCP6_DEC_MRD;
162 TxCb->RetryExp = Dhcp6CalculateExpireTime (
163 TxCb->RetryCtl.Irt,
164 TRUE,
165 TRUE
166 );
167 break;
168
169 case Dhcp6MsgRelease:
170 //
171 // Calculate the retransmission threshold value for release packet.
172 //
173 TxCb->RetryCtl.Irt = DHCP6_REL_IRT;
174 TxCb->RetryCtl.Mrc = DHCP6_REL_MRC;
175 TxCb->RetryCtl.Mrt = DHCP6_REL_MRT;
176 TxCb->RetryCtl.Mrd = DHCP6_REL_MRD;
177 TxCb->RetryExp = Dhcp6CalculateExpireTime (
178 TxCb->RetryCtl.Irt,
179 TRUE,
180 TRUE
181 );
182 break;
183
184 case Dhcp6MsgInfoRequest:
185 //
186 // Calculate the retransmission threshold value for info-request packet.
187 // Use the default value by rfc-3315 if user doesn't configure.
188 //
189 if (RetryCtl == NULL) {
190 TxCb->RetryCtl.Irt = DHCP6_INF_IRT;
191 TxCb->RetryCtl.Mrc = DHCP6_INF_MRC;
192 TxCb->RetryCtl.Mrt = DHCP6_INF_MRT;
193 TxCb->RetryCtl.Mrd = DHCP6_INF_MRD;
194 } else {
195 TxCb->RetryCtl.Irt = (RetryCtl->Irt != 0) ? RetryCtl->Irt : DHCP6_INF_IRT;
196 TxCb->RetryCtl.Mrc = (RetryCtl->Mrc != 0) ? RetryCtl->Mrc : DHCP6_INF_MRC;
197 TxCb->RetryCtl.Mrt = (RetryCtl->Mrt != 0) ? RetryCtl->Mrt : DHCP6_INF_MRT;
198 TxCb->RetryCtl.Mrd = (RetryCtl->Mrd != 0) ? RetryCtl->Mrd : DHCP6_INF_MRD;
199 }
200
201 TxCb->RetryExp = Dhcp6CalculateExpireTime (
202 TxCb->RetryCtl.Irt,
203 TRUE,
204 TRUE
205 );
206 break;
207
208 default:
209 //
210 // Unexpected message type.
211 //
212 return EFI_DEVICE_ERROR;
213 }
214
215 //
216 // Insert into the retransmit list of the instance.
217 //
218 InsertTailList (&Instance->TxList, &TxCb->Link);
219
220 return EFI_SUCCESS;
221}
222
223
224/**
225 Dequeue the packet from retry list if reply received or timeout at last.
226
227 @param[in] Instance The pointer to the Dhcp6 instance.
228 @param[in] PacketXid The packet transaction id to match.
229 @param[in] NeedSignal If TRUE, then an timeout event need be signaled when it is existed.
230 Otherwise, this parameter is ignored.
231
232 @retval EFI_SUCCESS Successfully dequeued the packet into retry list .
233 @retval EFI_NOT_FOUND There is no xid matched in retry list.
234
235**/
236EFI_STATUS
237Dhcp6DequeueRetry (
238 IN DHCP6_INSTANCE *Instance,
239 IN UINT32 PacketXid,
240 IN BOOLEAN NeedSignal
241 )
242{
243 LIST_ENTRY *Entry;
244 LIST_ENTRY *NextEntry;
245 DHCP6_TX_CB *TxCb;
246 DHCP6_INF_CB *InfCb;
247
248 //
249 // Seek the retransmit node in the retransmit list by packet xid.
250 //
251 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
252
253 TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
254 ASSERT(TxCb->TxPacket);
255
256 if (TxCb->Xid == PacketXid) {
257
258 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
259
260 //
261 // Seek the info-request node in the info-request list by packet xid.
262 //
263 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
264
265 InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
266
267 if (InfCb->Xid == PacketXid) {
268 //
269 // Remove the info-request node, and signal the event if timeout.
270 //
271 if (InfCb->TimeoutEvent != NULL && NeedSignal) {
272 gBS->SignalEvent (InfCb->TimeoutEvent);
273 }
274
275 RemoveEntryList (&InfCb->Link);
276 FreePool (InfCb);
277 }
278 }
279 }
280 //
281 // Remove the retransmit node.
282 //
283 RemoveEntryList (&TxCb->Link);
284 ASSERT(TxCb->TxPacket);
285 FreePool (TxCb->TxPacket);
286 FreePool (TxCb);
287 return EFI_SUCCESS;
288 }
289 }
290
291 return EFI_NOT_FOUND;
292}
293
294
295/**
296 Clean up the specific nodes in the retry list.
297
298 @param[in] Instance The pointer to the Dhcp6 instance.
299 @param[in] Scope The scope of cleanup nodes.
300
301**/
302VOID
303Dhcp6CleanupRetry (
304 IN DHCP6_INSTANCE *Instance,
305 IN UINT32 Scope
306 )
307{
308 LIST_ENTRY *Entry;
309 LIST_ENTRY *NextEntry;
310 DHCP6_TX_CB *TxCb;
311 DHCP6_INF_CB *InfCb;
312
313 //
314 // Clean up all the stateful messages from the retransmit list.
315 //
316 if (Scope == DHCP6_PACKET_STATEFUL || Scope == DHCP6_PACKET_ALL) {
317
318 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
319
320 TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
321 ASSERT(TxCb->TxPacket);
322
323 if (TxCb->TxPacket->Dhcp6.Header.MessageType != Dhcp6MsgInfoRequest) {
324 RemoveEntryList (&TxCb->Link);
325 FreePool (TxCb->TxPacket);
326 FreePool (TxCb);
327 }
328 }
329 }
330
331 //
332 // Clean up all the stateless messages from the retransmit list.
333 //
334 if (Scope == DHCP6_PACKET_STATELESS || Scope == DHCP6_PACKET_ALL) {
335
336 //
337 // Clean up all the retransmit list for stateless messages.
338 //
339 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
340
341 TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
342 ASSERT(TxCb->TxPacket);
343
344 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
345 RemoveEntryList (&TxCb->Link);
346 FreePool (TxCb->TxPacket);
347 FreePool (TxCb);
348 }
349 }
350
351 //
352 // Clean up all the info-request messages list.
353 //
354 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->InfList) {
355
356 InfCb = NET_LIST_USER_STRUCT (Entry, DHCP6_INF_CB, Link);
357
358 if (InfCb->TimeoutEvent != NULL) {
359 gBS->SignalEvent (InfCb->TimeoutEvent);
360 }
361 RemoveEntryList (&InfCb->Link);
362 FreePool (InfCb);
363 }
364 }
365}
366
367/**
368 Check whether the TxCb is still a valid control block in the instance's retry list.
369
370 @param[in] Instance The pointer to DHCP6_INSTANCE.
371 @param[in] TxCb The control block for a transmitted message.
372
373 @retval TRUE The control block is in Instance's retry list.
374 @retval FALSE The control block is NOT in Instance's retry list.
375
376**/
377BOOLEAN
378Dhcp6IsValidTxCb (
379 IN DHCP6_INSTANCE *Instance,
380 IN DHCP6_TX_CB *TxCb
381 )
382{
383 LIST_ENTRY *Entry;
384
385 NET_LIST_FOR_EACH (Entry, &Instance->TxList) {
386 if (TxCb == NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link)) {
387 return TRUE;
388 }
389 }
390
391 return FALSE;
392}
393
394/**
395 Clean up the session of the instance stateful exchange.
396
397 @param[in, out] Instance The pointer to the Dhcp6 instance.
398 @param[in] Status The return status from udp.
399
400**/
401VOID
402Dhcp6CleanupSession (
403 IN OUT DHCP6_INSTANCE *Instance,
404 IN EFI_STATUS Status
405 )
406{
407 UINTN Index;
408 EFI_DHCP6_IA *Ia;
409
410 ASSERT(Instance->Config);
411 ASSERT(Instance->IaCb.Ia);
412
413 //
414 // Clean up the retransmit list for stateful messages.
415 //
416 Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATEFUL);
417
418 if (Instance->Unicast != NULL) {
419 FreePool (Instance->Unicast);
420 }
421
422 if (Instance->AdSelect != NULL) {
423 FreePool (Instance->AdSelect);
424 }
425
426 if (Instance->IaCb.Ia->ReplyPacket != NULL) {
427 FreePool (Instance->IaCb.Ia->ReplyPacket);
428 }
429
430 //
431 // Reinitialize the Ia fields of the instance.
432 //
433 Instance->UdpSts = Status;
434 Instance->AdSelect = NULL;
435 Instance->AdPref = 0;
436 Instance->Unicast = NULL;
437 Instance->IaCb.T1 = 0;
438 Instance->IaCb.T2 = 0;
439 Instance->IaCb.AllExpireTime = 0;
440 Instance->IaCb.LeaseTime = 0;
441
442 //
443 // Clear start time
444 //
445 Instance->StartTime = 0;
446
447 Ia = Instance->IaCb.Ia;
448 Ia->State = Dhcp6Init;
449 Ia->ReplyPacket = NULL;
450
451 //
452 // Set the addresses as zero lifetime, and then the notify
453 // function in Ip6Config will remove these timeout address.
454 //
455 for (Index = 0; Index < Ia->IaAddressCount; Index++) {
456 Ia->IaAddress[Index].PreferredLifetime = 0;
457 Ia->IaAddress[Index].ValidLifetime = 0;
458 }
459
460 //
461 //
462 // Signal the Ia information updated event to informal user.
463 //
464 if (Instance->Config->IaInfoEvent != NULL) {
465 gBS->SignalEvent (Instance->Config->IaInfoEvent);
466 }
467}
468
469
470/**
471 Callback to user when Dhcp6 transmit/receive occurs.
472
473 @param[in] Instance The pointer to the Dhcp6 instance.
474 @param[in] Event The current Dhcp6 event.
475 @param[in, out] Packet The pointer to the packet sending or received.
476
477 @retval EFI_SUCCESS The user function returns success.
478 @retval EFI_NOT_READY Direct the caller to continue collecting the offer.
479 @retval EFI_ABORTED The user function ask it to abort.
480
481**/
482EFI_STATUS
483EFIAPI
484Dhcp6CallbackUser (
485 IN DHCP6_INSTANCE *Instance,
486 IN EFI_DHCP6_EVENT Event,
487 IN OUT EFI_DHCP6_PACKET **Packet
488 )
489{
490 EFI_STATUS Status;
491 EFI_DHCP6_PACKET *NewPacket;
492 EFI_DHCP6_CALLBACK Callback;
493 VOID *Context;
494
495 ASSERT (Packet != NULL);
496 ASSERT (Instance->Config != NULL);
497 ASSERT (Instance->IaCb.Ia != NULL);
498
499 NewPacket = NULL;
500 Status = EFI_SUCCESS;
501 Callback = Instance->Config->Dhcp6Callback;
502 Context = Instance->Config->CallbackContext;
503
504 //
505 // Callback to user with the new message if has.
506 //
507 if (Callback != NULL) {
508
509 Status = Callback (
510 &Instance->Dhcp6,
511 Context,
512 Instance->IaCb.Ia->State,
513 Event,
514 *Packet,
515 &NewPacket
516 );
517 //
518 // Updated the new packet from user to replace the original one.
519 //
520 if (NewPacket != NULL) {
521 ASSERT (*Packet != NULL);
522 FreePool (*Packet);
523 *Packet = NewPacket;
524 }
525 }
526
527 return Status;
528}
529
530
531/**
532 Update Ia according to the new reply message.
533
534 @param[in, out] Instance The pointer to the Dhcp6 instance.
535 @param[in] Packet The pointer to reply messages.
536
537 @retval EFI_SUCCESS Updated the Ia information successfully.
538 @retval EFI_DEVICE_ERROR An unexpected error.
539
540**/
541EFI_STATUS
542Dhcp6UpdateIaInfo (
543 IN OUT DHCP6_INSTANCE *Instance,
544 IN EFI_DHCP6_PACKET *Packet
545 )
546{
547 EFI_STATUS Status;
548 UINT8 *Option;
549 UINT8 *IaInnerOpt;
550 UINT16 IaInnerLen;
551 UINT16 StsCode;
552 UINT32 T1;
553 UINT32 T2;
554
555 ASSERT (Instance->Config != NULL);
556 //
557 // If the reply was received in reponse to a solicit with rapid commit option,
558 // request, renew or rebind message, the client updates the information it has
559 // recorded about IAs from the IA options contained in the reply message:
560 // 1. record the T1 and T2 times
561 // 2. add any new addresses in the IA
562 // 3. discard any addresses from the IA, that have a valid lifetime of 0
563 // 4. update lifetimes for any addresses that alread recorded
564 // 5. leave unchanged any information about addresses
565 //
566 // See details in the section-18.1.8 of rfc-3315.
567 //
568 Option = Dhcp6SeekIaOption (
569 Packet->Dhcp6.Option,
570 Packet->Length - sizeof (EFI_DHCP6_HEADER),
571 &Instance->Config->IaDescriptor
572 );
573 if (Option == NULL) {
574 return EFI_DEVICE_ERROR;
575 }
576
577 //
578 // The format of the IA_NA option is:
579 //
580 // 0 1 2 3
581 // 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
582 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
583 // | OPTION_IA_NA | option-len |
584 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
585 // | IAID (4 octets) |
586 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
587 // | T1 |
588 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
589 // | T2 |
590 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
591 // | |
592 // . IA_NA-options .
593 // . .
594 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
595 //
596 // The format of the IA_TA option is:
597 //
598 // 0 1 2 3
599 // 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
600 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
601 // | OPTION_IA_TA | option-len |
602 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
603 // | IAID (4 octets) |
604 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
605 // | |
606 // . IA_TA-options .
607 // . .
608 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
609 //
610
611 //
612 // sizeof (option-code + option-len + IaId) = 8
613 // sizeof (option-code + option-len + IaId + T1) = 12
614 // sizeof (option-code + option-len + IaId + T1 + T2) = 16
615 //
616 // The inner options still start with 2 bytes option-code and 2 bytes option-len.
617 //
618 if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
619 T1 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 8)));
620 T2 = NTOHL (ReadUnaligned32 ((UINT32 *) (Option + 12)));
621 //
622 // Refer to RFC3155 Chapter 22.4. If a client receives an IA_NA with T1 greater than T2,
623 // and both T1 and T2 are greater than 0, the client discards the IA_NA option and processes
624 // the remainder of the message as though the server had not included the invalid IA_NA option.
625 //
626 if (T1 > T2 && T2 > 0) {
627 return EFI_DEVICE_ERROR;
628 }
629 IaInnerOpt = Option + 16;
630 IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 12);
631 } else {
632 T1 = 0;
633 T2 = 0;
634 IaInnerOpt = Option + 8;
635 IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 2))) - 4);
636 }
637
638 //
639 // The format of the Status Code option is:
640 //
641 // 0 1 2 3
642 // 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
643 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
644 // | OPTION_STATUS_CODE | option-len |
645 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
646 // | status-code | |
647 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
648 // . .
649 // . status-message .
650 // . .
651 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
652 //
653
654 //
655 // sizeof (option-code + option-len) = 4
656 //
657 StsCode = Dhcp6StsSuccess;
658 Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
659
660 if (Option != NULL) {
661 StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
662 if (StsCode != Dhcp6StsSuccess) {
663 return EFI_DEVICE_ERROR;
664 }
665 }
666
667 //
668 // Generate control block for the Ia.
669 //
670 Status = Dhcp6GenerateIaCb (
671 Instance,
672 IaInnerOpt,
673 IaInnerLen,
674 T1,
675 T2
676 );
677
678 return Status;
679}
680
681
682
683/**
684 Seek StatusCode Option in package. A Status Code option may appear in the
685 options field of a DHCP message and/or in the options field of another option.
686 See details in section 22.13, RFC3315.
687
688 @param[in] Instance The pointer to the Dhcp6 instance.
689 @param[in] Packet The pointer to reply messages.
690 @param[out] Option The pointer to status code option.
691
692 @retval EFI_SUCCESS Seek status code option successfully.
693 @retval EFI_DEVICE_ERROR An unexpected error.
694
695**/
696EFI_STATUS
697Dhcp6SeekStsOption (
698 IN DHCP6_INSTANCE *Instance,
699 IN EFI_DHCP6_PACKET *Packet,
700 OUT UINT8 **Option
701 )
702{
703 UINT8 *IaInnerOpt;
704 UINT16 IaInnerLen;
705 UINT16 StsCode;
706
707 //
708 // Seek StatusCode option directly in DHCP message body. That is, search in
709 // non-encapsulated option fields.
710 //
711 *Option = Dhcp6SeekOption (
712 Packet->Dhcp6.Option,
713 Packet->Length - 4,
714 Dhcp6OptStatusCode
715 );
716
717 if (*Option != NULL) {
718 StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
719 if (StsCode != Dhcp6StsSuccess) {
720 return EFI_DEVICE_ERROR;
721 }
722 }
723
724 //
725 // Seek in encapsulated options, IA_NA and IA_TA.
726 //
727 *Option = Dhcp6SeekIaOption (
728 Packet->Dhcp6.Option,
729 Packet->Length - sizeof (EFI_DHCP6_HEADER),
730 &Instance->Config->IaDescriptor
731 );
732 if (*Option == NULL) {
733 return EFI_SUCCESS;
734 }
735
736 //
737 // The format of the IA_NA option is:
738 //
739 // 0 1 2 3
740 // 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
741 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
742 // | OPTION_IA_NA | option-len |
743 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
744 // | IAID (4 octets) |
745 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
746 // | T1 |
747 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
748 // | T2 |
749 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
750 // | |
751 // . IA_NA-options .
752 // . .
753 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
754 //
755 // The format of the IA_TA option is:
756 //
757 // 0 1 2 3
758 // 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
759 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
760 // | OPTION_IA_TA | option-len |
761 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
762 // | IAID (4 octets) |
763 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
764 // | |
765 // . IA_TA-options .
766 // . .
767 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
768 //
769
770 //
771 // sizeof (option-code + option-len + IaId) = 8
772 // sizeof (option-code + option-len + IaId + T1) = 12
773 // sizeof (option-code + option-len + IaId + T1 + T2) = 16
774 //
775 // The inner options still start with 2 bytes option-code and 2 bytes option-len.
776 //
777 if (Instance->Config->IaDescriptor.Type == Dhcp6OptIana) {
778 IaInnerOpt = *Option + 16;
779 IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 12);
780 } else {
781 IaInnerOpt = *Option + 8;
782 IaInnerLen = (UINT16) (NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 2))) - 4);
783 }
784
785 //
786 // The format of the Status Code option is:
787 //
788 // 0 1 2 3
789 // 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
790 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
791 // | OPTION_STATUS_CODE | option-len |
792 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
793 // | status-code | |
794 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
795 // . .
796 // . status-message .
797 // . .
798 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
799 //
800
801 //
802 // sizeof (option-code + option-len) = 4
803 //
804 *Option = Dhcp6SeekOption (IaInnerOpt, IaInnerLen, Dhcp6OptStatusCode);
805 if (*Option != NULL) {
806 StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (*Option + 4)));
807 if (StsCode != Dhcp6StsSuccess) {
808 return EFI_DEVICE_ERROR;
809 }
810 }
811
812 return EFI_SUCCESS;
813}
814
815
816/**
817 Transmit Dhcp6 message by udpio.
818
819 @param[in] Instance The pointer to the Dhcp6 instance.
820 @param[in] Packet The pointer to transmit message.
821 @param[in] Elapsed The pointer to the elapsed time value to fill in.
822
823 @retval EFI_SUCCESS Successfully transmitted the packet.
824 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
825 @retval Others Failed to transmit the packet.
826
827**/
828EFI_STATUS
829Dhcp6TransmitPacket (
830 IN DHCP6_INSTANCE *Instance,
831 IN EFI_DHCP6_PACKET *Packet,
832 IN UINT16 *Elapsed
833 )
834{
835 EFI_STATUS Status;
836 NET_BUF *Wrap;
837 NET_FRAGMENT Frag;
838 UDP_END_POINT EndPt;
839 DHCP6_SERVICE *Service;
840
841 Service = Instance->Service;
842
843 //
844 // Wrap it into a netbuf then send it.
845 //
846 Frag.Bulk = (UINT8 *) &Packet->Dhcp6.Header;
847 Frag.Len = Packet->Length;
848
849 //
850 // Do not register free packet here, which will be handled in retry list.
851 //
852 Wrap = NetbufFromExt (&Frag, 1, 0, 0, Dhcp6DummyExtFree, NULL);
853
854 if (Wrap == NULL) {
855 return EFI_OUT_OF_RESOURCES;
856 }
857
858 //
859 // Multicast the Dhcp6 message, unless get the unicast server address by option.
860 //
861 ZeroMem (&EndPt, sizeof (UDP_END_POINT));
862
863 if (Instance->Unicast != NULL) {
864 CopyMem (
865 &EndPt.RemoteAddr,
866 Instance->Unicast,
867 sizeof (EFI_IPv6_ADDRESS)
868 );
869 } else {
870 CopyMem (
871 &EndPt.RemoteAddr,
872 &mAllDhcpRelayAndServersAddress,
873 sizeof (EFI_IPv6_ADDRESS)
874 );
875 }
876
877 EndPt.RemotePort = DHCP6_PORT_SERVER;
878 EndPt.LocalPort = DHCP6_PORT_CLIENT;
879
880 //
881 // Update the elapsed time value.
882 //
883 if (Elapsed != NULL) {
884 SetElapsedTime (Elapsed, Instance);
885 }
886
887 //
888 // Send out the message by the configured Udp6Io.
889 //
890 Status = UdpIoSendDatagram (
891 Service->UdpIo,
892 Wrap,
893 &EndPt,
894 NULL,
895 Dhcp6OnTransmitted,
896 NULL
897 );
898
899 if (EFI_ERROR (Status)) {
900 NetbufFree (Wrap);
901 return Status;
902 }
903
904 return EFI_SUCCESS;
905}
906
907
908/**
909 Create the solicit message and send it.
910
911 @param[in] Instance The pointer to the Dhcp6 instance.
912
913 @retval EFI_SUCCESS Created and sent the solicit message successfully.
914 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
915 @retval Others Failed to send the solicit message.
916
917**/
918EFI_STATUS
919Dhcp6SendSolicitMsg (
920 IN DHCP6_INSTANCE *Instance
921 )
922{
923 EFI_STATUS Status;
924 EFI_DHCP6_PACKET *Packet;
925 EFI_DHCP6_PACKET_OPTION *UserOpt;
926 EFI_DHCP6_DUID *ClientId;
927 DHCP6_SERVICE *Service;
928 UINT8 *Cursor;
929 UINT16 *Elapsed;
930 UINT32 UserLen;
931 UINTN Index;
932 UINT16 Length;
933
934 Service = Instance->Service;
935 ClientId = Service->ClientId;
936 UserLen = 0;
937
938 ASSERT (Service->ClientId != NULL);
939 ASSERT (Instance->Config != NULL);
940 ASSERT (Instance->IaCb.Ia != NULL);
941
942 //
943 // Calculate the added length of customized option list.
944 //
945 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
946 UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
947 }
948
949 //
950 // Create the Dhcp6 packet and initialize commone fields.
951 //
952 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
953 if (Packet == NULL) {
954 return EFI_OUT_OF_RESOURCES;
955 }
956
957 Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
958 Packet->Length = sizeof (EFI_DHCP6_HEADER);
959 Packet->Dhcp6.Header.MessageType = Dhcp6MsgSolicit;
960 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
961
962 //
963 // Assembly Dhcp6 options for solicit message.
964 //
965 Cursor = Packet->Dhcp6.Option;
966
967 Length = HTONS (ClientId->Length);
968 Cursor = Dhcp6AppendOption (
969 Cursor,
970 HTONS (Dhcp6OptClientId),
971 Length,
972 ClientId->Duid
973 );
974
975 Cursor = Dhcp6AppendETOption (
976 Cursor,
977 Instance,
978 &Elapsed
979 );
980
981 Cursor = Dhcp6AppendIaOption (
982 Cursor,
983 Instance->IaCb.Ia,
984 Instance->IaCb.T1,
985 Instance->IaCb.T2,
986 Packet->Dhcp6.Header.MessageType
987 );
988
989 //
990 // Append user-defined when configurate Dhcp6 service.
991 //
992 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
993
994 UserOpt = Instance->Config->OptionList[Index];
995 Cursor = Dhcp6AppendOption(
996 Cursor,
997 UserOpt->OpCode,
998 UserOpt->OpLen,
999 UserOpt->Data
1000 );
1001 }
1002
1003 //
1004 // Determine the size/length of packet.
1005 //
1006 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1007 ASSERT (Packet->Size > Packet->Length + 8);
1008
1009 //
1010 // Callback to user with the packet to be sent and check the user's feedback.
1011 //
1012 Status = Dhcp6CallbackUser (Instance, Dhcp6SendSolicit, &Packet);
1013
1014 if (EFI_ERROR (Status)) {
1015 FreePool (Packet);
1016 return Status;
1017 }
1018
1019 //
1020 // Send solicit packet with the state transition from Dhcp6init to
1021 // Dhcp6selecting.
1022 //
1023 Instance->IaCb.Ia->State = Dhcp6Selecting;
1024 //
1025 // Clear initial time for current transaction.
1026 //
1027 Instance->StartTime = 0;
1028
1029 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1030
1031 if (EFI_ERROR (Status)) {
1032 FreePool (Packet);
1033 return Status;
1034 }
1035
1036 //
1037 // Enqueue the sent packet for the retransmission in case reply timeout.
1038 //
1039 return Dhcp6EnqueueRetry (
1040 Instance,
1041 Packet,
1042 Elapsed,
1043 Instance->Config->SolicitRetransmission
1044 );
1045}
1046
1047/**
1048 Configure some parameter to initiate SolicitMsg.
1049
1050 @param[in] Instance The pointer to the Dhcp6 instance.
1051
1052 @retval EFI_SUCCESS Created and sent the solicit message successfully.
1053 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1054 @retval Others Failed to send the solicit message.
1055
1056**/
1057EFI_STATUS
1058Dhcp6InitSolicitMsg (
1059 IN DHCP6_INSTANCE *Instance
1060 )
1061{
1062 Instance->IaCb.T1 = 0;
1063 Instance->IaCb.T2 = 0;
1064 Instance->IaCb.Ia->IaAddressCount = 0;
1065
1066 return Dhcp6SendSolicitMsg (Instance);
1067}
1068
1069
1070/**
1071 Create the request message and send it.
1072
1073 @param[in] Instance The pointer to the Dhcp6 instance.
1074
1075 @retval EFI_SUCCESS Created and sent the request message successfully.
1076 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1077 @retval EFI_DEVICE_ERROR An unexpected error.
1078 @retval Others Failed to send the request message.
1079
1080**/
1081EFI_STATUS
1082Dhcp6SendRequestMsg (
1083 IN DHCP6_INSTANCE *Instance
1084 )
1085{
1086 EFI_STATUS Status;
1087 EFI_DHCP6_PACKET *Packet;
1088 EFI_DHCP6_PACKET_OPTION *UserOpt;
1089 EFI_DHCP6_DUID *ClientId;
1090 EFI_DHCP6_DUID *ServerId;
1091 DHCP6_SERVICE *Service;
1092 UINT8 *Option;
1093 UINT8 *Cursor;
1094 UINT16 *Elapsed;
1095 UINT32 UserLen;
1096 UINTN Index;
1097 UINT16 Length;
1098
1099 ASSERT(Instance->AdSelect != NULL);
1100 ASSERT(Instance->Config != NULL);
1101 ASSERT(Instance->IaCb.Ia != NULL);
1102 ASSERT(Instance->Service != NULL);
1103
1104 Service = Instance->Service;
1105 ClientId = Service->ClientId;
1106
1107 ASSERT(ClientId != NULL);
1108
1109 //
1110 // Get the server Id from the selected advertisement message.
1111 //
1112 Option = Dhcp6SeekOption (
1113 Instance->AdSelect->Dhcp6.Option,
1114 Instance->AdSelect->Length - 4,
1115 Dhcp6OptServerId
1116 );
1117 if (Option == NULL) {
1118 return EFI_DEVICE_ERROR;
1119 }
1120
1121 ServerId = (EFI_DHCP6_DUID *) (Option + 2);
1122
1123 //
1124 // Calculate the added length of customized option list.
1125 //
1126 UserLen = 0;
1127 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
1128 UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
1129 }
1130
1131 //
1132 // Create the Dhcp6 packet and initialize commone fields.
1133 //
1134 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
1135 if (Packet == NULL) {
1136 return EFI_OUT_OF_RESOURCES;
1137 }
1138
1139 Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
1140 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1141 Packet->Dhcp6.Header.MessageType = Dhcp6MsgRequest;
1142 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1143
1144 //
1145 // Assembly Dhcp6 options for request message.
1146 //
1147 Cursor = Packet->Dhcp6.Option;
1148
1149 Length = HTONS (ClientId->Length);
1150 Cursor = Dhcp6AppendOption (
1151 Cursor,
1152 HTONS (Dhcp6OptClientId),
1153 Length,
1154 ClientId->Duid
1155 );
1156
1157 Cursor = Dhcp6AppendETOption (
1158 Cursor,
1159 Instance,
1160 &Elapsed
1161 );
1162
1163 Cursor = Dhcp6AppendOption (
1164 Cursor,
1165 HTONS (Dhcp6OptServerId),
1166 ServerId->Length,
1167 ServerId->Duid
1168 );
1169
1170 Cursor = Dhcp6AppendIaOption (
1171 Cursor,
1172 Instance->IaCb.Ia,
1173 Instance->IaCb.T1,
1174 Instance->IaCb.T2,
1175 Packet->Dhcp6.Header.MessageType
1176 );
1177
1178 //
1179 // Append user-defined when configurate Dhcp6 service.
1180 //
1181 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
1182
1183 UserOpt = Instance->Config->OptionList[Index];
1184 Cursor = Dhcp6AppendOption(
1185 Cursor,
1186 UserOpt->OpCode,
1187 UserOpt->OpLen,
1188 UserOpt->Data
1189 );
1190 }
1191
1192 //
1193 // Determine the size/length of packet.
1194 //
1195 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1196 ASSERT (Packet->Size > Packet->Length + 8);
1197
1198 //
1199 // Callback to user with the packet to be sent and check the user's feedback.
1200 //
1201 Status = Dhcp6CallbackUser (Instance, Dhcp6SendRequest, &Packet);
1202
1203 if (EFI_ERROR (Status)) {
1204 FreePool (Packet);
1205 return Status;
1206 }
1207
1208 //
1209 // Send request packet with the state transition from Dhcp6selecting to
1210 // Dhcp6requesting.
1211 //
1212 Instance->IaCb.Ia->State = Dhcp6Requesting;
1213 //
1214 // Clear initial time for current transaction.
1215 //
1216 Instance->StartTime = 0;
1217
1218 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1219
1220 if (EFI_ERROR (Status)) {
1221 FreePool (Packet);
1222 return Status;
1223 }
1224
1225 //
1226 // Enqueue the sent packet for the retransmission in case reply timeout.
1227 //
1228 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
1229}
1230
1231
1232/**
1233 Create the decline message and send it.
1234
1235 @param[in] Instance The pointer to the Dhcp6 instance.
1236 @param[in] DecIa The pointer to the decline Ia.
1237
1238 @retval EFI_SUCCESS Created and sent the decline message successfully.
1239 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1240 @retval EFI_DEVICE_ERROR An unexpected error.
1241 @retval Others Failed to send the decline message.
1242
1243**/
1244EFI_STATUS
1245Dhcp6SendDeclineMsg (
1246 IN DHCP6_INSTANCE *Instance,
1247 IN EFI_DHCP6_IA *DecIa
1248 )
1249{
1250 EFI_STATUS Status;
1251 EFI_DHCP6_PACKET *Packet;
1252 EFI_DHCP6_PACKET *LastReply;
1253 EFI_DHCP6_DUID *ClientId;
1254 EFI_DHCP6_DUID *ServerId;
1255 DHCP6_SERVICE *Service;
1256 UINT8 *Option;
1257 UINT8 *Cursor;
1258 UINT16 *Elapsed;
1259 UINT16 Length;
1260
1261 ASSERT (Instance->Config != NULL);
1262 ASSERT (Instance->IaCb.Ia != NULL);
1263 ASSERT (Instance->Service != NULL);
1264
1265 Service = Instance->Service;
1266 ClientId = Service->ClientId;
1267 LastReply = Instance->IaCb.Ia->ReplyPacket;
1268
1269 ASSERT (ClientId != NULL);
1270 ASSERT (LastReply != NULL);
1271
1272 //
1273 // Get the server Id from the last reply message.
1274 //
1275 Option = Dhcp6SeekOption (
1276 LastReply->Dhcp6.Option,
1277 LastReply->Length - 4,
1278 Dhcp6OptServerId
1279 );
1280 if (Option == NULL) {
1281 return EFI_DEVICE_ERROR;
1282 }
1283
1284 //
1285 // EFI_DHCP6_DUID contains a length field of 2 bytes.
1286 //
1287 ServerId = (EFI_DHCP6_DUID *) (Option + 2);
1288
1289 //
1290 // Create the Dhcp6 packet and initialize commone fields.
1291 //
1292 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
1293 if (Packet == NULL) {
1294 return EFI_OUT_OF_RESOURCES;
1295 }
1296
1297 Packet->Size = DHCP6_BASE_PACKET_SIZE;
1298 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1299 Packet->Dhcp6.Header.MessageType = Dhcp6MsgDecline;
1300 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1301
1302 //
1303 // Assembly Dhcp6 options for rebind/renew message.
1304 //
1305 Cursor = Packet->Dhcp6.Option;
1306
1307 Length = HTONS (ClientId->Length);
1308 Cursor = Dhcp6AppendOption (
1309 Cursor,
1310 HTONS (Dhcp6OptClientId),
1311 Length,
1312 ClientId->Duid
1313 );
1314
1315 Cursor = Dhcp6AppendETOption (
1316 Cursor,
1317 Instance,
1318 &Elapsed
1319 );
1320
1321 Cursor = Dhcp6AppendOption (
1322 Cursor,
1323 HTONS (Dhcp6OptServerId),
1324 ServerId->Length,
1325 ServerId->Duid
1326 );
1327
1328 Cursor = Dhcp6AppendIaOption (Cursor, DecIa, 0, 0, Packet->Dhcp6.Header.MessageType);
1329
1330 //
1331 // Determine the size/length of packet.
1332 //
1333 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1334 ASSERT (Packet->Size > Packet->Length + 8);
1335
1336 //
1337 // Callback to user with the packet to be sent and check the user's feedback.
1338 //
1339 Status = Dhcp6CallbackUser (Instance, Dhcp6SendDecline, &Packet);
1340
1341 if (EFI_ERROR (Status)) {
1342 FreePool (Packet);
1343 return Status;
1344 }
1345
1346 //
1347 // Send decline packet with the state transition from Dhcp6bound to
1348 // Dhcp6declining.
1349 //
1350 Instance->IaCb.Ia->State = Dhcp6Declining;
1351 //
1352 // Clear initial time for current transaction.
1353 //
1354 Instance->StartTime = 0;
1355
1356 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1357
1358 if (EFI_ERROR (Status)) {
1359 FreePool (Packet);
1360 return Status;
1361 }
1362
1363 //
1364 // Enqueue the sent packet for the retransmission in case reply timeout.
1365 //
1366 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
1367}
1368
1369
1370/**
1371 Create the release message and send it.
1372
1373 @param[in] Instance The pointer to the Dhcp6 instance.
1374 @param[in] RelIa The pointer to the release Ia.
1375
1376 @retval EFI_SUCCESS Created and sent the release message successfully.
1377 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1378 @retval EFI_DEVICE_ERROR An unexpected error.
1379 @retval Others Failed to send the release message.
1380
1381**/
1382EFI_STATUS
1383Dhcp6SendReleaseMsg (
1384 IN DHCP6_INSTANCE *Instance,
1385 IN EFI_DHCP6_IA *RelIa
1386 )
1387{
1388 EFI_STATUS Status;
1389 EFI_DHCP6_PACKET *Packet;
1390 EFI_DHCP6_PACKET *LastReply;
1391 EFI_DHCP6_DUID *ClientId;
1392 EFI_DHCP6_DUID *ServerId;
1393 DHCP6_SERVICE *Service;
1394 UINT8 *Option;
1395 UINT8 *Cursor;
1396 UINT16 *Elapsed;
1397 UINT16 Length;
1398
1399 ASSERT(Instance->Config);
1400 ASSERT(Instance->IaCb.Ia);
1401
1402 Service = Instance->Service;
1403 ClientId = Service->ClientId;
1404 LastReply = Instance->IaCb.Ia->ReplyPacket;
1405
1406 ASSERT(ClientId);
1407 ASSERT(LastReply);
1408
1409 //
1410 // Get the server Id from the last reply message.
1411 //
1412 Option = Dhcp6SeekOption (
1413 LastReply->Dhcp6.Option,
1414 LastReply->Length - 4,
1415 Dhcp6OptServerId
1416 );
1417 if (Option == NULL) {
1418 return EFI_DEVICE_ERROR;
1419 }
1420
1421 ServerId = (EFI_DHCP6_DUID *) (Option + 2);
1422
1423 //
1424 // Create the Dhcp6 packet and initialize commone fields.
1425 //
1426 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE);
1427 if (Packet == NULL) {
1428 return EFI_OUT_OF_RESOURCES;
1429 }
1430
1431 Packet->Size = DHCP6_BASE_PACKET_SIZE;
1432 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1433 Packet->Dhcp6.Header.MessageType = Dhcp6MsgRelease;
1434 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1435
1436 //
1437 // Assembly Dhcp6 options for rebind/renew message
1438 //
1439 Cursor = Packet->Dhcp6.Option;
1440
1441 Length = HTONS (ClientId->Length);
1442 Cursor = Dhcp6AppendOption (
1443 Cursor,
1444 HTONS (Dhcp6OptClientId),
1445 Length,
1446 ClientId->Duid
1447 );
1448
1449 //
1450 // ServerId is extracted from packet, it's network order.
1451 //
1452 Cursor = Dhcp6AppendOption (
1453 Cursor,
1454 HTONS (Dhcp6OptServerId),
1455 ServerId->Length,
1456 ServerId->Duid
1457 );
1458
1459 Cursor = Dhcp6AppendETOption (
1460 Cursor,
1461 Instance,
1462 &Elapsed
1463 );
1464
1465 Cursor = Dhcp6AppendIaOption (Cursor, RelIa, 0, 0, Packet->Dhcp6.Header.MessageType);
1466
1467 //
1468 // Determine the size/length of packet
1469 //
1470 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1471 ASSERT (Packet->Size > Packet->Length + 8);
1472
1473 //
1474 // Callback to user with the packet to be sent and check the user's feedback.
1475 //
1476 Status = Dhcp6CallbackUser (Instance, Dhcp6SendRelease, &Packet);
1477
1478 if (EFI_ERROR (Status)) {
1479 FreePool (Packet);
1480 return Status;
1481 }
1482
1483 //
1484 // Send release packet with the state transition from Dhcp6bound to
1485 // Dhcp6releasing.
1486 //
1487 Instance->IaCb.Ia->State = Dhcp6Releasing;
1488
1489 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1490
1491 if (EFI_ERROR (Status)) {
1492 FreePool (Packet);
1493 return Status;
1494 }
1495
1496 //
1497 // Enqueue the sent packet for the retransmission in case reply timeout.
1498 //
1499 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
1500}
1501
1502
1503/**
1504 Create the renew/rebind message and send it.
1505
1506 @param[in] Instance The pointer to the Dhcp6 instance.
1507 @param[in] RebindRequest If TRUE, it is a Rebind type message.
1508 Otherwise, it is a Renew type message.
1509
1510 @retval EFI_SUCCESS Created and sent the renew/rebind message successfully.
1511 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1512 @retval EFI_DEVICE_ERROR An unexpected error.
1513 @retval Others Failed to send the renew/rebind message.
1514
1515**/
1516EFI_STATUS
1517Dhcp6SendRenewRebindMsg (
1518 IN DHCP6_INSTANCE *Instance,
1519 IN BOOLEAN RebindRequest
1520 )
1521{
1522 EFI_STATUS Status;
1523 EFI_DHCP6_PACKET *Packet;
1524 EFI_DHCP6_PACKET *LastReply;
1525 EFI_DHCP6_PACKET_OPTION *UserOpt;
1526 EFI_DHCP6_DUID *ClientId;
1527 EFI_DHCP6_DUID *ServerId;
1528 EFI_DHCP6_STATE State;
1529 EFI_DHCP6_EVENT Event;
1530 DHCP6_SERVICE *Service;
1531 UINT8 *Option;
1532 UINT8 *Cursor;
1533 UINT16 *Elapsed;
1534 UINT32 UserLen;
1535 UINTN Index;
1536 UINT16 Length;
1537
1538 ASSERT(Instance->Config);
1539 ASSERT(Instance->IaCb.Ia);
1540
1541 Service = Instance->Service;
1542 ClientId = Service->ClientId;
1543
1544 ASSERT(ClientId);
1545
1546 //
1547 // Calculate the added length of customized option list.
1548 //
1549 UserLen = 0;
1550 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
1551 UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
1552 }
1553
1554 //
1555 // Create the Dhcp6 packet and initialize commone fields.
1556 //
1557 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
1558 if (Packet == NULL) {
1559 return EFI_OUT_OF_RESOURCES;
1560 }
1561
1562 Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
1563 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1564 Packet->Dhcp6.Header.MessageType = RebindRequest ? Dhcp6MsgRebind : Dhcp6MsgRenew;
1565 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1566
1567 //
1568 // Assembly Dhcp6 options for rebind/renew message.
1569 //
1570 Cursor = Packet->Dhcp6.Option;
1571
1572 Length = HTONS (ClientId->Length);
1573 Cursor = Dhcp6AppendOption (
1574 Cursor,
1575 HTONS (Dhcp6OptClientId),
1576 Length,
1577 ClientId->Duid
1578 );
1579
1580 Cursor = Dhcp6AppendETOption (
1581 Cursor,
1582 Instance,
1583 &Elapsed
1584 );
1585
1586 Cursor = Dhcp6AppendIaOption (
1587 Cursor,
1588 Instance->IaCb.Ia,
1589 Instance->IaCb.T1,
1590 Instance->IaCb.T2,
1591 Packet->Dhcp6.Header.MessageType
1592 );
1593
1594 if (!RebindRequest) {
1595 //
1596 // Get the server Id from the last reply message and
1597 // insert it for rebind request.
1598 //
1599 LastReply = Instance->IaCb.Ia->ReplyPacket;
1600 ASSERT (LastReply);
1601
1602 Option = Dhcp6SeekOption (
1603 LastReply->Dhcp6.Option,
1604 LastReply->Length - 4,
1605 Dhcp6OptServerId
1606 );
1607 if (Option == NULL) {
1608 FreePool (Packet);
1609 return EFI_DEVICE_ERROR;
1610 }
1611
1612 ServerId = (EFI_DHCP6_DUID *) (Option + 2);
1613
1614 Cursor = Dhcp6AppendOption (
1615 Cursor,
1616 HTONS (Dhcp6OptServerId),
1617 ServerId->Length,
1618 ServerId->Duid
1619 );
1620 }
1621
1622 //
1623 // Append user-defined when configurate Dhcp6 service.
1624 //
1625 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
1626
1627 UserOpt = Instance->Config->OptionList[Index];
1628 Cursor = Dhcp6AppendOption(
1629 Cursor,
1630 UserOpt->OpCode,
1631 UserOpt->OpLen,
1632 UserOpt->Data
1633 );
1634 }
1635
1636 //
1637 // Determine the size/length of packet.
1638 //
1639 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1640 ASSERT (Packet->Size > Packet->Length + 8);
1641
1642 //
1643 // Callback to user with the packet to be sent and check the user's feedback.
1644 //
1645 State = (RebindRequest) ? Dhcp6Rebinding : Dhcp6Renewing;
1646 Event = (RebindRequest) ? Dhcp6EnterRebinding : Dhcp6EnterRenewing;
1647
1648 Status = Dhcp6CallbackUser (Instance, Event, &Packet);
1649
1650 if (EFI_ERROR (Status)) {
1651 FreePool (Packet);
1652 return Status;
1653 }
1654
1655 //
1656 // Send renew/rebind packet with the state transition from Dhcp6bound to
1657 // Dhcp6renew/rebind.
1658 // And sync the lease time when send renew/rebind, in case that user send
1659 // renew/rebind actively.
1660 //
1661 Instance->IaCb.Ia->State = State;
1662 Instance->IaCb.LeaseTime = (RebindRequest) ? Instance->IaCb.T2 : Instance->IaCb.T1;
1663 //
1664 // Clear initial time for current transaction.
1665 //
1666 Instance->StartTime = 0;
1667
1668 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1669
1670 if (EFI_ERROR (Status)) {
1671 FreePool (Packet);
1672 return Status;
1673 }
1674
1675 //
1676 // Enqueue the sent packet for the retransmission in case reply timeout.
1677 //
1678 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
1679}
1680
1681/**
1682 Start the information request process.
1683
1684 @param[in] Instance The pointer to the Dhcp6 instance.
1685 @param[in] SendClientId If TRUE, the client identifier option will be included in
1686 information request message. Otherwise, the client identifier
1687 option will not be included.
1688 @param[in] OptionRequest The pointer to the option request option.
1689 @param[in] OptionCount The number options in the OptionList.
1690 @param[in] OptionList The array pointers to the appended options.
1691 @param[in] Retransmission The pointer to the retransmission control.
1692 @param[in] TimeoutEvent The event of timeout.
1693 @param[in] ReplyCallback The callback function when the reply was received.
1694 @param[in] CallbackContext The pointer to the parameter passed to the callback.
1695
1696 @retval EFI_SUCCESS Start the info-request process successfully.
1697 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1698 @retval EFI_NO_MAPPING No source address is available for use.
1699 @retval Others Failed to start the info-request process.
1700
1701**/
1702EFI_STATUS
1703Dhcp6StartInfoRequest (
1704 IN DHCP6_INSTANCE *Instance,
1705 IN BOOLEAN SendClientId,
1706 IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
1707 IN UINT32 OptionCount,
1708 IN EFI_DHCP6_PACKET_OPTION *OptionList[] OPTIONAL,
1709 IN EFI_DHCP6_RETRANSMISSION *Retransmission,
1710 IN EFI_EVENT TimeoutEvent OPTIONAL,
1711 IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,
1712 IN VOID *CallbackContext OPTIONAL
1713 )
1714{
1715 EFI_STATUS Status;
1716 DHCP6_INF_CB *InfCb;
1717 DHCP6_SERVICE *Service;
1718 EFI_TPL OldTpl;
1719
1720 Service = Instance->Service;
1721
1722 OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
1723 Instance->UdpSts = EFI_ALREADY_STARTED;
1724 //
1725 // Create and initialize the control block for the info-request.
1726 //
1727 InfCb = AllocateZeroPool (sizeof(DHCP6_INF_CB));
1728
1729 if (InfCb == NULL) {
1730 gBS->RestoreTPL (OldTpl);
1731 return EFI_OUT_OF_RESOURCES;
1732 }
1733
1734 InfCb->ReplyCallback = ReplyCallback;
1735 InfCb->CallbackContext = CallbackContext;
1736 InfCb->TimeoutEvent = TimeoutEvent;
1737
1738 InsertTailList (&Instance->InfList, &InfCb->Link);
1739
1740 //
1741 // Send the info-request message to start exchange process.
1742 //
1743 Status = Dhcp6SendInfoRequestMsg (
1744 Instance,
1745 InfCb,
1746 SendClientId,
1747 OptionRequest,
1748 OptionCount,
1749 OptionList,
1750 Retransmission
1751 );
1752
1753 if (EFI_ERROR (Status)) {
1754 goto ON_ERROR;
1755 }
1756
1757 //
1758 // Register receive callback for the stateless exchange process.
1759 //
1760 Status = UdpIoRecvDatagram(
1761 Service->UdpIo,
1762 Dhcp6ReceivePacket,
1763 Service,
1764 0
1765 );
1766
1767 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {
1768 goto ON_ERROR;
1769 }
1770
1771 gBS->RestoreTPL (OldTpl);
1772 return EFI_SUCCESS;
1773
1774ON_ERROR:
1775 gBS->RestoreTPL (OldTpl);
1776 RemoveEntryList (&InfCb->Link);
1777 FreePool (InfCb);
1778
1779 return Status;
1780}
1781
1782/**
1783 Create the information request message and send it.
1784
1785 @param[in] Instance The pointer to the Dhcp6 instance.
1786 @param[in] InfCb The pointer to the information request control block.
1787 @param[in] SendClientId If TRUE, the client identifier option will be included in
1788 information request message. Otherwise, the client identifier
1789 option will not be included.
1790 @param[in] OptionRequest The pointer to the option request option.
1791 @param[in] OptionCount The number options in the OptionList.
1792 @param[in] OptionList The array pointers to the appended options.
1793 @param[in] Retransmission The pointer to the retransmission control.
1794
1795 @retval EFI_SUCCESS Created and sent the info-request message successfully.
1796 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1797 @retval Others Failed to send the info-request message.
1798
1799**/
1800EFI_STATUS
1801Dhcp6SendInfoRequestMsg (
1802 IN DHCP6_INSTANCE *Instance,
1803 IN DHCP6_INF_CB *InfCb,
1804 IN BOOLEAN SendClientId,
1805 IN EFI_DHCP6_PACKET_OPTION *OptionRequest,
1806 IN UINT32 OptionCount,
1807 IN EFI_DHCP6_PACKET_OPTION *OptionList[],
1808 IN EFI_DHCP6_RETRANSMISSION *Retransmission
1809 )
1810{
1811 EFI_STATUS Status;
1812 EFI_DHCP6_PACKET *Packet;
1813 EFI_DHCP6_PACKET_OPTION *UserOpt;
1814 EFI_DHCP6_DUID *ClientId;
1815 DHCP6_SERVICE *Service;
1816 UINT8 *Cursor;
1817 UINT16 *Elapsed;
1818 UINT32 UserLen;
1819 UINTN Index;
1820 UINT16 Length;
1821
1822 ASSERT(OptionRequest);
1823
1824 Service = Instance->Service;
1825 ClientId = Service->ClientId;
1826 UserLen = NTOHS (OptionRequest->OpLen) + 4;
1827
1828 ASSERT(ClientId);
1829
1830 //
1831 // Calculate the added length of customized option list.
1832 //
1833 for (Index = 0; Index < OptionCount; Index++) {
1834 UserLen += (NTOHS (OptionList[Index]->OpLen) + 4);
1835 }
1836
1837 //
1838 // Create the Dhcp6 packet and initialize commone fields.
1839 //
1840 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
1841 if (Packet == NULL) {
1842 return EFI_OUT_OF_RESOURCES;
1843 }
1844
1845 Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
1846 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1847 Packet->Dhcp6.Header.MessageType = Dhcp6MsgInfoRequest;
1848 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1849
1850 InfCb->Xid = Packet->Dhcp6.Header.TransactionId;
1851
1852 //
1853 // Assembly Dhcp6 options for info-request message.
1854 //
1855 Cursor = Packet->Dhcp6.Option;
1856
1857 if (SendClientId) {
1858 Length = HTONS (ClientId->Length);
1859 Cursor = Dhcp6AppendOption (
1860 Cursor,
1861 HTONS (Dhcp6OptClientId),
1862 Length,
1863 ClientId->Duid
1864 );
1865 }
1866
1867 Cursor = Dhcp6AppendETOption (
1868 Cursor,
1869 Instance,
1870 &Elapsed
1871 );
1872
1873 Cursor = Dhcp6AppendOption (
1874 Cursor,
1875 OptionRequest->OpCode,
1876 OptionRequest->OpLen,
1877 OptionRequest->Data
1878 );
1879
1880 //
1881 // Append user-defined when configurate Dhcp6 service.
1882 //
1883 for (Index = 0; Index < OptionCount; Index++) {
1884
1885 UserOpt = OptionList[Index];
1886 Cursor = Dhcp6AppendOption(
1887 Cursor,
1888 UserOpt->OpCode,
1889 UserOpt->OpLen,
1890 UserOpt->Data
1891 );
1892 }
1893
1894 //
1895 // Determine the size/length of packet.
1896 //
1897 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
1898 ASSERT (Packet->Size > Packet->Length + 8);
1899
1900 //
1901 // Clear initial time for current transaction.
1902 //
1903 Instance->StartTime = 0;
1904
1905 //
1906 // Send info-request packet with no state.
1907 //
1908 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
1909
1910 if (EFI_ERROR (Status)) {
1911 FreePool (Packet);
1912 return Status;
1913 }
1914
1915 //
1916 // Enqueue the sent packet for the retransmission in case reply timeout.
1917 //
1918 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, Retransmission);
1919}
1920
1921
1922/**
1923 Create the Confirm message and send it.
1924
1925 @param[in] Instance The pointer to the Dhcp6 instance.
1926
1927 @retval EFI_SUCCESS Created and sent the confirm message successfully.
1928 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1929 @retval EFI_DEVICE_ERROR An unexpected error.
1930 @retval Others Failed to send the confirm message.
1931
1932**/
1933EFI_STATUS
1934Dhcp6SendConfirmMsg (
1935 IN DHCP6_INSTANCE *Instance
1936 )
1937{
1938 UINT8 *Cursor;
1939 UINTN Index;
1940 UINT16 Length;
1941 UINT32 UserLen;
1942 EFI_STATUS Status;
1943 DHCP6_SERVICE *Service;
1944 EFI_DHCP6_DUID *ClientId;
1945 EFI_DHCP6_PACKET *Packet;
1946 EFI_DHCP6_PACKET_OPTION *UserOpt;
1947 UINT16 *Elapsed;
1948
1949 ASSERT (Instance->Config != NULL);
1950 ASSERT (Instance->IaCb.Ia != NULL);
1951 ASSERT (Instance->Service != NULL);
1952
1953 Service = Instance->Service;
1954 ClientId = Service->ClientId;
1955 ASSERT (ClientId != NULL);
1956
1957 //
1958 // Calculate the added length of customized option list.
1959 //
1960 UserLen = 0;
1961 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
1962 UserLen += (NTOHS (Instance->Config->OptionList[Index]->OpLen) + 4);
1963 }
1964
1965 //
1966 // Create the Dhcp6 packet and initialize common fields.
1967 //
1968 Packet = AllocateZeroPool (DHCP6_BASE_PACKET_SIZE + UserLen);
1969 if (Packet == NULL) {
1970 return EFI_OUT_OF_RESOURCES;
1971 }
1972
1973 Packet->Size = DHCP6_BASE_PACKET_SIZE + UserLen;
1974 Packet->Length = sizeof (EFI_DHCP6_HEADER);
1975 Packet->Dhcp6.Header.MessageType = Dhcp6MsgConfirm;
1976 Packet->Dhcp6.Header.TransactionId = Service->Xid++;
1977
1978 //
1979 // Assembly Dhcp6 options for solicit message.
1980 //
1981 Cursor = Packet->Dhcp6.Option;
1982
1983 Length = HTONS (ClientId->Length);
1984 Cursor = Dhcp6AppendOption (
1985 Cursor,
1986 HTONS (Dhcp6OptClientId),
1987 Length,
1988 ClientId->Duid
1989 );
1990
1991 Cursor = Dhcp6AppendETOption (
1992 Cursor,
1993 Instance,
1994 &Elapsed
1995 );
1996
1997 Cursor = Dhcp6AppendIaOption (
1998 Cursor,
1999 Instance->IaCb.Ia,
2000 Instance->IaCb.T1,
2001 Instance->IaCb.T2,
2002 Packet->Dhcp6.Header.MessageType
2003 );
2004
2005 //
2006 // Append user-defined when configurate Dhcp6 service.
2007 //
2008 for (Index = 0; Index < Instance->Config->OptionCount; Index++) {
2009 UserOpt = Instance->Config->OptionList[Index];
2010 Cursor = Dhcp6AppendOption (
2011 Cursor,
2012 UserOpt->OpCode,
2013 UserOpt->OpLen,
2014 UserOpt->Data
2015 );
2016 }
2017
2018 //
2019 // Determine the size/length of packet.
2020 //
2021 Packet->Length += (UINT32) (Cursor - Packet->Dhcp6.Option);
2022 ASSERT (Packet->Size > Packet->Length + 8);
2023
2024 //
2025 // Callback to user with the packet to be sent and check the user's feedback.
2026 //
2027 Status = Dhcp6CallbackUser (Instance, Dhcp6SendConfirm, &Packet);
2028
2029 if (EFI_ERROR (Status)) {
2030 FreePool (Packet);
2031 return Status;
2032 }
2033
2034 //
2035 // Send confirm packet with the state transition from Dhcp6Bound to
2036 // Dhcp6Confirming.
2037 //
2038 Instance->IaCb.Ia->State = Dhcp6Confirming;
2039 //
2040 // Clear initial time for current transaction.
2041 //
2042 Instance->StartTime = 0;
2043
2044 Status = Dhcp6TransmitPacket (Instance, Packet, Elapsed);
2045
2046 if (EFI_ERROR (Status)) {
2047 FreePool (Packet);
2048 return Status;
2049 }
2050
2051 //
2052 // Enqueue the sent packet for the retransmission in case reply timeout.
2053 //
2054 return Dhcp6EnqueueRetry (Instance, Packet, Elapsed, NULL);
2055}
2056
2057
2058
2059/**
2060 Handle with the Dhcp6 reply message.
2061
2062 @param[in] Instance The pointer to Dhcp6 instance.
2063 @param[in] Packet The pointer to the Dhcp6 reply message.
2064
2065 @retval EFI_SUCCESS Processed the reply message successfully.
2066 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
2067 @retval EFI_DEVICE_ERROR An unexpected error.
2068 @retval Others Failed to process the reply message.
2069
2070**/
2071EFI_STATUS
2072Dhcp6HandleReplyMsg (
2073 IN DHCP6_INSTANCE *Instance,
2074 IN EFI_DHCP6_PACKET *Packet
2075 )
2076{
2077 EFI_STATUS Status;
2078 UINT8 *Option;
2079 UINT16 StsCode;
2080
2081 ASSERT (Instance->Config != NULL);
2082 ASSERT (Instance->IaCb.Ia != NULL);
2083 ASSERT (Packet != NULL);
2084
2085 Status = EFI_SUCCESS;
2086
2087 if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
2088 return EFI_DEVICE_ERROR;
2089 }
2090
2091 //
2092 // If the client subsequently receives a valid reply message that includes a
2093 // rapid commit option since send a solicit with rapid commit option before,
2094 // preocess the reply message and discard any reply messages received in
2095 // response to the request message.
2096 // See details in the section-17.1.4 of rfc-3315.
2097 //
2098 Option = Dhcp6SeekOption (
2099 Packet->Dhcp6.Option,
2100 Packet->Length - 4,
2101 Dhcp6OptRapidCommit
2102 );
2103
2104 if ((Option != NULL && !Instance->Config->RapidCommit) || (Option == NULL && Instance->Config->RapidCommit)) {
2105 return EFI_DEVICE_ERROR;
2106 }
2107
2108 //
2109 // As to a valid reply packet in response to a request/renew/rebind packet,
2110 // ignore the packet if not contains the Ia option
2111 //
2112 if (Instance->IaCb.Ia->State == Dhcp6Requesting ||
2113 Instance->IaCb.Ia->State == Dhcp6Renewing ||
2114 Instance->IaCb.Ia->State == Dhcp6Rebinding
2115 ) {
2116
2117 Option = Dhcp6SeekIaOption (
2118 Packet->Dhcp6.Option,
2119 Packet->Length,
2120 &Instance->Config->IaDescriptor
2121 );
2122 if (Option == NULL) {
2123 return EFI_SUCCESS;
2124 }
2125 }
2126
2127 //
2128 // Callback to user with the received packet and check the user's feedback.
2129 //
2130 Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdReply, &Packet);
2131
2132 if (EFI_ERROR (Status)) {
2133 return Status;
2134 }
2135
2136 //
2137 // When receive a valid reply packet in response to a decline/release packet,
2138 // the client considers the decline/release event completed regardless of the
2139 // status code.
2140 //
2141 if (Instance->IaCb.Ia->State == Dhcp6Declining || Instance->IaCb.Ia->State == Dhcp6Releasing) {
2142
2143 if (Instance->IaCb.Ia->IaAddressCount != 0) {
2144 Instance->IaCb.Ia->State = Dhcp6Bound;
2145 } else {
2146 ASSERT (Instance->IaCb.Ia->ReplyPacket);
2147 FreePool (Instance->IaCb.Ia->ReplyPacket);
2148 Instance->IaCb.Ia->ReplyPacket = NULL;
2149 Instance->IaCb.Ia->State = Dhcp6Init;
2150 }
2151
2152 //
2153 // For sync, set the success flag out of polling in decline/release.
2154 //
2155 Instance->UdpSts = EFI_SUCCESS;
2156
2157 //
2158 // For async, signal the Ia event to inform Ia infomation update.
2159 //
2160 if (Instance->Config->IaInfoEvent != NULL) {
2161 gBS->SignalEvent (Instance->Config->IaInfoEvent);
2162 }
2163
2164 //
2165 // Reset start time for next exchange.
2166 //
2167 Instance->StartTime = 0;
2168
2169 Status = EFI_SUCCESS;
2170 goto ON_EXIT;
2171 }
2172
2173 //
2174 // Upon the receipt of a valid reply packet in response to a solicit, request,
2175 // confirm, renew and rebind, the behavior depends on the status code option.
2176 // See the details in the section-18.1.8 of rfc-3315.
2177 //
2178 Option = NULL;
2179 Status = Dhcp6SeekStsOption (
2180 Instance,
2181 Packet,
2182 &Option
2183 );
2184
2185 if (!EFI_ERROR (Status)) {
2186 //
2187 // No status code or no error status code means succeed to reply.
2188 //
2189 Status = Dhcp6UpdateIaInfo (Instance, Packet);
2190 if (!EFI_ERROR (Status)) {
2191 //
2192 // Reset start time for next exchange.
2193 //
2194 Instance->StartTime = 0;
2195
2196 //
2197 // Set bound state and store the reply packet.
2198 //
2199 if (Instance->IaCb.Ia->ReplyPacket != NULL) {
2200 FreePool (Instance->IaCb.Ia->ReplyPacket);
2201 }
2202
2203 Instance->IaCb.Ia->ReplyPacket = AllocateZeroPool (Packet->Size);
2204
2205 if (Instance->IaCb.Ia->ReplyPacket == NULL) {
2206 Status = EFI_OUT_OF_RESOURCES;
2207 goto ON_EXIT;
2208 }
2209
2210 CopyMem (Instance->IaCb.Ia->ReplyPacket, Packet, Packet->Size);
2211
2212 Instance->IaCb.Ia->State = Dhcp6Bound;
2213
2214 //
2215 // For sync, set the success flag out of polling in start/renewrebind.
2216 //
2217 Instance->UdpSts = EFI_SUCCESS;
2218
2219 //
2220 // Maybe this is a new round DHCP process due to some reason, such as NotOnLink
2221 // ReplyMsg for ConfirmMsg should triger new round to acquire new address. In that
2222 // case, clear old address.ValidLifetime and append to new address. Therefore, DHCP
2223 // consumers can be notified to flush old address.
2224 //
2225 Dhcp6AppendCacheIa (Instance);
2226
2227 //
2228 // For async, signal the Ia event to inform Ia infomation update.
2229 //
2230 if (Instance->Config->IaInfoEvent != NULL) {
2231 gBS->SignalEvent (Instance->Config->IaInfoEvent);
2232 }
2233 } else if (Status == EFI_NOT_FOUND) {
2234 //
2235 // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message,
2236 // the client sends a Renew or Rebind if the IA is not in the Reply message.
2237 // Return EFI_SUCCESS so we can continue to restart the Renew/Rebind process.
2238 //
2239 return EFI_SUCCESS;
2240 }
2241
2242 goto ON_EXIT;
2243
2244 } else if (Option != NULL) {
2245 //
2246 // Any error status code option is found.
2247 //
2248 StsCode = NTOHS (ReadUnaligned16 ((UINT16 *) (Option + 4)));
2249 switch (StsCode) {
2250 case Dhcp6StsUnspecFail:
2251 //
2252 // It indicates the server is unable to process the message due to an
2253 // unspecified failure condition, so just retry if possible.
2254 //
2255 break;
2256
2257 case Dhcp6StsUseMulticast:
2258 //
2259 // It indicates the server receives a message via unicast from a client
2260 // to which the server has not sent a unicast option, so retry it by
2261 // multi-cast address.
2262 //
2263 if (Instance->Unicast != NULL) {
2264 FreePool (Instance->Unicast);
2265 Instance->Unicast = NULL;
2266 }
2267 break;
2268
2269 case Dhcp6StsNotOnLink:
2270 if (Instance->IaCb.Ia->State == Dhcp6Confirming) {
2271 //
2272 // Before initiate new round DHCP, cache the current IA.
2273 //
2274 Status = Dhcp6CacheIa (Instance);
2275 if (EFI_ERROR (Status)) {
2276 return Status;
2277 }
2278
2279 //
2280 // Restart S.A.R.R process to acquire new address.
2281 //
2282 Status = Dhcp6InitSolicitMsg (Instance);
2283 if (EFI_ERROR (Status)) {
2284 return Status;
2285 }
2286 }
2287 break;
2288
2289 case Dhcp6StsNoBinding:
2290 if (Instance->IaCb.Ia->State == Dhcp6Renewing || Instance->IaCb.Ia->State == Dhcp6Rebinding) {
2291 //
2292 // Refer to RFC3315 Chapter 18.1.8, for each IA in the original Renew or Rebind message, the client
2293 // sends a Request message if the IA contained a Status Code option with the NoBinding status.
2294 //
2295 Status = Dhcp6SendRequestMsg(Instance);
2296 if (EFI_ERROR (Status)) {
2297 return Status;
2298 }
2299 }
2300 break;
2301
2302 default:
2303 //
2304 // The other status code, just restart solicitation.
2305 //
2306 break;
2307 }
2308 }
2309
2310 return EFI_SUCCESS;
2311
2312ON_EXIT:
2313
2314 if (!EFI_ERROR(Status)) {
2315 Status = Dhcp6DequeueRetry (
2316 Instance,
2317 Packet->Dhcp6.Header.TransactionId,
2318 FALSE
2319 );
2320 }
2321
2322 return Status;
2323}
2324
2325
2326/**
2327 Select the appointed Dhcp6 advertisement message.
2328
2329 @param[in] Instance The pointer to the Dhcp6 instance.
2330 @param[in] AdSelect The pointer to the selected Dhcp6 advertisement message.
2331
2332 @retval EFI_SUCCESS Selected the right advertisement message successfully.
2333 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
2334 @retval Others Failed to select the advertise message.
2335
2336**/
2337EFI_STATUS
2338Dhcp6SelectAdvertiseMsg (
2339 IN DHCP6_INSTANCE *Instance,
2340 IN EFI_DHCP6_PACKET *AdSelect
2341 )
2342{
2343 EFI_STATUS Status;
2344 UINT8 *Option;
2345
2346 ASSERT (AdSelect != NULL);
2347
2348 //
2349 // Callback to user with the selected advertisement packet, and the user
2350 // might overwrite it.
2351 //
2352 Status = Dhcp6CallbackUser (Instance, Dhcp6SelectAdvertise, &AdSelect);
2353
2354 if (EFI_ERROR (Status)) {
2355 return Status;
2356 }
2357
2358 Instance->AdSelect = AdSelect;
2359
2360 //
2361 // Dequeue the sent packet for the retransmission since advertisement selected.
2362 //
2363 Status = Dhcp6DequeueRetry (
2364 Instance,
2365 AdSelect->Dhcp6.Header.TransactionId,
2366 FALSE
2367 );
2368
2369 if (EFI_ERROR(Status)) {
2370 return Status;
2371 }
2372
2373 //
2374 // Check whether there is server unicast option in the selected advertise
2375 // packet, and update it.
2376 //
2377 Option = Dhcp6SeekOption(
2378 AdSelect->Dhcp6.Option,
2379 AdSelect->Length - 4,
2380 Dhcp6OptServerUnicast
2381 );
2382
2383 if (Option != NULL) {
2384
2385 Instance->Unicast = AllocateZeroPool (sizeof(EFI_IPv6_ADDRESS));
2386
2387 if (Instance->Unicast == NULL) {
2388 return EFI_OUT_OF_RESOURCES;
2389 }
2390
2391 CopyMem (Instance->Unicast, Option + 4, sizeof(EFI_IPv6_ADDRESS));
2392 }
2393
2394 //
2395 // Update the information of the Ia by the selected advertisement message.
2396 //
2397 Status = Dhcp6UpdateIaInfo (Instance, AdSelect);
2398
2399 if (EFI_ERROR (Status)) {
2400 return Status;
2401 }
2402
2403 //
2404 // Send the request message to continue the S.A.R.R. process.
2405 //
2406 return Dhcp6SendRequestMsg (Instance);
2407}
2408
2409
2410/**
2411 Handle with the Dhcp6 advertisement message.
2412
2413 @param[in] Instance The pointer to the Dhcp6 instance.
2414 @param[in] Packet The pointer to the Dhcp6 advertisement message.
2415
2416 @retval EFI_SUCCESS Processed the advertisement message successfully.
2417 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
2418 @retval EFI_DEVICE_ERROR An unexpected error.
2419 @retval Others Failed to process the advertise message.
2420
2421**/
2422EFI_STATUS
2423Dhcp6HandleAdvertiseMsg (
2424 IN DHCP6_INSTANCE *Instance,
2425 IN EFI_DHCP6_PACKET *Packet
2426 )
2427{
2428 EFI_STATUS Status;
2429 UINT8 *Option;
2430 BOOLEAN Timeout;
2431
2432 ASSERT(Instance->Config);
2433 ASSERT(Instance->IaCb.Ia);
2434
2435 Timeout = FALSE;
2436
2437 //
2438 // If the client does receives a valid reply message that includes a rapid
2439 // commit option since a solicit with rapid commit optioin sent before, select
2440 // this reply message. Or else, process the advertise messages as normal.
2441 // See details in the section-17.1.4 of rfc-3315.
2442 //
2443 Option = Dhcp6SeekOption(
2444 Packet->Dhcp6.Option,
2445 Packet->Length - 4,
2446 Dhcp6OptRapidCommit
2447 );
2448
2449 if (Option != NULL && Instance->Config->RapidCommit && Packet->Dhcp6.Header.MessageType == Dhcp6MsgReply) {
2450
2451 return Dhcp6HandleReplyMsg (Instance, Packet);
2452 }
2453
2454 if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise) {
2455 return EFI_DEVICE_ERROR;
2456 }
2457
2458 //
2459 // Client must ignore any advertise message that includes a status code option
2460 // containing the value noaddrsavail, with the exception that the client may
2461 // display the associated status message to the user.
2462 // See the details in the section-17.1.3 of rfc-3315.
2463 //
2464 Status = Dhcp6SeekStsOption (
2465 Instance,
2466 Packet,
2467 &Option
2468 );
2469 if (EFI_ERROR (Status)) {
2470 return EFI_DEVICE_ERROR;
2471 }
2472
2473 //
2474 // Callback to user with the received packet and check the user's feedback.
2475 //
2476 Status = Dhcp6CallbackUser (Instance, Dhcp6RcvdAdvertise, &Packet);
2477
2478 if (!EFI_ERROR (Status)) {
2479 //
2480 // Success means user choose the current advertisement packet.
2481 //
2482 if (Instance->AdSelect != NULL) {
2483 FreePool (Instance->AdSelect);
2484 }
2485
2486 //
2487 // Store the selected advertisement packet and set a flag.
2488 //
2489 Instance->AdSelect = AllocateZeroPool (Packet->Size);
2490
2491 if (Instance->AdSelect == NULL) {
2492 return EFI_OUT_OF_RESOURCES;
2493 }
2494
2495 CopyMem (Instance->AdSelect, Packet, Packet->Size);
2496
2497 Instance->AdPref = 0xff;
2498
2499 } else if (Status == EFI_NOT_READY) {
2500 //
2501 // Not_ready means user wants to continue to receive more advertise packets.
2502 //
2503 if (Instance->AdPref == 0xff && Instance->AdSelect == NULL) {
2504 //
2505 // It's a tricky point. The timer routine set adpref as 0xff if the first
2506 // rt timeout and no advertisement received, which means any advertisement
2507 // received will be selected after the first rt.
2508 //
2509 Timeout = TRUE;
2510 }
2511
2512 //
2513 // Check whether the current packet has a 255 preference option or not.
2514 // Take non-preference option as 0 value.
2515 //
2516 Option = Dhcp6SeekOption(
2517 Packet->Dhcp6.Option,
2518 Packet->Length - 4,
2519 Dhcp6OptPreference
2520 );
2521
2522 if (Instance->AdSelect == NULL || (Option != NULL && *(Option + 4) > Instance->AdPref)) {
2523 //
2524 // No advertisements received before or preference is more than other
2525 // advertisements received before. Then store the new packet and the
2526 // preference value.
2527 //
2528 if (Instance->AdSelect != NULL) {
2529 FreePool (Instance->AdSelect);
2530 }
2531
2532 Instance->AdSelect = AllocateZeroPool (Packet->Size);
2533
2534 if (Instance->AdSelect == NULL) {
2535 return EFI_OUT_OF_RESOURCES;
2536 }
2537
2538 CopyMem (Instance->AdSelect, Packet, Packet->Size);
2539
2540 if (Option != NULL) {
2541 Instance->AdPref = *(Option + 4);
2542 }
2543 } else {
2544 //
2545 // Non-preference and other advertisements received before or current
2546 // preference is less than other advertisements received before.
2547 // Leave the packet alone.
2548 }
2549
2550 } else {
2551 //
2552 // Other error status means termination.
2553 //
2554 return Status;
2555 }
2556
2557 //
2558 // Client must collect advertise messages as more as possible until the first
2559 // RT has elapsed, or get a highest preference 255 advertise.
2560 // See details in the section-17.1.2 of rfc-3315.
2561 //
2562 if (Instance->AdPref == 0xff || Timeout) {
2563 Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
2564 }
2565
2566 return Status;
2567}
2568
2569
2570/**
2571 The Dhcp6 stateful exchange process routine.
2572
2573 @param[in] Instance The pointer to the Dhcp6 instance.
2574 @param[in] Packet The pointer to the received Dhcp6 message.
2575
2576**/
2577VOID
2578Dhcp6HandleStateful (
2579 IN DHCP6_INSTANCE *Instance,
2580 IN EFI_DHCP6_PACKET *Packet
2581 )
2582{
2583 EFI_STATUS Status;
2584 EFI_DHCP6_DUID *ClientId;
2585 DHCP6_SERVICE *Service;
2586 UINT8 *Option;
2587
2588 Service = Instance->Service;
2589 ClientId = Service->ClientId;
2590 Status = EFI_SUCCESS;
2591
2592 if (Instance->Config == NULL) {
2593 goto ON_CONTINUE;
2594 }
2595
2596 ASSERT (ClientId);
2597 ASSERT (Instance->Config);
2598 ASSERT (Instance->IaCb.Ia);
2599
2600 //
2601 // Discard the packet if not advertisement or reply packet.
2602 //
2603 if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgAdvertise && Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
2604 goto ON_CONTINUE;
2605 }
2606
2607 //
2608 // Check whether include client Id or not.
2609 //
2610 Option = Dhcp6SeekOption(
2611 Packet->Dhcp6.Option,
2612 Packet->Length - 4,
2613 Dhcp6OptClientId
2614 );
2615
2616 if (Option == NULL || CompareMem (Option + 4, ClientId->Duid, ClientId->Length) != 0) {
2617 goto ON_CONTINUE;
2618 }
2619
2620 //
2621 // Check whether include server Id or not.
2622 //
2623 Option = Dhcp6SeekOption(
2624 Packet->Dhcp6.Option,
2625 Packet->Length - 4,
2626 Dhcp6OptServerId
2627 );
2628
2629 if (Option == NULL) {
2630 goto ON_CONTINUE;
2631 }
2632
2633 switch (Instance->IaCb.Ia->State) {
2634 case Dhcp6Selecting:
2635 //
2636 // Handle the advertisement message when in the Dhcp6Selecting state.
2637 // Do not need check return status, if failed, just continue to the next.
2638 //
2639 Dhcp6HandleAdvertiseMsg (Instance, Packet);
2640 break;
2641
2642 case Dhcp6Requesting:
2643 case Dhcp6Confirming:
2644 case Dhcp6Renewing:
2645 case Dhcp6Rebinding:
2646 case Dhcp6Releasing:
2647 case Dhcp6Declining:
2648 //
2649 // Handle the reply message when in the Dhcp6Requesting, Dhcp6Renewing
2650 // Dhcp6Rebinding, Dhcp6Releasing and Dhcp6Declining state.
2651 // If failed here, it should reset the current session.
2652 //
2653 Status = Dhcp6HandleReplyMsg (Instance, Packet);
2654 if (EFI_ERROR (Status)) {
2655 goto ON_EXIT;
2656 }
2657 break;
2658 default:
2659 //
2660 // Other state has not supported yet.
2661 //
2662 break;
2663 }
2664
2665ON_CONTINUE:
2666 //
2667 // Continue to receive the following Dhcp6 message.
2668 //
2669 Status = UdpIoRecvDatagram (
2670 Service->UdpIo,
2671 Dhcp6ReceivePacket,
2672 Service,
2673 0
2674 );
2675ON_EXIT:
2676 if (EFI_ERROR (Status)) {
2677 Dhcp6CleanupSession (Instance, Status);
2678 }
2679}
2680
2681
2682/**
2683 The Dhcp6 stateless exchange process routine.
2684
2685 @param[in] Instance The pointer to the Dhcp6 instance.
2686 @param[in] Packet The pointer to the received Dhcp6 message.
2687
2688**/
2689VOID
2690Dhcp6HandleStateless (
2691 IN DHCP6_INSTANCE *Instance,
2692 IN EFI_DHCP6_PACKET *Packet
2693 )
2694{
2695 EFI_STATUS Status;
2696 DHCP6_SERVICE *Service;
2697 DHCP6_INF_CB *InfCb;
2698 UINT8 *Option;
2699 BOOLEAN IsMatched;
2700
2701 Service = Instance->Service;
2702 Status = EFI_SUCCESS;
2703 IsMatched = FALSE;
2704 InfCb = NULL;
2705
2706 if (Packet->Dhcp6.Header.MessageType != Dhcp6MsgReply) {
2707 goto ON_EXIT;
2708 }
2709
2710 //
2711 // Check whether it's a desired Info-request message by Xid.
2712 //
2713 while (!IsListEmpty (&Instance->InfList)) {
2714 InfCb = NET_LIST_HEAD (&Instance->InfList, DHCP6_INF_CB, Link);
2715 if (InfCb->Xid == Packet->Dhcp6.Header.TransactionId) {
2716 IsMatched = TRUE;
2717 break;
2718 }
2719 }
2720
2721 if (!IsMatched) {
2722 goto ON_EXIT;
2723 }
2724
2725 //
2726 // Check whether include server Id or not.
2727 //
2728 Option = Dhcp6SeekOption (
2729 Packet->Dhcp6.Option,
2730 Packet->Length - 4,
2731 Dhcp6OptServerId
2732 );
2733
2734 if (Option == NULL) {
2735 goto ON_EXIT;
2736 }
2737
2738 //
2739 // Callback to user with the received packet and check the user's feedback.
2740 //
2741 Status = InfCb->ReplyCallback (
2742 &Instance->Dhcp6,
2743 InfCb->CallbackContext,
2744 Packet
2745 );
2746
2747 if (Status == EFI_NOT_READY) {
2748 //
2749 // Success or aborted will both stop this info-request exchange process,
2750 // but not ready means user wants to continue to receive reply.
2751 //
2752 goto ON_EXIT;
2753 }
2754
2755 //
2756 // Dequeue the sent packet from the txlist if the xid matched, and ignore
2757 // if no xid matched.
2758 //
2759 Dhcp6DequeueRetry (
2760 Instance,
2761 Packet->Dhcp6.Header.TransactionId,
2762 FALSE
2763 );
2764
2765 //
2766 // For sync, set the status out of polling for info-request.
2767 //
2768 Instance->UdpSts = Status;
2769
2770ON_EXIT:
2771
2772 Status = UdpIoRecvDatagram (
2773 Service->UdpIo,
2774 Dhcp6ReceivePacket,
2775 Service,
2776 0
2777 );
2778
2779 if (EFI_ERROR (Status)) {
2780 Dhcp6CleanupRetry (Instance, DHCP6_PACKET_STATELESS);
2781 }
2782}
2783
2784
2785/**
2786 The receive callback function for Dhcp6 exchange process.
2787
2788 @param[in] Udp6Wrap The pointer to the received net buffer.
2789 @param[in] EndPoint The pointer to the udp end point.
2790 @param[in] IoStatus The return status from udp io.
2791 @param[in] Context The opaque parameter to the function.
2792
2793**/
2794VOID
2795EFIAPI
2796Dhcp6ReceivePacket (
2797 IN NET_BUF *Udp6Wrap,
2798 IN UDP_END_POINT *EndPoint,
2799 IN EFI_STATUS IoStatus,
2800 IN VOID *Context
2801 )
2802{
2803 EFI_DHCP6_HEADER *Head;
2804 EFI_DHCP6_PACKET *Packet;
2805 DHCP6_SERVICE *Service;
2806 DHCP6_INSTANCE *Instance;
2807 DHCP6_TX_CB *TxCb;
2808 UINT32 Size;
2809 BOOLEAN IsDispatched;
2810 BOOLEAN IsStateless;
2811 LIST_ENTRY *Entry1;
2812 LIST_ENTRY *Next1;
2813 LIST_ENTRY *Entry2;
2814 LIST_ENTRY *Next2;
2815 EFI_STATUS Status;
2816
2817 ASSERT (Udp6Wrap != NULL);
2818 ASSERT (Context != NULL);
2819
2820 Service = (DHCP6_SERVICE *) Context;
2821 Instance = NULL;
2822 Packet = NULL;
2823 IsDispatched = FALSE;
2824 IsStateless = FALSE;
2825
2826 if (EFI_ERROR (IoStatus)) {
2827 return ;
2828 }
2829
2830 //
2831 // Copy the net buffer received from upd6 to a Dhcp6 packet.
2832 //
2833 Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;
2834 Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);
2835
2836 if (Packet == NULL) {
2837 goto ON_CONTINUE;
2838 }
2839
2840 Packet->Size = Size;
2841 Head = &Packet->Dhcp6.Header;
2842 Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);
2843
2844 if (Packet->Length == 0) {
2845 goto ON_CONTINUE;
2846 }
2847
2848 //
2849 // Dispatch packet to right instance by transaction id.
2850 //
2851 NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
2852
2853 Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
2854
2855 NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {
2856
2857 TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);
2858
2859 if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {
2860 //
2861 // Find the corresponding packet in tx list, and check it whether belongs
2862 // to stateful exchange process.
2863 //
2864 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
2865 IsStateless = TRUE;
2866 }
2867 IsDispatched = TRUE;
2868 break;
2869 }
2870 }
2871
2872 if (IsDispatched) {
2873 break;
2874 }
2875 }
2876
2877 //
2878 // Skip this packet if not dispatched to any instance.
2879 //
2880 if (!IsDispatched) {
2881 goto ON_CONTINUE;
2882 }
2883
2884 //
2885 // Dispatch the received packet ot the right instance.
2886 //
2887 if (IsStateless) {
2888 Dhcp6HandleStateless (Instance, Packet);
2889 } else {
2890 Dhcp6HandleStateful (Instance, Packet);
2891 }
2892
2893ON_CONTINUE:
2894
2895 if (!IsDispatched) {
2896 Status = UdpIoRecvDatagram (
2897 Service->UdpIo,
2898 Dhcp6ReceivePacket,
2899 Service,
2900 0
2901 );
2902 if (EFI_ERROR (Status)) {
2903 NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
2904 Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
2905 Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);
2906 }
2907 }
2908 }
2909
2910 NetbufFree (Udp6Wrap);
2911
2912 if (Packet != NULL) {
2913 FreePool (Packet);
2914 }
2915}
2916
2917/**
2918 Detect Link movement for specified network device.
2919
2920 This routine will try to invoke Snp->GetStatus() to get the media status.
2921 If media present status switches from unpresent to present, a link movement
2922 is detected. Note that the underlying UNDI driver may not support reporting
2923 media status from GET_STATUS command. If that, fail to detect link movement.
2924
2925 @param[in] Instance The pointer to DHCP6_INSTANCE.
2926
2927 @retval TRUE A link movement is detected.
2928 @retval FALSE A link movement is not detected.
2929
2930**/
2931BOOLEAN
2932Dhcp6LinkMovDetect (
2933 IN DHCP6_INSTANCE *Instance
2934 )
2935{
2936 UINT32 InterruptStatus;
2937 BOOLEAN MediaPresent;
2938 EFI_STATUS Status;
2939 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
2940
2941 ASSERT (Instance != NULL);
2942 Snp = Instance->Service->Snp;
2943 MediaPresent = Instance->MediaPresent;
2944
2945 //
2946 // Check whether SNP support media detection
2947 //
2948 if (!Snp->Mode->MediaPresentSupported) {
2949 return FALSE;
2950 }
2951
2952 //
2953 // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
2954 //
2955 Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
2956 if (EFI_ERROR (Status)) {
2957 return FALSE;
2958 }
2959
2960 Instance->MediaPresent = Snp->Mode->MediaPresent;
2961 //
2962 // Media transimit Unpresent to Present means new link movement is detected.
2963 //
2964 if (!MediaPresent && Instance->MediaPresent) {
2965 return TRUE;
2966 }
2967 return FALSE;
2968}
2969
2970
2971/**
2972 The timer routine of the Dhcp6 instance for each second.
2973
2974 @param[in] Event The timer event.
2975 @param[in] Context The opaque parameter to the function.
2976
2977**/
2978VOID
2979EFIAPI
2980Dhcp6OnTimerTick (
2981 IN EFI_EVENT Event,
2982 IN VOID *Context
2983 )
2984{
2985 LIST_ENTRY *Entry;
2986 LIST_ENTRY *NextEntry;
2987 DHCP6_INSTANCE *Instance;
2988 DHCP6_TX_CB *TxCb;
2989 DHCP6_IA_CB *IaCb;
2990 UINT32 LossTime;
2991 EFI_STATUS Status;
2992
2993 ASSERT (Context != NULL);
2994
2995 Instance = (DHCP6_INSTANCE *) Context;
2996
2997 //
2998 // 1. Loop the tx list, count live time of every tx packet to check whether
2999 // need re-transmit or not.
3000 //
3001 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
3002
3003 TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
3004
3005 TxCb->TickTime++;
3006
3007 if (TxCb->TickTime > TxCb->RetryExp) {
3008 //
3009 // Handle the first rt in the transmission of solicit specially.
3010 //
3011 if ((TxCb->RetryCnt == 0 || TxCb->SolicitRetry) && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
3012 if (Instance->AdSelect == NULL) {
3013 //
3014 // Set adpref as 0xff here to indicate select any advertisement
3015 // afterwards.
3016 //
3017 Instance->AdPref = 0xff;
3018 } else {
3019 //
3020 // Select the advertisement received before.
3021 //
3022 Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
3023 if (Status == EFI_ABORTED) {
3024 goto ON_CLOSE;
3025 } else if (EFI_ERROR (Status)) {
3026 TxCb->RetryCnt++;
3027 }
3028 return;
3029 }
3030 }
3031 //
3032 // Increase the retry count for the packet and add up the total loss time.
3033 //
3034 TxCb->RetryCnt++;
3035 TxCb->RetryLos += TxCb->RetryExp;
3036
3037 //
3038 // Check whether overflow the max retry count limit for this packet
3039 //
3040 if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {
3041 Status = EFI_NO_RESPONSE;
3042 goto ON_CLOSE;
3043 }
3044
3045 //
3046 // Check whether overflow the max retry duration for this packet
3047 //
3048 if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {
3049 Status = EFI_NO_RESPONSE;
3050 goto ON_CLOSE;
3051 }
3052
3053 //
3054 // Re-calculate retry expire timeout for the next time.
3055 //
3056 // Firstly, Check the new calculated time whether overflow the max retry
3057 // expire time.
3058 //
3059 TxCb->RetryExp = Dhcp6CalculateExpireTime (
3060 TxCb->RetryExp,
3061 FALSE,
3062 TRUE
3063 );
3064
3065 if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {
3066 TxCb->RetryExp = Dhcp6CalculateExpireTime (
3067 TxCb->RetryCtl.Mrt,
3068 TRUE,
3069 TRUE
3070 );
3071 }
3072
3073 //
3074 // Secondly, Check the new calculated time whether overflow the max retry
3075 // duration time.
3076 //
3077 LossTime = TxCb->RetryLos + TxCb->RetryExp;
3078 if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {
3079 TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;
3080 }
3081
3082 //
3083 // Reset the tick time for the next retransmission
3084 //
3085 TxCb->TickTime = 0;
3086
3087 //
3088 // Retransmit the last sent packet again.
3089 //
3090 Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);
3091 TxCb->SolicitRetry = FALSE;
3092 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
3093 TxCb->SolicitRetry = TRUE;
3094 }
3095 }
3096 }
3097
3098 //
3099 // 2. Check the configured Ia, count lease time of every valid Ia to check
3100 // whether need to renew or rebind this Ia.
3101 //
3102 IaCb = &Instance->IaCb;
3103
3104 if (Instance->Config == NULL || IaCb->Ia == NULL) {
3105 return;
3106 }
3107
3108 if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {
3109
3110 IaCb->LeaseTime++;
3111
3112 if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {
3113 //
3114 // Exceed t2, send rebind packet to extend the Ia lease.
3115 //
3116 Dhcp6SendRenewRebindMsg (Instance, TRUE);
3117
3118 } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {
3119
3120 //
3121 // Exceed t1, send renew packet to extend the Ia lease.
3122 //
3123 Dhcp6SendRenewRebindMsg (Instance, FALSE);
3124 }
3125 }
3126
3127 //
3128 // 3. In any situation when a client may have moved to a new link, the
3129 // client MUST initiate a Confirm/Reply message exchange.
3130 //
3131 if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {
3132 Dhcp6SendConfirmMsg (Instance);
3133 }
3134
3135 return;
3136
3137 ON_CLOSE:
3138
3139 if (Dhcp6IsValidTxCb (Instance, TxCb) &&
3140 TxCb->TxPacket != NULL &&
3141 (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||
3142 TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||
3143 TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)
3144 ) {
3145 //
3146 // The failure of renew/Confirm will still switch to the bound state.
3147 //
3148 if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||
3149 (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {
3150 ASSERT (Instance->IaCb.Ia);
3151 Instance->IaCb.Ia->State = Dhcp6Bound;
3152 }
3153 //
3154 // The failure of info-request will return no response.
3155 //
3156 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
3157 Instance->UdpSts = EFI_NO_RESPONSE;
3158 }
3159 Dhcp6DequeueRetry (
3160 Instance,
3161 TxCb->Xid,
3162 TRUE
3163 );
3164 } else {
3165 //
3166 // The failure of the others will terminate current state machine if timeout.
3167 //
3168 Dhcp6CleanupSession (Instance, Status);
3169 }
3170}