2 This file implements the RFC2236: IGMP v2.
4 Copyright (c) 2005 - 2006, Intel Corporation.<BR>
5 All rights reserved. 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
);
79 gBS
->FreePool (Group
);
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
);
197 Igmp
->MaxRespTime
= 0;
199 Igmp
->Group
= HTONL (Group
);
200 Igmp
->Checksum
= (UINT16
) (~NetblockChecksum ((UINT8
*) Igmp
, sizeof (IGMP_HEAD
)));
203 Head
.Protocol
= IP4_PROTO_IGMP
;
207 Head
.Src
= IP4_ALLZERO_ADDRESS
;
214 (UINT8
*) &mRouteAlertOption
,
224 Send an IGMP membership report. Depends on whether the server is
225 v1 or v2, it will send either a V1 or V2 membership report.
227 @param[in] IpSb The IP4 service instance that requests the
229 @param[in] Group The group address to report
231 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message
232 @retval EFI_SUCCESS The IGMP report message is successfully send
233 @retval Others Failed to send the report.
238 IN IP4_SERVICE
*IpSb
,
242 if (IpSb
->IgmpCtrl
.Igmpv1QuerySeen
!= 0) {
243 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V1_MEMBERSHIP_REPORT
, Group
);
245 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V2_MEMBERSHIP_REPORT
, Group
);
251 Join the multicast group on behalf of this IP4 child
253 @param[in] IpInstance The IP4 child that wants to join the group
254 @param[in] Address The group to join
256 @retval EFI_SUCCESS Successfully join the multicast group
257 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources
258 @retval Others Failed to join the multicast group.
263 IN IP4_PROTOCOL
*IpInstance
,
267 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
269 IGMP_SERVICE_DATA
*IgmpCtrl
;
273 IpSb
= IpInstance
->Service
;
274 IgmpCtrl
= &IpSb
->IgmpCtrl
;
278 // If the IP service already is a member in the group, just
279 // increase the refernce count and return.
281 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
289 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
290 // send a report, then direct MNP to receive the multicast.
292 Group
= AllocatePool (sizeof (IGMP_GROUP
));
295 return EFI_OUT_OF_RESOURCES
;
298 Group
->Address
= Address
;
300 Group
->DelayTime
= IGMP_UNSOLICIATED_REPORT
;
301 Group
->ReportByUs
= TRUE
;
303 Status
= Ip4GetMulticastMac (Mnp
, Address
, &Group
->Mac
);
305 if (EFI_ERROR (Status
)) {
309 Status
= Ip4SendIgmpReport (IpSb
, Address
);
311 if (EFI_ERROR (Status
)) {
315 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
317 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
321 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
325 gBS
->FreePool (Group
);
331 Leave the IP4 multicast group on behalf of IpInstance.
333 @param[in] IpInstance The IP4 child that wants to leave the group
335 @param[in] Address The group address to leave
337 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group
338 @retval EFI_SUCCESS Successfully leave the multicast group.
339 @retval Others Failed to leave the multicast group.
344 IN IP4_PROTOCOL
*IpInstance
,
348 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
350 IGMP_SERVICE_DATA
*IgmpCtrl
;
354 IpSb
= IpInstance
->Service
;
355 IgmpCtrl
= &IpSb
->IgmpCtrl
;
358 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
361 return EFI_NOT_FOUND
;
365 // If more than one instance is in the group, decrease
366 // the RefCnt then return.
368 if (--Group
->RefCnt
> 0) {
373 // If multiple IP4 group addresses are mapped to the same
374 // multicast MAC address, don't configure the MNP to leave
377 if (Ip4FindMac (IgmpCtrl
, &Group
->Mac
) == 1) {
378 Status
= Mnp
->Groups (Mnp
, FALSE
, &Group
->Mac
);
380 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_FOUND
)) {
386 // Send a leave report if the membership is reported by us
387 // and we are talking IGMPv2.
389 if (Group
->ReportByUs
&& IgmpCtrl
->Igmpv1QuerySeen
== 0) {
390 Ip4SendIgmpMessage (IpSb
, IP4_ALLROUTER_ADDRESS
, IGMP_LEAVE_GROUP
, Group
->Address
);
393 RemoveEntryList (&Group
->Link
);
394 gBS
->FreePool (Group
);
401 Handle the received IGMP message for the IP4 service instance.
403 @param[in] IpSb The IP4 service instance that received the message
404 @param[in] Head The IP4 header of the received message
405 @param[in] Packet The IGMP message, without IP4 header
407 @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
408 @retval EFI_SUCCESS The IGMP message is successfully processed.
413 IN IP4_SERVICE
*IpSb
,
418 IGMP_SERVICE_DATA
*IgmpCtrl
;
424 IgmpCtrl
= &IpSb
->IgmpCtrl
;
427 // Must checksum over the whole packet, later IGMP version
428 // may employ message longer than 8 bytes. IP's header has
429 // already been trimmed off.
431 if ((Packet
->TotalSize
< sizeof (Igmp
)) || (NetbufChecksum (Packet
) != 0)) {
433 return EFI_INVALID_PARAMETER
;
437 // Copy the packet in case it is fragmented
439 NetbufCopy (Packet
, 0, sizeof (IGMP_HEAD
), (UINT8
*)&Igmp
);
442 case IGMP_MEMBERSHIP_QUERY
:
444 // If MaxRespTime is zero, it is most likely that we are
445 // talking to a V1 router
447 if (Igmp
.MaxRespTime
== 0) {
448 IgmpCtrl
->Igmpv1QuerySeen
= IGMP_V1ROUTER_PRESENT
;
449 Igmp
.MaxRespTime
= 100;
453 // Igmp is ticking once per second but MaxRespTime is in
454 // the unit of 100ms.
456 Igmp
.MaxRespTime
/= 10;
457 Address
= NTOHL (Igmp
.Group
);
459 if (Address
== IP4_ALLSYSTEM_ADDRESS
) {
463 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
464 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
467 // If address is all zero, all the memberships will be reported.
468 // otherwise only one is reported.
470 if ((Address
== IP4_ALLZERO_ADDRESS
) || (Address
== Group
->Address
)) {
472 // If the timer is pending, only update it if the time left
473 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
475 if ((Group
->DelayTime
== 0) || (Group
->DelayTime
> Igmp
.MaxRespTime
)) {
476 Group
->DelayTime
= MAX (1, Igmp
.MaxRespTime
);
483 case IGMP_V1_MEMBERSHIP_REPORT
:
484 case IGMP_V2_MEMBERSHIP_REPORT
:
485 Address
= NTOHL (Igmp
.Group
);
486 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
488 if ((Group
!= NULL
) && (Group
->DelayTime
> 0)) {
489 Group
->DelayTime
= 0;
490 Group
->ReportByUs
= FALSE
;
502 The periodical timer function for IGMP. It does the following
504 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
505 the IGMP server type.
506 2. Decrease the report timer for each IGMP group in "delaying
509 @param[in] IpSb The IP4 service instance that is ticking
517 IGMP_SERVICE_DATA
*IgmpCtrl
;
521 IgmpCtrl
= &IpSb
->IgmpCtrl
;
523 if (IgmpCtrl
->Igmpv1QuerySeen
> 0) {
524 IgmpCtrl
->Igmpv1QuerySeen
--;
528 // Decrease the report timer for each IGMP group in "delaying member"
530 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
531 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
532 ASSERT (Group
->DelayTime
>= 0);
534 if (Group
->DelayTime
> 0) {
537 if (Group
->DelayTime
== 0) {
538 Ip4SendIgmpReport (IpSb
, Group
->Address
);
539 Group
->ReportByUs
= TRUE
;
547 Add a group address to the array of group addresses.
548 The caller should make sure that no duplicated address
549 existed in the array. Although the function doesn't
550 assume the byte order of the both Source and Addr, the
551 network byte order is used by the caller.
553 @param[in] Source The array of group addresses to add to
554 @param[in] Count The number of group addresses in the Source
555 @param[in] Addr The IP4 multicast address to add
557 @return NULL if failed to allocate memory for the new groups,
558 otherwise the new combined group addresses.
570 Groups
= AllocatePool (sizeof (IP4_ADDR
) * (Count
+ 1));
572 if (Groups
== NULL
) {
576 CopyMem (Groups
, Source
, Count
* sizeof (IP4_ADDR
));
577 Groups
[Count
] = Addr
;
584 Remove a group address from the array of group addresses.
585 Although the function doesn't assume the byte order of the
586 both Groups and Addr, the network byte order is used by
589 @param Groups The array of group addresses to remove from
590 @param Count The number of group addresses in the Groups
591 @param Addr The IP4 multicast address to remove
593 @return The nubmer of group addresses in the Groups after remove.
594 It is Count if the Addr isn't in the Groups.
599 IN OUT IP4_ADDR
*Groups
,
606 for (Index
= 0; Index
< Count
; Index
++) {
607 if (Groups
[Index
] == Addr
) {
612 while (Index
< Count
- 1) {
613 Groups
[Index
] = Groups
[Index
+ 1];