]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/Dhcp6Dxe/Dhcp6Utility.c
Add new interface GetVariable2 and GetEfiGlobalVariable2 to return more info. Also...
[mirror_edk2.git] / NetworkPkg / Dhcp6Dxe / Dhcp6Utility.c
1 /** @file
2 Dhcp6 support functions 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 /**
20 Generate client Duid in the format of Duid-llt.
21
22 @param[in] Mode The pointer to the mode of SNP.
23
24 @retval NULL If it failed to generate a client Id.
25 @retval others The pointer to the new client id.
26
27 **/
28 EFI_DHCP6_DUID *
29 Dhcp6GenerateClientId (
30 IN EFI_SIMPLE_NETWORK_MODE *Mode
31 )
32 {
33 EFI_STATUS Status;
34 EFI_DHCP6_DUID *Duid;
35 EFI_TIME Time;
36 UINT32 Stamp;
37 EFI_GUID Uuid;
38
39
40 //
41 // Attempt to get client Id from variable to keep it constant.
42 // See details in section-9 of rfc-3315.
43 //
44 GetVariable2 (L"ClientId", &gEfiDhcp6ServiceBindingProtocolGuid, &Duid, NULL);
45 if (Duid != NULL) {
46 return Duid;
47 }
48
49 //
50 // The format of client identifier option:
51 //
52 // 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
53 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 // | OPTION_CLIENTID | option-len |
55 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56 // . .
57 // . DUID .
58 // . (variable length) .
59 // . .
60 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
61 //
62
63 //
64 // If System UUID is found from SMBIOS Table, use DUID-UUID type.
65 //
66 if (!EFI_ERROR (NetLibGetSystemGuid (&Uuid))) {
67 //
68 //
69 // The format of DUID-UUID:
70 //
71 // 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
72 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
73 // | DUID-Type (4) | UUID (128 bits) |
74 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
75 // | |
76 // | |
77 // | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
78 // | |
79 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
80
81 //
82 // sizeof (option-len + Duid-type + UUID-size) = 20 bytes
83 //
84 Duid = AllocateZeroPool (2 + 2 + sizeof (EFI_GUID));
85 if (Duid == NULL) {
86 return NULL;
87 }
88
89 //
90 // sizeof (Duid-type + UUID-size) = 18 bytes
91 //
92 Duid->Length = (UINT16) (18);
93
94 //
95 // Set the Duid-type and copy UUID.
96 //
97 WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeUuid));
98
99 CopyMem (Duid->Duid + 2, &Uuid, sizeof(EFI_GUID));
100
101 } else {
102
103 //
104 //
105 // The format of DUID-LLT:
106 //
107 // 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
108 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
109 // | Duid type (1) | hardware type (16 bits) |
110 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
111 // | time (32 bits) |
112 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
113 // . .
114 // . link-layer address (variable length) .
115 // . .
116 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
117 //
118
119 //
120 // Generate a time stamp of the seconds from 2000/1/1, assume 30day/month.
121 //
122 gRT->GetTime (&Time, NULL);
123 Stamp = (UINT32)
124 (
125 (((((Time.Year - 2000) * 360 + (Time.Month - 1)) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) *
126 60 +
127 Time.Second
128 );
129
130 //
131 // sizeof (option-len + Duid-type + hardware-type + time) = 10 bytes
132 //
133 Duid = AllocateZeroPool (10 + Mode->HwAddressSize);
134 if (Duid == NULL) {
135 return NULL;
136 }
137
138 //
139 // sizeof (Duid-type + hardware-type + time) = 8 bytes
140 //
141 Duid->Length = (UINT16) (Mode->HwAddressSize + 8);
142
143 //
144 // Set the Duid-type, hardware-type, time and copy the hardware address.
145 //
146 WriteUnaligned16 ((UINT16 *) (Duid->Duid), HTONS (Dhcp6DuidTypeLlt));
147 WriteUnaligned16 ((UINT16 *) (Duid->Duid + 2), HTONS (NET_IFTYPE_ETHERNET));
148 WriteUnaligned32 ((UINT32 *) (Duid->Duid + 4), HTONL (Stamp));
149
150 CopyMem (Duid->Duid + 8, &Mode->CurrentAddress, Mode->HwAddressSize);
151 }
152
153 Status = gRT->SetVariable (
154 L"ClientId",
155 &gEfiDhcp6ServiceBindingProtocolGuid,
156 (EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS),
157 Duid->Length + 2,
158 (VOID *) Duid
159 );
160 ASSERT_EFI_ERROR (Status);
161
162 return Duid;
163 }
164
165
166 /**
167 Copy the Dhcp6 configure data.
168
169 @param[in] DstCfg The pointer to the destination configure data.
170 @param[in] SorCfg The pointer to the source configure data.
171
172 @retval EFI_SUCCESS Copy the content from SorCfg from DstCfg successfully.
173 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
174
175 **/
176 EFI_STATUS
177 Dhcp6CopyConfigData (
178 IN EFI_DHCP6_CONFIG_DATA *DstCfg,
179 IN EFI_DHCP6_CONFIG_DATA *SorCfg
180 )
181 {
182 UINTN Index;
183 UINTN OptionListSize;
184 UINTN OptionSize;
185
186 CopyMem (DstCfg, SorCfg, sizeof (EFI_DHCP6_CONFIG_DATA));
187
188 //
189 // Allocate another buffer for solicitretransmission, and copy it.
190 //
191 if (SorCfg->SolicitRetransmission != NULL) {
192
193 DstCfg->SolicitRetransmission = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
194
195 if (DstCfg->SolicitRetransmission == NULL) {
196 //
197 // Error will be handled out of this function.
198 //
199 return EFI_OUT_OF_RESOURCES;
200 }
201
202 CopyMem (
203 DstCfg->SolicitRetransmission,
204 SorCfg->SolicitRetransmission,
205 sizeof (EFI_DHCP6_RETRANSMISSION)
206 );
207 }
208
209 if (SorCfg->OptionList != NULL && SorCfg->OptionCount != 0) {
210
211 OptionListSize = SorCfg->OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *);
212 DstCfg->OptionList = AllocateZeroPool (OptionListSize);
213
214 if (DstCfg->OptionList == NULL) {
215 //
216 // Error will be handled out of this function.
217 //
218 return EFI_OUT_OF_RESOURCES;
219 }
220
221 for (Index = 0; Index < SorCfg->OptionCount; Index++) {
222
223 OptionSize = NTOHS (SorCfg->OptionList[Index]->OpLen) + 4;
224 DstCfg->OptionList[Index] = AllocateZeroPool (OptionSize);
225
226 if (DstCfg->OptionList[Index] == NULL) {
227 //
228 // Error will be handled out of this function.
229 //
230 return EFI_OUT_OF_RESOURCES;
231 }
232
233 CopyMem (
234 DstCfg->OptionList[Index],
235 SorCfg->OptionList[Index],
236 OptionSize
237 );
238 }
239 }
240
241 return EFI_SUCCESS;
242 }
243
244
245 /**
246 Clean up the configure data.
247
248 @param[in, out] CfgData The pointer to the configure data.
249
250 **/
251 VOID
252 Dhcp6CleanupConfigData (
253 IN OUT EFI_DHCP6_CONFIG_DATA *CfgData
254 )
255 {
256 UINTN Index;
257
258 ASSERT (CfgData != NULL);
259 //
260 // Clean up all fields in config data including the reference buffers, but do
261 // not free the config data buffer itself.
262 //
263 if (CfgData->OptionList != NULL) {
264 for (Index = 0; Index < CfgData->OptionCount; Index++) {
265 if (CfgData->OptionList[Index] != NULL) {
266 FreePool (CfgData->OptionList[Index]);
267 }
268 }
269 FreePool (CfgData->OptionList);
270 }
271
272 if (CfgData->SolicitRetransmission != NULL) {
273 FreePool (CfgData->SolicitRetransmission);
274 }
275
276 ZeroMem (CfgData, sizeof (EFI_DHCP6_CONFIG_DATA));
277 }
278
279
280 /**
281 Clean up the mode data.
282
283 @param[in, out] ModeData The pointer to the mode data.
284
285 **/
286 VOID
287 Dhcp6CleanupModeData (
288 IN OUT EFI_DHCP6_MODE_DATA *ModeData
289 )
290 {
291 ASSERT (ModeData != NULL);
292 //
293 // Clean up all fields in mode data including the reference buffers, but do
294 // not free the mode data buffer itself.
295 //
296 if (ModeData->ClientId != NULL) {
297 FreePool (ModeData->ClientId);
298 }
299
300 if (ModeData->Ia != NULL) {
301
302 if (ModeData->Ia->ReplyPacket != NULL) {
303 FreePool (ModeData->Ia->ReplyPacket);
304 }
305 FreePool (ModeData->Ia);
306 }
307
308 ZeroMem (ModeData, sizeof (EFI_DHCP6_MODE_DATA));
309 }
310
311
312 /**
313 Calculate the expire time by the algorithm defined in rfc.
314
315 @param[in] Base The base value of the time.
316 @param[in] IsFirstRt If TRUE, it is the first time to calculate expire time.
317 @param[in] NeedSigned If TRUE, the the signed factor is needed.
318
319 @return Expire The calculated result for the new expire time.
320
321 **/
322 UINT32
323 Dhcp6CalculateExpireTime (
324 IN UINT32 Base,
325 IN BOOLEAN IsFirstRt,
326 IN BOOLEAN NeedSigned
327 )
328 {
329 EFI_TIME Time;
330 BOOLEAN Signed;
331 UINT32 Seed;
332 UINT32 Expire;
333
334 //
335 // Take the 10bits of microsecond in system time as a uniform distribution.
336 // Take the 10th bit as a flag to determine it's signed or not.
337 //
338 gRT->GetTime (&Time, NULL);
339 Seed = ((Time.Nanosecond >> 10) & DHCP6_10_BIT_MASK);
340 Signed = (BOOLEAN) ((((Time.Nanosecond >> 9) & 0x01) != 0) ? TRUE : FALSE);
341 Signed = (BOOLEAN) (NeedSigned ? Signed : FALSE);
342
343 //
344 // Calculate expire by the following algo:
345 // 1. base + base * (-0.1 ~ 0) for the first solicit
346 // 2. base + base * (-0.1 ~ 0.1) for the first other messages
347 // 3. 2 * base + base * (-0.1 ~ 0.1) for the subsequent all messages
348 // 4. base + base * (-0.1 ~ 0) for the more than mrt timeout
349 //
350 // The (Seed / 0x3ff / 10) is used to a random range (0, 0.1).
351 //
352 if (IsFirstRt && Signed) {
353
354 Expire = Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
355
356 } else if (IsFirstRt && !Signed) {
357
358 Expire = Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
359
360 } else if (!IsFirstRt && Signed) {
361
362 Expire = 2 * Base - (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
363
364 } else {
365
366 Expire = 2 * Base + (UINT32) (Base * Seed / DHCP6_10_BIT_MASK / 10);
367 }
368
369 Expire = (Expire != 0) ? Expire : 1;
370
371 return Expire;
372 }
373
374
375 /**
376 Calculate the lease time by the algorithm defined in rfc.
377
378 @param[in] IaCb The pointer to the Ia control block.
379
380 **/
381 VOID
382 Dhcp6CalculateLeaseTime (
383 IN DHCP6_IA_CB *IaCb
384 )
385 {
386 EFI_DHCP6_IA_ADDRESS *IaAddr;
387 UINT32 MinLt;
388 UINT32 MaxLt;
389 UINTN Index;
390
391 ASSERT (IaCb->Ia->IaAddressCount > 0);
392
393 MinLt = (UINT32) (-1);
394 MaxLt = 0;
395
396 //
397 // Calculate minlt as min of all valid life time, and maxlt as max of all
398 // valid life time.
399 //
400 for (Index = 0; Index < IaCb->Ia->IaAddressCount; Index++) {
401 IaAddr = IaCb->Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
402 MinLt = MIN (MinLt, IaAddr->ValidLifetime);
403 MaxLt = MAX (MinLt, IaAddr->ValidLifetime);
404 }
405
406 //
407 // Take 50% minlt as t1, and 80% maxlt as t2 if Dhcp6 server doesn't offer
408 // such information.
409 //
410 IaCb->T1 = (IaCb->T1 != 0) ? IaCb->T1 : (UINT32)(MinLt * 5 / 10);
411 IaCb->T2 = (IaCb->T2 != 0) ? IaCb->T2 : (UINT32)(MinLt * 8 / 10);
412 IaCb->AllExpireTime = MaxLt;
413 IaCb->LeaseTime = 0;
414 }
415
416
417 /**
418 Check whether the addresses are all included by the configured Ia.
419
420 @param[in] Ia The pointer to the Ia.
421 @param[in] AddressCount The number of addresses.
422 @param[in] Addresses The pointer to the addresses buffer.
423
424 @retval EFI_SUCCESS The addresses are all included by the configured IA.
425 @retval EFI_NOT_FOUND The addresses are not included by the configured IA.
426
427 **/
428 EFI_STATUS
429 Dhcp6CheckAddress (
430 IN EFI_DHCP6_IA *Ia,
431 IN UINT32 AddressCount,
432 IN EFI_IPv6_ADDRESS *Addresses
433 )
434 {
435 UINTN Index1;
436 UINTN Index2;
437 BOOLEAN Found;
438
439 //
440 // Check whether the addresses are all included by the configured IA. And it
441 // will return success if address count is zero, which means all addresses.
442 //
443 for (Index1 = 0; Index1 < AddressCount; Index1++) {
444
445 Found = FALSE;
446
447 for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
448
449 if (CompareMem (
450 &Addresses[Index1],
451 &Ia->IaAddress[Index2],
452 sizeof (EFI_IPv6_ADDRESS)
453 ) == 0) {
454
455 Found = TRUE;
456 break;
457 }
458 }
459
460 if (!Found) {
461 return EFI_NOT_FOUND;
462 }
463 }
464
465 return EFI_SUCCESS;
466 }
467
468
469 /**
470 Deprive the addresses from current Ia, and generate another eliminated Ia.
471
472 @param[in] Ia The pointer to the Ia.
473 @param[in] AddressCount The number of addresses.
474 @param[in] Addresses The pointer to the addresses buffer.
475
476 @retval NULL If it failed to generate the deprived Ia.
477 @retval others The pointer to the deprived Ia.
478
479 **/
480 EFI_DHCP6_IA *
481 Dhcp6DepriveAddress (
482 IN EFI_DHCP6_IA *Ia,
483 IN UINT32 AddressCount,
484 IN EFI_IPv6_ADDRESS *Addresses
485 )
486 {
487 EFI_DHCP6_IA *IaCopy;
488 UINTN IaCopySize;
489 UINTN Index1;
490 UINTN Index2;
491 BOOLEAN Found;
492
493 if (AddressCount == 0) {
494 //
495 // It means release all Ia addresses if address count is zero.
496 //
497 AddressCount = Ia->IaAddressCount;
498 }
499
500 ASSERT (AddressCount != 0);
501
502 IaCopySize = sizeof (EFI_DHCP6_IA) + (AddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
503 IaCopy = AllocateZeroPool (IaCopySize);
504
505 if (IaCopy == NULL) {
506 return NULL;
507 }
508
509 if (AddressCount == Ia->IaAddressCount) {
510 //
511 // If release all Ia addresses, just copy the configured Ia and then set
512 // its address count as zero.
513 // We may decline/release part of addresses at the begining. So it's a
514 // forwarding step to update address infor for decline/release, while the
515 // other infor such as Ia state will be updated when receiving reply.
516 //
517 CopyMem (IaCopy, Ia, IaCopySize);
518 Ia->IaAddressCount = 0;
519 return IaCopy;
520 }
521
522 CopyMem (IaCopy, Ia, sizeof (EFI_DHCP6_IA));
523
524 //
525 // Move the addresses from the Ia of instance to the deprived Ia.
526 //
527 for (Index1 = 0; Index1 < AddressCount; Index1++) {
528
529 Found = FALSE;
530
531 for (Index2 = 0; Index2 < Ia->IaAddressCount; Index2++) {
532
533 if (CompareMem (
534 &Addresses[Index1],
535 &Ia->IaAddress[Index2],
536 sizeof (EFI_IPv6_ADDRESS)
537 ) == 0) {
538 //
539 // Copy the deprived address to the copy of Ia
540 //
541 CopyMem (
542 &IaCopy->IaAddress[Index1],
543 &Ia->IaAddress[Index2],
544 sizeof (EFI_DHCP6_IA_ADDRESS)
545 );
546 //
547 // Delete the deprived address from the instance Ia
548 //
549 if (Index2 + 1 < Ia->IaAddressCount) {
550 CopyMem (
551 &Ia->IaAddress[Index2],
552 &Ia->IaAddress[Index2 + 1],
553 (Ia->IaAddressCount - Index2 - 1) * sizeof (EFI_DHCP6_IA_ADDRESS)
554 );
555 }
556 Found = TRUE;
557 break;
558 }
559 }
560 ASSERT (Found == TRUE);
561 }
562
563 Ia->IaAddressCount -= AddressCount;
564 IaCopy->IaAddressCount = AddressCount;
565
566 return IaCopy;
567 }
568
569
570 /**
571 The dummy ext buffer free callback routine.
572
573 @param[in] Arg The pointer to the parameter.
574
575 **/
576 VOID
577 EFIAPI
578 Dhcp6DummyExtFree (
579 IN VOID *Arg
580 )
581 {
582 }
583
584
585 /**
586 The callback routine once message transmitted.
587
588 @param[in] Wrap The pointer to the received net buffer.
589 @param[in] EndPoint The pointer to the udp end point.
590 @param[in] IoStatus The return status from udp io.
591 @param[in] Context The opaque parameter to the function.
592
593 **/
594 VOID
595 EFIAPI
596 Dhcp6OnTransmitted (
597 IN NET_BUF *Wrap,
598 IN UDP_END_POINT *EndPoint,
599 IN EFI_STATUS IoStatus,
600 IN VOID *Context
601 )
602 {
603 NetbufFree (Wrap);
604 }
605
606
607 /**
608 Append the option to Buf, and move Buf to the end.
609
610 @param[in, out] Buf The pointer to the buffer.
611 @param[in] OptType The option type.
612 @param[in] OptLen The length of option contents.
613 @param[in] Data The pointer to the option content.
614
615 @return Buf The position to append the next option.
616
617 **/
618 UINT8 *
619 Dhcp6AppendOption (
620 IN OUT UINT8 *Buf,
621 IN UINT16 OptType,
622 IN UINT16 OptLen,
623 IN UINT8 *Data
624 )
625 {
626 //
627 // The format of Dhcp6 option:
628 //
629 // 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
630 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
631 // | option-code | option-len (option data) |
632 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
633 // | option-data |
634 // | (option-len octets) |
635 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
636 //
637
638 ASSERT (OptLen != 0);
639
640 WriteUnaligned16 ((UINT16 *) Buf, OptType);
641 Buf += 2;
642 WriteUnaligned16 ((UINT16 *) Buf, OptLen);
643 Buf += 2;
644 CopyMem (Buf, Data, NTOHS (OptLen));
645 Buf += NTOHS (OptLen);
646
647 return Buf;
648 }
649
650
651 /**
652 Append the appointed Ia option to Buf, and move Buf to the end.
653
654 @param[in, out] Buf The pointer to the position to append.
655 @param[in] Ia The pointer to the Ia.
656 @param[in] T1 The time of T1.
657 @param[in] T2 The time of T2.
658
659 @return Buf The position to append the next Ia option.
660
661 **/
662 UINT8 *
663 Dhcp6AppendIaOption (
664 IN OUT UINT8 *Buf,
665 IN EFI_DHCP6_IA *Ia,
666 IN UINT32 T1,
667 IN UINT32 T2
668 )
669 {
670 UINT8 *AddrOpt;
671 UINT16 *Len;
672 UINTN Index;
673 UINT16 Length;
674
675 //
676 // The format of IA_NA and IA_TA option:
677 //
678 // 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
679 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
680 // | OPTION_IA_NA | option-len |
681 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
682 // | IAID (4 octets) |
683 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
684 // | T1 (only for IA_NA) |
685 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
686 // | T2 (only for IA_NA) |
687 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
688 // | |
689 // . IA_NA-options/IA_TA-options .
690 // . .
691 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
692 //
693
694 //
695 // Fill the value of Ia option type
696 //
697 WriteUnaligned16 ((UINT16 *) Buf, HTONS (Ia->Descriptor.Type));
698 Buf += 2;
699
700 //
701 // Fill the len of Ia option later, keep the pointer first
702 //
703 Len = (UINT16 *) Buf;
704 Buf += 2;
705
706 //
707 // Fill the value of iaid
708 //
709 WriteUnaligned32 ((UINT32 *) Buf, HTONL (Ia->Descriptor.IaId));
710 Buf += 4;
711
712 //
713 // Fill the value of t1 and t2 if iana, keep it 0xffffffff if no specified.
714 //
715 if (Ia->Descriptor.Type == Dhcp6OptIana) {
716 WriteUnaligned32 ((UINT32 *) Buf, ((T1 != 0) ? T1 : 0xffffffff));
717 Buf += 4;
718 WriteUnaligned32 ((UINT32 *) Buf, ((T2 != 0) ? T2 : 0xffffffff));
719 Buf += 4;
720 }
721
722 //
723 // Fill all the addresses belong to the Ia
724 //
725 for (Index = 0; Index < Ia->IaAddressCount; Index++) {
726
727 AddrOpt = (UINT8 *) Ia->IaAddress + Index * sizeof (EFI_DHCP6_IA_ADDRESS);
728 Length = HTONS ((UINT16) sizeof (EFI_DHCP6_IA_ADDRESS));
729 Buf = Dhcp6AppendOption (
730 Buf,
731 HTONS (Dhcp6OptIaAddr),
732 Length,
733 AddrOpt
734 );
735 }
736
737 //
738 // Fill the value of Ia option length
739 //
740 *Len = HTONS ((UINT16) (Buf - (UINT8 *) Len - 2));
741
742 return Buf;
743 }
744
745 /**
746 Append the appointed Elapsed time option to Buf, and move Buf to the end.
747
748 @param[in, out] Buf The pointer to the position to append.
749 @param[in] Instance The pointer to the Dhcp6 instance.
750 @param[out] Elapsed The pointer to the elapsed time value in
751 the generated packet.
752
753 @return Buf The position to append the next Ia option.
754
755 **/
756 UINT8 *
757 Dhcp6AppendETOption (
758 IN OUT UINT8 *Buf,
759 IN DHCP6_INSTANCE *Instance,
760 OUT UINT16 **Elapsed
761 )
762 {
763 //
764 // The format of elapsed time option:
765 //
766 // 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
767 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
768 // | OPTION_ELAPSED_TIME | option-len |
769 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
770 // | elapsed-time |
771 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
772 //
773
774 //
775 // Fill the value of elapsed-time option type.
776 //
777 WriteUnaligned16 ((UINT16 *) Buf, HTONS (Dhcp6OptElapsedTime));
778 Buf += 2;
779
780 //
781 // Fill the len of elapsed-time option, which is fixed.
782 //
783 WriteUnaligned16 ((UINT16 *) Buf, HTONS(2));
784 Buf += 2;
785
786 //
787 // Fill in elapsed time value with 0 value for now. The actual value is
788 // filled in later just before the packet is transmitted.
789 //
790 WriteUnaligned16 ((UINT16 *) Buf, HTONS(0));
791 *Elapsed = (UINT16 *) Buf;
792 Buf += 2;
793
794 return Buf;
795 }
796
797 /**
798 Set the elapsed time based on the given instance and the pointer to the
799 elapsed time option.
800
801 @param[in] Elapsed The pointer to the position to append.
802 @param[in] Instance The pointer to the Dhcp6 instance.
803
804 **/
805 VOID
806 SetElapsedTime (
807 IN UINT16 *Elapsed,
808 IN DHCP6_INSTANCE *Instance
809 )
810 {
811 EFI_TIME Time;
812 UINT64 CurrentStamp;
813 UINT64 ElapsedTimeValue;
814
815 //
816 // Generate a time stamp of the centiseconds from 2000/1/1, assume 30day/month.
817 //
818 gRT->GetTime (&Time, NULL);
819 CurrentStamp = (UINT64)
820 (
821 ((((((Time.Year - 2000) * 360 +
822 (Time.Month - 1)) * 30 +
823 (Time.Day - 1)) * 24 + Time.Hour) * 60 +
824 Time.Minute) * 60 + Time.Second) * 100
825 + DivU64x32(Time.Nanosecond, 10000000)
826 );
827
828 //
829 // Sentinel value of 0 means that this is the first DHCP packet that we are
830 // sending and that we need to initialize the value. First DHCP Solicit
831 // gets 0 elapsed-time. Otherwise, calculate based on StartTime.
832 //
833 if (Instance->StartTime == 0) {
834 ElapsedTimeValue = 0;
835 Instance->StartTime = CurrentStamp;
836 } else {
837 ElapsedTimeValue = CurrentStamp - Instance->StartTime;
838
839 //
840 // If elapsed time cannot fit in two bytes, set it to 0xffff.
841 //
842 if (ElapsedTimeValue > 0xffff) {
843 ElapsedTimeValue = 0xffff;
844 }
845 }
846 WriteUnaligned16 (Elapsed, HTONS((UINT16) ElapsedTimeValue));
847 }
848
849
850 /**
851 Seek the address of the first byte of the option header.
852
853 @param[in] Buf The pointer to the buffer.
854 @param[in] SeekLen The length to seek.
855 @param[in] OptType The option type.
856
857 @retval NULL If it failed to seek the option.
858 @retval others The position to the option.
859
860 **/
861 UINT8 *
862 Dhcp6SeekOption (
863 IN UINT8 *Buf,
864 IN UINT32 SeekLen,
865 IN UINT16 OptType
866 )
867 {
868 UINT8 *Cursor;
869 UINT8 *Option;
870 UINT16 DataLen;
871 UINT16 OpCode;
872
873 Option = NULL;
874 Cursor = Buf;
875
876 //
877 // The format of Dhcp6 option refers to Dhcp6AppendOption().
878 //
879 while (Cursor < Buf + SeekLen) {
880 OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
881 if (OpCode == HTONS (OptType)) {
882 Option = Cursor;
883 break;
884 }
885 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
886 Cursor += (DataLen + 4);
887 }
888
889 return Option;
890 }
891
892
893 /**
894 Seek the address of the first byte of the Ia option header.
895
896 @param[in] Buf The pointer to the buffer.
897 @param[in] SeekLen The length to seek.
898 @param[in] IaDesc The pointer to the Ia descriptor.
899
900 @retval NULL If it failed to seek the Ia option.
901 @retval others The position to the Ia option.
902
903 **/
904 UINT8 *
905 Dhcp6SeekIaOption (
906 IN UINT8 *Buf,
907 IN UINT32 SeekLen,
908 IN EFI_DHCP6_IA_DESCRIPTOR *IaDesc
909 )
910 {
911 UINT8 *Cursor;
912 UINT8 *Option;
913 UINT16 DataLen;
914 UINT16 OpCode;
915 UINT32 IaId;
916
917 //
918 // The format of IA_NA and IA_TA option refers to Dhcp6AppendIaOption().
919 //
920 Option = NULL;
921 Cursor = Buf;
922
923 while (Cursor < Buf + SeekLen) {
924 OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
925 IaId = ReadUnaligned32 ((UINT32 *) (Cursor + 4));
926 if (OpCode == HTONS (IaDesc->Type) && IaId == HTONL (IaDesc->IaId)) {
927 Option = Cursor;
928 break;
929 }
930 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
931 Cursor += (DataLen + 4);
932 }
933
934 return Option;
935 }
936
937
938 /**
939 Parse the address option and update the address infomation.
940
941 @param[in] IaInnerOpt The pointer to the buffer.
942 @param[in] IaInnerLen The length to parse.
943 @param[out] AddrNum The number of addresses.
944 @param[in, out] AddrBuf The pointer to the address buffer.
945
946 **/
947 VOID
948 Dhcp6ParseAddrOption (
949 IN UINT8 *IaInnerOpt,
950 IN UINT16 IaInnerLen,
951 OUT UINT32 *AddrNum,
952 IN OUT EFI_DHCP6_IA_ADDRESS *AddrBuf
953 )
954 {
955 UINT8 *Cursor;
956 UINT16 DataLen;
957 UINT16 OpCode;
958 UINT32 ValidLt;
959
960 //
961 // The format of the IA Address option:
962 //
963 // 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
964 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
965 // | OPTION_IAADDR | option-len |
966 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
967 // | |
968 // | IPv6 address |
969 // | |
970 // | |
971 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
972 // | preferred-lifetime |
973 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
974 // | valid-lifetime |
975 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
976 // . .
977 // . IAaddr-options .
978 // . .
979 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
980 //
981
982 //
983 // Two usage model:
984 //
985 // 1. Pass addrbuf == null, to get the addrnum over the Ia inner options.
986 // 2. Pass addrbuf != null, to resolve the addresses over the Ia inner
987 // options to the addrbuf.
988 //
989
990 Cursor = IaInnerOpt;
991 *AddrNum = 0;
992
993 while (Cursor < IaInnerOpt + IaInnerLen) {
994 //
995 // Count the Ia address option with non-0 valid time.
996 //
997 OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
998 ValidLt = ReadUnaligned32 ((UINT32 *) (Cursor + 24));
999 if (OpCode == HTONS (Dhcp6OptIaAddr) && ValidLt != 0) {
1000
1001 if (AddrBuf != NULL) {
1002 CopyMem (AddrBuf, Cursor + 4, sizeof (EFI_DHCP6_IA_ADDRESS));
1003 AddrBuf->PreferredLifetime = NTOHL (AddrBuf->PreferredLifetime);
1004 AddrBuf->ValidLifetime = NTOHL (AddrBuf->ValidLifetime);
1005 AddrBuf = (EFI_DHCP6_IA_ADDRESS *) ((UINT8 *) AddrBuf + sizeof (EFI_DHCP6_IA_ADDRESS));
1006 }
1007
1008 (*AddrNum)++;
1009 }
1010 DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
1011 Cursor += (DataLen + 4);
1012 }
1013 }
1014
1015
1016 /**
1017 Create a control blcok for the Ia according to the corresponding options.
1018
1019 @param[in] Instance The pointer to DHCP6 Instance.
1020 @param[in] IaInnerOpt The pointer to the inner options in the Ia option.
1021 @param[in] IaInnerLen The length of all the inner options in the Ia option.
1022 @param[in] T1 T1 time in the Ia option.
1023 @param[in] T2 T2 time in the Ia option.
1024
1025 @retval EFI_NOT_FOUND No valid IA option is found.
1026 @retval EFI_SUCCESS Create an IA control block successfully.
1027 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1028
1029 **/
1030 EFI_STATUS
1031 Dhcp6GenerateIaCb (
1032 IN DHCP6_INSTANCE *Instance,
1033 IN UINT8 *IaInnerOpt,
1034 IN UINT16 IaInnerLen,
1035 IN UINT32 T1,
1036 IN UINT32 T2
1037 )
1038 {
1039 UINT32 AddrNum;
1040 UINT32 IaSize;
1041 EFI_DHCP6_IA *Ia;
1042
1043 if (Instance->IaCb.Ia == NULL) {
1044 return EFI_NOT_FOUND;
1045 }
1046
1047 //
1048 // Calculate the number of addresses for this Ia, excluding the addresses with
1049 // the value 0 of valid lifetime.
1050 //
1051 Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, NULL);
1052
1053 if (AddrNum == 0) {
1054 return EFI_NOT_FOUND;
1055 }
1056
1057 //
1058 // Allocate for new IA.
1059 //
1060 IaSize = sizeof (EFI_DHCP6_IA) + (AddrNum - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
1061 Ia = AllocateZeroPool (IaSize);
1062
1063 if (Ia == NULL) {
1064 return EFI_OUT_OF_RESOURCES;
1065 }
1066
1067 //
1068 // Fill up this new IA fields.
1069 //
1070 Ia->State = Instance->IaCb.Ia->State;
1071 Ia->IaAddressCount = AddrNum;
1072 CopyMem (&Ia->Descriptor, &Instance->Config->IaDescriptor, sizeof (EFI_DHCP6_IA_DESCRIPTOR));
1073 Dhcp6ParseAddrOption (IaInnerOpt, IaInnerLen, &AddrNum, Ia->IaAddress);
1074
1075 //
1076 // Free original IA resource.
1077 //
1078 if (Instance->IaCb.Ia->ReplyPacket != NULL) {
1079 FreePool (Instance->IaCb.Ia->ReplyPacket);
1080 }
1081 FreePool (Instance->IaCb.Ia);
1082
1083
1084 ZeroMem (&Instance->IaCb, sizeof (DHCP6_IA_CB));
1085
1086 //
1087 // Update IaCb to use new IA.
1088 //
1089 Instance->IaCb.Ia = Ia;
1090
1091 //
1092
1093 // Fill in IaCb fields. Such as T1, T2, AllExpireTime and LeaseTime.
1094 //
1095 Instance->IaCb.T1 = T1;
1096 Instance->IaCb.T2 = T2;
1097 Dhcp6CalculateLeaseTime (&Instance->IaCb);
1098
1099 return EFI_SUCCESS;
1100 }
1101
1102
1103 /**
1104 Cache the current IA configuration information.
1105
1106 @param[in] Instance The pointer to DHCP6 Instance.
1107
1108 @retval EFI_SUCCESS Cache the current IA successfully.
1109 @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
1110
1111 **/
1112 EFI_STATUS
1113 Dhcp6CacheIa (
1114 IN DHCP6_INSTANCE *Instance
1115 )
1116 {
1117 UINTN IaSize;
1118 EFI_DHCP6_IA *Ia;
1119
1120 Ia = Instance->IaCb.Ia;
1121
1122 if ((Instance->CacheIa == NULL) && (Ia != NULL)) {
1123 //
1124 // Cache the current IA.
1125 //
1126 IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
1127
1128 Instance->CacheIa = AllocateZeroPool (IaSize);
1129 if (Instance->CacheIa == NULL) {
1130 return EFI_OUT_OF_RESOURCES;
1131 }
1132 CopyMem (Instance->CacheIa, Ia, IaSize);
1133 }
1134 return EFI_SUCCESS;
1135 }
1136
1137 /**
1138 Append CacheIa to the currrent IA. Meanwhile, clear CacheIa.ValidLifetime to 0.
1139
1140 @param[in] Instance The pointer to DHCP6 instance.
1141
1142 **/
1143 VOID
1144 Dhcp6AppendCacheIa (
1145 IN DHCP6_INSTANCE *Instance
1146 )
1147 {
1148 UINT8 *Ptr;
1149 UINTN Index;
1150 UINTN IaSize;
1151 UINTN NewIaSize;
1152 EFI_DHCP6_IA *Ia;
1153 EFI_DHCP6_IA *NewIa;
1154 EFI_DHCP6_IA *CacheIa;
1155
1156 Ia = Instance->IaCb.Ia;
1157 CacheIa = Instance->CacheIa;
1158
1159 if ((CacheIa != NULL) && (CacheIa->IaAddressCount != 0)) {
1160 //
1161 // There are old addresses existing. Merge with current addresses.
1162 //
1163 NewIaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount + CacheIa->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
1164 NewIa = AllocateZeroPool (NewIaSize);
1165 if (NewIa == NULL) {
1166 return;
1167 }
1168
1169 IaSize = sizeof (EFI_DHCP6_IA) + (Ia->IaAddressCount - 1) * sizeof (EFI_DHCP6_IA_ADDRESS);
1170 CopyMem (NewIa, Ia, IaSize);
1171
1172 //
1173 // Clear old address.ValidLifetime
1174 //
1175 for (Index = 0; Index < CacheIa->IaAddressCount; Index++) {
1176 CacheIa->IaAddress[Index].ValidLifetime = 0;
1177 }
1178
1179 NewIa->IaAddressCount += CacheIa->IaAddressCount;
1180 Ptr = (UINT8*)&NewIa->IaAddress[Ia->IaAddressCount];
1181 CopyMem (Ptr, CacheIa->IaAddress, CacheIa->IaAddressCount * sizeof (EFI_DHCP6_IA_ADDRESS));
1182
1183 //
1184 // Migrate to the NewIa and free previous.
1185 //
1186 FreePool (Instance->CacheIa);
1187 FreePool (Instance->IaCb.Ia);
1188 Instance->CacheIa = NULL;
1189 Instance->IaCb.Ia = NewIa;
1190 }
1191 }