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