1 /** @file
2 Dhcp6 internal functions implementation.
3
4 (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
5 Copyright (c) 2009 - 2016, 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 **/
35 EFI_STATUS
Dhcp6EnqueueRetry(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet,IN UINT16 * Elapsed,IN EFI_DHCP6_RETRANSMISSION * RetryCtl OPTIONAL)36 Dhcp6EnqueueRetry (
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 **/
236 EFI_STATUS
Dhcp6DequeueRetry(IN DHCP6_INSTANCE * Instance,IN UINT32 PacketXid,IN BOOLEAN NeedSignal)237 Dhcp6DequeueRetry (
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 **/
302 VOID
Dhcp6CleanupRetry(IN DHCP6_INSTANCE * Instance,IN UINT32 Scope)303 Dhcp6CleanupRetry (
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 **/
377 BOOLEAN
Dhcp6IsValidTxCb(IN DHCP6_INSTANCE * Instance,IN DHCP6_TX_CB * TxCb)378 Dhcp6IsValidTxCb (
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 **/
401 VOID
Dhcp6CleanupSession(IN OUT DHCP6_INSTANCE * Instance,IN EFI_STATUS Status)402 Dhcp6CleanupSession (
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 **/
482 EFI_STATUS
483 EFIAPI
Dhcp6CallbackUser(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_EVENT Event,IN OUT EFI_DHCP6_PACKET ** Packet)484 Dhcp6CallbackUser (
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 **/
541 EFI_STATUS
Dhcp6UpdateIaInfo(IN OUT DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet)542 Dhcp6UpdateIaInfo (
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 **/
696 EFI_STATUS
Dhcp6SeekStsOption(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet,OUT UINT8 ** Option)697 Dhcp6SeekStsOption (
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 **/
828 EFI_STATUS
Dhcp6TransmitPacket(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet,IN UINT16 * Elapsed)829 Dhcp6TransmitPacket (
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 **/
918 EFI_STATUS
Dhcp6SendSolicitMsg(IN DHCP6_INSTANCE * Instance)919 Dhcp6SendSolicitMsg (
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 **/
1057 EFI_STATUS
Dhcp6InitSolicitMsg(IN DHCP6_INSTANCE * Instance)1058 Dhcp6InitSolicitMsg (
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 **/
1081 EFI_STATUS
Dhcp6SendRequestMsg(IN DHCP6_INSTANCE * Instance)1082 Dhcp6SendRequestMsg (
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 **/
1244 EFI_STATUS
Dhcp6SendDeclineMsg(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_IA * DecIa)1245 Dhcp6SendDeclineMsg (
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 **/
1382 EFI_STATUS
Dhcp6SendReleaseMsg(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_IA * RelIa)1383 Dhcp6SendReleaseMsg (
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 **/
1516 EFI_STATUS
Dhcp6SendRenewRebindMsg(IN DHCP6_INSTANCE * Instance,IN BOOLEAN RebindRequest)1517 Dhcp6SendRenewRebindMsg (
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 **/
1702 EFI_STATUS
Dhcp6StartInfoRequest(IN DHCP6_INSTANCE * Instance,IN BOOLEAN SendClientId,IN EFI_DHCP6_PACKET_OPTION * OptionRequest,IN UINT32 OptionCount,IN EFI_DHCP6_PACKET_OPTION * OptionList[]OPTIONAL,IN EFI_DHCP6_RETRANSMISSION * Retransmission,IN EFI_EVENT TimeoutEvent OPTIONAL,IN EFI_DHCP6_INFO_CALLBACK ReplyCallback,IN VOID * CallbackContext OPTIONAL)1703 Dhcp6StartInfoRequest (
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
1774 ON_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 **/
1800 EFI_STATUS
Dhcp6SendInfoRequestMsg(IN DHCP6_INSTANCE * Instance,IN DHCP6_INF_CB * InfCb,IN BOOLEAN SendClientId,IN EFI_DHCP6_PACKET_OPTION * OptionRequest,IN UINT32 OptionCount,IN EFI_DHCP6_PACKET_OPTION * OptionList[],IN EFI_DHCP6_RETRANSMISSION * Retransmission)1801 Dhcp6SendInfoRequestMsg (
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 **/
1933 EFI_STATUS
Dhcp6SendConfirmMsg(IN DHCP6_INSTANCE * Instance)1934 Dhcp6SendConfirmMsg (
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 **/
2071 EFI_STATUS
Dhcp6HandleReplyMsg(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet)2072 Dhcp6HandleReplyMsg (
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
2312 ON_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 **/
2337 EFI_STATUS
Dhcp6SelectAdvertiseMsg(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * AdSelect)2338 Dhcp6SelectAdvertiseMsg (
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 **/
2422 EFI_STATUS
Dhcp6HandleAdvertiseMsg(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet)2423 Dhcp6HandleAdvertiseMsg (
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 **/
2577 VOID
Dhcp6HandleStateful(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet)2578 Dhcp6HandleStateful (
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
2665 ON_CONTINUE:
2666 //
2667 // Continue to receive the following Dhcp6 message.
2668 //
2669 Status = UdpIoRecvDatagram (
2670 Service->UdpIo,
2671 Dhcp6ReceivePacket,
2672 Service,
2673 0
2674 );
2675 ON_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 **/
2689 VOID
Dhcp6HandleStateless(IN DHCP6_INSTANCE * Instance,IN EFI_DHCP6_PACKET * Packet)2690 Dhcp6HandleStateless (
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
2770 ON_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 **/
2794 VOID
2795 EFIAPI
Dhcp6ReceivePacket(IN NET_BUF * Udp6Wrap,IN UDP_END_POINT * EndPoint,IN EFI_STATUS IoStatus,IN VOID * Context)2796 Dhcp6ReceivePacket (
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 if (Udp6Wrap->TotalSize < sizeof (EFI_DHCP6_HEADER)) {
2831 goto ON_CONTINUE;
2832 }
2833
2834 //
2835 // Copy the net buffer received from upd6 to a Dhcp6 packet.
2836 //
2837 Size = sizeof (EFI_DHCP6_PACKET) + Udp6Wrap->TotalSize;
2838 Packet = (EFI_DHCP6_PACKET *) AllocateZeroPool (Size);
2839
2840 if (Packet == NULL) {
2841 goto ON_CONTINUE;
2842 }
2843
2844 Packet->Size = Size;
2845 Head = &Packet->Dhcp6.Header;
2846 Packet->Length = NetbufCopy (Udp6Wrap, 0, Udp6Wrap->TotalSize, (UINT8 *) Head);
2847
2848 if (Packet->Length == 0) {
2849 goto ON_CONTINUE;
2850 }
2851
2852 //
2853 // Dispatch packet to right instance by transaction id.
2854 //
2855 NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
2856
2857 Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
2858
2859 NET_LIST_FOR_EACH_SAFE (Entry2, Next2, &Instance->TxList) {
2860
2861 TxCb = NET_LIST_USER_STRUCT (Entry2, DHCP6_TX_CB, Link);
2862
2863 if (Packet->Dhcp6.Header.TransactionId == TxCb->Xid) {
2864 //
2865 // Find the corresponding packet in tx list, and check it whether belongs
2866 // to stateful exchange process.
2867 //
2868 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
2869 IsStateless = TRUE;
2870 }
2871 IsDispatched = TRUE;
2872 break;
2873 }
2874 }
2875
2876 if (IsDispatched) {
2877 break;
2878 }
2879 }
2880
2881 //
2882 // Skip this packet if not dispatched to any instance.
2883 //
2884 if (!IsDispatched) {
2885 goto ON_CONTINUE;
2886 }
2887
2888 //
2889 // Dispatch the received packet ot the right instance.
2890 //
2891 if (IsStateless) {
2892 Dhcp6HandleStateless (Instance, Packet);
2893 } else {
2894 Dhcp6HandleStateful (Instance, Packet);
2895 }
2896
2897 ON_CONTINUE:
2898
2899 if (!IsDispatched) {
2900 Status = UdpIoRecvDatagram (
2901 Service->UdpIo,
2902 Dhcp6ReceivePacket,
2903 Service,
2904 0
2905 );
2906 if (EFI_ERROR (Status)) {
2907 NET_LIST_FOR_EACH_SAFE (Entry1, Next1, &Service->Child) {
2908 Instance = NET_LIST_USER_STRUCT (Entry1, DHCP6_INSTANCE, Link);
2909 Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);
2910 }
2911 }
2912 }
2913
2914 NetbufFree (Udp6Wrap);
2915
2916 if (Packet != NULL) {
2917 FreePool (Packet);
2918 }
2919 }
2920
2921 /**
2922 Detect Link movement for specified network device.
2923
2924 This routine will try to invoke Snp->GetStatus() to get the media status.
2925 If media present status switches from unpresent to present, a link movement
2926 is detected. Note that the underlying UNDI driver may not support reporting
2927 media status from GET_STATUS command. If that, fail to detect link movement.
2928
2929 @param[in] Instance The pointer to DHCP6_INSTANCE.
2930
2931 @retval TRUE A link movement is detected.
2932 @retval FALSE A link movement is not detected.
2933
2934 **/
2935 BOOLEAN
Dhcp6LinkMovDetect(IN DHCP6_INSTANCE * Instance)2936 Dhcp6LinkMovDetect (
2937 IN DHCP6_INSTANCE *Instance
2938 )
2939 {
2940 UINT32 InterruptStatus;
2941 BOOLEAN MediaPresent;
2942 EFI_STATUS Status;
2943 EFI_SIMPLE_NETWORK_PROTOCOL *Snp;
2944
2945 ASSERT (Instance != NULL);
2946 Snp = Instance->Service->Snp;
2947 MediaPresent = Instance->MediaPresent;
2948
2949 //
2950 // Check whether SNP support media detection
2951 //
2952 if (!Snp->Mode->MediaPresentSupported) {
2953 return FALSE;
2954 }
2955
2956 //
2957 // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data
2958 //
2959 Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);
2960 if (EFI_ERROR (Status)) {
2961 return FALSE;
2962 }
2963
2964 Instance->MediaPresent = Snp->Mode->MediaPresent;
2965 //
2966 // Media transimit Unpresent to Present means new link movement is detected.
2967 //
2968 if (!MediaPresent && Instance->MediaPresent) {
2969 return TRUE;
2970 }
2971 return FALSE;
2972 }
2973
2974
2975 /**
2976 The timer routine of the Dhcp6 instance for each second.
2977
2978 @param[in] Event The timer event.
2979 @param[in] Context The opaque parameter to the function.
2980
2981 **/
2982 VOID
2983 EFIAPI
Dhcp6OnTimerTick(IN EFI_EVENT Event,IN VOID * Context)2984 Dhcp6OnTimerTick (
2985 IN EFI_EVENT Event,
2986 IN VOID *Context
2987 )
2988 {
2989 LIST_ENTRY *Entry;
2990 LIST_ENTRY *NextEntry;
2991 DHCP6_INSTANCE *Instance;
2992 DHCP6_TX_CB *TxCb;
2993 DHCP6_IA_CB *IaCb;
2994 UINT32 LossTime;
2995 EFI_STATUS Status;
2996
2997 ASSERT (Context != NULL);
2998
2999 Instance = (DHCP6_INSTANCE *) Context;
3000
3001 //
3002 // 1. Loop the tx list, count live time of every tx packet to check whether
3003 // need re-transmit or not.
3004 //
3005 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Instance->TxList) {
3006
3007 TxCb = NET_LIST_USER_STRUCT (Entry, DHCP6_TX_CB, Link);
3008
3009 TxCb->TickTime++;
3010
3011 if (TxCb->TickTime > TxCb->RetryExp) {
3012 //
3013 // Handle the first rt in the transmission of solicit specially.
3014 //
3015 if ((TxCb->RetryCnt == 0 || TxCb->SolicitRetry) && TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
3016 if (Instance->AdSelect == NULL) {
3017 //
3018 // Set adpref as 0xff here to indicate select any advertisement
3019 // afterwards.
3020 //
3021 Instance->AdPref = 0xff;
3022 } else {
3023 //
3024 // Select the advertisement received before.
3025 //
3026 Status = Dhcp6SelectAdvertiseMsg (Instance, Instance->AdSelect);
3027 if (Status == EFI_ABORTED) {
3028 goto ON_CLOSE;
3029 } else if (EFI_ERROR (Status)) {
3030 TxCb->RetryCnt++;
3031 }
3032 return;
3033 }
3034 }
3035 //
3036 // Increase the retry count for the packet and add up the total loss time.
3037 //
3038 TxCb->RetryCnt++;
3039 TxCb->RetryLos += TxCb->RetryExp;
3040
3041 //
3042 // Check whether overflow the max retry count limit for this packet
3043 //
3044 if (TxCb->RetryCtl.Mrc != 0 && TxCb->RetryCtl.Mrc < TxCb->RetryCnt) {
3045 Status = EFI_NO_RESPONSE;
3046 goto ON_CLOSE;
3047 }
3048
3049 //
3050 // Check whether overflow the max retry duration for this packet
3051 //
3052 if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd <= TxCb->RetryLos) {
3053 Status = EFI_NO_RESPONSE;
3054 goto ON_CLOSE;
3055 }
3056
3057 //
3058 // Re-calculate retry expire timeout for the next time.
3059 //
3060 // Firstly, Check the new calculated time whether overflow the max retry
3061 // expire time.
3062 //
3063 TxCb->RetryExp = Dhcp6CalculateExpireTime (
3064 TxCb->RetryExp,
3065 FALSE,
3066 TRUE
3067 );
3068
3069 if (TxCb->RetryCtl.Mrt != 0 && TxCb->RetryCtl.Mrt < TxCb->RetryExp) {
3070 TxCb->RetryExp = Dhcp6CalculateExpireTime (
3071 TxCb->RetryCtl.Mrt,
3072 TRUE,
3073 TRUE
3074 );
3075 }
3076
3077 //
3078 // Secondly, Check the new calculated time whether overflow the max retry
3079 // duration time.
3080 //
3081 LossTime = TxCb->RetryLos + TxCb->RetryExp;
3082 if (TxCb->RetryCtl.Mrd != 0 && TxCb->RetryCtl.Mrd < LossTime) {
3083 TxCb->RetryExp = TxCb->RetryCtl.Mrd - TxCb->RetryLos;
3084 }
3085
3086 //
3087 // Reset the tick time for the next retransmission
3088 //
3089 TxCb->TickTime = 0;
3090
3091 //
3092 // Retransmit the last sent packet again.
3093 //
3094 Dhcp6TransmitPacket (Instance, TxCb->TxPacket, TxCb->Elapsed);
3095 TxCb->SolicitRetry = FALSE;
3096 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgSolicit) {
3097 TxCb->SolicitRetry = TRUE;
3098 }
3099 }
3100 }
3101
3102 //
3103 // 2. Check the configured Ia, count lease time of every valid Ia to check
3104 // whether need to renew or rebind this Ia.
3105 //
3106 IaCb = &Instance->IaCb;
3107
3108 if (Instance->Config == NULL || IaCb->Ia == NULL) {
3109 return;
3110 }
3111
3112 if (IaCb->Ia->State == Dhcp6Bound || IaCb->Ia->State == Dhcp6Renewing || IaCb->Ia->State == Dhcp6Rebinding) {
3113
3114 IaCb->LeaseTime++;
3115
3116 if (IaCb->LeaseTime > IaCb->T2 && IaCb->Ia->State == Dhcp6Bound) {
3117 //
3118 // Exceed t2, send rebind packet to extend the Ia lease.
3119 //
3120 Dhcp6SendRenewRebindMsg (Instance, TRUE);
3121
3122 } else if (IaCb->LeaseTime > IaCb->T1 && IaCb->Ia->State == Dhcp6Bound) {
3123
3124 //
3125 // Exceed t1, send renew packet to extend the Ia lease.
3126 //
3127 Dhcp6SendRenewRebindMsg (Instance, FALSE);
3128 }
3129 }
3130
3131 //
3132 // 3. In any situation when a client may have moved to a new link, the
3133 // client MUST initiate a Confirm/Reply message exchange.
3134 //
3135 if (Dhcp6LinkMovDetect (Instance) && (IaCb->Ia->State == Dhcp6Bound)) {
3136 Dhcp6SendConfirmMsg (Instance);
3137 }
3138
3139 return;
3140
3141 ON_CLOSE:
3142
3143 if (Dhcp6IsValidTxCb (Instance, TxCb) &&
3144 TxCb->TxPacket != NULL &&
3145 (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest ||
3146 TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew ||
3147 TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)
3148 ) {
3149 //
3150 // The failure of renew/Confirm will still switch to the bound state.
3151 //
3152 if ((TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgRenew) ||
3153 (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgConfirm)) {
3154 ASSERT (Instance->IaCb.Ia);
3155 Instance->IaCb.Ia->State = Dhcp6Bound;
3156 }
3157 //
3158 // The failure of info-request will return no response.
3159 //
3160 if (TxCb->TxPacket->Dhcp6.Header.MessageType == Dhcp6MsgInfoRequest) {
3161 Instance->UdpSts = EFI_NO_RESPONSE;
3162 }
3163 Dhcp6DequeueRetry (
3164 Instance,
3165 TxCb->Xid,
3166 TRUE
3167 );
3168 } else {
3169 //
3170 // The failure of the others will terminate current state machine if timeout.
3171 //
3172 Dhcp6CleanupSession (Instance, Status);
3173 }
3174 }
3175