1 /** @file
2   Implementation of Neighbor Discovery support routines.
3 
4   Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
5 
6   This program and the accompanying materials
7   are licensed and made available under the terms and conditions of the BSD License
8   which accompanies this distribution.  The full text of the license may be found at
9   http://opensource.org/licenses/bsd-license.php.
10 
11   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 
14 **/
15 
16 #include "Ip6Impl.h"
17 
18 EFI_MAC_ADDRESS mZeroMacAddress;
19 
20 /**
21   Update the ReachableTime in IP6 service binding instance data, in milliseconds.
22 
23   @param[in, out] IpSb     Points to the IP6_SERVICE.
24 
25 **/
26 VOID
Ip6UpdateReachableTime(IN OUT IP6_SERVICE * IpSb)27 Ip6UpdateReachableTime (
28   IN OUT IP6_SERVICE  *IpSb
29   )
30 {
31   UINT32              Random;
32 
33   Random = (NetRandomInitSeed () / 4294967295UL) * IP6_RANDOM_FACTOR_SCALE;
34   Random = Random + IP6_MIN_RANDOM_FACTOR_SCALED;
35   IpSb->ReachableTime = (IpSb->BaseReachableTime * Random) / IP6_RANDOM_FACTOR_SCALE;
36 }
37 
38 /**
39   Build a array of EFI_IP6_NEIGHBOR_CACHE to be returned to the caller. The number
40   of EFI_IP6_NEIGHBOR_CACHE is also returned.
41 
42   @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.
43   @param[out] NeighborCount     The number of returned neighbor cache entries.
44   @param[out] NeighborCache     The pointer to the array of EFI_IP6_NEIGHBOR_CACHE.
45 
46   @retval EFI_SUCCESS           The EFI_IP6_NEIGHBOR_CACHE successfully built.
47   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the route table.
48 
49 **/
50 EFI_STATUS
Ip6BuildEfiNeighborCache(IN IP6_PROTOCOL * IpInstance,OUT UINT32 * NeighborCount,OUT EFI_IP6_NEIGHBOR_CACHE ** NeighborCache)51 Ip6BuildEfiNeighborCache (
52   IN IP6_PROTOCOL            *IpInstance,
53   OUT UINT32                 *NeighborCount,
54   OUT EFI_IP6_NEIGHBOR_CACHE **NeighborCache
55   )
56 {
57   IP6_NEIGHBOR_ENTRY        *Neighbor;
58   LIST_ENTRY                *Entry;
59   IP6_SERVICE               *IpSb;
60   UINT32                    Count;
61   EFI_IP6_NEIGHBOR_CACHE    *EfiNeighborCache;
62   EFI_IP6_NEIGHBOR_CACHE    *NeighborCacheTmp;
63 
64   NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
65   ASSERT (NeighborCount != NULL && NeighborCache != NULL);
66 
67   IpSb  = IpInstance->Service;
68   Count = 0;
69 
70   NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
71     Count++;
72   }
73 
74   if (Count == 0) {
75     return EFI_SUCCESS;
76   }
77 
78   NeighborCacheTmp = AllocatePool (Count * sizeof (EFI_IP6_NEIGHBOR_CACHE));
79   if (NeighborCacheTmp == NULL) {
80     return EFI_OUT_OF_RESOURCES;
81   }
82 
83   *NeighborCount = Count;
84   Count          = 0;
85 
86   NET_LIST_FOR_EACH (Entry, &IpSb->NeighborTable) {
87     Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
88 
89     EfiNeighborCache = NeighborCacheTmp + Count;
90 
91    EfiNeighborCache->State = Neighbor->State;
92     IP6_COPY_ADDRESS (&EfiNeighborCache->Neighbor, &Neighbor->Neighbor);
93     IP6_COPY_LINK_ADDRESS (&EfiNeighborCache->LinkAddress, &Neighbor->LinkAddress);
94 
95     Count++;
96   }
97 
98   ASSERT (*NeighborCount == Count);
99   *NeighborCache = NeighborCacheTmp;
100 
101   return EFI_SUCCESS;
102 }
103 
104 /**
105   Build a array of EFI_IP6_ADDRESS_INFO to be returned to the caller. The number
106   of prefix entries is also returned.
107 
108   @param[in]  IpInstance        The pointer to IP6_PROTOCOL instance.
109   @param[out] PrefixCount       The number of returned prefix entries.
110   @param[out] PrefixTable       The pointer to the array of PrefixTable.
111 
112   @retval EFI_SUCCESS           The prefix table successfully built.
113   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the prefix table.
114 
115 **/
116 EFI_STATUS
Ip6BuildPrefixTable(IN IP6_PROTOCOL * IpInstance,OUT UINT32 * PrefixCount,OUT EFI_IP6_ADDRESS_INFO ** PrefixTable)117 Ip6BuildPrefixTable (
118   IN IP6_PROTOCOL           *IpInstance,
119   OUT UINT32                *PrefixCount,
120   OUT EFI_IP6_ADDRESS_INFO  **PrefixTable
121   )
122 {
123   LIST_ENTRY                *Entry;
124   IP6_SERVICE               *IpSb;
125   UINT32                    Count;
126   IP6_PREFIX_LIST_ENTRY     *PrefixList;
127   EFI_IP6_ADDRESS_INFO      *EfiPrefix;
128   EFI_IP6_ADDRESS_INFO      *PrefixTableTmp;
129 
130   NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);
131   ASSERT (PrefixCount != NULL && PrefixTable != NULL);
132 
133   IpSb  = IpInstance->Service;
134   Count = 0;
135 
136   NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
137     Count++;
138   }
139 
140   if (Count == 0) {
141     return EFI_SUCCESS;
142   }
143 
144   PrefixTableTmp = AllocatePool (Count * sizeof (EFI_IP6_ADDRESS_INFO));
145   if (PrefixTableTmp == NULL) {
146     return EFI_OUT_OF_RESOURCES;
147   }
148 
149   *PrefixCount = Count;
150   Count        = 0;
151 
152   NET_LIST_FOR_EACH (Entry, &IpSb->OnlinkPrefix) {
153     PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
154     EfiPrefix  = PrefixTableTmp + Count;
155     IP6_COPY_ADDRESS (&EfiPrefix->Address, &PrefixList->Prefix);
156     EfiPrefix->PrefixLength = PrefixList->PrefixLength;
157 
158     Count++;
159   }
160 
161   ASSERT (*PrefixCount == Count);
162   *PrefixTable = PrefixTableTmp;
163 
164   return EFI_SUCCESS;
165 }
166 
167 /**
168   Allocate and initialize a IP6 prefix list entry.
169 
170   @param[in]  IpSb              The pointer to IP6_SERVICE instance.
171   @param[in]  OnLinkOrAuto      If TRUE, the entry is created for the on link prefix list.
172                                 Otherwise, it is created for the autoconfiguration prefix list.
173   @param[in]  ValidLifetime     The length of time in seconds that the prefix
174                                 is valid for the purpose of on-link determination.
175   @param[in]  PreferredLifetime The length of time in seconds that addresses
176                                 generated from the prefix via stateless address
177                                 autoconfiguration remain preferred.
178   @param[in]  PrefixLength      The prefix length of the Prefix.
179   @param[in]  Prefix            The prefix address.
180 
181   @return NULL if it failed to allocate memory for the prefix node. Otherwise, point
182           to the created or existing prefix list entry.
183 
184 **/
185 IP6_PREFIX_LIST_ENTRY *
Ip6CreatePrefixListEntry(IN IP6_SERVICE * IpSb,IN BOOLEAN OnLinkOrAuto,IN UINT32 ValidLifetime,IN UINT32 PreferredLifetime,IN UINT8 PrefixLength,IN EFI_IPv6_ADDRESS * Prefix)186 Ip6CreatePrefixListEntry (
187   IN IP6_SERVICE            *IpSb,
188   IN BOOLEAN                OnLinkOrAuto,
189   IN UINT32                 ValidLifetime,
190   IN UINT32                 PreferredLifetime,
191   IN UINT8                  PrefixLength,
192   IN EFI_IPv6_ADDRESS       *Prefix
193   )
194 {
195   IP6_PREFIX_LIST_ENTRY     *PrefixEntry;
196   IP6_ROUTE_ENTRY           *RtEntry;
197   LIST_ENTRY                *ListHead;
198   LIST_ENTRY                *Entry;
199   IP6_PREFIX_LIST_ENTRY     *TmpPrefixEntry;
200 
201   if (Prefix == NULL || PreferredLifetime > ValidLifetime || PrefixLength >= IP6_PREFIX_NUM) {
202     return NULL;
203   }
204 
205   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
206 
207   PrefixEntry = Ip6FindPrefixListEntry (
208                   IpSb,
209                   OnLinkOrAuto,
210                   PrefixLength,
211                   Prefix
212                   );
213   if (PrefixEntry != NULL) {
214     PrefixEntry->RefCnt ++;
215     return PrefixEntry;
216   }
217 
218   PrefixEntry = AllocatePool (sizeof (IP6_PREFIX_LIST_ENTRY));
219   if (PrefixEntry == NULL) {
220     return NULL;
221   }
222 
223   PrefixEntry->RefCnt            = 1;
224   PrefixEntry->ValidLifetime     = ValidLifetime;
225   PrefixEntry->PreferredLifetime = PreferredLifetime;
226   PrefixEntry->PrefixLength      = PrefixLength;
227   IP6_COPY_ADDRESS (&PrefixEntry->Prefix, Prefix);
228 
229   ListHead = OnLinkOrAuto ? &IpSb->OnlinkPrefix : &IpSb->AutonomousPrefix;
230 
231   //
232   // Create a direct route entry for on-link prefix and insert to route area.
233   //
234   if (OnLinkOrAuto) {
235     RtEntry = Ip6CreateRouteEntry (Prefix, PrefixLength, NULL);
236     if (RtEntry == NULL) {
237       FreePool (PrefixEntry);
238       return NULL;
239     }
240 
241     RtEntry->Flag = IP6_DIRECT_ROUTE;
242     InsertHeadList (&IpSb->RouteTable->RouteArea[PrefixLength], &RtEntry->Link);
243     IpSb->RouteTable->TotalNum++;
244   }
245 
246   //
247   // Insert the prefix entry in the order that a prefix with longer prefix length
248   // is put ahead in the list.
249   //
250   NET_LIST_FOR_EACH (Entry, ListHead) {
251     TmpPrefixEntry = NET_LIST_USER_STRUCT(Entry, IP6_PREFIX_LIST_ENTRY, Link);
252 
253     if (TmpPrefixEntry->PrefixLength < PrefixEntry->PrefixLength) {
254       break;
255     }
256   }
257 
258   NetListInsertBefore (Entry, &PrefixEntry->Link);
259 
260   return PrefixEntry;
261 }
262 
263 /**
264   Destroy a IP6 prefix list entry.
265 
266   @param[in]  IpSb              The pointer to IP6_SERVICE instance.
267   @param[in]  PrefixEntry       The to be destroyed prefix list entry.
268   @param[in]  OnLinkOrAuto      If TRUE, the entry is removed from on link prefix list.
269                                 Otherwise remove from autoconfiguration prefix list.
270   @param[in]  ImmediateDelete   If TRUE, remove the entry directly.
271                                 Otherwise, check the reference count to see whether
272                                 it should be removed.
273 
274 **/
275 VOID
Ip6DestroyPrefixListEntry(IN IP6_SERVICE * IpSb,IN IP6_PREFIX_LIST_ENTRY * PrefixEntry,IN BOOLEAN OnLinkOrAuto,IN BOOLEAN ImmediateDelete)276 Ip6DestroyPrefixListEntry (
277   IN IP6_SERVICE            *IpSb,
278   IN IP6_PREFIX_LIST_ENTRY  *PrefixEntry,
279   IN BOOLEAN                OnLinkOrAuto,
280   IN BOOLEAN                ImmediateDelete
281   )
282 {
283   LIST_ENTRY      *Entry;
284   IP6_INTERFACE   *IpIf;
285   EFI_STATUS      Status;
286 
287   if ((!ImmediateDelete) && (PrefixEntry->RefCnt > 0) && ((--PrefixEntry->RefCnt) > 0)) {
288     return ;
289   }
290 
291   if (OnLinkOrAuto) {
292       //
293       // Remove the direct route for onlink prefix from route table.
294       //
295       do {
296         Status = Ip6DelRoute (
297                    IpSb->RouteTable,
298                    &PrefixEntry->Prefix,
299                    PrefixEntry->PrefixLength,
300                    NULL
301                    );
302       } while (Status != EFI_NOT_FOUND);
303   } else {
304     //
305     // Remove the corresponding addresses generated from this autonomous prefix.
306     //
307     NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
308       IpIf = NET_LIST_USER_STRUCT_S (Entry, IP6_INTERFACE, Link, IP6_INTERFACE_SIGNATURE);
309 
310       Ip6RemoveAddr (IpSb, &IpIf->AddressList, &IpIf->AddressCount, &PrefixEntry->Prefix, PrefixEntry->PrefixLength);
311     }
312   }
313 
314   RemoveEntryList (&PrefixEntry->Link);
315   FreePool (PrefixEntry);
316 }
317 
318 /**
319   Search the list array to find an IP6 prefix list entry.
320 
321   @param[in]  IpSb              The pointer to IP6_SERVICE instance.
322   @param[in]  OnLinkOrAuto      If TRUE, the search the link prefix list,
323                                 Otherwise search the autoconfiguration prefix list.
324   @param[in]  PrefixLength      The prefix length of the Prefix
325   @param[in]  Prefix            The prefix address.
326 
327   @return NULL if cannot find the IP6 prefix list entry. Otherwise, return the
328           pointer to the IP6 prefix list entry.
329 
330 **/
331 IP6_PREFIX_LIST_ENTRY *
Ip6FindPrefixListEntry(IN IP6_SERVICE * IpSb,IN BOOLEAN OnLinkOrAuto,IN UINT8 PrefixLength,IN EFI_IPv6_ADDRESS * Prefix)332 Ip6FindPrefixListEntry (
333   IN IP6_SERVICE            *IpSb,
334   IN BOOLEAN                OnLinkOrAuto,
335   IN UINT8                  PrefixLength,
336   IN EFI_IPv6_ADDRESS       *Prefix
337   )
338 {
339   IP6_PREFIX_LIST_ENTRY     *PrefixList;
340   LIST_ENTRY                *Entry;
341   LIST_ENTRY                *ListHead;
342 
343   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
344   ASSERT (Prefix != NULL);
345 
346   if (OnLinkOrAuto) {
347     ListHead = &IpSb->OnlinkPrefix;
348   } else {
349     ListHead = &IpSb->AutonomousPrefix;
350   }
351 
352   NET_LIST_FOR_EACH (Entry, ListHead) {
353     PrefixList = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
354     if (PrefixLength != 255) {
355       //
356       // Perform exactly prefix match.
357       //
358       if (PrefixList->PrefixLength == PrefixLength &&
359         NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixLength)) {
360         return PrefixList;
361       }
362     } else {
363       //
364       // Perform the longest prefix match. The list is already sorted with
365       // the longest length prefix put at the head of the list.
366       //
367       if (NetIp6IsNetEqual (&PrefixList->Prefix, Prefix, PrefixList->PrefixLength)) {
368         return PrefixList;
369       }
370     }
371   }
372 
373   return NULL;
374 }
375 
376 /**
377   Release the resource in the prefix list table, and destroy the list entry and
378   corresponding addresses or route entries.
379 
380   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
381   @param[in]  ListHead          The list entry head of the prefix list table.
382 
383 **/
384 VOID
Ip6CleanPrefixListTable(IN IP6_SERVICE * IpSb,IN LIST_ENTRY * ListHead)385 Ip6CleanPrefixListTable (
386   IN IP6_SERVICE            *IpSb,
387   IN LIST_ENTRY             *ListHead
388   )
389 {
390   IP6_PREFIX_LIST_ENTRY     *PrefixList;
391   BOOLEAN                   OnLink;
392 
393   OnLink = (BOOLEAN) (ListHead == &IpSb->OnlinkPrefix);
394 
395   while (!IsListEmpty (ListHead)) {
396     PrefixList = NET_LIST_HEAD (ListHead, IP6_PREFIX_LIST_ENTRY, Link);
397     Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
398   }
399 }
400 
401 /**
402   Callback function when address resolution is finished. It will cancel
403   all the queued frames if the address resolution failed, or transmit them
404   if the request succeeded.
405 
406   @param[in] Context The context of the callback, a pointer to IP6_NEIGHBOR_ENTRY.
407 
408 **/
409 VOID
Ip6OnArpResolved(IN VOID * Context)410 Ip6OnArpResolved (
411   IN VOID                   *Context
412   )
413 {
414   LIST_ENTRY                *Entry;
415   LIST_ENTRY                *Next;
416   IP6_NEIGHBOR_ENTRY        *ArpQue;
417   IP6_SERVICE               *IpSb;
418   IP6_LINK_TX_TOKEN         *Token;
419   EFI_STATUS                Status;
420   BOOLEAN                   Sent;
421 
422   ArpQue = (IP6_NEIGHBOR_ENTRY *) Context;
423   if ((ArpQue == NULL) || (ArpQue->Interface == NULL)) {
424     return ;
425   }
426 
427   IpSb   = ArpQue->Interface->Service;
428   if ((IpSb == NULL) || (IpSb->Signature != IP6_SERVICE_SIGNATURE)) {
429     return ;
430   }
431 
432   //
433   // ARP resolve failed for some reason. Release all the frame
434   // and ARP queue itself. Ip6FreeArpQue will call the frame's
435   // owner back.
436   //
437   if (NET_MAC_EQUAL (&ArpQue->LinkAddress, &mZeroMacAddress, IpSb->SnpMode.HwAddressSize)) {
438     Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, TRUE, EFI_NO_MAPPING, NULL, NULL);
439     return ;
440   }
441 
442   //
443   // ARP resolve succeeded, Transmit all the frame.
444   //
445   Sent = FALSE;
446   NET_LIST_FOR_EACH_SAFE (Entry, Next, &ArpQue->Frames) {
447     RemoveEntryList (Entry);
448 
449     Token = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
450     IP6_COPY_LINK_ADDRESS (&Token->DstMac, &ArpQue->LinkAddress);
451 
452     //
453     // Insert the tx token before transmitting it via MNP as the FrameSentDpc
454     // may be called before Mnp->Transmit returns which will remove this tx
455     // token from the SentFrames list. Remove it from the list if the returned
456     // Status of Mnp->Transmit is not EFI_SUCCESS as in this case the
457     // FrameSentDpc won't be queued.
458     //
459     InsertTailList (&ArpQue->Interface->SentFrames, &Token->Link);
460 
461     Status = IpSb->Mnp->Transmit (IpSb->Mnp, &Token->MnpToken);
462     if (EFI_ERROR (Status)) {
463       RemoveEntryList (&Token->Link);
464       Token->CallBack (Token->Packet, Status, 0, Token->Context);
465 
466       Ip6FreeLinkTxToken (Token);
467       continue;
468     } else {
469       Sent = TRUE;
470     }
471   }
472 
473   //
474   // Free the ArpQue only but not the whole neighbor entry.
475   //
476   Ip6FreeNeighborEntry (IpSb, ArpQue, FALSE, FALSE, EFI_SUCCESS, NULL, NULL);
477 
478   if (Sent && (ArpQue->State == EfiNeighborStale)) {
479     ArpQue->State = EfiNeighborDelay;
480     ArpQue->Ticks = (UINT32) IP6_GET_TICKS (IP6_DELAY_FIRST_PROBE_TIME);
481   }
482 }
483 
484 /**
485   Allocate and initialize an IP6 neighbor cache entry.
486 
487   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
488   @param[in]  CallBack          The callback function to be called when
489                                 address resolution is finished.
490   @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.
491   @param[in]  LinkAddress       Points to the MAC address of the neighbor.
492                                 Ignored if NULL.
493 
494   @return NULL if failed to allocate memory for the neighbor cache entry.
495           Otherwise, point to the created neighbor cache entry.
496 
497 **/
498 IP6_NEIGHBOR_ENTRY *
Ip6CreateNeighborEntry(IN IP6_SERVICE * IpSb,IN IP6_ARP_CALLBACK CallBack,IN EFI_IPv6_ADDRESS * Ip6Address,IN EFI_MAC_ADDRESS * LinkAddress OPTIONAL)499 Ip6CreateNeighborEntry (
500   IN IP6_SERVICE            *IpSb,
501   IN IP6_ARP_CALLBACK       CallBack,
502   IN EFI_IPv6_ADDRESS       *Ip6Address,
503   IN EFI_MAC_ADDRESS        *LinkAddress OPTIONAL
504   )
505 {
506   IP6_NEIGHBOR_ENTRY        *Entry;
507   IP6_DEFAULT_ROUTER        *DefaultRouter;
508 
509   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
510   ASSERT (Ip6Address!= NULL);
511 
512   Entry = AllocateZeroPool (sizeof (IP6_NEIGHBOR_ENTRY));
513   if (Entry == NULL) {
514     return NULL;
515   }
516 
517   Entry->RefCnt    = 1;
518   Entry->IsRouter  = FALSE;
519   Entry->ArpFree   = FALSE;
520   Entry->Dynamic   = FALSE;
521   Entry->State     = EfiNeighborInComplete;
522   Entry->Transmit  = IP6_MAX_MULTICAST_SOLICIT + 1;
523   Entry->CallBack  = CallBack;
524   Entry->Interface = NULL;
525 
526   InitializeListHead (&Entry->Frames);
527 
528   IP6_COPY_ADDRESS (&Entry->Neighbor, Ip6Address);
529 
530   if (LinkAddress != NULL) {
531     IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, LinkAddress);
532   } else {
533     IP6_COPY_LINK_ADDRESS (&Entry->LinkAddress, &mZeroMacAddress);
534   }
535 
536   InsertHeadList (&IpSb->NeighborTable, &Entry->Link);
537 
538   //
539   // If corresponding default router entry exists, establish the relationship.
540   //
541   DefaultRouter = Ip6FindDefaultRouter (IpSb, Ip6Address);
542   if (DefaultRouter != NULL) {
543     DefaultRouter->NeighborCache = Entry;
544   }
545 
546   return Entry;
547 }
548 
549 /**
550   Search a IP6 neighbor cache entry.
551 
552   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
553   @param[in]  Ip6Address        Points to the IPv6 address of the neighbor.
554 
555   @return NULL if it failed to find the matching neighbor cache entry.
556           Otherwise, point to the found neighbor cache entry.
557 
558 **/
559 IP6_NEIGHBOR_ENTRY *
Ip6FindNeighborEntry(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * Ip6Address)560 Ip6FindNeighborEntry (
561   IN IP6_SERVICE            *IpSb,
562   IN EFI_IPv6_ADDRESS       *Ip6Address
563   )
564 {
565   LIST_ENTRY                *Entry;
566   LIST_ENTRY                *Next;
567   IP6_NEIGHBOR_ENTRY        *Neighbor;
568 
569   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
570   ASSERT (Ip6Address != NULL);
571 
572   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
573     Neighbor = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
574     if (EFI_IP6_EQUAL (Ip6Address, &Neighbor->Neighbor)) {
575       RemoveEntryList (Entry);
576       InsertHeadList (&IpSb->NeighborTable, Entry);
577 
578       return Neighbor;
579     }
580   }
581 
582   return NULL;
583 }
584 
585 /**
586   Free a IP6 neighbor cache entry and remove all the frames on the address
587   resolution queue that pass the FrameToCancel. That is, either FrameToCancel
588   is NULL, or it returns true for the frame.
589 
590   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
591   @param[in]  NeighborCache     The to be free neighbor cache entry.
592   @param[in]  SendIcmpError     If TRUE, send out ICMP error.
593   @param[in]  FullFree          If TRUE, remove the neighbor cache entry.
594                                 Otherwise remove the pending frames.
595   @param[in]  IoStatus          The status returned to the cancelled frames'
596                                 callback function.
597   @param[in]  FrameToCancel     Function to select which frame to cancel.
598                                 This is an optional parameter that may be NULL.
599   @param[in]  Context           Opaque parameter to the FrameToCancel.
600                                 Ignored if FrameToCancel is NULL.
601 
602   @retval EFI_INVALID_PARAMETER The input parameter is invalid.
603   @retval EFI_SUCCESS           The operation finished successfully.
604 
605 **/
606 EFI_STATUS
Ip6FreeNeighborEntry(IN IP6_SERVICE * IpSb,IN IP6_NEIGHBOR_ENTRY * NeighborCache,IN BOOLEAN SendIcmpError,IN BOOLEAN FullFree,IN EFI_STATUS IoStatus,IN IP6_FRAME_TO_CANCEL FrameToCancel OPTIONAL,IN VOID * Context OPTIONAL)607 Ip6FreeNeighborEntry (
608   IN IP6_SERVICE            *IpSb,
609   IN IP6_NEIGHBOR_ENTRY     *NeighborCache,
610   IN BOOLEAN                SendIcmpError,
611   IN BOOLEAN                FullFree,
612   IN EFI_STATUS             IoStatus,
613   IN IP6_FRAME_TO_CANCEL    FrameToCancel OPTIONAL,
614   IN VOID                   *Context      OPTIONAL
615   )
616 {
617   IP6_LINK_TX_TOKEN         *TxToken;
618   LIST_ENTRY                *Entry;
619   LIST_ENTRY                *Next;
620   IP6_DEFAULT_ROUTER        *DefaultRouter;
621 
622   //
623   // If FrameToCancel fails, the token will not be released.
624   // To avoid the memory leak, stop this usage model.
625   //
626   if (FullFree && FrameToCancel != NULL) {
627     return EFI_INVALID_PARAMETER;
628   }
629 
630   NET_LIST_FOR_EACH_SAFE (Entry, Next, &NeighborCache->Frames) {
631     TxToken = NET_LIST_USER_STRUCT (Entry, IP6_LINK_TX_TOKEN, Link);
632 
633     if (SendIcmpError && !IP6_IS_MULTICAST (&TxToken->Packet->Ip.Ip6->DestinationAddress)) {
634       Ip6SendIcmpError (
635         IpSb,
636         TxToken->Packet,
637         NULL,
638         &TxToken->Packet->Ip.Ip6->SourceAddress,
639         ICMP_V6_DEST_UNREACHABLE,
640         ICMP_V6_ADDR_UNREACHABLE,
641         NULL
642         );
643     }
644 
645     if ((FrameToCancel == NULL) || FrameToCancel (TxToken, Context)) {
646       RemoveEntryList (Entry);
647       TxToken->CallBack (TxToken->Packet, IoStatus, 0, TxToken->Context);
648       Ip6FreeLinkTxToken (TxToken);
649     }
650   }
651 
652   if (NeighborCache->ArpFree && IsListEmpty (&NeighborCache->Frames)) {
653     RemoveEntryList (&NeighborCache->ArpList);
654     NeighborCache->ArpFree = FALSE;
655   }
656 
657   if (FullFree) {
658     if (NeighborCache->IsRouter) {
659       DefaultRouter = Ip6FindDefaultRouter (IpSb, &NeighborCache->Neighbor);
660       if (DefaultRouter != NULL) {
661         Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
662       }
663     }
664 
665     RemoveEntryList (&NeighborCache->Link);
666     FreePool (NeighborCache);
667   }
668 
669   return EFI_SUCCESS;
670 }
671 
672 /**
673   Allocate and initialize an IP6 default router entry.
674 
675   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
676   @param[in]  Ip6Address        The IPv6 address of the default router.
677   @param[in]  RouterLifetime    The lifetime associated with the default
678                                 router, in units of seconds.
679 
680   @return NULL if it failed to allocate memory for the default router node.
681           Otherwise, point to the created default router node.
682 
683 **/
684 IP6_DEFAULT_ROUTER *
Ip6CreateDefaultRouter(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * Ip6Address,IN UINT16 RouterLifetime)685 Ip6CreateDefaultRouter (
686   IN IP6_SERVICE            *IpSb,
687   IN EFI_IPv6_ADDRESS       *Ip6Address,
688   IN UINT16                 RouterLifetime
689   )
690 {
691   IP6_DEFAULT_ROUTER        *Entry;
692   IP6_ROUTE_ENTRY           *RtEntry;
693 
694   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
695   ASSERT (Ip6Address != NULL);
696 
697   Entry = AllocatePool (sizeof (IP6_DEFAULT_ROUTER));
698   if (Entry == NULL) {
699     return NULL;
700   }
701 
702   Entry->RefCnt        = 1;
703   Entry->Lifetime      = RouterLifetime;
704   Entry->NeighborCache = Ip6FindNeighborEntry (IpSb, Ip6Address);
705   IP6_COPY_ADDRESS (&Entry->Router, Ip6Address);
706 
707   //
708   // Add a default route into route table with both Destination and PrefixLength set to zero.
709   //
710   RtEntry = Ip6CreateRouteEntry (NULL, 0, Ip6Address);
711   if (RtEntry == NULL) {
712     FreePool (Entry);
713     return NULL;
714   }
715 
716   InsertHeadList (&IpSb->RouteTable->RouteArea[0], &RtEntry->Link);
717   IpSb->RouteTable->TotalNum++;
718 
719   InsertTailList (&IpSb->DefaultRouterList, &Entry->Link);
720 
721   return Entry;
722 }
723 
724 /**
725   Destroy an IP6 default router entry.
726 
727   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
728   @param[in]  DefaultRouter     The to be destroyed IP6_DEFAULT_ROUTER.
729 
730 **/
731 VOID
Ip6DestroyDefaultRouter(IN IP6_SERVICE * IpSb,IN IP6_DEFAULT_ROUTER * DefaultRouter)732 Ip6DestroyDefaultRouter (
733   IN IP6_SERVICE            *IpSb,
734   IN IP6_DEFAULT_ROUTER     *DefaultRouter
735   )
736 {
737   EFI_STATUS                Status;
738 
739   RemoveEntryList (&DefaultRouter->Link);
740 
741   //
742   // Update the Destination Cache - all entries using the time-out router as next-hop
743   // should perform next-hop determination again.
744   //
745   do {
746     Status = Ip6DelRoute (IpSb->RouteTable, NULL, 0, &DefaultRouter->Router);
747   } while (Status != EFI_NOT_FOUND);
748 
749   FreePool (DefaultRouter);
750 }
751 
752 /**
753   Clean an IP6 default router list.
754 
755   @param[in]  IpSb              The pointer to the IP6_SERVICE instance.
756 
757 **/
758 VOID
Ip6CleanDefaultRouterList(IN IP6_SERVICE * IpSb)759 Ip6CleanDefaultRouterList (
760   IN IP6_SERVICE            *IpSb
761   )
762 {
763   IP6_DEFAULT_ROUTER        *DefaultRouter;
764 
765   while (!IsListEmpty (&IpSb->DefaultRouterList)) {
766     DefaultRouter = NET_LIST_HEAD (&IpSb->DefaultRouterList, IP6_DEFAULT_ROUTER, Link);
767     Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
768   }
769 }
770 
771 /**
772   Search a default router node from an IP6 default router list.
773 
774   @param[in]  IpSb          The pointer to the IP6_SERVICE instance.
775   @param[in]  Ip6Address    The IPv6 address of the to be searched default router node.
776 
777   @return NULL if it failed to find the matching default router node.
778           Otherwise, point to the found default router node.
779 
780 **/
781 IP6_DEFAULT_ROUTER *
Ip6FindDefaultRouter(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * Ip6Address)782 Ip6FindDefaultRouter (
783   IN IP6_SERVICE            *IpSb,
784   IN EFI_IPv6_ADDRESS       *Ip6Address
785   )
786 {
787   LIST_ENTRY                *Entry;
788   IP6_DEFAULT_ROUTER        *DefaultRouter;
789 
790   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
791   ASSERT (Ip6Address != NULL);
792 
793   NET_LIST_FOR_EACH (Entry, &IpSb->DefaultRouterList) {
794     DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
795     if (EFI_IP6_EQUAL (Ip6Address, &DefaultRouter->Router)) {
796       return DefaultRouter;
797     }
798   }
799 
800   return NULL;
801 }
802 
803 /**
804   The function to be called after DAD (Duplicate Address Detection) is performed.
805 
806   @param[in]  IsDadPassed   If TRUE, the DAD operation succeed. Otherwise, the DAD operation failed.
807   @param[in]  IpIf          Points to the IP6_INTERFACE.
808   @param[in]  DadEntry      The DAD entry which already performed DAD.
809 
810 **/
811 VOID
Ip6OnDADFinished(IN BOOLEAN IsDadPassed,IN IP6_INTERFACE * IpIf,IN IP6_DAD_ENTRY * DadEntry)812 Ip6OnDADFinished (
813   IN BOOLEAN        IsDadPassed,
814   IN IP6_INTERFACE  *IpIf,
815   IN IP6_DAD_ENTRY  *DadEntry
816   )
817 {
818   IP6_SERVICE               *IpSb;
819   IP6_ADDRESS_INFO          *AddrInfo;
820   EFI_DHCP6_PROTOCOL        *Dhcp6;
821   UINT16                    OptBuf[4];
822   EFI_DHCP6_PACKET_OPTION   *Oro;
823   EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;
824   EFI_IPv6_ADDRESS          AllNodes;
825 
826   IpSb     = IpIf->Service;
827   AddrInfo = DadEntry->AddressInfo;
828 
829   if (IsDadPassed) {
830     //
831     // DAD succeed.
832     //
833     if (NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
834       ASSERT (!IpSb->LinkLocalOk);
835 
836       IP6_COPY_ADDRESS (&IpSb->LinkLocalAddr, &AddrInfo->Address);
837       IpSb->LinkLocalOk = TRUE;
838       IpIf->Configured  = TRUE;
839 
840       //
841       // Check whether DHCP6 need to be started.
842       //
843       Dhcp6 = IpSb->Ip6ConfigInstance.Dhcp6;
844 
845       if (IpSb->Dhcp6NeedStart) {
846         Dhcp6->Start (Dhcp6);
847         IpSb->Dhcp6NeedStart = FALSE;
848       }
849 
850       if (IpSb->Dhcp6NeedInfoRequest) {
851         //
852         // Set the exta options to send. Here we only want the option request option
853         // with DNS SERVERS.
854         //
855         Oro         = (EFI_DHCP6_PACKET_OPTION *) OptBuf;
856         Oro->OpCode = HTONS (IP6_CONFIG_DHCP6_OPTION_ORO);
857         Oro->OpLen  = HTONS (2);
858         *((UINT16 *) &Oro->Data[0]) = HTONS (IP6_CONFIG_DHCP6_OPTION_DNS_SERVERS);
859 
860         InfoReqReXmit.Irt = 4;
861         InfoReqReXmit.Mrc = 64;
862         InfoReqReXmit.Mrt = 60;
863         InfoReqReXmit.Mrd = 0;
864 
865         Dhcp6->InfoRequest (
866                  Dhcp6,
867                  TRUE,
868                  Oro,
869                  0,
870                  NULL,
871                  &InfoReqReXmit,
872                  IpSb->Ip6ConfigInstance.Dhcp6Event,
873                  Ip6ConfigOnDhcp6Reply,
874                  &IpSb->Ip6ConfigInstance
875                  );
876       }
877 
878       //
879       // Add an on-link prefix for link-local address.
880       //
881       Ip6CreatePrefixListEntry (
882         IpSb,
883         TRUE,
884         (UINT32) IP6_INFINIT_LIFETIME,
885         (UINT32) IP6_INFINIT_LIFETIME,
886         IP6_LINK_LOCAL_PREFIX_LENGTH,
887         &IpSb->LinkLocalAddr
888         );
889 
890     } else {
891       //
892       // Global scope unicast address.
893       //
894       Ip6AddAddr (IpIf, AddrInfo);
895 
896       //
897       // Add an on-link prefix for this address.
898       //
899       Ip6CreatePrefixListEntry (
900         IpSb,
901         TRUE,
902         AddrInfo->ValidLifetime,
903         AddrInfo->PreferredLifetime,
904         AddrInfo->PrefixLength,
905         &AddrInfo->Address
906         );
907 
908       IpIf->Configured = TRUE;
909     }
910   } else {
911     //
912     // Leave the group we joined before.
913     //
914     Ip6LeaveGroup (IpSb, &DadEntry->Destination);
915   }
916 
917   if (DadEntry->Callback != NULL) {
918     DadEntry->Callback (IsDadPassed, &AddrInfo->Address, DadEntry->Context);
919   }
920 
921   if (!IsDadPassed && NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
922     FreePool (AddrInfo);
923     RemoveEntryList (&DadEntry->Link);
924     FreePool (DadEntry);
925     //
926     // Leave link-scope all-nodes multicast address (FF02::1)
927     //
928     Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
929     Ip6LeaveGroup (IpSb, &AllNodes);
930     //
931     // Disable IP operation since link-local address is a duplicate address.
932     //
933     IpSb->LinkLocalDadFail = TRUE;
934     IpSb->Mnp->Configure (IpSb->Mnp, NULL);
935     gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
936     gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);
937     return ;
938   }
939 
940   if (!IsDadPassed || NetIp6IsLinkLocalAddr (&AddrInfo->Address)) {
941     //
942     // Free the AddressInfo we hold if DAD fails or it is a link-local address.
943     //
944     FreePool (AddrInfo);
945   }
946 
947   RemoveEntryList (&DadEntry->Link);
948   FreePool (DadEntry);
949 }
950 
951 /**
952   Create a DAD (Duplicate Address Detection) entry and queue it to be performed.
953 
954   @param[in]  IpIf          Points to the IP6_INTERFACE.
955   @param[in]  AddressInfo   The address information which needs DAD performed.
956   @param[in]  Callback      The callback routine that will be called after DAD
957                             is performed. This is an optional parameter that
958                             may be NULL.
959   @param[in]  Context       The opaque parameter for a DAD callback routine.
960                             This is an optional parameter that may be NULL.
961 
962   @retval EFI_SUCCESS           The DAD entry was created and queued.
963   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory to complete the
964                                 operation.
965 
966 
967 **/
968 EFI_STATUS
Ip6InitDADProcess(IN IP6_INTERFACE * IpIf,IN IP6_ADDRESS_INFO * AddressInfo,IN IP6_DAD_CALLBACK Callback OPTIONAL,IN VOID * Context OPTIONAL)969 Ip6InitDADProcess (
970   IN IP6_INTERFACE          *IpIf,
971   IN IP6_ADDRESS_INFO       *AddressInfo,
972   IN IP6_DAD_CALLBACK       Callback  OPTIONAL,
973   IN VOID                   *Context  OPTIONAL
974   )
975 {
976   IP6_DAD_ENTRY                             *Entry;
977   EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS  *DadXmits;
978   IP6_SERVICE                               *IpSb;
979   EFI_STATUS                                Status;
980   UINT32                                    MaxDelayTick;
981 
982   NET_CHECK_SIGNATURE (IpIf, IP6_INTERFACE_SIGNATURE);
983   ASSERT (AddressInfo != NULL);
984 
985   Status   = EFI_SUCCESS;
986   IpSb     = IpIf->Service;
987   DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;
988 
989   //
990   // Allocate the resources and insert info
991   //
992   Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));
993   if (Entry == NULL) {
994     return EFI_OUT_OF_RESOURCES;
995   }
996 
997   //
998   // Map the incoming unicast address to solicited-node multicast address
999   //
1000   Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);
1001 
1002   //
1003   // Join in the solicited-node multicast address.
1004   //
1005   Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);
1006   if (EFI_ERROR (Status)) {
1007     FreePool (Entry);
1008     return Status;
1009   }
1010 
1011   Entry->Signature    = IP6_DAD_ENTRY_SIGNATURE;
1012   Entry->MaxTransmit  = DadXmits->DupAddrDetectTransmits;
1013   Entry->Transmit     = 0;
1014   Entry->Receive      = 0;
1015   MaxDelayTick        = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;
1016   Entry->RetransTick  = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;
1017   Entry->AddressInfo  = AddressInfo;
1018   Entry->Callback     = Callback;
1019   Entry->Context      = Context;
1020   InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);
1021 
1022   if (Entry->MaxTransmit == 0) {
1023     //
1024     // DAD is disabled on this interface, immediately mark this DAD successful.
1025     //
1026     Ip6OnDADFinished (TRUE, IpIf, Entry);
1027   }
1028 
1029   return EFI_SUCCESS;
1030 }
1031 
1032 /**
1033   Search IP6_DAD_ENTRY from the Duplicate Address Detection List.
1034 
1035   @param[in]  IpSb          The pointer to the IP6_SERVICE instance.
1036   @param[in]  Target        The address information which needs DAD performed .
1037   @param[out] Interface     If not NULL, output the IP6 interface that configures
1038                             the tentative address.
1039 
1040   @return NULL if failed to find the matching DAD entry.
1041           Otherwise, point to the found DAD entry.
1042 
1043 **/
1044 IP6_DAD_ENTRY *
Ip6FindDADEntry(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * Target,OUT IP6_INTERFACE ** Interface OPTIONAL)1045 Ip6FindDADEntry (
1046   IN  IP6_SERVICE      *IpSb,
1047   IN  EFI_IPv6_ADDRESS *Target,
1048   OUT IP6_INTERFACE    **Interface OPTIONAL
1049   )
1050 {
1051   LIST_ENTRY                *Entry;
1052   LIST_ENTRY                *Entry2;
1053   IP6_INTERFACE             *IpIf;
1054   IP6_DAD_ENTRY             *DupAddrDetect;
1055   IP6_ADDRESS_INFO          *AddrInfo;
1056 
1057   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
1058     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
1059 
1060     NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {
1061       DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);
1062       AddrInfo      = DupAddrDetect->AddressInfo;
1063       if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {
1064         if (Interface != NULL) {
1065           *Interface = IpIf;
1066         }
1067         return DupAddrDetect;
1068       }
1069     }
1070   }
1071 
1072   return NULL;
1073 }
1074 
1075 /**
1076   Generate router solicit message and send it out to Destination Address or
1077   All Router Link Local scope multicast address.
1078 
1079   @param[in]  IpSb               The IP service to send the packet.
1080   @param[in]  Interface          If not NULL, points to the IP6 interface to send
1081                                  the packet.
1082   @param[in]  SourceAddress      If not NULL, the source address of the message.
1083   @param[in]  DestinationAddress If not NULL, the destination address of the message.
1084   @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.
1085                                  A source link-layer address option will be appended
1086                                  to the message.
1087 
1088   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1089                                  operation.
1090   @retval EFI_SUCCESS            The router solicit message was successfully sent.
1091 
1092 **/
1093 EFI_STATUS
Ip6SendRouterSolicit(IN IP6_SERVICE * IpSb,IN IP6_INTERFACE * Interface OPTIONAL,IN EFI_IPv6_ADDRESS * SourceAddress OPTIONAL,IN EFI_IPv6_ADDRESS * DestinationAddress OPTIONAL,IN EFI_MAC_ADDRESS * SourceLinkAddress OPTIONAL)1094 Ip6SendRouterSolicit (
1095   IN IP6_SERVICE            *IpSb,
1096   IN IP6_INTERFACE          *Interface          OPTIONAL,
1097   IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,
1098   IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,
1099   IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL
1100   )
1101 {
1102   NET_BUF                   *Packet;
1103   EFI_IP6_HEADER            Head;
1104   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1105   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1106   UINT16                    PayloadLen;
1107   IP6_INTERFACE             *IpIf;
1108 
1109   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1110 
1111   IpIf = Interface;
1112   if (IpIf == NULL && IpSb->DefaultInterface != NULL) {
1113     IpIf = IpSb->DefaultInterface;
1114   }
1115 
1116   //
1117   // Generate the packet to be sent
1118   //
1119 
1120   PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);
1121   if (SourceLinkAddress != NULL) {
1122     PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);
1123   }
1124 
1125   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1126   if (Packet == NULL) {
1127     return EFI_OUT_OF_RESOURCES;
1128   }
1129 
1130   //
1131   // Create the basic IPv6 header.
1132   //
1133   Head.FlowLabelL     = 0;
1134   Head.FlowLabelH     = 0;
1135   Head.PayloadLength  = HTONS (PayloadLen);
1136   Head.NextHeader     = IP6_ICMP;
1137   Head.HopLimit       = IP6_HOP_LIMIT;
1138 
1139   if (SourceAddress != NULL) {
1140     IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1141   } else {
1142     ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
1143   }
1144 
1145 
1146   if (DestinationAddress != NULL) {
1147     IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1148   } else {
1149     Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);
1150   }
1151 
1152   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1153 
1154   //
1155   // Fill in the ICMP header, and Source link-layer address if contained.
1156   //
1157 
1158   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1159   ASSERT (IcmpHead != NULL);
1160   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1161   IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;
1162   IcmpHead->Head.Code = 0;
1163 
1164   LinkLayerOption = NULL;
1165   if (SourceLinkAddress != NULL) {
1166     LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1167                                                   Packet,
1168                                                   sizeof (IP6_ETHER_ADDR_OPTION),
1169                                                   FALSE
1170                                                   );
1171     ASSERT (LinkLayerOption != NULL);
1172     LinkLayerOption->Type   = Ip6OptionEtherSource;
1173     LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);
1174     CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
1175   }
1176 
1177   //
1178   // Transmit the packet
1179   //
1180   return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1181 }
1182 
1183 /**
1184   Generate a Neighbor Advertisement message and send it out to Destination Address.
1185 
1186   @param[in]  IpSb               The IP service to send the packet.
1187   @param[in]  SourceAddress      The source address of the message.
1188   @param[in]  DestinationAddress The destination address of the message.
1189   @param[in]  TargetIp6Address   The target address field in the Neighbor Solicitation
1190                                  message that prompted this advertisement.
1191   @param[in]  TargetLinkAddress  The MAC address for the target, i.e. the sender
1192                                  of the advertisement.
1193   @param[in]  IsRouter           If TRUE, indicates the sender is a router.
1194   @param[in]  Override           If TRUE, indicates the advertisement should override
1195                                  an existing cache entry and update the MAC address.
1196   @param[in]  Solicited          If TRUE, indicates the advertisement was sent
1197                                  in response to a Neighbor Solicitation from
1198                                  the Destination address.
1199 
1200   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1201                                  operation.
1202   @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.
1203 
1204 **/
1205 EFI_STATUS
Ip6SendNeighborAdvertise(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * SourceAddress,IN EFI_IPv6_ADDRESS * DestinationAddress,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * TargetLinkAddress,IN BOOLEAN IsRouter,IN BOOLEAN Override,IN BOOLEAN Solicited)1206 Ip6SendNeighborAdvertise (
1207   IN IP6_SERVICE            *IpSb,
1208   IN EFI_IPv6_ADDRESS       *SourceAddress,
1209   IN EFI_IPv6_ADDRESS       *DestinationAddress,
1210   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
1211   IN EFI_MAC_ADDRESS        *TargetLinkAddress,
1212   IN BOOLEAN                IsRouter,
1213   IN BOOLEAN                Override,
1214   IN BOOLEAN                Solicited
1215   )
1216 {
1217   NET_BUF                   *Packet;
1218   EFI_IP6_HEADER            Head;
1219   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1220   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1221   EFI_IPv6_ADDRESS          *Target;
1222   UINT16                    PayloadLen;
1223 
1224   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1225 
1226   //
1227   // The Neighbor Advertisement message must include a Target link-layer address option
1228   // when responding to multicast solicitation and should include such option when
1229   // responding to unicast solicitation. It also must include such option as unsolicited
1230   // advertisement.
1231   //
1232   ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);
1233 
1234   PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));
1235 
1236   //
1237   // Generate the packet to be sent
1238   //
1239 
1240   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1241   if (Packet == NULL) {
1242     return EFI_OUT_OF_RESOURCES;
1243   }
1244 
1245   //
1246   // Create the basic IPv6 header.
1247   //
1248   Head.FlowLabelL     = 0;
1249   Head.FlowLabelH     = 0;
1250   Head.PayloadLength  = HTONS (PayloadLen);
1251   Head.NextHeader     = IP6_ICMP;
1252   Head.HopLimit       = IP6_HOP_LIMIT;
1253 
1254   IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1255   IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1256 
1257   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1258 
1259   //
1260   // Fill in the ICMP header, Target address, and Target link-layer address.
1261   // Set the Router flag, Solicited flag and Override flag.
1262   //
1263 
1264   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1265   ASSERT (IcmpHead != NULL);
1266   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1267   IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;
1268   IcmpHead->Head.Code = 0;
1269 
1270   if (IsRouter) {
1271     IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;
1272   }
1273 
1274   if (Solicited) {
1275     IcmpHead->Fourth |= IP6_SOLICITED_FLAG;
1276   }
1277 
1278   if (Override) {
1279     IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;
1280   }
1281 
1282   Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
1283   ASSERT (Target != NULL);
1284   IP6_COPY_ADDRESS (Target, TargetIp6Address);
1285 
1286   LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1287                                                 Packet,
1288                                                 sizeof (IP6_ETHER_ADDR_OPTION),
1289                                                 FALSE
1290                                                 );
1291   ASSERT (LinkLayerOption != NULL);
1292   LinkLayerOption->Type   = Ip6OptionEtherTarget;
1293   LinkLayerOption->Length = 1;
1294   CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);
1295 
1296   //
1297   // Transmit the packet
1298   //
1299   return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1300 }
1301 
1302 /**
1303   Generate the Neighbor Solicitation message and send it to the Destination Address.
1304 
1305   @param[in]  IpSb               The IP service to send the packet
1306   @param[in]  SourceAddress      The source address of the message.
1307   @param[in]  DestinationAddress The destination address of the message.
1308   @param[in]  TargetIp6Address   The IP address of the target of the solicitation.
1309                                  It must not be a multicast address.
1310   @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,
1311                                  a source link-layer address option will be appended
1312                                  to the message.
1313 
1314   @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.
1315   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1316                                  operation.
1317   @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.
1318 
1319 **/
1320 EFI_STATUS
Ip6SendNeighborSolicit(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * SourceAddress,IN EFI_IPv6_ADDRESS * DestinationAddress,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * SourceLinkAddress OPTIONAL)1321 Ip6SendNeighborSolicit (
1322   IN IP6_SERVICE            *IpSb,
1323   IN EFI_IPv6_ADDRESS       *SourceAddress,
1324   IN EFI_IPv6_ADDRESS       *DestinationAddress,
1325   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
1326   IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL
1327   )
1328 {
1329   NET_BUF                   *Packet;
1330   EFI_IP6_HEADER            Head;
1331   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1332   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1333   EFI_IPv6_ADDRESS          *Target;
1334   BOOLEAN                   IsDAD;
1335   UINT16                    PayloadLen;
1336   IP6_NEIGHBOR_ENTRY        *Neighbor;
1337 
1338   //
1339   // Check input parameters
1340   //
1341   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1342   if (DestinationAddress == NULL || TargetIp6Address == NULL) {
1343     return EFI_INVALID_PARAMETER;
1344   }
1345 
1346   IsDAD = FALSE;
1347 
1348   if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {
1349     IsDAD = TRUE;
1350   }
1351 
1352   //
1353   // The Neighbor Solicitation message should include a source link-layer address option
1354   // if the solicitation is not sent by performing DAD - Duplicate Address Detection.
1355   // Otherwise must not include it.
1356   //
1357   PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));
1358 
1359   if (!IsDAD) {
1360     if (SourceLinkAddress == NULL) {
1361       return EFI_INVALID_PARAMETER;
1362     }
1363 
1364     PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));
1365   }
1366 
1367   //
1368   // Generate the packet to be sent
1369   //
1370 
1371   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1372   if (Packet == NULL) {
1373     return EFI_OUT_OF_RESOURCES;
1374   }
1375 
1376   //
1377   // Create the basic IPv6 header
1378   //
1379   Head.FlowLabelL     = 0;
1380   Head.FlowLabelH     = 0;
1381   Head.PayloadLength  = HTONS (PayloadLen);
1382   Head.NextHeader     = IP6_ICMP;
1383   Head.HopLimit       = IP6_HOP_LIMIT;
1384 
1385   if (SourceAddress != NULL) {
1386     IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1387   } else {
1388     ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
1389   }
1390 
1391   IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1392 
1393   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1394 
1395   //
1396   // Fill in the ICMP header, Target address, and Source link-layer address.
1397   //
1398   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1399   ASSERT (IcmpHead != NULL);
1400   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1401   IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;
1402   IcmpHead->Head.Code = 0;
1403 
1404   Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
1405   ASSERT (Target != NULL);
1406   IP6_COPY_ADDRESS (Target, TargetIp6Address);
1407 
1408   LinkLayerOption = NULL;
1409   if (!IsDAD) {
1410 
1411     //
1412     // Fill in the source link-layer address option
1413     //
1414     LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1415                                                   Packet,
1416                                                   sizeof (IP6_ETHER_ADDR_OPTION),
1417                                                   FALSE
1418                                                   );
1419     ASSERT (LinkLayerOption != NULL);
1420     LinkLayerOption->Type   = Ip6OptionEtherSource;
1421     LinkLayerOption->Length = 1;
1422     CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
1423   }
1424 
1425   //
1426   // Create a Neighbor Cache entry in the INCOMPLETE state when performing
1427   // address resolution.
1428   //
1429   if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {
1430     Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
1431     if (Neighbor == NULL) {
1432       Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);
1433       ASSERT (Neighbor != NULL);
1434     }
1435   }
1436 
1437   //
1438   // Transmit the packet
1439   //
1440   return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1441 }
1442 
1443 /**
1444   Process the Neighbor Solicitation message. The message may be sent for Duplicate
1445   Address Detection or Address Resolution.
1446 
1447   @param[in]  IpSb               The IP service that received the packet.
1448   @param[in]  Head               The IP head of the message.
1449   @param[in]  Packet             The content of the message with IP head removed.
1450 
1451   @retval EFI_SUCCESS            The packet processed successfully.
1452   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1453   @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.
1454   @retval Others                 Failed to process the packet.
1455 
1456 **/
1457 EFI_STATUS
Ip6ProcessNeighborSolicit(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1458 Ip6ProcessNeighborSolicit (
1459   IN IP6_SERVICE            *IpSb,
1460   IN EFI_IP6_HEADER         *Head,
1461   IN NET_BUF                *Packet
1462   )
1463 {
1464   IP6_ICMP_INFORMATION_HEAD Icmp;
1465   EFI_IPv6_ADDRESS          Target;
1466   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1467   BOOLEAN                   IsDAD;
1468   BOOLEAN                   IsUnicast;
1469   BOOLEAN                   IsMaintained;
1470   IP6_DAD_ENTRY             *DupAddrDetect;
1471   IP6_INTERFACE             *IpIf;
1472   IP6_NEIGHBOR_ENTRY        *Neighbor;
1473   BOOLEAN                   Solicited;
1474   BOOLEAN                   UpdateCache;
1475   EFI_IPv6_ADDRESS          Dest;
1476   UINT16                    OptionLen;
1477   UINT8                     *Option;
1478   BOOLEAN                   Provided;
1479   EFI_STATUS                Status;
1480   VOID                      *MacAddress;
1481 
1482   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1483   NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
1484 
1485   //
1486   // Perform Message Validation:
1487   // The IP Hop Limit field has a value of 255, i.e., the packet
1488   // could not possibly have been forwarded by a router.
1489   // ICMP Code is 0.
1490   // Target Address is not a multicast address.
1491   //
1492   Status = EFI_INVALID_PARAMETER;
1493 
1494   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
1495     goto Exit;
1496   }
1497 
1498   //
1499   // ICMP length is 24 or more octets.
1500   //
1501   OptionLen = 0;
1502   if (Head->PayloadLength < IP6_ND_LENGTH) {
1503     goto Exit;
1504   } else {
1505     OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
1506     if (OptionLen != 0) {
1507       Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
1508       ASSERT (Option != NULL);
1509 
1510       //
1511       // All included options should have a length that is greater than zero.
1512       //
1513       if (!Ip6IsNDOptionValid (Option, OptionLen)) {
1514         goto Exit;
1515       }
1516     }
1517   }
1518 
1519   IsDAD        = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);
1520   IsUnicast    = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);
1521   IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);
1522 
1523   Provided = FALSE;
1524   if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
1525     NetbufCopy (
1526       Packet,
1527       IP6_ND_LENGTH,
1528       sizeof (IP6_ETHER_ADDR_OPTION),
1529       (UINT8 *) &LinkLayerOption
1530       );
1531     //
1532     // The solicitation for neighbor discovery should include a source link-layer
1533     // address option. If the option is not recognized, silently ignore it.
1534     //
1535     if (LinkLayerOption.Type == Ip6OptionEtherSource) {
1536       if (IsDAD) {
1537         //
1538         // If the IP source address is the unspecified address, the source
1539         // link-layer address option must not be included in the message.
1540         //
1541         goto Exit;
1542       }
1543 
1544       Provided = TRUE;
1545     }
1546   }
1547 
1548   //
1549   // If the IP source address is the unspecified address, the IP
1550   // destination address is a solicited-node multicast address.
1551   //
1552   if (IsDAD && IsUnicast) {
1553     goto Exit;
1554   }
1555 
1556   //
1557   // If the target address is tentative, and the source address is a unicast address,
1558   // the solicitation's sender is performing address resolution on the target;
1559   //  the solicitation should be silently ignored.
1560   //
1561   if (!IsDAD && !IsMaintained) {
1562     goto Exit;
1563   }
1564 
1565   //
1566   // If received unicast neighbor solicitation but destination is not this node,
1567   // drop the packet.
1568   //
1569   if (IsUnicast && !IsMaintained) {
1570     goto Exit;
1571   }
1572 
1573   //
1574   // In DAD, when target address is a tentative address,
1575   // process the received neighbor solicitation message but not send out response.
1576   //
1577   if (IsDAD && !IsMaintained) {
1578     DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
1579     if (DupAddrDetect != NULL) {
1580       if (DupAddrDetect->Transmit == 0) {
1581         //
1582         // The NS is from another node to performing DAD on the same address since
1583         // we haven't send out any NS yet. Fail DAD for the tentative address.
1584         //
1585         Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
1586         Status = EFI_ICMP_ERROR;
1587         goto Exit;
1588       }
1589 
1590       //
1591       // Check the MAC address of the incoming packet.
1592       //
1593       if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {
1594         goto Exit;
1595       }
1596 
1597       MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;
1598       if (MacAddress != NULL) {
1599         if (CompareMem (
1600               MacAddress,
1601               &IpSb->SnpMode.CurrentAddress,
1602               IpSb->SnpMode.HwAddressSize
1603               ) != 0) {
1604           //
1605           // The NS is from another node to performing DAD on the same address.
1606           // Fail DAD for the tentative address.
1607           //
1608           Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
1609           Status = EFI_ICMP_ERROR;
1610         } else {
1611           //
1612           // The below layer loopback the NS we sent. Record it and wait for more.
1613           //
1614           DupAddrDetect->Receive++;
1615           Status = EFI_SUCCESS;
1616         }
1617       }
1618     }
1619     goto Exit;
1620   }
1621 
1622   //
1623   // If the solicitation does not contain a link-layer address, DO NOT create or
1624   // update the neighbor cache entries.
1625   //
1626   if (Provided) {
1627     Neighbor    = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
1628     UpdateCache = FALSE;
1629 
1630     if (Neighbor == NULL) {
1631       Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);
1632       if (Neighbor == NULL) {
1633         Status = EFI_OUT_OF_RESOURCES;
1634         goto Exit;
1635       }
1636       UpdateCache = TRUE;
1637     } else {
1638       if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {
1639         UpdateCache = TRUE;
1640       }
1641     }
1642 
1643     if (UpdateCache) {
1644       Neighbor->State = EfiNeighborStale;
1645       Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1646       CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1647       //
1648       // Send queued packets if exist.
1649       //
1650       Neighbor->CallBack ((VOID *) Neighbor);
1651     }
1652   }
1653 
1654   //
1655   // Sends a Neighbor Advertisement as response.
1656   // Set the Router flag to zero since the node is a host.
1657   // If the source address of the solicitation is unspeicifed, and target address
1658   // is one of the maintained address, reply a unsolicited multicast advertisement.
1659   //
1660   if (IsDAD && IsMaintained) {
1661     Solicited = FALSE;
1662     Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);
1663   } else {
1664     Solicited = TRUE;
1665     IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);
1666   }
1667 
1668   Status = Ip6SendNeighborAdvertise (
1669              IpSb,
1670              &Target,
1671              &Dest,
1672              &Target,
1673              &IpSb->SnpMode.CurrentAddress,
1674              FALSE,
1675              TRUE,
1676              Solicited
1677              );
1678 Exit:
1679   NetbufFree (Packet);
1680   return Status;
1681 }
1682 
1683 /**
1684   Process the Neighbor Advertisement message.
1685 
1686   @param[in]  IpSb               The IP service that received the packet.
1687   @param[in]  Head               The IP head of the message.
1688   @param[in]  Packet             The content of the message with IP head removed.
1689 
1690   @retval EFI_SUCCESS            The packet processed successfully.
1691   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1692   @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.
1693   @retval Others                 Failed to process the packet.
1694 
1695 **/
1696 EFI_STATUS
Ip6ProcessNeighborAdvertise(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1697 Ip6ProcessNeighborAdvertise (
1698   IN IP6_SERVICE            *IpSb,
1699   IN EFI_IP6_HEADER         *Head,
1700   IN NET_BUF                *Packet
1701   )
1702 {
1703   IP6_ICMP_INFORMATION_HEAD Icmp;
1704   EFI_IPv6_ADDRESS          Target;
1705   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1706   BOOLEAN                   Provided;
1707   INTN                      Compare;
1708   IP6_NEIGHBOR_ENTRY        *Neighbor;
1709   IP6_DEFAULT_ROUTER        *DefaultRouter;
1710   BOOLEAN                   Solicited;
1711   BOOLEAN                   IsRouter;
1712   BOOLEAN                   Override;
1713   IP6_DAD_ENTRY             *DupAddrDetect;
1714   IP6_INTERFACE             *IpIf;
1715   UINT16                    OptionLen;
1716   UINT8                     *Option;
1717   EFI_STATUS                Status;
1718 
1719   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1720   NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
1721 
1722   //
1723   // Validate the incoming Neighbor Advertisement
1724   //
1725   Status = EFI_INVALID_PARAMETER;
1726   //
1727   // The IP Hop Limit field has a value of 255, i.e., the packet
1728   // could not possibly have been forwarded by a router.
1729   // ICMP Code is 0.
1730   // Target Address is not a multicast address.
1731   //
1732   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
1733     goto Exit;
1734   }
1735 
1736   //
1737   // ICMP length is 24 or more octets.
1738   //
1739   Provided  = FALSE;
1740   OptionLen = 0;
1741   if (Head->PayloadLength < IP6_ND_LENGTH) {
1742     goto Exit;
1743   } else {
1744     OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
1745     if (OptionLen != 0) {
1746       Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
1747       ASSERT (Option != NULL);
1748 
1749       //
1750       // All included options should have a length that is greater than zero.
1751       //
1752       if (!Ip6IsNDOptionValid (Option, OptionLen)) {
1753         goto Exit;
1754       }
1755     }
1756   }
1757 
1758   //
1759   // If the IP destination address is a multicast address, Solicited Flag is ZERO.
1760   //
1761   Solicited = FALSE;
1762   if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {
1763     Solicited = TRUE;
1764   }
1765   if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {
1766     goto Exit;
1767   }
1768 
1769   //
1770   // DAD - Check whether the Target is one of our tentative address.
1771   //
1772   DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
1773   if (DupAddrDetect != NULL) {
1774     //
1775     // DAD fails, some other node is using this address.
1776     //
1777     NetbufFree (Packet);
1778     Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
1779     return EFI_ICMP_ERROR;
1780   }
1781 
1782   //
1783   // Search the Neighbor Cache for the target's entry. If no entry exists,
1784   // the advertisement should be silently discarded.
1785   //
1786   Neighbor = Ip6FindNeighborEntry (IpSb, &Target);
1787   if (Neighbor == NULL) {
1788     goto Exit;
1789   }
1790 
1791   //
1792   // Get IsRouter Flag and Override Flag
1793   //
1794   IsRouter = FALSE;
1795   Override = FALSE;
1796   if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {
1797     IsRouter = TRUE;
1798   }
1799   if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {
1800     Override = TRUE;
1801   }
1802 
1803   //
1804   // Check whether link layer option is included.
1805   //
1806   if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
1807     NetbufCopy (
1808       Packet,
1809       IP6_ND_LENGTH,
1810       sizeof (IP6_ETHER_ADDR_OPTION),
1811       (UINT8 *) &LinkLayerOption
1812       );
1813 
1814     if (LinkLayerOption.Type == Ip6OptionEtherTarget) {
1815       Provided = TRUE;
1816     }
1817   }
1818 
1819   Compare = 0;
1820   if (Provided) {
1821     Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1822   }
1823 
1824   if (!Neighbor->IsRouter && IsRouter) {
1825     DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
1826     if (DefaultRouter != NULL) {
1827       DefaultRouter->NeighborCache = Neighbor;
1828     }
1829   }
1830 
1831   if (Neighbor->State == EfiNeighborInComplete) {
1832     //
1833     // If the target's Neighbor Cache entry is in INCOMPLETE state and no
1834     // Target Link-Layer address option is included while link layer has
1835     // address, the message should be silently discarded.
1836     //
1837     if (!Provided) {
1838       goto Exit;
1839     }
1840     //
1841     // Update the Neighbor Cache
1842     //
1843     CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1844     if (Solicited) {
1845       Neighbor->State = EfiNeighborReachable;
1846       Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
1847     } else {
1848       Neighbor->State = EfiNeighborStale;
1849       Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1850       //
1851       // Send any packets queued for the neighbor awaiting address resolution.
1852       //
1853       Neighbor->CallBack ((VOID *) Neighbor);
1854     }
1855 
1856     Neighbor->IsRouter = IsRouter;
1857 
1858   } else {
1859     if (!Override && Compare != 0) {
1860       //
1861       // When the Override Flag is clear and supplied link-layer address differs from
1862       // that in the cache, if the state of the entry is not REACHABLE, ignore the
1863       // message. Otherwise set it to STALE but do not update the entry in any
1864       // other way.
1865       //
1866       if (Neighbor->State == EfiNeighborReachable) {
1867         Neighbor->State = EfiNeighborStale;
1868         Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1869       }
1870     } else {
1871       if (Compare != 0) {
1872         CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1873       }
1874       //
1875       // Update the entry's state
1876       //
1877       if (Solicited) {
1878         Neighbor->State = EfiNeighborReachable;
1879         Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
1880       } else {
1881         if (Compare != 0) {
1882           Neighbor->State = EfiNeighborStale;
1883           Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1884         }
1885       }
1886 
1887       //
1888       // When IsRouter is changed from TRUE to FALSE, remove the router from the
1889       // Default Router List and remove the Destination Cache entries for all destinations
1890       // using the neighbor as a router.
1891       //
1892       if (Neighbor->IsRouter && !IsRouter) {
1893         DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
1894         if (DefaultRouter != NULL) {
1895           Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
1896         }
1897       }
1898 
1899       Neighbor->IsRouter = IsRouter;
1900     }
1901   }
1902 
1903   if (Neighbor->State == EfiNeighborReachable) {
1904     Neighbor->CallBack ((VOID *) Neighbor);
1905   }
1906 
1907   Status = EFI_SUCCESS;
1908 
1909 Exit:
1910   NetbufFree (Packet);
1911   return Status;
1912 }
1913 
1914 /**
1915   Process the Router Advertisement message according to RFC4861.
1916 
1917   @param[in]  IpSb               The IP service that received the packet.
1918   @param[in]  Head               The IP head of the message.
1919   @param[in]  Packet             The content of the message with the IP head removed.
1920 
1921   @retval EFI_SUCCESS            The packet processed successfully.
1922   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1923   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1924                                  operation.
1925   @retval Others                 Failed to process the packet.
1926 
1927 **/
1928 EFI_STATUS
Ip6ProcessRouterAdvertise(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1929 Ip6ProcessRouterAdvertise (
1930   IN IP6_SERVICE            *IpSb,
1931   IN EFI_IP6_HEADER         *Head,
1932   IN NET_BUF                *Packet
1933   )
1934 {
1935   IP6_ICMP_INFORMATION_HEAD Icmp;
1936   UINT32                    ReachableTime;
1937   UINT32                    RetransTimer;
1938   UINT16                    RouterLifetime;
1939   UINT16                    Offset;
1940   UINT8                     Type;
1941   UINT8                     Length;
1942   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1943   UINT32                    Fourth;
1944   UINT8                     CurHopLimit;
1945   BOOLEAN                   Mflag;
1946   BOOLEAN                   Oflag;
1947   IP6_DEFAULT_ROUTER        *DefaultRouter;
1948   IP6_NEIGHBOR_ENTRY        *NeighborCache;
1949   EFI_MAC_ADDRESS           LinkLayerAddress;
1950   IP6_MTU_OPTION            MTUOption;
1951   IP6_PREFIX_INFO_OPTION    PrefixOption;
1952   IP6_PREFIX_LIST_ENTRY     *PrefixList;
1953   BOOLEAN                   OnLink;
1954   BOOLEAN                   Autonomous;
1955   EFI_IPv6_ADDRESS          StatelessAddress;
1956   EFI_STATUS                Status;
1957   UINT16                    OptionLen;
1958   UINT8                     *Option;
1959   INTN                      Result;
1960 
1961   Status = EFI_INVALID_PARAMETER;
1962 
1963   if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {
1964     //
1965     // Skip the process below as it's not required under the current policy.
1966     //
1967     goto Exit;
1968   }
1969 
1970   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1971 
1972   //
1973   // Validate the incoming Router Advertisement
1974   //
1975 
1976   //
1977   // The IP source address must be a link-local address
1978   //
1979   if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
1980     goto Exit;
1981   }
1982   //
1983   // The IP Hop Limit field has a value of 255, i.e. the packet
1984   // could not possibly have been forwarded by a router.
1985   // ICMP Code is 0.
1986   // ICMP length (derived from the IP length) is 16 or more octets.
1987   //
1988   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||
1989       Head->PayloadLength < IP6_RA_LENGTH) {
1990     goto Exit;
1991   }
1992 
1993   //
1994   // All included options have a length that is greater than zero.
1995   //
1996   OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);
1997   if (OptionLen != 0) {
1998     Option    = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);
1999     ASSERT (Option != NULL);
2000 
2001     if (!Ip6IsNDOptionValid (Option, OptionLen)) {
2002       goto Exit;
2003     }
2004   }
2005 
2006   //
2007   // Process Fourth field.
2008   // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,
2009   // and Router Lifetime (16 bit).
2010   //
2011 
2012   Fourth = NTOHL (Icmp.Fourth);
2013   CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));
2014 
2015   //
2016   // If the source address already in the default router list, update it.
2017   // Otherwise create a new entry.
2018   // A Lifetime of zero indicates that the router is not a default router.
2019   //
2020   DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);
2021   if (DefaultRouter == NULL) {
2022     if (RouterLifetime != 0) {
2023       DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);
2024       if (DefaultRouter == NULL) {
2025         Status = EFI_OUT_OF_RESOURCES;
2026         goto Exit;
2027       }
2028     }
2029   } else {
2030     if (RouterLifetime != 0) {
2031       DefaultRouter->Lifetime = RouterLifetime;
2032       //
2033       // Check the corresponding neighbor cache entry here.
2034       //
2035       if (DefaultRouter->NeighborCache == NULL) {
2036         DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
2037       }
2038     } else {
2039       //
2040       // If the address is in the host's default router list and the router lifetime is zero,
2041       // immediately time-out the entry.
2042       //
2043       Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
2044     }
2045   }
2046 
2047   CurHopLimit = *((UINT8 *) &Fourth + 3);
2048   if (CurHopLimit != 0) {
2049     IpSb->CurHopLimit = CurHopLimit;
2050   }
2051 
2052   Mflag = FALSE;
2053   Oflag = FALSE;
2054   if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {
2055     Mflag = TRUE;
2056   } else {
2057     if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {
2058       Oflag = TRUE;
2059     }
2060   }
2061 
2062   if (Mflag || Oflag) {
2063     //
2064     // Use Ip6Config to get available addresses or other configuration from DHCP.
2065     //
2066     Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);
2067   }
2068 
2069   //
2070   // Process Reachable Time and Retrans Timer fields.
2071   //
2072   NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);
2073   NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);
2074   ReachableTime = NTOHL (ReachableTime);
2075   RetransTimer  = NTOHL (RetransTimer);
2076 
2077   if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {
2078     //
2079     // If new value is not unspecified and differs from the previous one, record it
2080     // in BaseReachableTime and recompute a ReachableTime.
2081     //
2082     IpSb->BaseReachableTime = ReachableTime;
2083     Ip6UpdateReachableTime (IpSb);
2084   }
2085 
2086   if (RetransTimer != 0) {
2087     IpSb->RetransTimer = RetransTimer;
2088   }
2089 
2090   //
2091   // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.
2092   //
2093   NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
2094   if (NeighborCache != NULL) {
2095     NeighborCache->IsRouter = TRUE;
2096   }
2097 
2098   //
2099   // If an valid router advertisment is received, stops router solicitation.
2100   //
2101   IpSb->RouterAdvertiseReceived = TRUE;
2102 
2103   //
2104   // The only defined options that may appear are the Source
2105   // Link-Layer Address, Prefix information and MTU options.
2106   // All included options have a length that is greater than zero.
2107   //
2108   Offset = 16;
2109   while (Offset < Head->PayloadLength) {
2110     NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);
2111     switch (Type) {
2112     case Ip6OptionEtherSource:
2113       //
2114       // Update the neighbor cache
2115       //
2116       NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);
2117       if (LinkLayerOption.Length <= 0) {
2118         goto Exit;
2119       }
2120 
2121       ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));
2122       CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);
2123 
2124       if (NeighborCache == NULL) {
2125         NeighborCache = Ip6CreateNeighborEntry (
2126                           IpSb,
2127                           Ip6OnArpResolved,
2128                           &Head->SourceAddress,
2129                           &LinkLayerAddress
2130                           );
2131         if (NeighborCache == NULL) {
2132           Status = EFI_OUT_OF_RESOURCES;
2133           goto Exit;
2134         }
2135         NeighborCache->IsRouter = TRUE;
2136         NeighborCache->State    = EfiNeighborStale;
2137         NeighborCache->Ticks    = (UINT32) IP6_INFINIT_LIFETIME;
2138       } else {
2139         Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);
2140 
2141         //
2142         // If the link-local address is the same as that already in the cache,
2143         // the cache entry's state remains unchanged. Otherwise update the
2144         // reachability state to STALE.
2145         //
2146         if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
2147           CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);
2148 
2149           NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2150 
2151           if (NeighborCache->State == EfiNeighborInComplete) {
2152             //
2153             // Send queued packets if exist.
2154             //
2155             NeighborCache->State = EfiNeighborStale;
2156             NeighborCache->CallBack ((VOID *) NeighborCache);
2157           } else {
2158             NeighborCache->State = EfiNeighborStale;
2159           }
2160         }
2161       }
2162 
2163       Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);
2164       break;
2165     case Ip6OptionPrefixInfo:
2166       NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);
2167       if (PrefixOption.Length != 4) {
2168         goto Exit;
2169       }
2170       PrefixOption.ValidLifetime     = NTOHL (PrefixOption.ValidLifetime);
2171       PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);
2172 
2173       //
2174       // Get L and A flag, recorded in the lower 2 bits of Reserved1
2175       //
2176       OnLink = FALSE;
2177       if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {
2178         OnLink = TRUE;
2179       }
2180       Autonomous = FALSE;
2181       if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {
2182         Autonomous = TRUE;
2183       }
2184 
2185       //
2186       // If the prefix is the link-local prefix, silently ignore the prefix option.
2187       //
2188       if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&
2189           NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)
2190           ) {
2191         Offset += sizeof (IP6_PREFIX_INFO_OPTION);
2192         break;
2193       }
2194       //
2195       // Do following if on-link flag is set according to RFC4861.
2196       //
2197       if (OnLink) {
2198         PrefixList = Ip6FindPrefixListEntry (
2199                        IpSb,
2200                        TRUE,
2201                        PrefixOption.PrefixLength,
2202                        &PrefixOption.Prefix
2203                        );
2204         //
2205         // Create a new entry for the prefix, if the ValidLifetime is zero,
2206         // silently ignore the prefix option.
2207         //
2208         if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {
2209           PrefixList = Ip6CreatePrefixListEntry (
2210                          IpSb,
2211                          TRUE,
2212                          PrefixOption.ValidLifetime,
2213                          PrefixOption.PreferredLifetime,
2214                          PrefixOption.PrefixLength,
2215                          &PrefixOption.Prefix
2216                          );
2217           if (PrefixList == NULL) {
2218             Status = EFI_OUT_OF_RESOURCES;
2219             goto Exit;
2220           }
2221         } else if (PrefixList != NULL) {
2222           if (PrefixOption.ValidLifetime != 0) {
2223             PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
2224           } else {
2225             //
2226             // If the prefix exists and incoming ValidLifetime is zero, immediately
2227             // remove the prefix.
2228             Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
2229           }
2230         }
2231       }
2232 
2233       //
2234       // Do following if Autonomous flag is set according to RFC4862.
2235       //
2236       if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {
2237         PrefixList = Ip6FindPrefixListEntry (
2238                        IpSb,
2239                        FALSE,
2240                        PrefixOption.PrefixLength,
2241                        &PrefixOption.Prefix
2242                        );
2243         //
2244         // Create a new entry for the prefix, and form an address by prefix + interface id
2245         // If the sum of the prefix length and interface identifier length
2246         // does not equal 128 bits, the Prefix Information option MUST be ignored.
2247         //
2248         if (PrefixList == NULL &&
2249             PrefixOption.ValidLifetime != 0 &&
2250             PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128
2251             ) {
2252           //
2253           // Form the address in network order.
2254           //
2255           CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));
2256           CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));
2257 
2258           //
2259           // If the address is not yet in the assigned address list, adds it into.
2260           //
2261           if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {
2262             //
2263             // And also not in the DAD process, check its uniqeness firstly.
2264             //
2265             if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {
2266               Status = Ip6SetAddress (
2267                          IpSb->DefaultInterface,
2268                          &StatelessAddress,
2269                          FALSE,
2270                          PrefixOption.PrefixLength,
2271                          PrefixOption.ValidLifetime,
2272                          PrefixOption.PreferredLifetime,
2273                          NULL,
2274                          NULL
2275                          );
2276               if (EFI_ERROR (Status)) {
2277                 goto Exit;
2278               }
2279             }
2280           }
2281 
2282           //
2283           // Adds the prefix option to stateless prefix option list.
2284           //
2285           PrefixList = Ip6CreatePrefixListEntry (
2286                          IpSb,
2287                          FALSE,
2288                          PrefixOption.ValidLifetime,
2289                          PrefixOption.PreferredLifetime,
2290                          PrefixOption.PrefixLength,
2291                          &PrefixOption.Prefix
2292                          );
2293           if (PrefixList == NULL) {
2294             Status = EFI_OUT_OF_RESOURCES;
2295             goto Exit;
2296           }
2297         } else if (PrefixList != NULL) {
2298 
2299           //
2300           // Reset the preferred lifetime of the address if the advertised prefix exists.
2301           // Perform specific action to valid lifetime together.
2302           //
2303           PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;
2304           if ((PrefixOption.ValidLifetime > 7200) ||
2305               (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {
2306             //
2307             // If the received Valid Lifetime is greater than 2 hours or
2308             // greater than RemainingLifetime, set the valid lifetime of the
2309             // corresponding address to the advertised Valid Lifetime.
2310             //
2311             PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
2312 
2313           } else if (PrefixList->ValidLifetime <= 7200) {
2314             //
2315             // If RemainingLifetime is less than or equls to 2 hours, ignore the
2316             // Prefix Information option with regards to the valid lifetime.
2317             // TODO: If this option has been authenticated, set the valid lifetime.
2318             //
2319           } else {
2320             //
2321             // Otherwise, reset the valid lifetime of the corresponding
2322             // address to 2 hours.
2323             //
2324             PrefixList->ValidLifetime = 7200;
2325           }
2326         }
2327       }
2328 
2329       Offset += sizeof (IP6_PREFIX_INFO_OPTION);
2330       break;
2331     case Ip6OptionMtu:
2332       NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);
2333       if (MTUOption.Length != 1) {
2334         goto Exit;
2335       }
2336 
2337       //
2338       // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order
2339       // to omit implementation of Path MTU Discovery. Thus ignore the MTU option
2340       // in Router Advertisement.
2341       //
2342 
2343       Offset += sizeof (IP6_MTU_OPTION);
2344       break;
2345     default:
2346       //
2347       // Silently ignore unrecognized options
2348       //
2349       NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);
2350       if (Length <= 0) {
2351         goto Exit;
2352       }
2353 
2354       Offset = (UINT16) (Offset + (UINT16) Length * 8);
2355       break;
2356     }
2357   }
2358 
2359   Status = EFI_SUCCESS;
2360 
2361 Exit:
2362   NetbufFree (Packet);
2363   return Status;
2364 }
2365 
2366 /**
2367   Process the ICMPv6 redirect message. Find the instance, then update
2368   its route cache.
2369 
2370   @param[in]  IpSb               The IP6 service binding instance that received
2371                                  the packet.
2372   @param[in]  Head               The IP head of the received ICMPv6 packet.
2373   @param[in]  Packet             The content of the ICMPv6 redirect packet with
2374                                  the IP head removed.
2375 
2376   @retval EFI_INVALID_PARAMETER  The parameter is invalid.
2377   @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the
2378                                  operation.
2379   @retval EFI_SUCCESS            Successfully updated the route caches.
2380 
2381 **/
2382 EFI_STATUS
Ip6ProcessRedirect(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)2383 Ip6ProcessRedirect (
2384   IN IP6_SERVICE            *IpSb,
2385   IN EFI_IP6_HEADER         *Head,
2386   IN NET_BUF                *Packet
2387   )
2388 {
2389   IP6_ICMP_INFORMATION_HEAD *Icmp;
2390   EFI_IPv6_ADDRESS          *Target;
2391   EFI_IPv6_ADDRESS          *IcmpDest;
2392   UINT8                     *Option;
2393   UINT16                    OptionLen;
2394   IP6_ROUTE_ENTRY           *RouteEntry;
2395   IP6_ROUTE_CACHE_ENTRY     *RouteCache;
2396   IP6_NEIGHBOR_ENTRY        *NeighborCache;
2397   INT32                     Length;
2398   UINT8                     OptLen;
2399   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
2400   EFI_MAC_ADDRESS           Mac;
2401   UINT32                    Index;
2402   BOOLEAN                   IsRouter;
2403   EFI_STATUS                Status;
2404   INTN                      Result;
2405 
2406   Status = EFI_INVALID_PARAMETER;
2407 
2408   Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);
2409   if (Icmp == NULL) {
2410     goto Exit;
2411   }
2412 
2413   //
2414   // Validate the incoming Redirect message
2415   //
2416 
2417   //
2418   // The IP Hop Limit field has a value of 255, i.e. the packet
2419   // could not possibly have been forwarded by a router.
2420   // ICMP Code is 0.
2421   // ICMP length (derived from the IP length) is 40 or more octets.
2422   //
2423   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||
2424       Head->PayloadLength < IP6_REDITECT_LENGTH) {
2425     goto Exit;
2426   }
2427 
2428   //
2429   // The IP source address must be a link-local address
2430   //
2431   if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
2432     goto Exit;
2433   }
2434 
2435   //
2436   // The dest of this ICMP redirect message is not us.
2437   //
2438   if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
2439     goto Exit;
2440   }
2441 
2442   //
2443   // All included options have a length that is greater than zero.
2444   //
2445   OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);
2446   if (OptionLen != 0) {
2447     Option    = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);
2448     ASSERT (Option != NULL);
2449 
2450     if (!Ip6IsNDOptionValid (Option, OptionLen)) {
2451       goto Exit;
2452     }
2453   }
2454 
2455   Target   = (EFI_IPv6_ADDRESS *) (Icmp + 1);
2456   IcmpDest = Target + 1;
2457 
2458   //
2459   // The ICMP Destination Address field in the redirect message does not contain
2460   // a multicast address.
2461   //
2462   if (IP6_IS_MULTICAST (IcmpDest)) {
2463     goto Exit;
2464   }
2465 
2466   //
2467   // The ICMP Target Address is either a link-local address (when redirected to
2468   // a router) or the same as the ICMP Destination Address (when redirected to
2469   // the on-link destination).
2470   //
2471   IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);
2472   if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {
2473     goto Exit;
2474   }
2475 
2476   //
2477   // Check the options. The only interested option here is the target-link layer
2478   // address option.
2479   //
2480   Length          = Packet->TotalSize - 40;
2481   Option          = (UINT8 *) (IcmpDest + 1);
2482   LinkLayerOption = NULL;
2483   while (Length > 0) {
2484     switch (*Option) {
2485     case Ip6OptionEtherTarget:
2486 
2487       LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;
2488       OptLen          = LinkLayerOption->Length;
2489       if (OptLen != 1) {
2490         //
2491         // For ethernet, the length must be 1.
2492         //
2493         goto Exit;
2494       }
2495       break;
2496 
2497     default:
2498 
2499       OptLen = *(Option + 1);
2500       if (OptLen == 0) {
2501         //
2502         // A length of 0 is invalid.
2503         //
2504         goto Exit;
2505       }
2506       break;
2507     }
2508 
2509     Length -= 8 * OptLen;
2510     Option += 8 * OptLen;
2511   }
2512 
2513   if (Length != 0) {
2514     goto Exit;
2515   }
2516 
2517   //
2518   // The IP source address of the Redirect is the same as the current
2519   // first-hop router for the specified ICMP Destination Address.
2520   //
2521   RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);
2522   if (RouteCache != NULL) {
2523     if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {
2524       //
2525       // The source of this Redirect message must match the NextHop of the
2526       // corresponding route cache entry.
2527       //
2528       goto Exit;
2529     }
2530 
2531     //
2532     // Update the NextHop.
2533     //
2534     IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);
2535 
2536     if (!IsRouter) {
2537       RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;
2538       RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;
2539     }
2540 
2541   } else {
2542     //
2543     // Get the Route Entry.
2544     //
2545     RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);
2546     if (RouteEntry == NULL) {
2547       RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);
2548       if (RouteEntry == NULL) {
2549         Status = EFI_OUT_OF_RESOURCES;
2550         goto Exit;
2551       }
2552     }
2553 
2554     if (!IsRouter) {
2555       RouteEntry->Flag = IP6_DIRECT_ROUTE;
2556     }
2557 
2558     //
2559     // Create a route cache for this.
2560     //
2561     RouteCache = Ip6CreateRouteCacheEntry (
2562                    IcmpDest,
2563                    &Head->DestinationAddress,
2564                    Target,
2565                    (UINTN) RouteEntry
2566                    );
2567     if (RouteCache == NULL) {
2568       Status = EFI_OUT_OF_RESOURCES;
2569       goto Exit;
2570     }
2571 
2572     //
2573     // Insert the newly created route cache entry.
2574     //
2575     Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);
2576     InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);
2577   }
2578 
2579   //
2580   // Try to locate the neighbor cache for the Target.
2581   //
2582   NeighborCache = Ip6FindNeighborEntry (IpSb, Target);
2583 
2584   if (LinkLayerOption != NULL) {
2585     if (NeighborCache == NULL) {
2586       //
2587       // Create a neighbor cache for the Target.
2588       //
2589       ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));
2590       CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);
2591       NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);
2592       if (NeighborCache == NULL) {
2593         //
2594         // Just report a success here. The neighbor cache can be created in
2595         // some other place.
2596         //
2597         Status = EFI_SUCCESS;
2598         goto Exit;
2599       }
2600 
2601       NeighborCache->State = EfiNeighborStale;
2602       NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2603     } else {
2604       Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);
2605 
2606       //
2607       // If the link-local address is the same as that already in the cache,
2608       // the cache entry's state remains unchanged. Otherwise update the
2609       // reachability state to STALE.
2610       //
2611       if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
2612         CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);
2613 
2614         NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2615 
2616         if (NeighborCache->State == EfiNeighborInComplete) {
2617           //
2618           // Send queued packets if exist.
2619           //
2620           NeighborCache->State = EfiNeighborStale;
2621           NeighborCache->CallBack ((VOID *) NeighborCache);
2622         } else {
2623           NeighborCache->State = EfiNeighborStale;
2624         }
2625       }
2626     }
2627   }
2628 
2629   if (NeighborCache != NULL && IsRouter) {
2630     //
2631     // The Target is a router, set IsRouter to TRUE.
2632     //
2633     NeighborCache->IsRouter = TRUE;
2634   }
2635 
2636   Status = EFI_SUCCESS;
2637 
2638 Exit:
2639   NetbufFree (Packet);
2640   return Status;
2641 }
2642 
2643 /**
2644   Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().
2645 
2646   @param[in]  IpSb               The IP6 service binding instance.
2647   @param[in]  TargetIp6Address   Pointer to Target IPv6 address.
2648   @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.
2649   @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor
2650                                  cache. It will be deleted after Timeout. A value of zero means that
2651                                  the entry is permanent. A non-zero value means that the entry is
2652                                  dynamic.
2653   @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will
2654                                  be overridden and updated; if FALSE, and if a
2655                                  corresponding cache entry already existed, EFI_ACCESS_DENIED
2656                                  will be returned.
2657 
2658   @retval  EFI_SUCCESS           The neighbor cache entry has been added.
2659   @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache
2660                                  due to insufficient resources.
2661   @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.
2662   @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,
2663                                  and that entry is tagged as un-overridden (when DeleteFlag
2664                                  is FALSE).
2665 
2666 **/
2667 EFI_STATUS
Ip6AddNeighbor(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * TargetLinkAddress OPTIONAL,IN UINT32 Timeout,IN BOOLEAN Override)2668 Ip6AddNeighbor (
2669   IN IP6_SERVICE            *IpSb,
2670   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
2671   IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,
2672   IN UINT32                 Timeout,
2673   IN BOOLEAN                Override
2674   )
2675 {
2676   IP6_NEIGHBOR_ENTRY        *Neighbor;
2677 
2678   Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
2679   if (Neighbor != NULL) {
2680     if (!Override) {
2681       return EFI_ACCESS_DENIED;
2682     } else {
2683       if (TargetLinkAddress != NULL) {
2684         IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);
2685       }
2686     }
2687   } else {
2688     if (TargetLinkAddress == NULL) {
2689       return EFI_NOT_FOUND;
2690     }
2691 
2692     Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);
2693     if (Neighbor == NULL) {
2694       return EFI_OUT_OF_RESOURCES;
2695     }
2696   }
2697 
2698   Neighbor->State = EfiNeighborReachable;
2699 
2700   if (Timeout != 0) {
2701     Neighbor->Ticks   = IP6_GET_TICKS (Timeout / TICKS_PER_MS);
2702     Neighbor->Dynamic = TRUE;
2703   } else {
2704     Neighbor->Ticks   = (UINT32) IP6_INFINIT_LIFETIME;
2705   }
2706 
2707   return EFI_SUCCESS;
2708 }
2709 
2710 /**
2711   Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().
2712 
2713   @param[in]  IpSb               The IP6 service binding instance.
2714   @param[in]  TargetIp6Address   Pointer to Target IPv6 address.
2715   @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.
2716   @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor
2717                                  cache. It will be deleted after Timeout. A value of zero means that
2718                                  the entry is permanent. A non-zero value means that the entry is
2719                                  dynamic.
2720   @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will
2721                                  be overridden and updated; if FALSE, and if a
2722                                  corresponding cache entry already existed, EFI_ACCESS_DENIED
2723                                  will be returned.
2724 
2725   @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.
2726   @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.
2727 
2728 **/
2729 EFI_STATUS
Ip6DelNeighbor(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * TargetLinkAddress OPTIONAL,IN UINT32 Timeout,IN BOOLEAN Override)2730 Ip6DelNeighbor (
2731   IN IP6_SERVICE            *IpSb,
2732   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
2733   IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,
2734   IN UINT32                 Timeout,
2735   IN BOOLEAN                Override
2736   )
2737 {
2738   IP6_NEIGHBOR_ENTRY        *Neighbor;
2739 
2740   Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
2741   if (Neighbor == NULL) {
2742     return EFI_NOT_FOUND;
2743   }
2744 
2745   RemoveEntryList (&Neighbor->Link);
2746   FreePool (Neighbor);
2747 
2748   return EFI_SUCCESS;
2749 }
2750 
2751 /**
2752   The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.
2753   This time routine handles DAD module and neighbor state transition.
2754   It is also responsible for sending out router solicitations.
2755 
2756   @param[in]  Event                 The IP6 service instance's heartbeat timer.
2757   @param[in]  Context               The IP6 service instance.
2758 
2759 **/
2760 VOID
2761 EFIAPI
Ip6NdFasterTimerTicking(IN EFI_EVENT Event,IN VOID * Context)2762 Ip6NdFasterTimerTicking (
2763   IN EFI_EVENT              Event,
2764   IN VOID                   *Context
2765   )
2766 {
2767   LIST_ENTRY                *Entry;
2768   LIST_ENTRY                *Next;
2769   LIST_ENTRY                *Entry2;
2770   IP6_INTERFACE             *IpIf;
2771   IP6_DELAY_JOIN_LIST       *DelayNode;
2772   EFI_IPv6_ADDRESS          Source;
2773   IP6_DAD_ENTRY             *DupAddrDetect;
2774   EFI_STATUS                Status;
2775   IP6_NEIGHBOR_ENTRY        *NeighborCache;
2776   EFI_IPv6_ADDRESS          Destination;
2777   IP6_SERVICE               *IpSb;
2778   BOOLEAN                   Flag;
2779 
2780   IpSb = (IP6_SERVICE *) Context;
2781   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
2782 
2783   ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));
2784 
2785   //
2786   // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router
2787   // Solicitation messages, each separated by at least
2788   // RTR_SOLICITATION_INTERVAL (4) seconds.
2789   //
2790   if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&
2791       !IpSb->RouterAdvertiseReceived &&
2792       IpSb->SolicitTimer > 0
2793       ) {
2794     if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {
2795       Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);
2796       if (!EFI_ERROR (Status)) {
2797         IpSb->SolicitTimer--;
2798         IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);
2799       }
2800     }
2801   }
2802 
2803   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
2804     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
2805 
2806     //
2807     // Process the delay list to join the solicited-node multicast address.
2808     //
2809     NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {
2810       DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);
2811       if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {
2812         //
2813         // The timer expires, init the duplicate address detection.
2814         //
2815         Ip6InitDADProcess (
2816           DelayNode->Interface,
2817           DelayNode->AddressInfo,
2818           DelayNode->DadCallback,
2819           DelayNode->Context
2820           );
2821 
2822         //
2823         // Remove the delay node
2824         //
2825         RemoveEntryList (&DelayNode->Link);
2826         FreePool (DelayNode);
2827       }
2828     }
2829 
2830     //
2831     // Process the duplicate address detection list.
2832     //
2833     NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {
2834       DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);
2835 
2836       if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {
2837         //
2838         // The timer expires, check the remaining transmit counts.
2839         //
2840         if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {
2841           //
2842           // Send the Neighbor Solicitation message with
2843           // Source - unspecified address, destination - solicited-node multicast address
2844           // Target - the address to be validated
2845           //
2846           Status = Ip6SendNeighborSolicit (
2847                      IpSb,
2848                      NULL,
2849                      &DupAddrDetect->Destination,
2850                      &DupAddrDetect->AddressInfo->Address,
2851                      NULL
2852                      );
2853           if (EFI_ERROR (Status)) {
2854             return;
2855           }
2856 
2857           DupAddrDetect->Transmit++;
2858           DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);
2859         } else {
2860           //
2861           // All required solicitation has been sent out, and the RetransTime after the last
2862           // Neighbor Solicit is elapsed, finish the DAD process.
2863           //
2864           Flag = FALSE;
2865           if ((DupAddrDetect->Receive == 0) ||
2866               (DupAddrDetect->Transmit == DupAddrDetect->Receive)) {
2867             Flag = TRUE;
2868           }
2869 
2870           Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);
2871         }
2872       }
2873     }
2874   }
2875 
2876   //
2877   // Polling the state of Neighbor cache
2878   //
2879   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
2880     NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
2881 
2882     switch (NeighborCache->State) {
2883     case EfiNeighborInComplete:
2884       if (NeighborCache->Ticks > 0) {
2885         --NeighborCache->Ticks;
2886       }
2887 
2888       //
2889       // Retransmit Neighbor Solicitation messages approximately every
2890       // RetransTimer milliseconds while awaiting a response.
2891       //
2892       if (NeighborCache->Ticks == 0) {
2893         if (NeighborCache->Transmit > 1) {
2894           //
2895           // Send out multicast neighbor solicitation for address resolution.
2896           // After last neighbor solicitation message has been sent out, wait
2897           // for RetransTimer and then remove entry if no response is received.
2898           //
2899           Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
2900           Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
2901           if (EFI_ERROR (Status)) {
2902             return;
2903           }
2904 
2905           Status = Ip6SendNeighborSolicit (
2906                      IpSb,
2907                      &Source,
2908                      &Destination,
2909                      &NeighborCache->Neighbor,
2910                      &IpSb->SnpMode.CurrentAddress
2911                      );
2912           if (EFI_ERROR (Status)) {
2913             return;
2914           }
2915         }
2916 
2917         //
2918         // Update the retransmit times.
2919         //
2920         if (NeighborCache->Transmit > 0) {
2921           --NeighborCache->Transmit;
2922           NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
2923         }
2924       }
2925 
2926       if (NeighborCache->Transmit == 0) {
2927         //
2928         // Timeout, send ICMP destination unreachable packet and then remove entry
2929         //
2930         Status = Ip6FreeNeighborEntry (
2931                    IpSb,
2932                    NeighborCache,
2933                    TRUE,
2934                    TRUE,
2935                    EFI_ICMP_ERROR,
2936                    NULL,
2937                    NULL
2938                    );
2939         if (EFI_ERROR (Status)) {
2940           return;
2941         }
2942       }
2943 
2944       break;
2945 
2946     case EfiNeighborReachable:
2947       //
2948       // This entry is inserted by EfiIp6Neighbors() as static entry
2949       // and will not timeout.
2950       //
2951       if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {
2952         break;
2953       }
2954 
2955       if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
2956         if (NeighborCache->Dynamic) {
2957           //
2958           // This entry is inserted by EfiIp6Neighbors() as dynamic entry
2959           // and will be deleted after timeout.
2960           //
2961           Status = Ip6FreeNeighborEntry (
2962                      IpSb,
2963                      NeighborCache,
2964                      FALSE,
2965                      TRUE,
2966                      EFI_TIMEOUT,
2967                      NULL,
2968                      NULL
2969                      );
2970           if (EFI_ERROR (Status)) {
2971             return;
2972           }
2973         } else {
2974           NeighborCache->State = EfiNeighborStale;
2975           NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2976         }
2977       }
2978 
2979       break;
2980 
2981     case EfiNeighborDelay:
2982       if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
2983 
2984         NeighborCache->State    = EfiNeighborProbe;
2985         NeighborCache->Ticks    = IP6_GET_TICKS (IpSb->RetransTimer);
2986         NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;
2987         //
2988         // Send out unicast neighbor solicitation for Neighbor Unreachability Detection
2989         //
2990         Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
2991         if (EFI_ERROR (Status)) {
2992           return;
2993         }
2994 
2995         Status = Ip6SendNeighborSolicit (
2996                    IpSb,
2997                    &Source,
2998                    &NeighborCache->Neighbor,
2999                    &NeighborCache->Neighbor,
3000                    &IpSb->SnpMode.CurrentAddress
3001                    );
3002         if (EFI_ERROR (Status)) {
3003           return;
3004         }
3005 
3006         NeighborCache->Transmit--;
3007       }
3008 
3009       break;
3010 
3011     case EfiNeighborProbe:
3012       if (NeighborCache->Ticks > 0) {
3013         --NeighborCache->Ticks;
3014       }
3015 
3016       //
3017       // Retransmit Neighbor Solicitation messages approximately every
3018       // RetransTimer milliseconds while awaiting a response.
3019       //
3020       if (NeighborCache->Ticks == 0) {
3021         if (NeighborCache->Transmit > 1) {
3022           //
3023           // Send out unicast neighbor solicitation for Neighbor Unreachability
3024           // Detection. After last neighbor solicitation message has been sent out,
3025           // wait for RetransTimer and then remove entry if no response is received.
3026           //
3027           Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
3028           if (EFI_ERROR (Status)) {
3029             return;
3030           }
3031 
3032           Status = Ip6SendNeighborSolicit (
3033                      IpSb,
3034                      &Source,
3035                      &NeighborCache->Neighbor,
3036                      &NeighborCache->Neighbor,
3037                      &IpSb->SnpMode.CurrentAddress
3038                      );
3039           if (EFI_ERROR (Status)) {
3040             return;
3041           }
3042         }
3043 
3044         //
3045         // Update the retransmit times.
3046         //
3047         if (NeighborCache->Transmit > 0) {
3048           --NeighborCache->Transmit;
3049           NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
3050         }
3051       }
3052 
3053       if (NeighborCache->Transmit == 0) {
3054         //
3055         // Delete the neighbor entry.
3056         //
3057         Status = Ip6FreeNeighborEntry (
3058                    IpSb,
3059                    NeighborCache,
3060                    FALSE,
3061                    TRUE,
3062                    EFI_TIMEOUT,
3063                    NULL,
3064                    NULL
3065                    );
3066         if (EFI_ERROR (Status)) {
3067           return;
3068         }
3069       }
3070 
3071       break;
3072 
3073     default:
3074       break;
3075     }
3076   }
3077 }
3078 
3079 /**
3080   The heartbeat timer of ND module in 1 second. This time routine handles following
3081   things: 1) maitain default router list; 2) maintain prefix options;
3082   3) maintain route caches.
3083 
3084   @param[in]  IpSb              The IP6 service binding instance.
3085 
3086 **/
3087 VOID
Ip6NdTimerTicking(IN IP6_SERVICE * IpSb)3088 Ip6NdTimerTicking (
3089   IN IP6_SERVICE            *IpSb
3090   )
3091 {
3092   LIST_ENTRY                *Entry;
3093   LIST_ENTRY                *Next;
3094   IP6_DEFAULT_ROUTER        *DefaultRouter;
3095   IP6_PREFIX_LIST_ENTRY     *PrefixOption;
3096   UINT8                     Index;
3097   IP6_ROUTE_CACHE_ENTRY     *RouteCache;
3098 
3099   //
3100   // Decrease the lifetime of default router, if expires remove it from default router list.
3101   //
3102   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {
3103     DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
3104     if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {
3105       if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {
3106         Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
3107       }
3108     }
3109   }
3110 
3111   //
3112   // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.
3113   //
3114   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {
3115     PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
3116     if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
3117       if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {
3118         if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&
3119             (PrefixOption->PreferredLifetime > 0)
3120             ) {
3121           --PrefixOption->PreferredLifetime;
3122         }
3123       } else {
3124         Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);
3125       }
3126     }
3127   }
3128 
3129   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {
3130     PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
3131     if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
3132       if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {
3133         Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);
3134       }
3135     }
3136   }
3137 
3138   //
3139   // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.
3140   // Remove the entries at the tail of the bucket. These entries
3141   // are likely to be used least.
3142   // Reclaim frequency is set to 1 second.
3143   //
3144   for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
3145     while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {
3146       Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);
3147       if (Entry == NULL) {
3148         break;
3149       }
3150 
3151       RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
3152       Ip6FreeRouteCacheEntry (RouteCache);
3153       ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);
3154       IpSb->RouteTable->Cache.CacheNum[Index]--;
3155     }
3156   }
3157 }
3158 
3159