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