3 Copyright (c) 2005 - 2006, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 This file implements the RFC2236: IGMP v2
27 // Route Alert option in IGMP report to direct routers to
28 // examine the packet more closely.
30 UINT32 mRouteAlertOption
= 0x00000494;
34 Init the IGMP control data of the IP4 service instance, configure
35 MNP to receive ALL SYSTEM multicast.
37 @param IpSb The IP4 service whose IGMP is to be initialized.
39 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
40 @retval Others Failed to initialize the IGMP of IpSb.
41 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
49 IGMP_SERVICE_DATA
*IgmpCtrl
;
50 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
54 IgmpCtrl
= &IpSb
->IgmpCtrl
;
57 // Configure MNP to receive ALL_SYSTEM multicast
59 Group
= AllocatePool (sizeof (IGMP_GROUP
));
62 return EFI_OUT_OF_RESOURCES
;
67 Group
->Address
= IP4_ALLSYSTEM_ADDRESS
;
70 Group
->ReportByUs
= FALSE
;
72 Status
= Ip4GetMulticastMac (Mnp
, IP4_ALLSYSTEM_ADDRESS
, &Group
->Mac
);
74 if (EFI_ERROR (Status
)) {
78 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
80 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
84 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
88 gBS
->FreePool (Group
);
94 Find the IGMP_GROUP structure which contains the status of multicast
95 group Address in this IGMP control block
97 @param IgmpCtrl The IGMP control block to search from
98 @param Address The multicast address to search
100 @return NULL if the multicast address isn't in the IGMP control block. Otherwise
101 @return the point to the IGMP_GROUP which contains the status of multicast group
107 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
114 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
115 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
117 if (Group
->Address
== Address
) {
127 Count the number of IP4 multicast groups that are mapped to the
128 same MAC address. Several IP4 multicast address may be mapped to
129 the same MAC address.
131 @param IgmpCtrl The IGMP control block to search in
132 @param Mac The MAC address to search
134 @return The number of the IP4 multicast group that mapped to the same
135 @return multicast group Mac.
140 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
141 IN EFI_MAC_ADDRESS
*Mac
150 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
151 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
153 if (NET_MAC_EQUAL (&Group
->Mac
, Mac
, sizeof (EFI_MAC_ADDRESS
))) {
163 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
165 @param IpSb The IP4 service instance that requests the
167 @param Dst The destinaton to send to
168 @param Type The IGMP message type, such as IGMP v2 membership
170 @param Group The group address in the IGMP message head.
172 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message
173 @retval EFI_SUCCESS The IGMP message is successfully send
174 @retval Others Failed to send the IGMP message.
179 IN IP4_SERVICE
*IpSb
,
190 // Allocate a net buffer to hold the message
192 Packet
= NetbufAlloc (IP4_MAX_HEADLEN
+ sizeof (IGMP_HEAD
));
194 if (Packet
== NULL
) {
195 return EFI_OUT_OF_RESOURCES
;
199 // Fill in the IGMP and IP header, then transmit the message
201 NetbufReserve (Packet
, IP4_MAX_HEADLEN
);
203 Igmp
= (IGMP_HEAD
*) NetbufAllocSpace (Packet
, sizeof (IGMP_HEAD
), FALSE
);
206 Igmp
->MaxRespTime
= 0;
208 Igmp
->Group
= HTONL (Group
);
209 Igmp
->Checksum
= (UINT16
) (~NetblockChecksum ((UINT8
*) Igmp
, sizeof (IGMP_HEAD
)));
212 Head
.Protocol
= IP4_PROTO_IGMP
;
216 Head
.Src
= IP4_ALLZERO_ADDRESS
;
223 (UINT8
*) &mRouteAlertOption
,
233 Send an IGMP membership report. Depends on whether the server is
234 v1 or v2, it will send either a V1 or V2 membership report.
236 @param IpSb The IP4 service instance that requests the
238 @param Group The group address to report
240 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message
241 @retval EFI_SUCCESS The IGMP report message is successfully send
242 @retval Others Failed to send the report.
247 IN IP4_SERVICE
*IpSb
,
251 if (IpSb
->IgmpCtrl
.Igmpv1QuerySeen
!= 0) {
252 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V1_MEMBERSHIP_REPORT
, Group
);
254 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V2_MEMBERSHIP_REPORT
, Group
);
260 Join the multicast group on behavior of this IP4 child
262 @param IpInstance The IP4 child that wants to join the group
263 @param Address The group to join
265 @retval EFI_SUCCESS Successfully join the multicast group
266 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources
267 @retval Others Failed to join the multicast group.
272 IN IP4_PROTOCOL
*IpInstance
,
276 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
278 IGMP_SERVICE_DATA
*IgmpCtrl
;
282 IpSb
= IpInstance
->Service
;
283 IgmpCtrl
= &IpSb
->IgmpCtrl
;
287 // If the IP service already is a member in the group, just
288 // increase the refernce count and return.
290 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
298 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
299 // send a report, then direct MNP to receive the multicast.
301 Group
= AllocatePool (sizeof (IGMP_GROUP
));
304 return EFI_OUT_OF_RESOURCES
;
307 Group
->Address
= Address
;
309 Group
->DelayTime
= IGMP_UNSOLICIATED_REPORT
;
310 Group
->ReportByUs
= TRUE
;
312 Status
= Ip4GetMulticastMac (Mnp
, Address
, &Group
->Mac
);
314 if (EFI_ERROR (Status
)) {
318 Status
= Ip4SendIgmpReport (IpSb
, Address
);
320 if (EFI_ERROR (Status
)) {
324 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
326 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
330 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
334 gBS
->FreePool (Group
);
340 Leave the IP4 multicast group on behavior of IpInstance.
342 @param IpInstance The IP4 child that wants to leave the group
344 @param Address The group address to leave
346 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group
347 @retval EFI_SUCCESS Successfully leave the multicast group.
348 @retval Others Failed to leave the multicast group.
353 IN IP4_PROTOCOL
*IpInstance
,
357 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
359 IGMP_SERVICE_DATA
*IgmpCtrl
;
363 IpSb
= IpInstance
->Service
;
364 IgmpCtrl
= &IpSb
->IgmpCtrl
;
367 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
370 return EFI_NOT_FOUND
;
374 // If more than one instance is in the group, decrease
375 // the RefCnt then return.
377 if (--Group
->RefCnt
> 0) {
382 // If multiple IP4 group addresses are mapped to the same
383 // multicast MAC address, don't configure the MNP to leave
386 if (Ip4FindMac (IgmpCtrl
, &Group
->Mac
) == 1) {
387 Status
= Mnp
->Groups (Mnp
, FALSE
, &Group
->Mac
);
389 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_FOUND
)) {
395 // Send a leave report if the membership is reported by us
396 // and we are talking IGMPv2.
398 if (Group
->ReportByUs
&& !IgmpCtrl
->Igmpv1QuerySeen
) {
399 Ip4SendIgmpMessage (IpSb
, IP4_ALLROUTER_ADDRESS
, IGMP_LEAVE_GROUP
, Group
->Address
);
402 RemoveEntryList (&Group
->Link
);
403 gBS
->FreePool (Group
);
410 Handle the received IGMP message for the IP4 service instance.
412 @param IpSb The IP4 service instance that received the message
413 @param Head The IP4 header of the received message
414 @param Packet The IGMP message, without IP4 header
416 @retval EFI_INVALID_PARAMETER The IGMP message is malformated.
417 @retval EFI_SUCCESS The IGMP message is successfully processed.
422 IN IP4_SERVICE
*IpSb
,
427 IGMP_SERVICE_DATA
*IgmpCtrl
;
433 IgmpCtrl
= &IpSb
->IgmpCtrl
;
436 // Must checksum over the whole packet, later IGMP version
437 // may employ message longer than 8 bytes. IP's header has
438 // already been trimmed off.
440 if ((Packet
->TotalSize
< sizeof (Igmp
)) || (NetbufChecksum (Packet
) != 0)) {
442 return EFI_INVALID_PARAMETER
;
446 // Copy the packet in case it is fragmented
448 NetbufCopy (Packet
, 0, sizeof (IGMP_HEAD
), (UINT8
*)&Igmp
);
451 case IGMP_MEMBERSHIP_QUERY
:
453 // If MaxRespTIme is zero, it is most likely that we are
454 // talking to a V1 router
456 if (Igmp
.MaxRespTime
== 0) {
457 IgmpCtrl
->Igmpv1QuerySeen
= IGMP_V1ROUTER_PRESENT
;
458 Igmp
.MaxRespTime
= 100;
462 // Igmp is ticking once per second but MaxRespTime is in
463 // the unit of 100ms.
465 Igmp
.MaxRespTime
/= 10;
466 Address
= NTOHL (Igmp
.Group
);
468 if (Address
== IP4_ALLSYSTEM_ADDRESS
) {
472 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
473 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
476 // If address is all zero, all the memberships will be reported.
477 // otherwise only one is reported.
479 if ((Address
== IP4_ALLZERO_ADDRESS
) || (Address
== Group
->Address
)) {
481 // If the timer is pending, only update it if the time left
482 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
484 if ((Group
->DelayTime
== 0) || (Group
->DelayTime
> Igmp
.MaxRespTime
)) {
485 Group
->DelayTime
= MAX (1, Igmp
.MaxRespTime
);
492 case IGMP_V1_MEMBERSHIP_REPORT
:
493 case IGMP_V2_MEMBERSHIP_REPORT
:
494 Address
= NTOHL (Igmp
.Group
);
495 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
497 if ((Group
!= NULL
) && (Group
->DelayTime
> 0)) {
498 Group
->DelayTime
= 0;
499 Group
->ReportByUs
= FALSE
;
511 The periodical timer function for IGMP. It does the following
513 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
514 the IGMP server type.
515 2. Decrease the report timer for each IGMP group in "delaying
518 @param IpSb The IP4 service instance that is ticking
528 IGMP_SERVICE_DATA
*IgmpCtrl
;
532 IgmpCtrl
= &IpSb
->IgmpCtrl
;
534 if (IgmpCtrl
->Igmpv1QuerySeen
> 0) {
535 IgmpCtrl
->Igmpv1QuerySeen
--;
539 // Decrease the report timer for each IGMP group in "delaying member"
541 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
542 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
543 ASSERT (Group
->DelayTime
>= 0);
545 if (Group
->DelayTime
> 0) {
548 if (Group
->DelayTime
== 0) {
549 Ip4SendIgmpReport (IpSb
, Group
->Address
);
550 Group
->ReportByUs
= TRUE
;
558 Add a group address to the array of group addresses.
559 The caller should make sure that no duplicated address
560 existed in the array. Although the function doesn't
561 assume the byte order of the both Source and Addr, the
562 network byte order is used by the caller.
564 @param Source The array of group addresses to add to
565 @param Count The number of group addresses in the Source
566 @param Addr The IP4 multicast address to add
568 @return NULL if failed to allocate memory for the new groups,
569 @return otherwise the new combined group addresses.
581 Groups
= AllocatePool (sizeof (IP4_ADDR
) * (Count
+ 1));
583 if (Groups
== NULL
) {
587 CopyMem (Groups
, Source
, Count
* sizeof (IP4_ADDR
));
588 Groups
[Count
] = Addr
;
595 Remove a group address frome the array of group addresses.
596 Although the function doesn't assume the byte order of the
597 both Groups and Addr, the network byte order is used by
600 @param Groups The array of group addresses to remove from
601 @param Count The number of group addresses in the Groups
602 @param Addr The IP4 multicast address to remove
604 @return The nubmer of group addresses in the Groups after remove.
605 @return It is Count if the Addr isn't in the Groups.
617 for (Index
= 0; Index
< Count
; Index
++) {
618 if (Groups
[Index
] == Addr
) {
623 while (Index
< Count
- 1) {
624 Groups
[Index
] = Groups
[Index
+ 1];