]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/Ip6Dxe/Ip6Mld.c
BaseTools/Capsule: Do not support -o with --dump-info
[mirror_edk2.git] / NetworkPkg / Ip6Dxe / Ip6Mld.c
CommitLineData
a3bcde70
HT
1/** @file\r
2 Multicast Listener Discovery support routines.\r
3\r
4 Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php.\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "Ip6Impl.h"\r
17\r
18/**\r
19 Create a IP6_MLD_GROUP list entry node and record to IP6 service binding data.\r
20\r
21 @param[in, out] IpSb Points to IP6 service binding instance.\r
22 @param[in] MulticastAddr The IPv6 multicast address to be recorded.\r
23 @param[in] DelayTimer The maximum allowed delay before sending a responding\r
24 report, in units of milliseconds.\r
25 @return The created IP6_ML_GROUP list entry or NULL.\r
26\r
27**/\r
28IP6_MLD_GROUP *\r
29Ip6CreateMldEntry (\r
30 IN OUT IP6_SERVICE *IpSb,\r
31 IN EFI_IPv6_ADDRESS *MulticastAddr,\r
32 IN UINT32 DelayTimer\r
33 )\r
34{\r
35 IP6_MLD_GROUP *Entry;\r
36\r
37 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
38 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
39\r
40 Entry = AllocatePool (sizeof (IP6_MLD_GROUP));\r
41 if (Entry != NULL) {\r
42 Entry->RefCnt = 1;\r
43 Entry->DelayTimer = DelayTimer;\r
44 Entry->SendByUs = FALSE;\r
45 IP6_COPY_ADDRESS (&Entry->Address, MulticastAddr);\r
46 InsertTailList (&IpSb->MldCtrl.Groups, &Entry->Link);\r
47 }\r
48\r
49 return Entry;\r
50}\r
51\r
52/**\r
53 Search a IP6_MLD_GROUP list entry node from a list array.\r
54\r
55 @param[in] IpSb Points to IP6 service binding instance.\r
56 @param[in] MulticastAddr The IPv6 multicast address to be searched.\r
57\r
58 @return The found IP6_ML_GROUP list entry or NULL.\r
59\r
60**/\r
61IP6_MLD_GROUP *\r
62Ip6FindMldEntry (\r
63 IN IP6_SERVICE *IpSb,\r
64 IN EFI_IPv6_ADDRESS *MulticastAddr\r
65 )\r
66{\r
67 LIST_ENTRY *Entry;\r
68 IP6_MLD_GROUP *Group;\r
69\r
70 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
71 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
72\r
73 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
74 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
75 if (EFI_IP6_EQUAL (MulticastAddr, &Group->Address)) {\r
76 return Group;\r
77 }\r
78 }\r
79\r
80 return NULL;\r
81}\r
82\r
83/**\r
84 Count the number of IP6 multicast groups that are mapped to the\r
85 same MAC address. Several IP6 multicast address may be mapped to\r
86 the same MAC address.\r
87\r
88 @param[in] MldCtrl The MLD control block to search in.\r
89 @param[in] Mac The MAC address to search.\r
90\r
91 @return The number of the IP6 multicast group that mapped to the same\r
92 multicast group Mac.\r
93\r
94**/\r
95INTN\r
96Ip6FindMac (\r
97 IN IP6_MLD_SERVICE_DATA *MldCtrl,\r
98 IN EFI_MAC_ADDRESS *Mac\r
99 )\r
100{\r
101 LIST_ENTRY *Entry;\r
102 IP6_MLD_GROUP *Group;\r
103 INTN Count;\r
104\r
105 Count = 0;\r
106\r
107 NET_LIST_FOR_EACH (Entry, &MldCtrl->Groups) {\r
108 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
109\r
110 if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
111 Count++;\r
112 }\r
113 }\r
114\r
115 return Count;\r
116}\r
117\r
118/**\r
119 Generate MLD report message and send it out to MulticastAddr.\r
120\r
121 @param[in] IpSb The IP service to send the packet.\r
122 @param[in] Interface The IP interface to send the packet.\r
123 If NULL, a system interface will be selected.\r
124 @param[in] MulticastAddr The specific IPv6 multicast address to which\r
125 the message sender is listening.\r
126\r
127 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the\r
128 operation.\r
129 @retval EFI_SUCCESS The MLD report message was successfully sent out.\r
130\r
131**/\r
132EFI_STATUS\r
133Ip6SendMldReport (\r
134 IN IP6_SERVICE *IpSb,\r
135 IN IP6_INTERFACE *Interface OPTIONAL,\r
136 IN EFI_IPv6_ADDRESS *MulticastAddr\r
137 )\r
138{\r
139 IP6_MLD_HEAD *MldHead;\r
140 NET_BUF *Packet;\r
141 EFI_IP6_HEADER Head;\r
142 UINT16 PayloadLen;\r
143 UINTN OptionLen;\r
144 UINT8 *Options;\r
145 EFI_STATUS Status;\r
146 UINT16 HeadChecksum;\r
147 UINT16 PseudoChecksum;\r
148\r
149 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
150 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
151\r
152 //\r
153 // Generate the packet to be sent\r
154 // IPv6 basic header + Hop by Hop option + MLD message\r
155 //\r
156\r
157 OptionLen = 0;\r
158 Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
159 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
160\r
161 PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
162 Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
163 if (Packet == NULL) {\r
164 return EFI_OUT_OF_RESOURCES;\r
165 }\r
166\r
167 //\r
168 // Create the basic IPv6 header.\r
169 // RFC3590: Use link-local address as source address if it is available,\r
170 // otherwise use the unspecified address.\r
171 //\r
172 Head.FlowLabelL = 0;\r
173 Head.FlowLabelH = 0;\r
174 Head.PayloadLength = HTONS (PayloadLen);\r
175 Head.NextHeader = IP6_HOP_BY_HOP;\r
176 Head.HopLimit = 1;\r
177 IP6_COPY_ADDRESS (&Head.DestinationAddress, MulticastAddr);\r
178\r
179 //\r
180 // If Link-Local address is not ready, we use unspecified address.\r
181 //\r
182 IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
183\r
184 NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
185\r
186 //\r
187 // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
188 //\r
189 Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
190 ASSERT (Options != NULL);\r
191 Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
192 if (EFI_ERROR (Status)) {\r
193 NetbufFree (Packet);\r
194 Packet = NULL;\r
195 return Status;\r
196 }\r
197\r
198 //\r
199 // Fill in MLD message - Report\r
200 //\r
201 MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
202 ASSERT (MldHead != NULL);\r
203 ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
204 MldHead->Head.Type = ICMP_V6_LISTENER_REPORT;\r
205 MldHead->Head.Code = 0;\r
206 IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
207\r
208 HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
209 PseudoChecksum = NetIp6PseudoHeadChecksum (\r
210 &Head.SourceAddress,\r
211 &Head.DestinationAddress,\r
212 IP6_ICMP,\r
213 sizeof (IP6_MLD_HEAD)\r
214 );\r
215\r
216 MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
217\r
218 //\r
219 // Transmit the packet\r
220 //\r
221 return Ip6Output (IpSb, Interface, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
222}\r
223\r
224/**\r
225 Generate MLD Done message and send it out to MulticastAddr.\r
226\r
227 @param[in] IpSb The IP service to send the packet.\r
228 @param[in] MulticastAddr The specific IPv6 multicast address to which\r
229 the message sender is ceasing to listen.\r
230\r
231 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete the\r
232 operation.\r
233 @retval EFI_SUCCESS The MLD report message was successfully sent out.\r
234\r
235**/\r
236EFI_STATUS\r
237Ip6SendMldDone (\r
238 IN IP6_SERVICE *IpSb,\r
239 IN EFI_IPv6_ADDRESS *MulticastAddr\r
240 )\r
241{\r
242 IP6_MLD_HEAD *MldHead;\r
243 NET_BUF *Packet;\r
244 EFI_IP6_HEADER Head;\r
245 UINT16 PayloadLen;\r
246 UINTN OptionLen;\r
247 UINT8 *Options;\r
248 EFI_STATUS Status;\r
249 EFI_IPv6_ADDRESS Destination;\r
250 UINT16 HeadChecksum;\r
251 UINT16 PseudoChecksum;\r
252\r
253 NET_CHECK_SIGNATURE (IpSb, IP6_SERVICE_SIGNATURE);\r
254 ASSERT (MulticastAddr != NULL && IP6_IS_MULTICAST (MulticastAddr));\r
255\r
256 //\r
257 // Generate the packet to be sent\r
258 // IPv6 basic header + Hop by Hop option + MLD message\r
259 //\r
260\r
261 OptionLen = 0;\r
262 Status = Ip6FillHopByHop (NULL, &OptionLen, IP6_ICMP);\r
263 ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
264\r
265 PayloadLen = (UINT16) (OptionLen + sizeof (IP6_MLD_HEAD));\r
266 Packet = NetbufAlloc (sizeof (EFI_IP6_HEADER) + (UINT32) PayloadLen);\r
267 if (Packet == NULL) {\r
268 return EFI_OUT_OF_RESOURCES;\r
269 }\r
270\r
271 //\r
272 // Create the basic IPv6 header.\r
273 //\r
274 Head.FlowLabelL = 0;\r
275 Head.FlowLabelH = 0;\r
276 Head.PayloadLength = HTONS (PayloadLen);\r
277 Head.NextHeader = IP6_HOP_BY_HOP;\r
278 Head.HopLimit = 1;\r
279\r
280 //\r
281 // If Link-Local address is not ready, we use unspecified address.\r
282 //\r
283 IP6_COPY_ADDRESS (&Head.SourceAddress, &IpSb->LinkLocalAddr);\r
284\r
285 Ip6SetToAllNodeMulticast (TRUE, IP6_LINK_LOCAL_SCOPE, &Destination);\r
286 IP6_COPY_ADDRESS (&Head.DestinationAddress, &Destination);\r
287\r
288 NetbufReserve (Packet, sizeof (EFI_IP6_HEADER));\r
289\r
290 //\r
291 // Fill a IPv6 Router Alert option in a Hop-by-Hop Options Header\r
292 //\r
293 Options = NetbufAllocSpace (Packet, (UINT32) OptionLen, FALSE);\r
294 ASSERT (Options != NULL);\r
295 Status = Ip6FillHopByHop (Options, &OptionLen, IP6_ICMP);\r
296 if (EFI_ERROR (Status)) {\r
297 NetbufFree (Packet);\r
298 Packet = NULL;\r
299 return Status;\r
300 }\r
301\r
302 //\r
303 // Fill in MLD message - Done\r
304 //\r
305 MldHead = (IP6_MLD_HEAD *) NetbufAllocSpace (Packet, sizeof (IP6_MLD_HEAD), FALSE);\r
306 ASSERT (MldHead != NULL);\r
307 ZeroMem (MldHead, sizeof (IP6_MLD_HEAD));\r
308 MldHead->Head.Type = ICMP_V6_LISTENER_DONE;\r
309 MldHead->Head.Code = 0;\r
310 IP6_COPY_ADDRESS (&MldHead->Group, MulticastAddr);\r
311\r
312 HeadChecksum = NetblockChecksum ((UINT8 *) MldHead, sizeof (IP6_MLD_HEAD));\r
313 PseudoChecksum = NetIp6PseudoHeadChecksum (\r
314 &Head.SourceAddress,\r
315 &Head.DestinationAddress,\r
316 IP6_ICMP,\r
317 sizeof (IP6_MLD_HEAD)\r
318 );\r
319\r
320 MldHead->Head.Checksum = (UINT16) ~NetAddChecksum (HeadChecksum, PseudoChecksum);\r
321\r
322 //\r
323 // Transmit the packet\r
324 //\r
325 return Ip6Output (IpSb, NULL, NULL, Packet, &Head, NULL, 0, Ip6SysPacketSent, NULL);\r
326}\r
327\r
328/**\r
329 Init the MLD data of the IP6 service instance. Configure\r
330 MNP to receive ALL SYSTEM multicast.\r
331\r
332 @param[in] IpSb The IP6 service whose MLD is to be initialized.\r
333\r
334 @retval EFI_OUT_OF_RESOURCES There are not sufficient resourcet to complete the\r
335 operation.\r
336 @retval EFI_SUCCESS The MLD module successfully initialized.\r
337\r
338**/\r
339EFI_STATUS\r
340Ip6InitMld (\r
341 IN IP6_SERVICE *IpSb\r
342 )\r
343{\r
344 EFI_IPv6_ADDRESS AllNodes;\r
345 IP6_MLD_GROUP *Group;\r
346 EFI_STATUS Status;\r
347\r
348 //\r
349 // Join the link-scope all-nodes multicast address (FF02::1).\r
350 // This address is started in Idle Listener state and never transitions to\r
351 // another state, and never sends a Report or Done for that address.\r
352 //\r
353\r
354 Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
355\r
356 Group = Ip6CreateMldEntry (IpSb, &AllNodes, (UINT32) IP6_INFINIT_LIFETIME);\r
357 if (Group == NULL) {\r
358 return EFI_OUT_OF_RESOURCES;\r
359 }\r
360\r
361 Status = Ip6GetMulticastMac (IpSb->Mnp, &AllNodes, &Group->Mac);\r
362 if (EFI_ERROR (Status)) {\r
363 goto ERROR;\r
364 }\r
365\r
366 //\r
367 // Configure MNP to receive all-nodes multicast\r
368 //\r
369 Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
370 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
371 goto ERROR;\r
372 }\r
373\r
374 return EFI_SUCCESS;\r
375\r
376ERROR:\r
377 RemoveEntryList (&Group->Link);\r
378 FreePool (Group);\r
379 return Status;\r
380}\r
381\r
382/**\r
383 Add a group address to the array of group addresses.\r
384 The caller should make sure that no duplicated address\r
385 existed in the array.\r
386\r
387 @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.\r
388 @param[in] Group The IP6 multicast address to add.\r
389\r
390 @retval EFI_OUT_OF_RESOURCES There are not sufficient resources to complete\r
391 the operation.\r
392 @retval EFI_SUCESS The address is added to the group address array.\r
393\r
394**/\r
395EFI_STATUS\r
396Ip6CombineGroups (\r
397 IN OUT IP6_PROTOCOL *IpInstance,\r
398 IN EFI_IPv6_ADDRESS *Group\r
399 )\r
400{\r
401 EFI_IPv6_ADDRESS *GroupList;\r
402\r
403 NET_CHECK_SIGNATURE (IpInstance, IP6_PROTOCOL_SIGNATURE);\r
404 ASSERT (Group != NULL && IP6_IS_MULTICAST (Group));\r
405\r
406 IpInstance->GroupCount++;\r
407\r
408 GroupList = AllocatePool (IpInstance->GroupCount * sizeof (EFI_IPv6_ADDRESS));\r
409 if (GroupList == NULL) {\r
410 return EFI_OUT_OF_RESOURCES;\r
411 }\r
412\r
413 if (IpInstance->GroupCount > 1) {\r
414 ASSERT (IpInstance->GroupList != NULL);\r
415\r
416 CopyMem (\r
417 GroupList,\r
418 IpInstance->GroupList,\r
419 (IpInstance->GroupCount - 1) * sizeof (EFI_IPv6_ADDRESS)\r
420 );\r
421\r
422 FreePool (IpInstance->GroupList);\r
423 }\r
424\r
425 IP6_COPY_ADDRESS (GroupList + (IpInstance->GroupCount - 1), Group);\r
426\r
427 IpInstance->GroupList = GroupList;\r
428\r
429 return EFI_SUCCESS;\r
430}\r
431\r
432/**\r
433 Remove a group address from the array of group addresses.\r
434 Although the function doesn't assume the byte order of Group,\r
435 the network byte order is used by the caller.\r
436\r
437 @param[in, out] IpInstance Points to an IP6_PROTOCOL instance.\r
438 @param[in] Group The IP6 multicast address to remove.\r
439\r
440 @retval EFI_NOT_FOUND Cannot find the to be removed group address.\r
441 @retval EFI_SUCCESS The group address was successfully removed.\r
442\r
443**/\r
444EFI_STATUS\r
445Ip6RemoveGroup (\r
446 IN OUT IP6_PROTOCOL *IpInstance,\r
447 IN EFI_IPv6_ADDRESS *Group\r
448 )\r
449{\r
450 UINT32 Index;\r
451 UINT32 Count;\r
452\r
453 Count = IpInstance->GroupCount;\r
454\r
455 for (Index = 0; Index < Count; Index++) {\r
456 if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, Group)) {\r
457 break;\r
458 }\r
459 }\r
460\r
461 if (Index == Count) {\r
462 return EFI_NOT_FOUND;\r
463 }\r
464\r
465 while (Index < Count - 1) {\r
466 IP6_COPY_ADDRESS (IpInstance->GroupList + Index, IpInstance->GroupList + Index + 1);\r
467 Index++;\r
468 }\r
469\r
470 ASSERT (IpInstance->GroupCount > 0);\r
471 IpInstance->GroupCount--;\r
472\r
473 return EFI_SUCCESS;\r
474}\r
475\r
476/**\r
477 Join the multicast group on behalf of this IP6 service binding instance.\r
478\r
479 @param[in] IpSb The IP6 service binding instance.\r
480 @param[in] Interface Points to an IP6_INTERFACE structure.\r
481 @param[in] Address The group address to join.\r
482\r
483 @retval EFI_SUCCESS Successfully join the multicast group.\r
484 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.\r
485 @retval Others Failed to join the multicast group.\r
486\r
487**/\r
488EFI_STATUS\r
489Ip6JoinGroup (\r
490 IN IP6_SERVICE *IpSb,\r
491 IN IP6_INTERFACE *Interface,\r
492 IN EFI_IPv6_ADDRESS *Address\r
493 )\r
494{\r
495 IP6_MLD_GROUP *Group;\r
496 EFI_STATUS Status;\r
497\r
498 Group = Ip6FindMldEntry (IpSb, Address);\r
499 if (Group != NULL) {\r
500 Group->RefCnt++;\r
501 return EFI_SUCCESS;\r
502 }\r
503\r
504 //\r
505 // Repeat the report once or twcie after short delays [Unsolicited Report Interval] (default:10s)\r
506 // Simulate this operation as a Multicast-Address-Specific Query was received for that addresss.\r
507 //\r
508 Group = Ip6CreateMldEntry (IpSb, Address, IP6_UNSOLICITED_REPORT_INTERVAL);\r
509 if (Group == NULL) {\r
510 return EFI_OUT_OF_RESOURCES;\r
511 }\r
512\r
513 Group->SendByUs = TRUE;\r
514\r
515 Status = Ip6GetMulticastMac (IpSb->Mnp, Address, &Group->Mac);\r
516 if (EFI_ERROR (Status)) {\r
517 return Status;\r
518 }\r
519\r
520 Status = IpSb->Mnp->Groups (IpSb->Mnp, TRUE, &Group->Mac);\r
521 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
522 goto ERROR;\r
523 }\r
524\r
525 //\r
526 // Send unsolicited report when a node starts listening to a multicast address\r
527 //\r
528 Status = Ip6SendMldReport (IpSb, Interface, Address);\r
529 if (EFI_ERROR (Status)) {\r
530 goto ERROR;\r
531 }\r
532\r
533 return EFI_SUCCESS;\r
534\r
535ERROR:\r
536 RemoveEntryList (&Group->Link);\r
537 FreePool (Group);\r
538 return Status;\r
539}\r
540\r
541/**\r
542 Leave the IP6 multicast group.\r
543\r
544 @param[in] IpSb The IP6 service binding instance.\r
545 @param[in] Address The group address to leave.\r
546\r
547 @retval EFI_NOT_FOUND The IP6 service instance isn't in the group.\r
548 @retval EFI_SUCCESS Successfully leave the multicast group..\r
549 @retval Others Failed to leave the multicast group.\r
550\r
551**/\r
552EFI_STATUS\r
553Ip6LeaveGroup (\r
554 IN IP6_SERVICE *IpSb,\r
555 IN EFI_IPv6_ADDRESS *Address\r
556 )\r
557{\r
558 IP6_MLD_GROUP *Group;\r
559 EFI_STATUS Status;\r
560\r
561 Group = Ip6FindMldEntry (IpSb, Address);\r
562 if (Group == NULL) {\r
563 return EFI_NOT_FOUND;\r
564 }\r
565\r
566 //\r
567 // If more than one instance is in the group, decrease\r
568 // the RefCnt then return.\r
569 //\r
570 if ((Group->RefCnt > 0) && (--Group->RefCnt > 0)) {\r
571 return EFI_SUCCESS;\r
572 }\r
573\r
574 //\r
575 // If multiple IP6 group addresses are mapped to the same\r
576 // multicast MAC address, don't configure the MNP to leave\r
577 // the MAC.\r
578 //\r
579 if (Ip6FindMac (&IpSb->MldCtrl, &Group->Mac) == 1) {\r
580 Status = IpSb->Mnp->Groups (IpSb->Mnp, FALSE, &Group->Mac);\r
581 if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
582 return Status;\r
583 }\r
584 }\r
585\r
586 //\r
587 // Send a leave report if we are the last node to report\r
588 //\r
589 if (Group->SendByUs) {\r
590 Status = Ip6SendMldDone (IpSb, Address);\r
591 if (EFI_ERROR (Status)) {\r
592 return Status;\r
593 }\r
594 }\r
595\r
596 RemoveEntryList (&Group->Link);\r
597 FreePool (Group);\r
598\r
599 return EFI_SUCCESS;\r
600}\r
601\r
602/**\r
603 Worker function for EfiIp6Groups(). The caller\r
604 should make sure that the parameters are valid.\r
605\r
606 @param[in] IpInstance The IP6 child to change the setting.\r
607 @param[in] JoinFlag TRUE to join the group, otherwise leave it.\r
608 @param[in] GroupAddress The target group address. If NULL, leave all\r
609 the group addresses.\r
610\r
611 @retval EFI_ALREADY_STARTED Wants to join the group, but is already a member of it\r
612 @retval EFI_OUT_OF_RESOURCES Failed to allocate sufficient resources.\r
613 @retval EFI_DEVICE_ERROR Failed to set the group configuraton.\r
614 @retval EFI_SUCCESS Successfully updated the group setting.\r
615 @retval EFI_NOT_FOUND Try to leave the group which it isn't a member.\r
616\r
617**/\r
618EFI_STATUS\r
619Ip6Groups (\r
620 IN IP6_PROTOCOL *IpInstance,\r
621 IN BOOLEAN JoinFlag,\r
622 IN EFI_IPv6_ADDRESS *GroupAddress OPTIONAL\r
623 )\r
624{\r
625 EFI_STATUS Status;\r
626 IP6_SERVICE *IpSb;\r
627 UINT32 Index;\r
628 EFI_IPv6_ADDRESS *Group;\r
629\r
630 IpSb = IpInstance->Service;\r
631\r
632 if (JoinFlag) {\r
633 ASSERT (GroupAddress != NULL);\r
634\r
635 for (Index = 0; Index < IpInstance->GroupCount; Index++) {\r
636 if (EFI_IP6_EQUAL (IpInstance->GroupList + Index, GroupAddress)) {\r
637 return EFI_ALREADY_STARTED;\r
638 }\r
639 }\r
640\r
641 Status = Ip6JoinGroup (IpSb, IpInstance->Interface, GroupAddress);\r
642 if (!EFI_ERROR (Status)) {\r
643 return Ip6CombineGroups (IpInstance, GroupAddress);\r
644 }\r
645\r
646 return Status;\r
647 }\r
648\r
649 //\r
650 // Leave the group. Leave all the groups if GroupAddress is NULL.\r
651 //\r
652 for (Index = IpInstance->GroupCount; Index > 0; Index--) {\r
653 Group = IpInstance->GroupList + (Index - 1);\r
654\r
655 if ((GroupAddress == NULL) || EFI_IP6_EQUAL (Group, GroupAddress)) {\r
656 Status = Ip6LeaveGroup (IpInstance->Service, Group);\r
657 if (EFI_ERROR (Status)) {\r
658 return Status;\r
659 }\r
660\r
661 Ip6RemoveGroup (IpInstance, Group);\r
662\r
663 if (IpInstance->GroupCount == 0) {\r
664 ASSERT (Index == 1);\r
665 FreePool (IpInstance->GroupList);\r
666 IpInstance->GroupList = NULL;\r
667 }\r
668\r
669 if (GroupAddress != NULL) {\r
670 return EFI_SUCCESS;\r
671 }\r
672 }\r
673 }\r
674\r
675 return ((GroupAddress != NULL) ? EFI_NOT_FOUND : EFI_SUCCESS);\r
676}\r
677\r
678/**\r
679 Set a random value of the delay timer for the multicast address from the range\r
680 [0, Maximum Response Delay]. If a timer for any address is already\r
681 running, it is reset to the new random value only if the requested\r
682 Maximum Response Delay is less than the remaining value of the\r
683 running timer. If the Query packet specifies a Maximum Response\r
684 Delay of zero, each timer is effectively set to zero, and the action\r
685 specified below for timer expiration is performed immediately.\r
686\r
687 @param[in] IpSb The IP6 service binding instance.\r
688 @param[in] MaxRespDelay The Maximum Response Delay, in milliseconds.\r
689 @param[in] MulticastAddr The multicast address.\r
690 @param[in, out] Group Points to a IP6_MLD_GROUP list entry node.\r
691\r
692 @retval EFI_SUCCESS The delay timer is successfully updated or\r
693 timer expiration is performed immediately.\r
694 @retval Others Failed to send out MLD report message.\r
695\r
696**/\r
697EFI_STATUS\r
698Ip6UpdateDelayTimer (\r
699 IN IP6_SERVICE *IpSb,\r
700 IN UINT16 MaxRespDelay,\r
701 IN EFI_IPv6_ADDRESS *MulticastAddr,\r
702 IN OUT IP6_MLD_GROUP *Group\r
703 )\r
704{\r
705 UINT32 Delay;\r
706\r
707 //\r
708 // If the Query packet specifies a Maximum Response Delay of zero, perform timer\r
709 // expiration immediately.\r
710 //\r
711 if (MaxRespDelay == 0) {\r
712 Group->DelayTimer = 0;\r
713 return Ip6SendMldReport (IpSb, NULL, MulticastAddr);\r
714 }\r
715\r
716 Delay = (UINT32) (MaxRespDelay / 1000);\r
717\r
718 //\r
719 // Sets a delay timer to a random value selected from the range [0, Maximum Response Delay]\r
720 // If a timer is already running, resets it if the request Maximum Response Delay\r
721 // is less than the remaining value of the running timer.\r
722 //\r
723 if (Group->DelayTimer == 0 || Delay < Group->DelayTimer) {\r
724 Group->DelayTimer = Delay / 4294967295UL * NET_RANDOM (NetRandomInitSeed ());\r
725 }\r
726\r
727 return EFI_SUCCESS;\r
728}\r
729\r
730/**\r
731 Process the Multicast Listener Query message.\r
732\r
733 @param[in] IpSb The IP service that received the packet.\r
734 @param[in] Head The IP head of the MLD query packet.\r
735 @param[in] Packet The content of the MLD query packet with IP head\r
736 removed.\r
737\r
738 @retval EFI_SUCCESS The MLD query packet processed successfully.\r
739 @retval EFI_INVALID_PARAMETER The packet is invalid.\r
740 @retval Others Failed to process the packet.\r
741\r
742**/\r
743EFI_STATUS\r
744Ip6ProcessMldQuery (\r
745 IN IP6_SERVICE *IpSb,\r
746 IN EFI_IP6_HEADER *Head,\r
747 IN NET_BUF *Packet\r
748 )\r
749{\r
750 EFI_IPv6_ADDRESS AllNodes;\r
751 IP6_MLD_GROUP *Group;\r
752 IP6_MLD_HEAD MldPacket;\r
753 LIST_ENTRY *Entry;\r
754 EFI_STATUS Status;\r
755\r
756 Status = EFI_INVALID_PARAMETER;\r
757\r
758 //\r
759 // Check the validity of the packet, generic query or specific query\r
760 //\r
761 if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
762 goto Exit;\r
763 }\r
764\r
765 if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
766 goto Exit;\r
767 }\r
768\r
769 //\r
770 // The Packet points to MLD report raw data without Hop-By-Hop option.\r
771 //\r
772 NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
773 MldPacket.MaxRespDelay = NTOHS (MldPacket.MaxRespDelay);\r
774\r
775 Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);\r
776 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &AllNodes)) {\r
777 //\r
778 // Receives a Multicast-Address-Specific Query, check it firstly\r
779 //\r
780 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
781 goto Exit;\r
782 }\r
783 //\r
784 // The node is not listening but it receives the specific query. Just return.\r
785 //\r
786 Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
787 if (Group == NULL) {\r
788 Status = EFI_SUCCESS;\r
789 goto Exit;\r
790 }\r
791\r
792 Status = Ip6UpdateDelayTimer (\r
793 IpSb,\r
794 MldPacket.MaxRespDelay,\r
795 &MldPacket.Group,\r
796 Group\r
797 );\r
798 goto Exit;\r
799 }\r
800\r
801 //\r
802 // Receives a General Query, sets a delay timer for each multicast address it is listening\r
803 //\r
804 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
805 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
806 Status = Ip6UpdateDelayTimer (IpSb, MldPacket.MaxRespDelay, &Group->Address, Group);\r
807 if (EFI_ERROR (Status)) {\r
808 goto Exit;\r
809 }\r
810 }\r
811\r
812 Status = EFI_SUCCESS;\r
813\r
814Exit:\r
815 NetbufFree (Packet);\r
816 return Status;\r
817}\r
818\r
819/**\r
820 Process the Multicast Listener Report message.\r
821\r
822 @param[in] IpSb The IP service that received the packet.\r
823 @param[in] Head The IP head of the MLD report packet.\r
824 @param[in] Packet The content of the MLD report packet with IP head\r
825 removed.\r
826\r
827 @retval EFI_SUCCESS The MLD report packet processed successfully.\r
828 @retval EFI_INVALID_PARAMETER The packet is invalid.\r
829\r
830**/\r
831EFI_STATUS\r
832Ip6ProcessMldReport (\r
833 IN IP6_SERVICE *IpSb,\r
834 IN EFI_IP6_HEADER *Head,\r
835 IN NET_BUF *Packet\r
836 )\r
837{\r
838 IP6_MLD_HEAD MldPacket;\r
839 IP6_MLD_GROUP *Group;\r
840 EFI_STATUS Status;\r
841\r
842 Status = EFI_INVALID_PARAMETER;\r
843\r
844 //\r
845 // Validate the incoming message, if invalid, drop it.\r
846 //\r
847 if (!NetIp6IsUnspecifiedAddr (&Head->SourceAddress) && !NetIp6IsLinkLocalAddr (&Head->SourceAddress)) {\r
848 goto Exit;\r
849 }\r
850\r
851 if (Head->HopLimit != 1 || !IP6_IS_MULTICAST (&Head->DestinationAddress)) {\r
852 goto Exit;\r
853 }\r
854\r
855 //\r
856 // The Packet points to MLD report raw data without Hop-By-Hop option.\r
857 //\r
858 NetbufCopy (Packet, 0, sizeof (IP6_MLD_HEAD), (UINT8 *) &MldPacket);\r
859 if (!EFI_IP6_EQUAL (&Head->DestinationAddress, &MldPacket.Group)) {\r
860 goto Exit;\r
861 }\r
862\r
863 Group = Ip6FindMldEntry (IpSb, &MldPacket.Group);\r
864 if (Group == NULL) {\r
865 goto Exit;\r
866 }\r
867\r
868 //\r
869 // The report is sent by another node, stop its own timer relates to the multicast address and clear\r
870 //\r
871\r
872 if (!Group->SendByUs) {\r
873 Group->DelayTimer = 0;\r
874 }\r
875\r
876 Status = EFI_SUCCESS;\r
877\r
878Exit:\r
879 NetbufFree (Packet);\r
880 return Status;\r
881}\r
882\r
883/**\r
884 The heartbeat timer of MLD module. It sends out a solicited MLD report when\r
885 DelayTimer expires.\r
886\r
887 @param[in] IpSb The IP6 service binding instance.\r
888\r
889**/\r
890VOID\r
891Ip6MldTimerTicking (\r
892 IN IP6_SERVICE *IpSb\r
893 )\r
894{\r
895 IP6_MLD_GROUP *Group;\r
896 LIST_ENTRY *Entry;\r
897\r
898 //\r
899 // Send solicited report when timer expires\r
900 //\r
901 NET_LIST_FOR_EACH (Entry, &IpSb->MldCtrl.Groups) {\r
902 Group = NET_LIST_USER_STRUCT (Entry, IP6_MLD_GROUP, Link);\r
903 if ((Group->DelayTimer > 0) && (--Group->DelayTimer == 0)) {\r
904 Ip6SendMldReport (IpSb, NULL, &Group->Address);\r
905 }\r
906 }\r
907}\r
908\r