2 This file implements the RFC2236: IGMP v2.
4 Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
12 // Route Alert option in IGMP report to direct routers to
13 // examine the packet more closely.
15 UINT32 mRouteAlertOption
= 0x00000494;
18 Init the IGMP control data of the IP4 service instance, configure
19 MNP to receive ALL SYSTEM multicast.
21 @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.
23 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
24 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
25 @retval Others Failed to initialize the IGMP of IpSb.
30 IN OUT IP4_SERVICE
*IpSb
33 IGMP_SERVICE_DATA
*IgmpCtrl
;
34 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
38 IgmpCtrl
= &IpSb
->IgmpCtrl
;
41 // Configure MNP to receive ALL_SYSTEM multicast
43 Group
= AllocatePool (sizeof (IGMP_GROUP
));
46 return EFI_OUT_OF_RESOURCES
;
51 Group
->Address
= IP4_ALLSYSTEM_ADDRESS
;
54 Group
->ReportByUs
= FALSE
;
56 Status
= Ip4GetMulticastMac (Mnp
, IP4_ALLSYSTEM_ADDRESS
, &Group
->Mac
);
58 if (EFI_ERROR (Status
)) {
62 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
64 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
68 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
77 Find the IGMP_GROUP structure which contains the status of multicast
78 group Address in this IGMP control block
80 @param[in] IgmpCtrl The IGMP control block to search from.
81 @param[in] Address The multicast address to search.
83 @return NULL if the multicast address isn't in the IGMP control block. Otherwise
84 the point to the IGMP_GROUP which contains the status of multicast group
90 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
97 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
98 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
100 if (Group
->Address
== Address
) {
109 Count the number of IP4 multicast groups that are mapped to the
110 same MAC address. Several IP4 multicast address may be mapped to
111 the same MAC address.
113 @param[in] IgmpCtrl The IGMP control block to search in.
114 @param[in] Mac The MAC address to search.
116 @return The number of the IP4 multicast group that mapped to the same
122 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
123 IN EFI_MAC_ADDRESS
*Mac
132 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
133 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
135 if (NET_MAC_EQUAL (&Group
->Mac
, Mac
, sizeof (EFI_MAC_ADDRESS
))) {
144 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
146 @param[in] IpSb The IP4 service instance that requests the
148 @param[in] Dst The destination to send to.
149 @param[in] Type The IGMP message type, such as IGMP v1 membership
151 @param[in] Group The group address in the IGMP message head.
153 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
154 @retval EFI_SUCCESS The IGMP message is successfully send.
155 @retval Others Failed to send the IGMP message.
160 IN IP4_SERVICE
*IpSb
,
171 // Allocate a net buffer to hold the message
173 Packet
= NetbufAlloc (IP4_MAX_HEADLEN
+ sizeof (IGMP_HEAD
));
175 if (Packet
== NULL
) {
176 return EFI_OUT_OF_RESOURCES
;
180 // Fill in the IGMP and IP header, then transmit the message
182 NetbufReserve (Packet
, IP4_MAX_HEADLEN
);
184 Igmp
= (IGMP_HEAD
*)NetbufAllocSpace (Packet
, sizeof (IGMP_HEAD
), FALSE
);
186 return EFI_OUT_OF_RESOURCES
;
190 Igmp
->MaxRespTime
= 0;
192 Igmp
->Group
= HTONL (Group
);
193 Igmp
->Checksum
= (UINT16
)(~NetblockChecksum ((UINT8
*)Igmp
, sizeof (IGMP_HEAD
)));
196 Head
.Protocol
= IP4_PROTO_IGMP
;
200 Head
.Src
= IP4_ALLZERO_ADDRESS
;
207 (UINT8
*)&mRouteAlertOption
,
216 Send an IGMP membership report. Depends on whether the server is
217 v1 or v2, it will send either a V1 or V2 membership report.
219 @param[in] IpSb The IP4 service instance that requests the
221 @param[in] Group The group address to report.
223 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
224 @retval EFI_SUCCESS The IGMP report message is successfully send.
225 @retval Others Failed to send the report.
230 IN IP4_SERVICE
*IpSb
,
234 if (IpSb
->IgmpCtrl
.Igmpv1QuerySeen
!= 0) {
235 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V1_MEMBERSHIP_REPORT
, Group
);
237 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V2_MEMBERSHIP_REPORT
, Group
);
242 Join the multicast group on behalf of this IP4 child
244 @param[in] IpInstance The IP4 child that wants to join the group.
245 @param[in] Address The group to join.
247 @retval EFI_SUCCESS Successfully join the multicast group.
248 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
249 @retval Others Failed to join the multicast group.
254 IN IP4_PROTOCOL
*IpInstance
,
258 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
260 IGMP_SERVICE_DATA
*IgmpCtrl
;
264 IpSb
= IpInstance
->Service
;
265 IgmpCtrl
= &IpSb
->IgmpCtrl
;
269 // If the IP service already is a member in the group, just
270 // increase the reference count and return.
272 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
280 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
281 // send a report, then direct MNP to receive the multicast.
283 Group
= AllocatePool (sizeof (IGMP_GROUP
));
286 return EFI_OUT_OF_RESOURCES
;
289 Group
->Address
= Address
;
291 Group
->DelayTime
= IGMP_UNSOLICIATED_REPORT
;
292 Group
->ReportByUs
= TRUE
;
294 Status
= Ip4GetMulticastMac (Mnp
, Address
, &Group
->Mac
);
296 if (EFI_ERROR (Status
)) {
300 Status
= Ip4SendIgmpReport (IpSb
, Address
);
302 if (EFI_ERROR (Status
)) {
306 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
308 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
312 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
321 Leave the IP4 multicast group on behalf of IpInstance.
323 @param[in] IpInstance The IP4 child that wants to leave the group
325 @param[in] Address The group address to leave.
327 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group.
328 @retval EFI_SUCCESS Successfully leave the multicast group.
329 @retval Others Failed to leave the multicast group.
334 IN IP4_PROTOCOL
*IpInstance
,
338 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
340 IGMP_SERVICE_DATA
*IgmpCtrl
;
344 IpSb
= IpInstance
->Service
;
345 IgmpCtrl
= &IpSb
->IgmpCtrl
;
348 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
351 return EFI_NOT_FOUND
;
355 // If more than one instance is in the group, decrease
356 // the RefCnt then return.
358 if (--Group
->RefCnt
> 0) {
363 // If multiple IP4 group addresses are mapped to the same
364 // multicast MAC address, don't configure the MNP to leave
367 if (Ip4FindMac (IgmpCtrl
, &Group
->Mac
) == 1) {
368 Status
= Mnp
->Groups (Mnp
, FALSE
, &Group
->Mac
);
370 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_FOUND
)) {
376 // Send a leave report if the membership is reported by us
377 // and we are talking IGMPv2.
379 if (Group
->ReportByUs
&& (IgmpCtrl
->Igmpv1QuerySeen
== 0)) {
380 Ip4SendIgmpMessage (IpSb
, IP4_ALLROUTER_ADDRESS
, IGMP_LEAVE_GROUP
, Group
->Address
);
383 RemoveEntryList (&Group
->Link
);
390 Handle the received IGMP message for the IP4 service instance.
392 @param[in] IpSb The IP4 service instance that received the message.
393 @param[in] Head The IP4 header of the received message.
394 @param[in] Packet The IGMP message, without IP4 header.
396 @retval EFI_INVALID_PARAMETER The IGMP message is malformatted.
397 @retval EFI_SUCCESS The IGMP message is successfully processed.
402 IN IP4_SERVICE
*IpSb
,
407 IGMP_SERVICE_DATA
*IgmpCtrl
;
413 IgmpCtrl
= &IpSb
->IgmpCtrl
;
416 // Must checksum over the whole packet, later IGMP version
417 // may employ message longer than 8 bytes. IP's header has
418 // already been trimmed off.
420 if ((Packet
->TotalSize
< sizeof (Igmp
)) || (NetbufChecksum (Packet
) != 0)) {
422 return EFI_INVALID_PARAMETER
;
426 // Copy the packet in case it is fragmented
428 NetbufCopy (Packet
, 0, sizeof (IGMP_HEAD
), (UINT8
*)&Igmp
);
431 case IGMP_MEMBERSHIP_QUERY
:
433 // If MaxRespTime is zero, it is most likely that we are
434 // talking to a V1 router
436 if (Igmp
.MaxRespTime
== 0) {
437 IgmpCtrl
->Igmpv1QuerySeen
= IGMP_V1ROUTER_PRESENT
;
438 Igmp
.MaxRespTime
= 100;
442 // Igmp is ticking once per second but MaxRespTime is in
443 // the unit of 100ms.
445 Igmp
.MaxRespTime
/= 10;
446 Address
= NTOHL (Igmp
.Group
);
448 if (Address
== IP4_ALLSYSTEM_ADDRESS
) {
452 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
453 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
456 // If address is all zero, all the memberships will be reported.
457 // otherwise only one is reported.
459 if ((Address
== IP4_ALLZERO_ADDRESS
) || (Address
== Group
->Address
)) {
461 // If the timer is pending, only update it if the time left
462 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
464 if ((Group
->DelayTime
== 0) || (Group
->DelayTime
> Igmp
.MaxRespTime
)) {
465 Group
->DelayTime
= MAX (1, Igmp
.MaxRespTime
);
472 case IGMP_V1_MEMBERSHIP_REPORT
:
473 case IGMP_V2_MEMBERSHIP_REPORT
:
474 Address
= NTOHL (Igmp
.Group
);
475 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
477 if ((Group
!= NULL
) && (Group
->DelayTime
> 0)) {
478 Group
->DelayTime
= 0;
479 Group
->ReportByUs
= FALSE
;
490 The periodical timer function for IGMP. It does the following
492 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
493 the IGMP server type.
494 2. Decrease the report timer for each IGMP group in "delaying
497 @param[in] IpSb The IP4 service instance that is ticking.
505 IGMP_SERVICE_DATA
*IgmpCtrl
;
509 IgmpCtrl
= &IpSb
->IgmpCtrl
;
511 if (IgmpCtrl
->Igmpv1QuerySeen
> 0) {
512 IgmpCtrl
->Igmpv1QuerySeen
--;
516 // Decrease the report timer for each IGMP group in "delaying member"
518 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
519 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
520 ASSERT (Group
->DelayTime
>= 0);
522 if (Group
->DelayTime
> 0) {
525 if (Group
->DelayTime
== 0) {
526 Ip4SendIgmpReport (IpSb
, Group
->Address
);
527 Group
->ReportByUs
= TRUE
;
534 Add a group address to the array of group addresses.
535 The caller should make sure that no duplicated address
536 existed in the array. Although the function doesn't
537 assume the byte order of the both Source and Addr, the
538 network byte order is used by the caller.
540 @param[in] Source The array of group addresses to add to.
541 @param[in] Count The number of group addresses in the Source.
542 @param[in] Addr The IP4 multicast address to add.
544 @return NULL if failed to allocate memory for the new groups,
545 otherwise the new combined group addresses.
557 Groups
= AllocatePool (sizeof (IP4_ADDR
) * (Count
+ 1));
559 if (Groups
== NULL
) {
563 CopyMem (Groups
, Source
, Count
* sizeof (IP4_ADDR
));
564 Groups
[Count
] = Addr
;
570 Remove a group address from the array of group addresses.
571 Although the function doesn't assume the byte order of the
572 both Groups and Addr, the network byte order is used by
575 @param Groups The array of group addresses to remove from.
576 @param Count The number of group addresses in the Groups.
577 @param Addr The IP4 multicast address to remove.
579 @return The number of group addresses in the Groups after remove.
580 It is Count if the Addr isn't in the Groups.
585 IN OUT IP4_ADDR
*Groups
,
592 for (Index
= 0; Index
< Count
; Index
++) {
593 if (Groups
[Index
] == Addr
) {
598 while (Index
< Count
- 1) {
599 Groups
[Index
] = Groups
[Index
+ 1];