• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   Implementation of Neighbor Discovery support routines.
3 
4   Copyright (c) 2009 - 2016, 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_MAX) {
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 (DHCP6_OPT_ORO);
857         Oro->OpLen  = HTONS (2);
858         *((UINT16 *) &Oro->Data[0]) = HTONS (DHCP6_OPT_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   //
986   // Do nothing if we have already started DAD on the address.
987   //
988   if (Ip6FindDADEntry (IpIf->Service, &AddressInfo->Address, NULL) != NULL) {
989     return EFI_SUCCESS;
990   }
991 
992   Status   = EFI_SUCCESS;
993   IpSb     = IpIf->Service;
994   DadXmits = &IpSb->Ip6ConfigInstance.DadXmits;
995 
996   //
997   // Allocate the resources and insert info
998   //
999   Entry = AllocatePool (sizeof (IP6_DAD_ENTRY));
1000   if (Entry == NULL) {
1001     return EFI_OUT_OF_RESOURCES;
1002   }
1003 
1004   //
1005   // Map the incoming unicast address to solicited-node multicast address
1006   //
1007   Ip6CreateSNMulticastAddr (&AddressInfo->Address, &Entry->Destination);
1008 
1009   //
1010   // Join in the solicited-node multicast address.
1011   //
1012   Status = Ip6JoinGroup (IpSb, IpIf, &Entry->Destination);
1013   if (EFI_ERROR (Status)) {
1014     FreePool (Entry);
1015     return Status;
1016   }
1017 
1018   Entry->Signature    = IP6_DAD_ENTRY_SIGNATURE;
1019   Entry->MaxTransmit  = DadXmits->DupAddrDetectTransmits;
1020   Entry->Transmit     = 0;
1021   Entry->Receive      = 0;
1022   MaxDelayTick        = IP6_MAX_RTR_SOLICITATION_DELAY / IP6_TIMER_INTERVAL_IN_MS;
1023   Entry->RetransTick  = (MaxDelayTick * ((NET_RANDOM (NetRandomInitSeed ()) % 5) + 1)) / 5;
1024   Entry->AddressInfo  = AddressInfo;
1025   Entry->Callback     = Callback;
1026   Entry->Context      = Context;
1027   InsertTailList (&IpIf->DupAddrDetectList, &Entry->Link);
1028 
1029   if (Entry->MaxTransmit == 0) {
1030     //
1031     // DAD is disabled on this interface, immediately mark this DAD successful.
1032     //
1033     Ip6OnDADFinished (TRUE, IpIf, Entry);
1034   }
1035 
1036   return EFI_SUCCESS;
1037 }
1038 
1039 /**
1040   Search IP6_DAD_ENTRY from the Duplicate Address Detection List.
1041 
1042   @param[in]  IpSb          The pointer to the IP6_SERVICE instance.
1043   @param[in]  Target        The address information which needs DAD performed .
1044   @param[out] Interface     If not NULL, output the IP6 interface that configures
1045                             the tentative address.
1046 
1047   @return NULL if failed to find the matching DAD entry.
1048           Otherwise, point to the found DAD entry.
1049 
1050 **/
1051 IP6_DAD_ENTRY *
Ip6FindDADEntry(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * Target,OUT IP6_INTERFACE ** Interface OPTIONAL)1052 Ip6FindDADEntry (
1053   IN  IP6_SERVICE      *IpSb,
1054   IN  EFI_IPv6_ADDRESS *Target,
1055   OUT IP6_INTERFACE    **Interface OPTIONAL
1056   )
1057 {
1058   LIST_ENTRY                *Entry;
1059   LIST_ENTRY                *Entry2;
1060   IP6_INTERFACE             *IpIf;
1061   IP6_DAD_ENTRY             *DupAddrDetect;
1062   IP6_ADDRESS_INFO          *AddrInfo;
1063 
1064   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
1065     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
1066 
1067     NET_LIST_FOR_EACH (Entry2, &IpIf->DupAddrDetectList) {
1068       DupAddrDetect = NET_LIST_USER_STRUCT_S (Entry2, IP6_DAD_ENTRY, Link, IP6_DAD_ENTRY_SIGNATURE);
1069       AddrInfo      = DupAddrDetect->AddressInfo;
1070       if (EFI_IP6_EQUAL (&AddrInfo->Address, Target)) {
1071         if (Interface != NULL) {
1072           *Interface = IpIf;
1073         }
1074         return DupAddrDetect;
1075       }
1076     }
1077   }
1078 
1079   return NULL;
1080 }
1081 
1082 /**
1083   Generate router solicit message and send it out to Destination Address or
1084   All Router Link Local scope multicast address.
1085 
1086   @param[in]  IpSb               The IP service to send the packet.
1087   @param[in]  Interface          If not NULL, points to the IP6 interface to send
1088                                  the packet.
1089   @param[in]  SourceAddress      If not NULL, the source address of the message.
1090   @param[in]  DestinationAddress If not NULL, the destination address of the message.
1091   @param[in]  SourceLinkAddress  If not NULL, the MAC address of the source.
1092                                  A source link-layer address option will be appended
1093                                  to the message.
1094 
1095   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1096                                  operation.
1097   @retval EFI_SUCCESS            The router solicit message was successfully sent.
1098 
1099 **/
1100 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)1101 Ip6SendRouterSolicit (
1102   IN IP6_SERVICE            *IpSb,
1103   IN IP6_INTERFACE          *Interface          OPTIONAL,
1104   IN EFI_IPv6_ADDRESS       *SourceAddress      OPTIONAL,
1105   IN EFI_IPv6_ADDRESS       *DestinationAddress OPTIONAL,
1106   IN EFI_MAC_ADDRESS        *SourceLinkAddress  OPTIONAL
1107   )
1108 {
1109   NET_BUF                   *Packet;
1110   EFI_IP6_HEADER            Head;
1111   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1112   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1113   UINT16                    PayloadLen;
1114   IP6_INTERFACE             *IpIf;
1115 
1116   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1117 
1118   IpIf = Interface;
1119   if (IpIf == NULL && IpSb->DefaultInterface != NULL) {
1120     IpIf = IpSb->DefaultInterface;
1121   }
1122 
1123   //
1124   // Generate the packet to be sent
1125   //
1126 
1127   PayloadLen = (UINT16) sizeof (IP6_ICMP_INFORMATION_HEAD);
1128   if (SourceLinkAddress != NULL) {
1129     PayloadLen += sizeof (IP6_ETHER_ADDR_OPTION);
1130   }
1131 
1132   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1133   if (Packet == NULL) {
1134     return EFI_OUT_OF_RESOURCES;
1135   }
1136 
1137   //
1138   // Create the basic IPv6 header.
1139   //
1140   Head.FlowLabelL     = 0;
1141   Head.FlowLabelH     = 0;
1142   Head.PayloadLength  = HTONS (PayloadLen);
1143   Head.NextHeader     = IP6_ICMP;
1144   Head.HopLimit       = IP6_HOP_LIMIT;
1145 
1146   if (SourceAddress != NULL) {
1147     IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1148   } else {
1149     ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
1150   }
1151 
1152 
1153   if (DestinationAddress != NULL) {
1154     IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1155   } else {
1156     Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Head.DestinationAddress);
1157   }
1158 
1159   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1160 
1161   //
1162   // Fill in the ICMP header, and Source link-layer address if contained.
1163   //
1164 
1165   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1166   ASSERT (IcmpHead != NULL);
1167   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1168   IcmpHead->Head.Type = ICMP_V6_ROUTER_SOLICIT;
1169   IcmpHead->Head.Code = 0;
1170 
1171   LinkLayerOption = NULL;
1172   if (SourceLinkAddress != NULL) {
1173     LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1174                                                   Packet,
1175                                                   sizeof (IP6_ETHER_ADDR_OPTION),
1176                                                   FALSE
1177                                                   );
1178     ASSERT (LinkLayerOption != NULL);
1179     LinkLayerOption->Type   = Ip6OptionEtherSource;
1180     LinkLayerOption->Length = (UINT8) sizeof (IP6_ETHER_ADDR_OPTION);
1181     CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
1182   }
1183 
1184   //
1185   // Transmit the packet
1186   //
1187   return Ip6Output (IpSb, IpIf, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1188 }
1189 
1190 /**
1191   Generate a Neighbor Advertisement message and send it out to Destination Address.
1192 
1193   @param[in]  IpSb               The IP service to send the packet.
1194   @param[in]  SourceAddress      The source address of the message.
1195   @param[in]  DestinationAddress The destination address of the message.
1196   @param[in]  TargetIp6Address   The target address field in the Neighbor Solicitation
1197                                  message that prompted this advertisement.
1198   @param[in]  TargetLinkAddress  The MAC address for the target, i.e. the sender
1199                                  of the advertisement.
1200   @param[in]  IsRouter           If TRUE, indicates the sender is a router.
1201   @param[in]  Override           If TRUE, indicates the advertisement should override
1202                                  an existing cache entry and update the MAC address.
1203   @param[in]  Solicited          If TRUE, indicates the advertisement was sent
1204                                  in response to a Neighbor Solicitation from
1205                                  the Destination address.
1206 
1207   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1208                                  operation.
1209   @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.
1210 
1211 **/
1212 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)1213 Ip6SendNeighborAdvertise (
1214   IN IP6_SERVICE            *IpSb,
1215   IN EFI_IPv6_ADDRESS       *SourceAddress,
1216   IN EFI_IPv6_ADDRESS       *DestinationAddress,
1217   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
1218   IN EFI_MAC_ADDRESS        *TargetLinkAddress,
1219   IN BOOLEAN                IsRouter,
1220   IN BOOLEAN                Override,
1221   IN BOOLEAN                Solicited
1222   )
1223 {
1224   NET_BUF                   *Packet;
1225   EFI_IP6_HEADER            Head;
1226   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1227   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1228   EFI_IPv6_ADDRESS          *Target;
1229   UINT16                    PayloadLen;
1230 
1231   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1232 
1233   //
1234   // The Neighbor Advertisement message must include a Target link-layer address option
1235   // when responding to multicast solicitation and should include such option when
1236   // responding to unicast solicitation. It also must include such option as unsolicited
1237   // advertisement.
1238   //
1239   ASSERT (DestinationAddress != NULL && TargetIp6Address != NULL && TargetLinkAddress != NULL);
1240 
1241   PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS) + sizeof (IP6_ETHER_ADDR_OPTION));
1242 
1243   //
1244   // Generate the packet to be sent
1245   //
1246 
1247   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1248   if (Packet == NULL) {
1249     return EFI_OUT_OF_RESOURCES;
1250   }
1251 
1252   //
1253   // Create the basic IPv6 header.
1254   //
1255   Head.FlowLabelL     = 0;
1256   Head.FlowLabelH     = 0;
1257   Head.PayloadLength  = HTONS (PayloadLen);
1258   Head.NextHeader     = IP6_ICMP;
1259   Head.HopLimit       = IP6_HOP_LIMIT;
1260 
1261   IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1262   IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1263 
1264   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1265 
1266   //
1267   // Fill in the ICMP header, Target address, and Target link-layer address.
1268   // Set the Router flag, Solicited flag and Override flag.
1269   //
1270 
1271   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1272   ASSERT (IcmpHead != NULL);
1273   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1274   IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_ADVERTISE;
1275   IcmpHead->Head.Code = 0;
1276 
1277   if (IsRouter) {
1278     IcmpHead->Fourth |= IP6_IS_ROUTER_FLAG;
1279   }
1280 
1281   if (Solicited) {
1282     IcmpHead->Fourth |= IP6_SOLICITED_FLAG;
1283   }
1284 
1285   if (Override) {
1286     IcmpHead->Fourth |= IP6_OVERRIDE_FLAG;
1287   }
1288 
1289   Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
1290   ASSERT (Target != NULL);
1291   IP6_COPY_ADDRESS (Target, TargetIp6Address);
1292 
1293   LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1294                                                 Packet,
1295                                                 sizeof (IP6_ETHER_ADDR_OPTION),
1296                                                 FALSE
1297                                                 );
1298   ASSERT (LinkLayerOption != NULL);
1299   LinkLayerOption->Type   = Ip6OptionEtherTarget;
1300   LinkLayerOption->Length = 1;
1301   CopyMem (LinkLayerOption->EtherAddr, TargetLinkAddress, 6);
1302 
1303   //
1304   // Transmit the packet
1305   //
1306   return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1307 }
1308 
1309 /**
1310   Generate the Neighbor Solicitation message and send it to the Destination Address.
1311 
1312   @param[in]  IpSb               The IP service to send the packet
1313   @param[in]  SourceAddress      The source address of the message.
1314   @param[in]  DestinationAddress The destination address of the message.
1315   @param[in]  TargetIp6Address   The IP address of the target of the solicitation.
1316                                  It must not be a multicast address.
1317   @param[in]  SourceLinkAddress  The MAC address for the sender. If not NULL,
1318                                  a source link-layer address option will be appended
1319                                  to the message.
1320 
1321   @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.
1322   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1323                                  operation.
1324   @retval EFI_SUCCESS            The Neighbor Advertise message was successfully sent.
1325 
1326 **/
1327 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)1328 Ip6SendNeighborSolicit (
1329   IN IP6_SERVICE            *IpSb,
1330   IN EFI_IPv6_ADDRESS       *SourceAddress,
1331   IN EFI_IPv6_ADDRESS       *DestinationAddress,
1332   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
1333   IN EFI_MAC_ADDRESS        *SourceLinkAddress OPTIONAL
1334   )
1335 {
1336   NET_BUF                   *Packet;
1337   EFI_IP6_HEADER            Head;
1338   IP6_ICMP_INFORMATION_HEAD *IcmpHead;
1339   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
1340   EFI_IPv6_ADDRESS          *Target;
1341   BOOLEAN                   IsDAD;
1342   UINT16                    PayloadLen;
1343   IP6_NEIGHBOR_ENTRY        *Neighbor;
1344 
1345   //
1346   // Check input parameters
1347   //
1348   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
1349   if (DestinationAddress == NULL || TargetIp6Address == NULL) {
1350     return EFI_INVALID_PARAMETER;
1351   }
1352 
1353   IsDAD = FALSE;
1354 
1355   if (SourceAddress == NULL || (SourceAddress != NULL && NetIp6IsUnspecifiedAddr (SourceAddress))) {
1356     IsDAD = TRUE;
1357   }
1358 
1359   //
1360   // The Neighbor Solicitation message should include a source link-layer address option
1361   // if the solicitation is not sent by performing DAD - Duplicate Address Detection.
1362   // Otherwise must not include it.
1363   //
1364   PayloadLen = (UINT16) (sizeof (IP6_ICMP_INFORMATION_HEAD) + sizeof (EFI_IPv6_ADDRESS));
1365 
1366   if (!IsDAD) {
1367     if (SourceLinkAddress == NULL) {
1368       return EFI_INVALID_PARAMETER;
1369     }
1370 
1371     PayloadLen = (UINT16) (PayloadLen + sizeof (IP6_ETHER_ADDR_OPTION));
1372   }
1373 
1374   //
1375   // Generate the packet to be sent
1376   //
1377 
1378   Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);
1379   if (Packet == NULL) {
1380     return EFI_OUT_OF_RESOURCES;
1381   }
1382 
1383   //
1384   // Create the basic IPv6 header
1385   //
1386   Head.FlowLabelL     = 0;
1387   Head.FlowLabelH     = 0;
1388   Head.PayloadLength  = HTONS (PayloadLen);
1389   Head.NextHeader     = IP6_ICMP;
1390   Head.HopLimit       = IP6_HOP_LIMIT;
1391 
1392   if (SourceAddress != NULL) {
1393     IP6_COPY_ADDRESS (&Head.SourceAddress, SourceAddress);
1394   } else {
1395     ZeroMem (&Head.SourceAddress, sizeof (EFI_IPv6_ADDRESS));
1396   }
1397 
1398   IP6_COPY_ADDRESS (&Head.DestinationAddress, DestinationAddress);
1399 
1400   NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));
1401 
1402   //
1403   // Fill in the ICMP header, Target address, and Source link-layer address.
1404   //
1405   IcmpHead = (IP6_ICMP_INFORMATION_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_ICMP_INFORMATION_HEAD), FALSE);
1406   ASSERT (IcmpHead != NULL);
1407   ZeroMem (IcmpHead, sizeof (IP6_ICMP_INFORMATION_HEAD));
1408   IcmpHead->Head.Type = ICMP_V6_NEIGHBOR_SOLICIT;
1409   IcmpHead->Head.Code = 0;
1410 
1411   Target = (EFI_IPv6_ADDRESS *) NetbufAllocSpace (Packet, sizeof (EFI_IPv6_ADDRESS), FALSE);
1412   ASSERT (Target != NULL);
1413   IP6_COPY_ADDRESS (Target, TargetIp6Address);
1414 
1415   LinkLayerOption = NULL;
1416   if (!IsDAD) {
1417 
1418     //
1419     // Fill in the source link-layer address option
1420     //
1421     LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) NetbufAllocSpace (
1422                                                   Packet,
1423                                                   sizeof (IP6_ETHER_ADDR_OPTION),
1424                                                   FALSE
1425                                                   );
1426     ASSERT (LinkLayerOption != NULL);
1427     LinkLayerOption->Type   = Ip6OptionEtherSource;
1428     LinkLayerOption->Length = 1;
1429     CopyMem (LinkLayerOption->EtherAddr, SourceLinkAddress, 6);
1430   }
1431 
1432   //
1433   // Create a Neighbor Cache entry in the INCOMPLETE state when performing
1434   // address resolution.
1435   //
1436   if (!IsDAD && Ip6IsSNMulticastAddr (DestinationAddress)) {
1437     Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
1438     if (Neighbor == NULL) {
1439       Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, NULL);
1440       ASSERT (Neighbor != NULL);
1441     }
1442   }
1443 
1444   //
1445   // Transmit the packet
1446   //
1447   return Ip6Output (IpSb, IpSb->DefaultInterface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);
1448 }
1449 
1450 /**
1451   Process the Neighbor Solicitation message. The message may be sent for Duplicate
1452   Address Detection or Address Resolution.
1453 
1454   @param[in]  IpSb               The IP service that received the packet.
1455   @param[in]  Head               The IP head of the message.
1456   @param[in]  Packet             The content of the message with IP head removed.
1457 
1458   @retval EFI_SUCCESS            The packet processed successfully.
1459   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1460   @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.
1461   @retval Others                 Failed to process the packet.
1462 
1463 **/
1464 EFI_STATUS
Ip6ProcessNeighborSolicit(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1465 Ip6ProcessNeighborSolicit (
1466   IN IP6_SERVICE            *IpSb,
1467   IN EFI_IP6_HEADER         *Head,
1468   IN NET_BUF                *Packet
1469   )
1470 {
1471   IP6_ICMP_INFORMATION_HEAD Icmp;
1472   EFI_IPv6_ADDRESS          Target;
1473   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1474   BOOLEAN                   IsDAD;
1475   BOOLEAN                   IsUnicast;
1476   BOOLEAN                   IsMaintained;
1477   IP6_DAD_ENTRY             *DupAddrDetect;
1478   IP6_INTERFACE             *IpIf;
1479   IP6_NEIGHBOR_ENTRY        *Neighbor;
1480   BOOLEAN                   Solicited;
1481   BOOLEAN                   UpdateCache;
1482   EFI_IPv6_ADDRESS          Dest;
1483   UINT16                    OptionLen;
1484   UINT8                     *Option;
1485   BOOLEAN                   Provided;
1486   EFI_STATUS                Status;
1487   VOID                      *MacAddress;
1488 
1489   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1490   NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
1491 
1492   //
1493   // Perform Message Validation:
1494   // The IP Hop Limit field has a value of 255, i.e., the packet
1495   // could not possibly have been forwarded by a router.
1496   // ICMP Code is 0.
1497   // Target Address is not a multicast address.
1498   //
1499   Status = EFI_INVALID_PARAMETER;
1500 
1501   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
1502     goto Exit;
1503   }
1504 
1505   //
1506   // ICMP length is 24 or more octets.
1507   //
1508   OptionLen = 0;
1509   if (Head->PayloadLength < IP6_ND_LENGTH) {
1510     goto Exit;
1511   } else {
1512     OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
1513     if (OptionLen != 0) {
1514       Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
1515       ASSERT (Option != NULL);
1516 
1517       //
1518       // All included options should have a length that is greater than zero.
1519       //
1520       if (!Ip6IsNDOptionValid (Option, OptionLen)) {
1521         goto Exit;
1522       }
1523     }
1524   }
1525 
1526   IsDAD        = NetIp6IsUnspecifiedAddr (&Head->SourceAddress);
1527   IsUnicast    = (BOOLEAN) !Ip6IsSNMulticastAddr (&Head->DestinationAddress);
1528   IsMaintained = Ip6IsOneOfSetAddress (IpSb, &Target, &IpIf, NULL);
1529 
1530   Provided = FALSE;
1531   if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
1532     NetbufCopy (
1533       Packet,
1534       IP6_ND_LENGTH,
1535       sizeof (IP6_ETHER_ADDR_OPTION),
1536       (UINT8 *) &LinkLayerOption
1537       );
1538     //
1539     // The solicitation for neighbor discovery should include a source link-layer
1540     // address option. If the option is not recognized, silently ignore it.
1541     //
1542     if (LinkLayerOption.Type == Ip6OptionEtherSource) {
1543       if (IsDAD) {
1544         //
1545         // If the IP source address is the unspecified address, the source
1546         // link-layer address option must not be included in the message.
1547         //
1548         goto Exit;
1549       }
1550 
1551       Provided = TRUE;
1552     }
1553   }
1554 
1555   //
1556   // If the IP source address is the unspecified address, the IP
1557   // destination address is a solicited-node multicast address.
1558   //
1559   if (IsDAD && IsUnicast) {
1560     goto Exit;
1561   }
1562 
1563   //
1564   // If the target address is tentative, and the source address is a unicast address,
1565   // the solicitation's sender is performing address resolution on the target;
1566   //  the solicitation should be silently ignored.
1567   //
1568   if (!IsDAD && !IsMaintained) {
1569     goto Exit;
1570   }
1571 
1572   //
1573   // If received unicast neighbor solicitation but destination is not this node,
1574   // drop the packet.
1575   //
1576   if (IsUnicast && !IsMaintained) {
1577     goto Exit;
1578   }
1579 
1580   //
1581   // In DAD, when target address is a tentative address,
1582   // process the received neighbor solicitation message but not send out response.
1583   //
1584   if (IsDAD && !IsMaintained) {
1585     DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
1586     if (DupAddrDetect != NULL) {
1587       //
1588       // Check the MAC address of the incoming packet.
1589       //
1590       if (IpSb->RecvRequest.MnpToken.Packet.RxData == NULL) {
1591         goto Exit;
1592       }
1593 
1594       MacAddress = IpSb->RecvRequest.MnpToken.Packet.RxData->SourceAddress;
1595       if (MacAddress != NULL) {
1596         if (CompareMem (
1597               MacAddress,
1598               &IpSb->SnpMode.CurrentAddress,
1599               IpSb->SnpMode.HwAddressSize
1600               ) != 0) {
1601           //
1602           // The NS is from another node to performing DAD on the same address.
1603           // Fail DAD for the tentative address.
1604           //
1605           Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
1606           Status = EFI_ICMP_ERROR;
1607         } else {
1608           //
1609           // The below layer loopback the NS we sent. Record it and wait for more.
1610           //
1611           DupAddrDetect->Receive++;
1612           Status = EFI_SUCCESS;
1613         }
1614       }
1615     }
1616     goto Exit;
1617   }
1618 
1619   //
1620   // If the solicitation does not contain a link-layer address, DO NOT create or
1621   // update the neighbor cache entries.
1622   //
1623   if (Provided) {
1624     Neighbor    = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
1625     UpdateCache = FALSE;
1626 
1627     if (Neighbor == NULL) {
1628       Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, &Head->SourceAddress, NULL);
1629       if (Neighbor == NULL) {
1630         Status = EFI_OUT_OF_RESOURCES;
1631         goto Exit;
1632       }
1633       UpdateCache = TRUE;
1634     } else {
1635       if (CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6) != 0) {
1636         UpdateCache = TRUE;
1637       }
1638     }
1639 
1640     if (UpdateCache) {
1641       Neighbor->State = EfiNeighborStale;
1642       Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1643       CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1644       //
1645       // Send queued packets if exist.
1646       //
1647       Neighbor->CallBack ((VOID *) Neighbor);
1648     }
1649   }
1650 
1651   //
1652   // Sends a Neighbor Advertisement as response.
1653   // Set the Router flag to zero since the node is a host.
1654   // If the source address of the solicitation is unspeicifed, and target address
1655   // is one of the maintained address, reply a unsolicited multicast advertisement.
1656   //
1657   if (IsDAD && IsMaintained) {
1658     Solicited = FALSE;
1659     Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &Dest);
1660   } else {
1661     Solicited = TRUE;
1662     IP6_COPY_ADDRESS (&Dest, &Head->SourceAddress);
1663   }
1664 
1665   Status = Ip6SendNeighborAdvertise (
1666              IpSb,
1667              &Target,
1668              &Dest,
1669              &Target,
1670              &IpSb->SnpMode.CurrentAddress,
1671              FALSE,
1672              TRUE,
1673              Solicited
1674              );
1675 Exit:
1676   NetbufFree (Packet);
1677   return Status;
1678 }
1679 
1680 /**
1681   Process the Neighbor Advertisement message.
1682 
1683   @param[in]  IpSb               The IP service that received the packet.
1684   @param[in]  Head               The IP head of the message.
1685   @param[in]  Packet             The content of the message with IP head removed.
1686 
1687   @retval EFI_SUCCESS            The packet processed successfully.
1688   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1689   @retval EFI_ICMP_ERROR         The packet indicates that DAD is failed.
1690   @retval Others                 Failed to process the packet.
1691 
1692 **/
1693 EFI_STATUS
Ip6ProcessNeighborAdvertise(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1694 Ip6ProcessNeighborAdvertise (
1695   IN IP6_SERVICE            *IpSb,
1696   IN EFI_IP6_HEADER         *Head,
1697   IN NET_BUF                *Packet
1698   )
1699 {
1700   IP6_ICMP_INFORMATION_HEAD Icmp;
1701   EFI_IPv6_ADDRESS          Target;
1702   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1703   BOOLEAN                   Provided;
1704   INTN                      Compare;
1705   IP6_NEIGHBOR_ENTRY        *Neighbor;
1706   IP6_DEFAULT_ROUTER        *DefaultRouter;
1707   BOOLEAN                   Solicited;
1708   BOOLEAN                   IsRouter;
1709   BOOLEAN                   Override;
1710   IP6_DAD_ENTRY             *DupAddrDetect;
1711   IP6_INTERFACE             *IpIf;
1712   UINT16                    OptionLen;
1713   UINT8                     *Option;
1714   EFI_STATUS                Status;
1715 
1716   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1717   NetbufCopy (Packet, sizeof (Icmp), sizeof (Target), Target.Addr);
1718 
1719   //
1720   // Validate the incoming Neighbor Advertisement
1721   //
1722   Status = EFI_INVALID_PARAMETER;
1723   //
1724   // The IP Hop Limit field has a value of 255, i.e., the packet
1725   // could not possibly have been forwarded by a router.
1726   // ICMP Code is 0.
1727   // Target Address is not a multicast address.
1728   //
1729   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 || !NetIp6IsValidUnicast (&Target)) {
1730     goto Exit;
1731   }
1732 
1733   //
1734   // ICMP length is 24 or more octets.
1735   //
1736   Provided  = FALSE;
1737   OptionLen = 0;
1738   if (Head->PayloadLength < IP6_ND_LENGTH) {
1739     goto Exit;
1740   } else {
1741     OptionLen = (UINT16) (Head->PayloadLength - IP6_ND_LENGTH);
1742     if (OptionLen != 0) {
1743       Option    = NetbufGetByte (Packet, IP6_ND_LENGTH, NULL);
1744       ASSERT (Option != NULL);
1745 
1746       //
1747       // All included options should have a length that is greater than zero.
1748       //
1749       if (!Ip6IsNDOptionValid (Option, OptionLen)) {
1750         goto Exit;
1751       }
1752     }
1753   }
1754 
1755   //
1756   // If the IP destination address is a multicast address, Solicited Flag is ZERO.
1757   //
1758   Solicited = FALSE;
1759   if ((Icmp.Fourth & IP6_SOLICITED_FLAG) == IP6_SOLICITED_FLAG) {
1760     Solicited = TRUE;
1761   }
1762   if (IP6_IS_MULTICAST (&Head->DestinationAddress) && Solicited) {
1763     goto Exit;
1764   }
1765 
1766   //
1767   // DAD - Check whether the Target is one of our tentative address.
1768   //
1769   DupAddrDetect = Ip6FindDADEntry (IpSb, &Target, &IpIf);
1770   if (DupAddrDetect != NULL) {
1771     //
1772     // DAD fails, some other node is using this address.
1773     //
1774     NetbufFree (Packet);
1775     Ip6OnDADFinished (FALSE, IpIf, DupAddrDetect);
1776     return EFI_ICMP_ERROR;
1777   }
1778 
1779   //
1780   // Search the Neighbor Cache for the target's entry. If no entry exists,
1781   // the advertisement should be silently discarded.
1782   //
1783   Neighbor = Ip6FindNeighborEntry (IpSb, &Target);
1784   if (Neighbor == NULL) {
1785     goto Exit;
1786   }
1787 
1788   //
1789   // Get IsRouter Flag and Override Flag
1790   //
1791   IsRouter = FALSE;
1792   Override = FALSE;
1793   if ((Icmp.Fourth & IP6_IS_ROUTER_FLAG) == IP6_IS_ROUTER_FLAG) {
1794     IsRouter = TRUE;
1795   }
1796   if ((Icmp.Fourth & IP6_OVERRIDE_FLAG) == IP6_OVERRIDE_FLAG) {
1797     Override = TRUE;
1798   }
1799 
1800   //
1801   // Check whether link layer option is included.
1802   //
1803   if (OptionLen >= sizeof (IP6_ETHER_ADDR_OPTION)) {
1804     NetbufCopy (
1805       Packet,
1806       IP6_ND_LENGTH,
1807       sizeof (IP6_ETHER_ADDR_OPTION),
1808       (UINT8 *) &LinkLayerOption
1809       );
1810 
1811     if (LinkLayerOption.Type == Ip6OptionEtherTarget) {
1812       Provided = TRUE;
1813     }
1814   }
1815 
1816   Compare = 0;
1817   if (Provided) {
1818     Compare = CompareMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1819   }
1820 
1821   if (!Neighbor->IsRouter && IsRouter) {
1822     DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
1823     if (DefaultRouter != NULL) {
1824       DefaultRouter->NeighborCache = Neighbor;
1825     }
1826   }
1827 
1828   if (Neighbor->State == EfiNeighborInComplete) {
1829     //
1830     // If the target's Neighbor Cache entry is in INCOMPLETE state and no
1831     // Target Link-Layer address option is included while link layer has
1832     // address, the message should be silently discarded.
1833     //
1834     if (!Provided) {
1835       goto Exit;
1836     }
1837     //
1838     // Update the Neighbor Cache
1839     //
1840     CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1841     if (Solicited) {
1842       Neighbor->State = EfiNeighborReachable;
1843       Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
1844     } else {
1845       Neighbor->State = EfiNeighborStale;
1846       Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1847       //
1848       // Send any packets queued for the neighbor awaiting address resolution.
1849       //
1850       Neighbor->CallBack ((VOID *) Neighbor);
1851     }
1852 
1853     Neighbor->IsRouter = IsRouter;
1854 
1855   } else {
1856     if (!Override && Compare != 0) {
1857       //
1858       // When the Override Flag is clear and supplied link-layer address differs from
1859       // that in the cache, if the state of the entry is not REACHABLE, ignore the
1860       // message. Otherwise set it to STALE but do not update the entry in any
1861       // other way.
1862       //
1863       if (Neighbor->State == EfiNeighborReachable) {
1864         Neighbor->State = EfiNeighborStale;
1865         Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1866       }
1867     } else {
1868       if (Compare != 0) {
1869         CopyMem (Neighbor->LinkAddress.Addr, LinkLayerOption.EtherAddr, 6);
1870       }
1871       //
1872       // Update the entry's state
1873       //
1874       if (Solicited) {
1875         Neighbor->State = EfiNeighborReachable;
1876         Neighbor->Ticks = IP6_GET_TICKS (IpSb->ReachableTime);
1877       } else {
1878         if (Compare != 0) {
1879           Neighbor->State = EfiNeighborStale;
1880           Neighbor->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
1881         }
1882       }
1883 
1884       //
1885       // When IsRouter is changed from TRUE to FALSE, remove the router from the
1886       // Default Router List and remove the Destination Cache entries for all destinations
1887       // using the neighbor as a router.
1888       //
1889       if (Neighbor->IsRouter && !IsRouter) {
1890         DefaultRouter = Ip6FindDefaultRouter (IpSb, &Target);
1891         if (DefaultRouter != NULL) {
1892           Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
1893         }
1894       }
1895 
1896       Neighbor->IsRouter = IsRouter;
1897     }
1898   }
1899 
1900   if (Neighbor->State == EfiNeighborReachable) {
1901     Neighbor->CallBack ((VOID *) Neighbor);
1902   }
1903 
1904   Status = EFI_SUCCESS;
1905 
1906 Exit:
1907   NetbufFree (Packet);
1908   return Status;
1909 }
1910 
1911 /**
1912   Process the Router Advertisement message according to RFC4861.
1913 
1914   @param[in]  IpSb               The IP service that received the packet.
1915   @param[in]  Head               The IP head of the message.
1916   @param[in]  Packet             The content of the message with the IP head removed.
1917 
1918   @retval EFI_SUCCESS            The packet processed successfully.
1919   @retval EFI_INVALID_PARAMETER  The packet is invalid.
1920   @retval EFI_OUT_OF_RESOURCES   Insufficient resources to complete the
1921                                  operation.
1922   @retval Others                 Failed to process the packet.
1923 
1924 **/
1925 EFI_STATUS
Ip6ProcessRouterAdvertise(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)1926 Ip6ProcessRouterAdvertise (
1927   IN IP6_SERVICE            *IpSb,
1928   IN EFI_IP6_HEADER         *Head,
1929   IN NET_BUF                *Packet
1930   )
1931 {
1932   IP6_ICMP_INFORMATION_HEAD Icmp;
1933   UINT32                    ReachableTime;
1934   UINT32                    RetransTimer;
1935   UINT16                    RouterLifetime;
1936   UINT16                    Offset;
1937   UINT8                     Type;
1938   UINT8                     Length;
1939   IP6_ETHER_ADDR_OPTION     LinkLayerOption;
1940   UINT32                    Fourth;
1941   UINT8                     CurHopLimit;
1942   BOOLEAN                   Mflag;
1943   BOOLEAN                   Oflag;
1944   IP6_DEFAULT_ROUTER        *DefaultRouter;
1945   IP6_NEIGHBOR_ENTRY        *NeighborCache;
1946   EFI_MAC_ADDRESS           LinkLayerAddress;
1947   IP6_MTU_OPTION            MTUOption;
1948   IP6_PREFIX_INFO_OPTION    PrefixOption;
1949   IP6_PREFIX_LIST_ENTRY     *PrefixList;
1950   BOOLEAN                   OnLink;
1951   BOOLEAN                   Autonomous;
1952   EFI_IPv6_ADDRESS          StatelessAddress;
1953   EFI_STATUS                Status;
1954   UINT16                    OptionLen;
1955   UINT8                     *Option;
1956   INTN                      Result;
1957 
1958   Status = EFI_INVALID_PARAMETER;
1959 
1960   if (IpSb->Ip6ConfigInstance.Policy != Ip6ConfigPolicyAutomatic) {
1961     //
1962     // Skip the process below as it's not required under the current policy.
1963     //
1964     goto Exit;
1965   }
1966 
1967   NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp);
1968 
1969   //
1970   // Validate the incoming Router Advertisement
1971   //
1972 
1973   //
1974   // The IP source address must be a link-local address
1975   //
1976   if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
1977     goto Exit;
1978   }
1979   //
1980   // The IP Hop Limit field has a value of 255, i.e. the packet
1981   // could not possibly have been forwarded by a router.
1982   // ICMP Code is 0.
1983   // ICMP length (derived from the IP length) is 16 or more octets.
1984   //
1985   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp.Head.Code != 0 ||
1986       Head->PayloadLength < IP6_RA_LENGTH) {
1987     goto Exit;
1988   }
1989 
1990   //
1991   // All included options have a length that is greater than zero.
1992   //
1993   OptionLen = (UINT16) (Head->PayloadLength - IP6_RA_LENGTH);
1994   if (OptionLen != 0) {
1995     Option    = NetbufGetByte (Packet, IP6_RA_LENGTH, NULL);
1996     ASSERT (Option != NULL);
1997 
1998     if (!Ip6IsNDOptionValid (Option, OptionLen)) {
1999       goto Exit;
2000     }
2001   }
2002 
2003   //
2004   // Process Fourth field.
2005   // In Router Advertisement, Fourth is composed of CurHopLimit (8bit), M flag, O flag,
2006   // and Router Lifetime (16 bit).
2007   //
2008 
2009   Fourth = NTOHL (Icmp.Fourth);
2010   CopyMem (&RouterLifetime, &Fourth, sizeof (UINT16));
2011 
2012   //
2013   // If the source address already in the default router list, update it.
2014   // Otherwise create a new entry.
2015   // A Lifetime of zero indicates that the router is not a default router.
2016   //
2017   DefaultRouter = Ip6FindDefaultRouter (IpSb, &Head->SourceAddress);
2018   if (DefaultRouter == NULL) {
2019     if (RouterLifetime != 0) {
2020       DefaultRouter = Ip6CreateDefaultRouter (IpSb, &Head->SourceAddress, RouterLifetime);
2021       if (DefaultRouter == NULL) {
2022         Status = EFI_OUT_OF_RESOURCES;
2023         goto Exit;
2024       }
2025     }
2026   } else {
2027     if (RouterLifetime != 0) {
2028       DefaultRouter->Lifetime = RouterLifetime;
2029       //
2030       // Check the corresponding neighbor cache entry here.
2031       //
2032       if (DefaultRouter->NeighborCache == NULL) {
2033         DefaultRouter->NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
2034       }
2035     } else {
2036       //
2037       // If the address is in the host's default router list and the router lifetime is zero,
2038       // immediately time-out the entry.
2039       //
2040       Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
2041     }
2042   }
2043 
2044   CurHopLimit = *((UINT8 *) &Fourth + 3);
2045   if (CurHopLimit != 0) {
2046     IpSb->CurHopLimit = CurHopLimit;
2047   }
2048 
2049   Mflag = FALSE;
2050   Oflag = FALSE;
2051   if ((*((UINT8 *) &Fourth + 2) & IP6_M_ADDR_CONFIG_FLAG) == IP6_M_ADDR_CONFIG_FLAG) {
2052     Mflag = TRUE;
2053   } else {
2054     if ((*((UINT8 *) &Fourth + 2) & IP6_O_CONFIG_FLAG) == IP6_O_CONFIG_FLAG) {
2055       Oflag = TRUE;
2056     }
2057   }
2058 
2059   if (Mflag || Oflag) {
2060     //
2061     // Use Ip6Config to get available addresses or other configuration from DHCP.
2062     //
2063     Ip6ConfigStartStatefulAutoConfig (&IpSb->Ip6ConfigInstance, Oflag);
2064   }
2065 
2066   //
2067   // Process Reachable Time and Retrans Timer fields.
2068   //
2069   NetbufCopy (Packet, sizeof (Icmp), sizeof (UINT32), (UINT8 *) &ReachableTime);
2070   NetbufCopy (Packet, sizeof (Icmp) + sizeof (UINT32), sizeof (UINT32), (UINT8 *) &RetransTimer);
2071   ReachableTime = NTOHL (ReachableTime);
2072   RetransTimer  = NTOHL (RetransTimer);
2073 
2074   if (ReachableTime != 0 && ReachableTime != IpSb->BaseReachableTime) {
2075     //
2076     // If new value is not unspecified and differs from the previous one, record it
2077     // in BaseReachableTime and recompute a ReachableTime.
2078     //
2079     IpSb->BaseReachableTime = ReachableTime;
2080     Ip6UpdateReachableTime (IpSb);
2081   }
2082 
2083   if (RetransTimer != 0) {
2084     IpSb->RetransTimer = RetransTimer;
2085   }
2086 
2087   //
2088   // IsRouter flag must be set to TRUE if corresponding neighbor cache entry exists.
2089   //
2090   NeighborCache = Ip6FindNeighborEntry (IpSb, &Head->SourceAddress);
2091   if (NeighborCache != NULL) {
2092     NeighborCache->IsRouter = TRUE;
2093   }
2094 
2095   //
2096   // If an valid router advertisment is received, stops router solicitation.
2097   //
2098   IpSb->RouterAdvertiseReceived = TRUE;
2099 
2100   //
2101   // The only defined options that may appear are the Source
2102   // Link-Layer Address, Prefix information and MTU options.
2103   // All included options have a length that is greater than zero.
2104   //
2105   Offset = 16;
2106   while (Offset < Head->PayloadLength) {
2107     NetbufCopy (Packet, Offset, sizeof (UINT8), &Type);
2108     switch (Type) {
2109     case Ip6OptionEtherSource:
2110       //
2111       // Update the neighbor cache
2112       //
2113       NetbufCopy (Packet, Offset, sizeof (IP6_ETHER_ADDR_OPTION), (UINT8 *) &LinkLayerOption);
2114       if (LinkLayerOption.Length <= 0) {
2115         goto Exit;
2116       }
2117 
2118       ZeroMem (&LinkLayerAddress, sizeof (EFI_MAC_ADDRESS));
2119       CopyMem (&LinkLayerAddress, LinkLayerOption.EtherAddr, 6);
2120 
2121       if (NeighborCache == NULL) {
2122         NeighborCache = Ip6CreateNeighborEntry (
2123                           IpSb,
2124                           Ip6OnArpResolved,
2125                           &Head->SourceAddress,
2126                           &LinkLayerAddress
2127                           );
2128         if (NeighborCache == NULL) {
2129           Status = EFI_OUT_OF_RESOURCES;
2130           goto Exit;
2131         }
2132         NeighborCache->IsRouter = TRUE;
2133         NeighborCache->State    = EfiNeighborStale;
2134         NeighborCache->Ticks    = (UINT32) IP6_INFINIT_LIFETIME;
2135       } else {
2136         Result = CompareMem (&LinkLayerAddress, &NeighborCache->LinkAddress, 6);
2137 
2138         //
2139         // If the link-local address is the same as that already in the cache,
2140         // the cache entry's state remains unchanged. Otherwise update the
2141         // reachability state to STALE.
2142         //
2143         if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
2144           CopyMem (&NeighborCache->LinkAddress, &LinkLayerAddress, 6);
2145 
2146           NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2147 
2148           if (NeighborCache->State == EfiNeighborInComplete) {
2149             //
2150             // Send queued packets if exist.
2151             //
2152             NeighborCache->State = EfiNeighborStale;
2153             NeighborCache->CallBack ((VOID *) NeighborCache);
2154           } else {
2155             NeighborCache->State = EfiNeighborStale;
2156           }
2157         }
2158       }
2159 
2160       Offset = (UINT16) (Offset + (UINT16) LinkLayerOption.Length * 8);
2161       break;
2162     case Ip6OptionPrefixInfo:
2163       NetbufCopy (Packet, Offset, sizeof (IP6_PREFIX_INFO_OPTION), (UINT8 *) &PrefixOption);
2164       if (PrefixOption.Length != 4) {
2165         goto Exit;
2166       }
2167       PrefixOption.ValidLifetime     = NTOHL (PrefixOption.ValidLifetime);
2168       PrefixOption.PreferredLifetime = NTOHL (PrefixOption.PreferredLifetime);
2169 
2170       //
2171       // Get L and A flag, recorded in the lower 2 bits of Reserved1
2172       //
2173       OnLink = FALSE;
2174       if ((PrefixOption.Reserved1 & IP6_ON_LINK_FLAG) == IP6_ON_LINK_FLAG) {
2175         OnLink = TRUE;
2176       }
2177       Autonomous = FALSE;
2178       if ((PrefixOption.Reserved1 & IP6_AUTO_CONFIG_FLAG) == IP6_AUTO_CONFIG_FLAG) {
2179         Autonomous = TRUE;
2180       }
2181 
2182       //
2183       // If the prefix is the link-local prefix, silently ignore the prefix option.
2184       //
2185       if (PrefixOption.PrefixLength == IP6_LINK_LOCAL_PREFIX_LENGTH &&
2186           NetIp6IsLinkLocalAddr (&PrefixOption.Prefix)
2187           ) {
2188         Offset += sizeof (IP6_PREFIX_INFO_OPTION);
2189         break;
2190       }
2191       //
2192       // Do following if on-link flag is set according to RFC4861.
2193       //
2194       if (OnLink) {
2195         PrefixList = Ip6FindPrefixListEntry (
2196                        IpSb,
2197                        TRUE,
2198                        PrefixOption.PrefixLength,
2199                        &PrefixOption.Prefix
2200                        );
2201         //
2202         // Create a new entry for the prefix, if the ValidLifetime is zero,
2203         // silently ignore the prefix option.
2204         //
2205         if (PrefixList == NULL && PrefixOption.ValidLifetime != 0) {
2206           PrefixList = Ip6CreatePrefixListEntry (
2207                          IpSb,
2208                          TRUE,
2209                          PrefixOption.ValidLifetime,
2210                          PrefixOption.PreferredLifetime,
2211                          PrefixOption.PrefixLength,
2212                          &PrefixOption.Prefix
2213                          );
2214           if (PrefixList == NULL) {
2215             Status = EFI_OUT_OF_RESOURCES;
2216             goto Exit;
2217           }
2218         } else if (PrefixList != NULL) {
2219           if (PrefixOption.ValidLifetime != 0) {
2220             PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
2221           } else {
2222             //
2223             // If the prefix exists and incoming ValidLifetime is zero, immediately
2224             // remove the prefix.
2225             Ip6DestroyPrefixListEntry (IpSb, PrefixList, OnLink, TRUE);
2226           }
2227         }
2228       }
2229 
2230       //
2231       // Do following if Autonomous flag is set according to RFC4862.
2232       //
2233       if (Autonomous && PrefixOption.PreferredLifetime <= PrefixOption.ValidLifetime) {
2234         PrefixList = Ip6FindPrefixListEntry (
2235                        IpSb,
2236                        FALSE,
2237                        PrefixOption.PrefixLength,
2238                        &PrefixOption.Prefix
2239                        );
2240         //
2241         // Create a new entry for the prefix, and form an address by prefix + interface id
2242         // If the sum of the prefix length and interface identifier length
2243         // does not equal 128 bits, the Prefix Information option MUST be ignored.
2244         //
2245         if (PrefixList == NULL &&
2246             PrefixOption.ValidLifetime != 0 &&
2247             PrefixOption.PrefixLength + IpSb->InterfaceIdLen * 8 == 128
2248             ) {
2249           //
2250           // Form the address in network order.
2251           //
2252           CopyMem (&StatelessAddress, &PrefixOption.Prefix, sizeof (UINT64));
2253           CopyMem (&StatelessAddress.Addr[8], IpSb->InterfaceId, sizeof (UINT64));
2254 
2255           //
2256           // If the address is not yet in the assigned address list, adds it into.
2257           //
2258           if (!Ip6IsOneOfSetAddress (IpSb, &StatelessAddress, NULL, NULL)) {
2259             //
2260             // And also not in the DAD process, check its uniqeness firstly.
2261             //
2262             if (Ip6FindDADEntry (IpSb, &StatelessAddress, NULL) == NULL) {
2263               Status = Ip6SetAddress (
2264                          IpSb->DefaultInterface,
2265                          &StatelessAddress,
2266                          FALSE,
2267                          PrefixOption.PrefixLength,
2268                          PrefixOption.ValidLifetime,
2269                          PrefixOption.PreferredLifetime,
2270                          NULL,
2271                          NULL
2272                          );
2273               if (EFI_ERROR (Status)) {
2274                 goto Exit;
2275               }
2276             }
2277           }
2278 
2279           //
2280           // Adds the prefix option to stateless prefix option list.
2281           //
2282           PrefixList = Ip6CreatePrefixListEntry (
2283                          IpSb,
2284                          FALSE,
2285                          PrefixOption.ValidLifetime,
2286                          PrefixOption.PreferredLifetime,
2287                          PrefixOption.PrefixLength,
2288                          &PrefixOption.Prefix
2289                          );
2290           if (PrefixList == NULL) {
2291             Status = EFI_OUT_OF_RESOURCES;
2292             goto Exit;
2293           }
2294         } else if (PrefixList != NULL) {
2295 
2296           //
2297           // Reset the preferred lifetime of the address if the advertised prefix exists.
2298           // Perform specific action to valid lifetime together.
2299           //
2300           PrefixList->PreferredLifetime = PrefixOption.PreferredLifetime;
2301           if ((PrefixOption.ValidLifetime > 7200) ||
2302               (PrefixOption.ValidLifetime > PrefixList->ValidLifetime)) {
2303             //
2304             // If the received Valid Lifetime is greater than 2 hours or
2305             // greater than RemainingLifetime, set the valid lifetime of the
2306             // corresponding address to the advertised Valid Lifetime.
2307             //
2308             PrefixList->ValidLifetime = PrefixOption.ValidLifetime;
2309 
2310           } else if (PrefixList->ValidLifetime <= 7200) {
2311             //
2312             // If RemainingLifetime is less than or equls to 2 hours, ignore the
2313             // Prefix Information option with regards to the valid lifetime.
2314             // TODO: If this option has been authenticated, set the valid lifetime.
2315             //
2316           } else {
2317             //
2318             // Otherwise, reset the valid lifetime of the corresponding
2319             // address to 2 hours.
2320             //
2321             PrefixList->ValidLifetime = 7200;
2322           }
2323         }
2324       }
2325 
2326       Offset += sizeof (IP6_PREFIX_INFO_OPTION);
2327       break;
2328     case Ip6OptionMtu:
2329       NetbufCopy (Packet, Offset, sizeof (IP6_MTU_OPTION), (UINT8 *) &MTUOption);
2330       if (MTUOption.Length != 1) {
2331         goto Exit;
2332       }
2333 
2334       //
2335       // Use IPv6 minimum link MTU 1280 bytes as the maximum packet size in order
2336       // to omit implementation of Path MTU Discovery. Thus ignore the MTU option
2337       // in Router Advertisement.
2338       //
2339 
2340       Offset += sizeof (IP6_MTU_OPTION);
2341       break;
2342     default:
2343       //
2344       // Silently ignore unrecognized options
2345       //
2346       NetbufCopy (Packet, Offset + sizeof (UINT8), sizeof (UINT8), &Length);
2347       if (Length <= 0) {
2348         goto Exit;
2349       }
2350 
2351       Offset = (UINT16) (Offset + (UINT16) Length * 8);
2352       break;
2353     }
2354   }
2355 
2356   Status = EFI_SUCCESS;
2357 
2358 Exit:
2359   NetbufFree (Packet);
2360   return Status;
2361 }
2362 
2363 /**
2364   Process the ICMPv6 redirect message. Find the instance, then update
2365   its route cache.
2366 
2367   @param[in]  IpSb               The IP6 service binding instance that received
2368                                  the packet.
2369   @param[in]  Head               The IP head of the received ICMPv6 packet.
2370   @param[in]  Packet             The content of the ICMPv6 redirect packet with
2371                                  the IP head removed.
2372 
2373   @retval EFI_INVALID_PARAMETER  The parameter is invalid.
2374   @retval EFI_OUT_OF_RESOURCES   Insuffcient resources to complete the
2375                                  operation.
2376   @retval EFI_SUCCESS            Successfully updated the route caches.
2377 
2378 **/
2379 EFI_STATUS
Ip6ProcessRedirect(IN IP6_SERVICE * IpSb,IN EFI_IP6_HEADER * Head,IN NET_BUF * Packet)2380 Ip6ProcessRedirect (
2381   IN IP6_SERVICE            *IpSb,
2382   IN EFI_IP6_HEADER         *Head,
2383   IN NET_BUF                *Packet
2384   )
2385 {
2386   IP6_ICMP_INFORMATION_HEAD *Icmp;
2387   EFI_IPv6_ADDRESS          *Target;
2388   EFI_IPv6_ADDRESS          *IcmpDest;
2389   UINT8                     *Option;
2390   UINT16                    OptionLen;
2391   IP6_ROUTE_ENTRY           *RouteEntry;
2392   IP6_ROUTE_CACHE_ENTRY     *RouteCache;
2393   IP6_NEIGHBOR_ENTRY        *NeighborCache;
2394   INT32                     Length;
2395   UINT8                     OptLen;
2396   IP6_ETHER_ADDR_OPTION     *LinkLayerOption;
2397   EFI_MAC_ADDRESS           Mac;
2398   UINT32                    Index;
2399   BOOLEAN                   IsRouter;
2400   EFI_STATUS                Status;
2401   INTN                      Result;
2402 
2403   Status = EFI_INVALID_PARAMETER;
2404 
2405   Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Packet, 0, NULL);
2406   if (Icmp == NULL) {
2407     goto Exit;
2408   }
2409 
2410   //
2411   // Validate the incoming Redirect message
2412   //
2413 
2414   //
2415   // The IP Hop Limit field has a value of 255, i.e. the packet
2416   // could not possibly have been forwarded by a router.
2417   // ICMP Code is 0.
2418   // ICMP length (derived from the IP length) is 40 or more octets.
2419   //
2420   if (Head->HopLimit != IP6_HOP_LIMIT || Icmp->Head.Code != 0 ||
2421       Head->PayloadLength < IP6_REDITECT_LENGTH) {
2422     goto Exit;
2423   }
2424 
2425   //
2426   // The IP source address must be a link-local address
2427   //
2428   if (!NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {
2429     goto Exit;
2430   }
2431 
2432   //
2433   // The dest of this ICMP redirect message is not us.
2434   //
2435   if (!Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) {
2436     goto Exit;
2437   }
2438 
2439   //
2440   // All included options have a length that is greater than zero.
2441   //
2442   OptionLen = (UINT16) (Head->PayloadLength - IP6_REDITECT_LENGTH);
2443   if (OptionLen != 0) {
2444     Option    = NetbufGetByte (Packet, IP6_REDITECT_LENGTH, NULL);
2445     ASSERT (Option != NULL);
2446 
2447     if (!Ip6IsNDOptionValid (Option, OptionLen)) {
2448       goto Exit;
2449     }
2450   }
2451 
2452   Target   = (EFI_IPv6_ADDRESS *) (Icmp + 1);
2453   IcmpDest = Target + 1;
2454 
2455   //
2456   // The ICMP Destination Address field in the redirect message does not contain
2457   // a multicast address.
2458   //
2459   if (IP6_IS_MULTICAST (IcmpDest)) {
2460     goto Exit;
2461   }
2462 
2463   //
2464   // The ICMP Target Address is either a link-local address (when redirected to
2465   // a router) or the same as the ICMP Destination Address (when redirected to
2466   // the on-link destination).
2467   //
2468   IsRouter = (BOOLEAN) !EFI_IP6_EQUAL (Target, IcmpDest);
2469   if (!NetIp6IsLinkLocalAddr (Target) && IsRouter) {
2470     goto Exit;
2471   }
2472 
2473   //
2474   // Check the options. The only interested option here is the target-link layer
2475   // address option.
2476   //
2477   Length          = Packet->TotalSize - 40;
2478   Option          = (UINT8 *) (IcmpDest + 1);
2479   LinkLayerOption = NULL;
2480   while (Length > 0) {
2481     switch (*Option) {
2482     case Ip6OptionEtherTarget:
2483 
2484       LinkLayerOption = (IP6_ETHER_ADDR_OPTION *) Option;
2485       OptLen          = LinkLayerOption->Length;
2486       if (OptLen != 1) {
2487         //
2488         // For ethernet, the length must be 1.
2489         //
2490         goto Exit;
2491       }
2492       break;
2493 
2494     default:
2495 
2496       OptLen = *(Option + 1);
2497       if (OptLen == 0) {
2498         //
2499         // A length of 0 is invalid.
2500         //
2501         goto Exit;
2502       }
2503       break;
2504     }
2505 
2506     Length -= 8 * OptLen;
2507     Option += 8 * OptLen;
2508   }
2509 
2510   if (Length != 0) {
2511     goto Exit;
2512   }
2513 
2514   //
2515   // The IP source address of the Redirect is the same as the current
2516   // first-hop router for the specified ICMP Destination Address.
2517   //
2518   RouteCache = Ip6FindRouteCache (IpSb->RouteTable, IcmpDest, &Head->DestinationAddress);
2519   if (RouteCache != NULL) {
2520     if (!EFI_IP6_EQUAL (&RouteCache->NextHop, &Head->SourceAddress)) {
2521       //
2522       // The source of this Redirect message must match the NextHop of the
2523       // corresponding route cache entry.
2524       //
2525       goto Exit;
2526     }
2527 
2528     //
2529     // Update the NextHop.
2530     //
2531     IP6_COPY_ADDRESS (&RouteCache->NextHop, Target);
2532 
2533     if (!IsRouter) {
2534       RouteEntry = (IP6_ROUTE_ENTRY *) RouteCache->Tag;
2535       RouteEntry->Flag = RouteEntry->Flag | IP6_DIRECT_ROUTE;
2536     }
2537 
2538   } else {
2539     //
2540     // Get the Route Entry.
2541     //
2542     RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, IcmpDest, NULL);
2543     if (RouteEntry == NULL) {
2544       RouteEntry = Ip6CreateRouteEntry (IcmpDest, 0, NULL);
2545       if (RouteEntry == NULL) {
2546         Status = EFI_OUT_OF_RESOURCES;
2547         goto Exit;
2548       }
2549     }
2550 
2551     if (!IsRouter) {
2552       RouteEntry->Flag = IP6_DIRECT_ROUTE;
2553     }
2554 
2555     //
2556     // Create a route cache for this.
2557     //
2558     RouteCache = Ip6CreateRouteCacheEntry (
2559                    IcmpDest,
2560                    &Head->DestinationAddress,
2561                    Target,
2562                    (UINTN) RouteEntry
2563                    );
2564     if (RouteCache == NULL) {
2565       Status = EFI_OUT_OF_RESOURCES;
2566       goto Exit;
2567     }
2568 
2569     //
2570     // Insert the newly created route cache entry.
2571     //
2572     Index = IP6_ROUTE_CACHE_HASH (IcmpDest, &Head->DestinationAddress);
2573     InsertHeadList (&IpSb->RouteTable->Cache.CacheBucket[Index], &RouteCache->Link);
2574   }
2575 
2576   //
2577   // Try to locate the neighbor cache for the Target.
2578   //
2579   NeighborCache = Ip6FindNeighborEntry (IpSb, Target);
2580 
2581   if (LinkLayerOption != NULL) {
2582     if (NeighborCache == NULL) {
2583       //
2584       // Create a neighbor cache for the Target.
2585       //
2586       ZeroMem (&Mac, sizeof (EFI_MAC_ADDRESS));
2587       CopyMem (&Mac, LinkLayerOption->EtherAddr, 6);
2588       NeighborCache = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, Target, &Mac);
2589       if (NeighborCache == NULL) {
2590         //
2591         // Just report a success here. The neighbor cache can be created in
2592         // some other place.
2593         //
2594         Status = EFI_SUCCESS;
2595         goto Exit;
2596       }
2597 
2598       NeighborCache->State = EfiNeighborStale;
2599       NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2600     } else {
2601       Result = CompareMem (LinkLayerOption->EtherAddr, &NeighborCache->LinkAddress, 6);
2602 
2603       //
2604       // If the link-local address is the same as that already in the cache,
2605       // the cache entry's state remains unchanged. Otherwise update the
2606       // reachability state to STALE.
2607       //
2608       if ((NeighborCache->State == EfiNeighborInComplete) || (Result != 0)) {
2609         CopyMem (&NeighborCache->LinkAddress, LinkLayerOption->EtherAddr, 6);
2610 
2611         NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2612 
2613         if (NeighborCache->State == EfiNeighborInComplete) {
2614           //
2615           // Send queued packets if exist.
2616           //
2617           NeighborCache->State = EfiNeighborStale;
2618           NeighborCache->CallBack ((VOID *) NeighborCache);
2619         } else {
2620           NeighborCache->State = EfiNeighborStale;
2621         }
2622       }
2623     }
2624   }
2625 
2626   if (NeighborCache != NULL && IsRouter) {
2627     //
2628     // The Target is a router, set IsRouter to TRUE.
2629     //
2630     NeighborCache->IsRouter = TRUE;
2631   }
2632 
2633   Status = EFI_SUCCESS;
2634 
2635 Exit:
2636   NetbufFree (Packet);
2637   return Status;
2638 }
2639 
2640 /**
2641   Add Neighbor cache entries. It is a work function for EfiIp6Neighbors().
2642 
2643   @param[in]  IpSb               The IP6 service binding instance.
2644   @param[in]  TargetIp6Address   Pointer to Target IPv6 address.
2645   @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.
2646   @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor
2647                                  cache. It will be deleted after Timeout. A value of zero means that
2648                                  the entry is permanent. A non-zero value means that the entry is
2649                                  dynamic.
2650   @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will
2651                                  be overridden and updated; if FALSE, and if a
2652                                  corresponding cache entry already existed, EFI_ACCESS_DENIED
2653                                  will be returned.
2654 
2655   @retval  EFI_SUCCESS           The neighbor cache entry has been added.
2656   @retval  EFI_OUT_OF_RESOURCES  Could not add the entry to the neighbor cache
2657                                  due to insufficient resources.
2658   @retval  EFI_NOT_FOUND         TargetLinkAddress is NULL.
2659   @retval  EFI_ACCESS_DENIED     The to-be-added entry is already defined in the neighbor cache,
2660                                  and that entry is tagged as un-overridden (when DeleteFlag
2661                                  is FALSE).
2662 
2663 **/
2664 EFI_STATUS
Ip6AddNeighbor(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * TargetLinkAddress OPTIONAL,IN UINT32 Timeout,IN BOOLEAN Override)2665 Ip6AddNeighbor (
2666   IN IP6_SERVICE            *IpSb,
2667   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
2668   IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,
2669   IN UINT32                 Timeout,
2670   IN BOOLEAN                Override
2671   )
2672 {
2673   IP6_NEIGHBOR_ENTRY        *Neighbor;
2674 
2675   Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
2676   if (Neighbor != NULL) {
2677     if (!Override) {
2678       return EFI_ACCESS_DENIED;
2679     } else {
2680       if (TargetLinkAddress != NULL) {
2681         IP6_COPY_LINK_ADDRESS (&Neighbor->LinkAddress, TargetLinkAddress);
2682       }
2683     }
2684   } else {
2685     if (TargetLinkAddress == NULL) {
2686       return EFI_NOT_FOUND;
2687     }
2688 
2689     Neighbor = Ip6CreateNeighborEntry (IpSb, Ip6OnArpResolved, TargetIp6Address, TargetLinkAddress);
2690     if (Neighbor == NULL) {
2691       return EFI_OUT_OF_RESOURCES;
2692     }
2693   }
2694 
2695   Neighbor->State = EfiNeighborReachable;
2696 
2697   if (Timeout != 0) {
2698     Neighbor->Ticks   = IP6_GET_TICKS (Timeout / TICKS_PER_MS);
2699     Neighbor->Dynamic = TRUE;
2700   } else {
2701     Neighbor->Ticks   = (UINT32) IP6_INFINIT_LIFETIME;
2702   }
2703 
2704   return EFI_SUCCESS;
2705 }
2706 
2707 /**
2708   Delete or update Neighbor cache entries. It is a work function for EfiIp6Neighbors().
2709 
2710   @param[in]  IpSb               The IP6 service binding instance.
2711   @param[in]  TargetIp6Address   Pointer to Target IPv6 address.
2712   @param[in]  TargetLinkAddress  Pointer to link-layer address of the target. Ignored if NULL.
2713   @param[in]  Timeout            Time in 100-ns units that this entry will remain in the neighbor
2714                                  cache. It will be deleted after Timeout. A value of zero means that
2715                                  the entry is permanent. A non-zero value means that the entry is
2716                                  dynamic.
2717   @param[in]  Override           If TRUE, the cached link-layer address of the matching entry will
2718                                  be overridden and updated; if FALSE, and if a
2719                                  corresponding cache entry already existed, EFI_ACCESS_DENIED
2720                                  will be returned.
2721 
2722   @retval  EFI_SUCCESS           The neighbor cache entry has been updated or deleted.
2723   @retval  EFI_NOT_FOUND         This entry is not in the neighbor cache.
2724 
2725 **/
2726 EFI_STATUS
Ip6DelNeighbor(IN IP6_SERVICE * IpSb,IN EFI_IPv6_ADDRESS * TargetIp6Address,IN EFI_MAC_ADDRESS * TargetLinkAddress OPTIONAL,IN UINT32 Timeout,IN BOOLEAN Override)2727 Ip6DelNeighbor (
2728   IN IP6_SERVICE            *IpSb,
2729   IN EFI_IPv6_ADDRESS       *TargetIp6Address,
2730   IN EFI_MAC_ADDRESS        *TargetLinkAddress OPTIONAL,
2731   IN UINT32                 Timeout,
2732   IN BOOLEAN                Override
2733   )
2734 {
2735   IP6_NEIGHBOR_ENTRY        *Neighbor;
2736 
2737   Neighbor = Ip6FindNeighborEntry (IpSb, TargetIp6Address);
2738   if (Neighbor == NULL) {
2739     return EFI_NOT_FOUND;
2740   }
2741 
2742   RemoveEntryList (&Neighbor->Link);
2743   FreePool (Neighbor);
2744 
2745   return EFI_SUCCESS;
2746 }
2747 
2748 /**
2749   The heartbeat timer of ND module in IP6_TIMER_INTERVAL_IN_MS milliseconds.
2750   This time routine handles DAD module and neighbor state transition.
2751   It is also responsible for sending out router solicitations.
2752 
2753   @param[in]  Event                 The IP6 service instance's heartbeat timer.
2754   @param[in]  Context               The IP6 service instance.
2755 
2756 **/
2757 VOID
2758 EFIAPI
Ip6NdFasterTimerTicking(IN EFI_EVENT Event,IN VOID * Context)2759 Ip6NdFasterTimerTicking (
2760   IN EFI_EVENT              Event,
2761   IN VOID                   *Context
2762   )
2763 {
2764   LIST_ENTRY                *Entry;
2765   LIST_ENTRY                *Next;
2766   LIST_ENTRY                *Entry2;
2767   IP6_INTERFACE             *IpIf;
2768   IP6_DELAY_JOIN_LIST       *DelayNode;
2769   EFI_IPv6_ADDRESS          Source;
2770   IP6_DAD_ENTRY             *DupAddrDetect;
2771   EFI_STATUS                Status;
2772   IP6_NEIGHBOR_ENTRY        *NeighborCache;
2773   EFI_IPv6_ADDRESS          Destination;
2774   IP6_SERVICE               *IpSb;
2775   BOOLEAN                   Flag;
2776 
2777   IpSb = (IP6_SERVICE *) Context;
2778   NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);
2779 
2780   ZeroMem (&Source, sizeof (EFI_IPv6_ADDRESS));
2781 
2782   //
2783   // A host SHOULD transmit up to MAX_RTR_SOLICITATIONS (3) Router
2784   // Solicitation messages, each separated by at least
2785   // RTR_SOLICITATION_INTERVAL (4) seconds.
2786   //
2787   if ((IpSb->Ip6ConfigInstance.Policy == Ip6ConfigPolicyAutomatic) &&
2788       !IpSb->RouterAdvertiseReceived &&
2789       IpSb->SolicitTimer > 0
2790       ) {
2791     if ((IpSb->Ticks == 0) || (--IpSb->Ticks == 0)) {
2792       Status = Ip6SendRouterSolicit (IpSb, NULL, NULL, NULL, NULL);
2793       if (!EFI_ERROR (Status)) {
2794         IpSb->SolicitTimer--;
2795         IpSb->Ticks = (UINT32) IP6_GET_TICKS (IP6_RTR_SOLICITATION_INTERVAL);
2796       }
2797     }
2798   }
2799 
2800   NET_LIST_FOR_EACH (Entry, &IpSb->Interfaces) {
2801     IpIf = NET_LIST_USER_STRUCT (Entry, IP6_INTERFACE, Link);
2802 
2803     //
2804     // Process the delay list to join the solicited-node multicast address.
2805     //
2806     NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DelayJoinList) {
2807       DelayNode = NET_LIST_USER_STRUCT (Entry2, IP6_DELAY_JOIN_LIST, Link);
2808       if ((DelayNode->DelayTime == 0) || (--DelayNode->DelayTime == 0)) {
2809         //
2810         // The timer expires, init the duplicate address detection.
2811         //
2812         Ip6InitDADProcess (
2813           DelayNode->Interface,
2814           DelayNode->AddressInfo,
2815           DelayNode->DadCallback,
2816           DelayNode->Context
2817           );
2818 
2819         //
2820         // Remove the delay node
2821         //
2822         RemoveEntryList (&DelayNode->Link);
2823         FreePool (DelayNode);
2824       }
2825     }
2826 
2827     //
2828     // Process the duplicate address detection list.
2829     //
2830     NET_LIST_FOR_EACH_SAFE (Entry2, Next, &IpIf->DupAddrDetectList) {
2831       DupAddrDetect = NET_LIST_USER_STRUCT (Entry2, IP6_DAD_ENTRY, Link);
2832 
2833       if ((DupAddrDetect->RetransTick == 0) || (--DupAddrDetect->RetransTick == 0)) {
2834         //
2835         // The timer expires, check the remaining transmit counts.
2836         //
2837         if (DupAddrDetect->Transmit < DupAddrDetect->MaxTransmit) {
2838           //
2839           // Send the Neighbor Solicitation message with
2840           // Source - unspecified address, destination - solicited-node multicast address
2841           // Target - the address to be validated
2842           //
2843           Status = Ip6SendNeighborSolicit (
2844                      IpSb,
2845                      NULL,
2846                      &DupAddrDetect->Destination,
2847                      &DupAddrDetect->AddressInfo->Address,
2848                      NULL
2849                      );
2850           if (EFI_ERROR (Status)) {
2851             return;
2852           }
2853 
2854           DupAddrDetect->Transmit++;
2855           DupAddrDetect->RetransTick = IP6_GET_TICKS (IpSb->RetransTimer);
2856         } else {
2857           //
2858           // All required solicitation has been sent out, and the RetransTime after the last
2859           // Neighbor Solicit is elapsed, finish the DAD process.
2860           //
2861           Flag = FALSE;
2862           if ((DupAddrDetect->Receive == 0) ||
2863               (DupAddrDetect->Transmit <= DupAddrDetect->Receive)) {
2864             Flag = TRUE;
2865           }
2866 
2867           Ip6OnDADFinished (Flag, IpIf, DupAddrDetect);
2868         }
2869       }
2870     }
2871   }
2872 
2873   //
2874   // Polling the state of Neighbor cache
2875   //
2876   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->NeighborTable) {
2877     NeighborCache = NET_LIST_USER_STRUCT (Entry, IP6_NEIGHBOR_ENTRY, Link);
2878 
2879     switch (NeighborCache->State) {
2880     case EfiNeighborInComplete:
2881       if (NeighborCache->Ticks > 0) {
2882         --NeighborCache->Ticks;
2883       }
2884 
2885       //
2886       // Retransmit Neighbor Solicitation messages approximately every
2887       // RetransTimer milliseconds while awaiting a response.
2888       //
2889       if (NeighborCache->Ticks == 0) {
2890         if (NeighborCache->Transmit > 1) {
2891           //
2892           // Send out multicast neighbor solicitation for address resolution.
2893           // After last neighbor solicitation message has been sent out, wait
2894           // for RetransTimer and then remove entry if no response is received.
2895           //
2896           Ip6CreateSNMulticastAddr (&NeighborCache->Neighbor, &Destination);
2897           Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
2898           if (EFI_ERROR (Status)) {
2899             return;
2900           }
2901 
2902           Status = Ip6SendNeighborSolicit (
2903                      IpSb,
2904                      &Source,
2905                      &Destination,
2906                      &NeighborCache->Neighbor,
2907                      &IpSb->SnpMode.CurrentAddress
2908                      );
2909           if (EFI_ERROR (Status)) {
2910             return;
2911           }
2912         }
2913 
2914         //
2915         // Update the retransmit times.
2916         //
2917         if (NeighborCache->Transmit > 0) {
2918           --NeighborCache->Transmit;
2919           NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
2920         }
2921       }
2922 
2923       if (NeighborCache->Transmit == 0) {
2924         //
2925         // Timeout, send ICMP destination unreachable packet and then remove entry
2926         //
2927         Status = Ip6FreeNeighborEntry (
2928                    IpSb,
2929                    NeighborCache,
2930                    TRUE,
2931                    TRUE,
2932                    EFI_ICMP_ERROR,
2933                    NULL,
2934                    NULL
2935                    );
2936         if (EFI_ERROR (Status)) {
2937           return;
2938         }
2939       }
2940 
2941       break;
2942 
2943     case EfiNeighborReachable:
2944       //
2945       // This entry is inserted by EfiIp6Neighbors() as static entry
2946       // and will not timeout.
2947       //
2948       if (!NeighborCache->Dynamic && (NeighborCache->Ticks == IP6_INFINIT_LIFETIME)) {
2949         break;
2950       }
2951 
2952       if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
2953         if (NeighborCache->Dynamic) {
2954           //
2955           // This entry is inserted by EfiIp6Neighbors() as dynamic entry
2956           // and will be deleted after timeout.
2957           //
2958           Status = Ip6FreeNeighborEntry (
2959                      IpSb,
2960                      NeighborCache,
2961                      FALSE,
2962                      TRUE,
2963                      EFI_TIMEOUT,
2964                      NULL,
2965                      NULL
2966                      );
2967           if (EFI_ERROR (Status)) {
2968             return;
2969           }
2970         } else {
2971           NeighborCache->State = EfiNeighborStale;
2972           NeighborCache->Ticks = (UINT32) IP6_INFINIT_LIFETIME;
2973         }
2974       }
2975 
2976       break;
2977 
2978     case EfiNeighborDelay:
2979       if ((NeighborCache->Ticks == 0) || (--NeighborCache->Ticks == 0)) {
2980 
2981         NeighborCache->State    = EfiNeighborProbe;
2982         NeighborCache->Ticks    = IP6_GET_TICKS (IpSb->RetransTimer);
2983         NeighborCache->Transmit = IP6_MAX_UNICAST_SOLICIT + 1;
2984         //
2985         // Send out unicast neighbor solicitation for Neighbor Unreachability Detection
2986         //
2987         Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
2988         if (EFI_ERROR (Status)) {
2989           return;
2990         }
2991 
2992         Status = Ip6SendNeighborSolicit (
2993                    IpSb,
2994                    &Source,
2995                    &NeighborCache->Neighbor,
2996                    &NeighborCache->Neighbor,
2997                    &IpSb->SnpMode.CurrentAddress
2998                    );
2999         if (EFI_ERROR (Status)) {
3000           return;
3001         }
3002 
3003         NeighborCache->Transmit--;
3004       }
3005 
3006       break;
3007 
3008     case EfiNeighborProbe:
3009       if (NeighborCache->Ticks > 0) {
3010         --NeighborCache->Ticks;
3011       }
3012 
3013       //
3014       // Retransmit Neighbor Solicitation messages approximately every
3015       // RetransTimer milliseconds while awaiting a response.
3016       //
3017       if (NeighborCache->Ticks == 0) {
3018         if (NeighborCache->Transmit > 1) {
3019           //
3020           // Send out unicast neighbor solicitation for Neighbor Unreachability
3021           // Detection. After last neighbor solicitation message has been sent out,
3022           // wait for RetransTimer and then remove entry if no response is received.
3023           //
3024           Status = Ip6SelectSourceAddress (IpSb, &NeighborCache->Neighbor, &Source);
3025           if (EFI_ERROR (Status)) {
3026             return;
3027           }
3028 
3029           Status = Ip6SendNeighborSolicit (
3030                      IpSb,
3031                      &Source,
3032                      &NeighborCache->Neighbor,
3033                      &NeighborCache->Neighbor,
3034                      &IpSb->SnpMode.CurrentAddress
3035                      );
3036           if (EFI_ERROR (Status)) {
3037             return;
3038           }
3039         }
3040 
3041         //
3042         // Update the retransmit times.
3043         //
3044         if (NeighborCache->Transmit > 0) {
3045           --NeighborCache->Transmit;
3046           NeighborCache->Ticks = IP6_GET_TICKS (IpSb->RetransTimer);
3047         }
3048       }
3049 
3050       if (NeighborCache->Transmit == 0) {
3051         //
3052         // Delete the neighbor entry.
3053         //
3054         Status = Ip6FreeNeighborEntry (
3055                    IpSb,
3056                    NeighborCache,
3057                    FALSE,
3058                    TRUE,
3059                    EFI_TIMEOUT,
3060                    NULL,
3061                    NULL
3062                    );
3063         if (EFI_ERROR (Status)) {
3064           return;
3065         }
3066       }
3067 
3068       break;
3069 
3070     default:
3071       break;
3072     }
3073   }
3074 }
3075 
3076 /**
3077   The heartbeat timer of ND module in 1 second. This time routine handles following
3078   things: 1) maitain default router list; 2) maintain prefix options;
3079   3) maintain route caches.
3080 
3081   @param[in]  IpSb              The IP6 service binding instance.
3082 
3083 **/
3084 VOID
Ip6NdTimerTicking(IN IP6_SERVICE * IpSb)3085 Ip6NdTimerTicking (
3086   IN IP6_SERVICE            *IpSb
3087   )
3088 {
3089   LIST_ENTRY                *Entry;
3090   LIST_ENTRY                *Next;
3091   IP6_DEFAULT_ROUTER        *DefaultRouter;
3092   IP6_PREFIX_LIST_ENTRY     *PrefixOption;
3093   UINT8                     Index;
3094   IP6_ROUTE_CACHE_ENTRY     *RouteCache;
3095 
3096   //
3097   // Decrease the lifetime of default router, if expires remove it from default router list.
3098   //
3099   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->DefaultRouterList) {
3100     DefaultRouter = NET_LIST_USER_STRUCT (Entry, IP6_DEFAULT_ROUTER, Link);
3101     if (DefaultRouter->Lifetime != IP6_INF_ROUTER_LIFETIME) {
3102       if ((DefaultRouter->Lifetime == 0) || (--DefaultRouter->Lifetime == 0)) {
3103         Ip6DestroyDefaultRouter (IpSb, DefaultRouter);
3104       }
3105     }
3106   }
3107 
3108   //
3109   // Decrease Valid lifetime and Preferred lifetime of Prefix options and corresponding addresses.
3110   //
3111   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->AutonomousPrefix) {
3112     PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
3113     if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
3114       if ((PrefixOption->ValidLifetime > 0) && (--PrefixOption->ValidLifetime > 0)) {
3115         if ((PrefixOption->PreferredLifetime != (UINT32) IP6_INFINIT_LIFETIME) &&
3116             (PrefixOption->PreferredLifetime > 0)
3117             ) {
3118           --PrefixOption->PreferredLifetime;
3119         }
3120       } else {
3121         Ip6DestroyPrefixListEntry (IpSb, PrefixOption, FALSE, TRUE);
3122       }
3123     }
3124   }
3125 
3126   NET_LIST_FOR_EACH_SAFE (Entry, Next, &IpSb->OnlinkPrefix) {
3127     PrefixOption = NET_LIST_USER_STRUCT (Entry, IP6_PREFIX_LIST_ENTRY, Link);
3128     if (PrefixOption->ValidLifetime != (UINT32) IP6_INFINIT_LIFETIME) {
3129       if ((PrefixOption->ValidLifetime == 0) || (--PrefixOption->ValidLifetime == 0)) {
3130         Ip6DestroyPrefixListEntry (IpSb, PrefixOption, TRUE, TRUE);
3131       }
3132     }
3133   }
3134 
3135   //
3136   // Each bucket of route cache can contain at most IP6_ROUTE_CACHE_MAX entries.
3137   // Remove the entries at the tail of the bucket. These entries
3138   // are likely to be used least.
3139   // Reclaim frequency is set to 1 second.
3140   //
3141   for (Index = 0; Index < IP6_ROUTE_CACHE_HASH_SIZE; Index++) {
3142     while (IpSb->RouteTable->Cache.CacheNum[Index] > IP6_ROUTE_CACHE_MAX) {
3143       Entry = NetListRemoveTail (&IpSb->RouteTable->Cache.CacheBucket[Index]);
3144       if (Entry == NULL) {
3145         break;
3146       }
3147 
3148       RouteCache = NET_LIST_USER_STRUCT (Entry, IP6_ROUTE_CACHE_ENTRY, Link);
3149       Ip6FreeRouteCacheEntry (RouteCache);
3150       ASSERT (IpSb->RouteTable->Cache.CacheNum[Index] > 0);
3151       IpSb->RouteTable->Cache.CacheNum[Index]--;
3152     }
3153   }
3154 }
3155 
3156