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