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