2 This file implements the RFC2236: IGMP v2.
4 Copyright (c) 2005 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 // Route Alert option in IGMP report to direct routers to
19 // examine the packet more closely.
21 UINT32 mRouteAlertOption
= 0x00000494;
25 Init the IGMP control data of the IP4 service instance, configure
26 MNP to receive ALL SYSTEM multicast.
28 @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.
30 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
31 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
32 @retval Others Failed to initialize the IGMP of IpSb.
37 IN OUT IP4_SERVICE
*IpSb
40 IGMP_SERVICE_DATA
*IgmpCtrl
;
41 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
45 IgmpCtrl
= &IpSb
->IgmpCtrl
;
48 // Configure MNP to receive ALL_SYSTEM multicast
50 Group
= AllocatePool (sizeof (IGMP_GROUP
));
53 return EFI_OUT_OF_RESOURCES
;
58 Group
->Address
= IP4_ALLSYSTEM_ADDRESS
;
61 Group
->ReportByUs
= FALSE
;
63 Status
= Ip4GetMulticastMac (Mnp
, IP4_ALLSYSTEM_ADDRESS
, &Group
->Mac
);
65 if (EFI_ERROR (Status
)) {
69 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
71 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
75 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
85 Find the IGMP_GROUP structure which contains the status of multicast
86 group Address in this IGMP control block
88 @param[in] IgmpCtrl The IGMP control block to search from.
89 @param[in] Address The multicast address to search.
91 @return NULL if the multicast address isn't in the IGMP control block. Otherwise
92 the point to the IGMP_GROUP which contains the status of multicast group
98 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
105 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
106 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
108 if (Group
->Address
== Address
) {
118 Count the number of IP4 multicast groups that are mapped to the
119 same MAC address. Several IP4 multicast address may be mapped to
120 the same MAC address.
122 @param[in] IgmpCtrl The IGMP control block to search in.
123 @param[in] Mac The MAC address to search.
125 @return The number of the IP4 multicast group that mapped to the same
131 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
132 IN EFI_MAC_ADDRESS
*Mac
141 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
142 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
144 if (NET_MAC_EQUAL (&Group
->Mac
, Mac
, sizeof (EFI_MAC_ADDRESS
))) {
154 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
156 @param[in] IpSb The IP4 service instance that requests the
158 @param[in] Dst The destinaton to send to.
159 @param[in] Type The IGMP message type, such as IGMP v1 membership
161 @param[in] Group The group address in the IGMP message head.
163 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
164 @retval EFI_SUCCESS The IGMP message is successfully send.
165 @retval Others Failed to send the IGMP message.
170 IN IP4_SERVICE
*IpSb
,
181 // Allocate a net buffer to hold the message
183 Packet
= NetbufAlloc (IP4_MAX_HEADLEN
+ sizeof (IGMP_HEAD
));
185 if (Packet
== NULL
) {
186 return EFI_OUT_OF_RESOURCES
;
190 // Fill in the IGMP and IP header, then transmit the message
192 NetbufReserve (Packet
, IP4_MAX_HEADLEN
);
194 Igmp
= (IGMP_HEAD
*) NetbufAllocSpace (Packet
, sizeof (IGMP_HEAD
), FALSE
);
196 return EFI_OUT_OF_RESOURCES
;
200 Igmp
->MaxRespTime
= 0;
202 Igmp
->Group
= HTONL (Group
);
203 Igmp
->Checksum
= (UINT16
) (~NetblockChecksum ((UINT8
*) Igmp
, sizeof (IGMP_HEAD
)));
206 Head
.Protocol
= IP4_PROTO_IGMP
;
210 Head
.Src
= IP4_ALLZERO_ADDRESS
;
217 (UINT8
*) &mRouteAlertOption
,
227 Send an IGMP membership report. Depends on whether the server is
228 v1 or v2, it will send either a V1 or V2 membership report.
230 @param[in] IpSb The IP4 service instance that requests the
232 @param[in] Group The group address to report.
234 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
235 @retval EFI_SUCCESS The IGMP report message is successfully send.
236 @retval Others Failed to send the report.
241 IN IP4_SERVICE
*IpSb
,
245 if (IpSb
->IgmpCtrl
.Igmpv1QuerySeen
!= 0) {
246 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V1_MEMBERSHIP_REPORT
, Group
);
248 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V2_MEMBERSHIP_REPORT
, Group
);
254 Join the multicast group on behalf of this IP4 child
256 @param[in] IpInstance The IP4 child that wants to join the group.
257 @param[in] Address The group to join.
259 @retval EFI_SUCCESS Successfully join the multicast group.
260 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
261 @retval Others Failed to join the multicast group.
266 IN IP4_PROTOCOL
*IpInstance
,
270 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
272 IGMP_SERVICE_DATA
*IgmpCtrl
;
276 IpSb
= IpInstance
->Service
;
277 IgmpCtrl
= &IpSb
->IgmpCtrl
;
281 // If the IP service already is a member in the group, just
282 // increase the refernce count and return.
284 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
292 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
293 // send a report, then direct MNP to receive the multicast.
295 Group
= AllocatePool (sizeof (IGMP_GROUP
));
298 return EFI_OUT_OF_RESOURCES
;
301 Group
->Address
= Address
;
303 Group
->DelayTime
= IGMP_UNSOLICIATED_REPORT
;
304 Group
->ReportByUs
= TRUE
;
306 Status
= Ip4GetMulticastMac (Mnp
, Address
, &Group
->Mac
);
308 if (EFI_ERROR (Status
)) {
312 Status
= Ip4SendIgmpReport (IpSb
, Address
);
314 if (EFI_ERROR (Status
)) {
318 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
320 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
324 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
334 Leave the IP4 multicast group on behalf of IpInstance.
336 @param[in] IpInstance The IP4 child that wants to leave the group
338 @param[in] Address The group address to leave.
340 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group.
341 @retval EFI_SUCCESS Successfully leave the multicast group.
342 @retval Others Failed to leave the multicast group.
347 IN IP4_PROTOCOL
*IpInstance
,
351 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
353 IGMP_SERVICE_DATA
*IgmpCtrl
;
357 IpSb
= IpInstance
->Service
;
358 IgmpCtrl
= &IpSb
->IgmpCtrl
;
361 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
364 return EFI_NOT_FOUND
;
368 // If more than one instance is in the group, decrease
369 // the RefCnt then return.
371 if (--Group
->RefCnt
> 0) {
376 // If multiple IP4 group addresses are mapped to the same
377 // multicast MAC address, don't configure the MNP to leave
380 if (Ip4FindMac (IgmpCtrl
, &Group
->Mac
) == 1) {
381 Status
= Mnp
->Groups (Mnp
, FALSE
, &Group
->Mac
);
383 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_FOUND
)) {
389 // Send a leave report if the membership is reported by us
390 // and we are talking IGMPv2.
392 if (Group
->ReportByUs
&& IgmpCtrl
->Igmpv1QuerySeen
== 0) {
393 Ip4SendIgmpMessage (IpSb
, IP4_ALLROUTER_ADDRESS
, IGMP_LEAVE_GROUP
, Group
->Address
);
396 RemoveEntryList (&Group
->Link
);
404 Handle the received IGMP message for the IP4 service instance.
406 @param[in] IpSb The IP4 service instance that received the message.
407 @param[in] Head The IP4 header of the received message.
408 @param[in] Packet The IGMP message, without IP4 header.
410 @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
411 @retval EFI_SUCCESS The IGMP message is successfully processed.
416 IN IP4_SERVICE
*IpSb
,
421 IGMP_SERVICE_DATA
*IgmpCtrl
;
427 IgmpCtrl
= &IpSb
->IgmpCtrl
;
430 // Must checksum over the whole packet, later IGMP version
431 // may employ message longer than 8 bytes. IP's header has
432 // already been trimmed off.
434 if ((Packet
->TotalSize
< sizeof (Igmp
)) || (NetbufChecksum (Packet
) != 0)) {
436 return EFI_INVALID_PARAMETER
;
440 // Copy the packet in case it is fragmented
442 NetbufCopy (Packet
, 0, sizeof (IGMP_HEAD
), (UINT8
*)&Igmp
);
445 case IGMP_MEMBERSHIP_QUERY
:
447 // If MaxRespTime is zero, it is most likely that we are
448 // talking to a V1 router
450 if (Igmp
.MaxRespTime
== 0) {
451 IgmpCtrl
->Igmpv1QuerySeen
= IGMP_V1ROUTER_PRESENT
;
452 Igmp
.MaxRespTime
= 100;
456 // Igmp is ticking once per second but MaxRespTime is in
457 // the unit of 100ms.
459 Igmp
.MaxRespTime
/= 10;
460 Address
= NTOHL (Igmp
.Group
);
462 if (Address
== IP4_ALLSYSTEM_ADDRESS
) {
466 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
467 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
470 // If address is all zero, all the memberships will be reported.
471 // otherwise only one is reported.
473 if ((Address
== IP4_ALLZERO_ADDRESS
) || (Address
== Group
->Address
)) {
475 // If the timer is pending, only update it if the time left
476 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
478 if ((Group
->DelayTime
== 0) || (Group
->DelayTime
> Igmp
.MaxRespTime
)) {
479 Group
->DelayTime
= MAX (1, Igmp
.MaxRespTime
);
486 case IGMP_V1_MEMBERSHIP_REPORT
:
487 case IGMP_V2_MEMBERSHIP_REPORT
:
488 Address
= NTOHL (Igmp
.Group
);
489 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
491 if ((Group
!= NULL
) && (Group
->DelayTime
> 0)) {
492 Group
->DelayTime
= 0;
493 Group
->ReportByUs
= FALSE
;
505 The periodical timer function for IGMP. It does the following
507 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
508 the IGMP server type.
509 2. Decrease the report timer for each IGMP group in "delaying
512 @param[in] IpSb The IP4 service instance that is ticking.
520 IGMP_SERVICE_DATA
*IgmpCtrl
;
524 IgmpCtrl
= &IpSb
->IgmpCtrl
;
526 if (IgmpCtrl
->Igmpv1QuerySeen
> 0) {
527 IgmpCtrl
->Igmpv1QuerySeen
--;
531 // Decrease the report timer for each IGMP group in "delaying member"
533 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
534 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
535 ASSERT (Group
->DelayTime
>= 0);
537 if (Group
->DelayTime
> 0) {
540 if (Group
->DelayTime
== 0) {
541 Ip4SendIgmpReport (IpSb
, Group
->Address
);
542 Group
->ReportByUs
= TRUE
;
550 Add a group address to the array of group addresses.
551 The caller should make sure that no duplicated address
552 existed in the array. Although the function doesn't
553 assume the byte order of the both Source and Addr, the
554 network byte order is used by the caller.
556 @param[in] Source The array of group addresses to add to.
557 @param[in] Count The number of group addresses in the Source.
558 @param[in] Addr The IP4 multicast address to add.
560 @return NULL if failed to allocate memory for the new groups,
561 otherwise the new combined group addresses.
573 Groups
= AllocatePool (sizeof (IP4_ADDR
) * (Count
+ 1));
575 if (Groups
== NULL
) {
579 CopyMem (Groups
, Source
, Count
* sizeof (IP4_ADDR
));
580 Groups
[Count
] = Addr
;
587 Remove a group address from the array of group addresses.
588 Although the function doesn't assume the byte order of the
589 both Groups and Addr, the network byte order is used by
592 @param Groups The array of group addresses to remove from.
593 @param Count The number of group addresses in the Groups.
594 @param Addr The IP4 multicast address to remove.
596 @return The nubmer of group addresses in the Groups after remove.
597 It is Count if the Addr isn't in the Groups.
602 IN OUT IP4_ADDR
*Groups
,
609 for (Index
= 0; Index
< Count
; Index
++) {
610 if (Groups
[Index
] == Addr
) {
615 while (Index
< Count
- 1) {
616 Groups
[Index
] = Groups
[Index
+ 1];