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