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