• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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