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;
19 Init the IGMP control data of the IP4 service instance, configure
20 MNP to receive ALL SYSTEM multicast.
22 @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.
24 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.
25 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.
26 @retval Others Failed to initialize the IGMP of IpSb.
31 IN OUT IP4_SERVICE
*IpSb
34 IGMP_SERVICE_DATA
*IgmpCtrl
;
35 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
39 IgmpCtrl
= &IpSb
->IgmpCtrl
;
42 // Configure MNP to receive ALL_SYSTEM multicast
44 Group
= AllocatePool (sizeof (IGMP_GROUP
));
47 return EFI_OUT_OF_RESOURCES
;
52 Group
->Address
= IP4_ALLSYSTEM_ADDRESS
;
55 Group
->ReportByUs
= FALSE
;
57 Status
= Ip4GetMulticastMac (Mnp
, IP4_ALLSYSTEM_ADDRESS
, &Group
->Mac
);
59 if (EFI_ERROR (Status
)) {
63 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
65 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
69 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
79 Find the IGMP_GROUP structure which contains the status of multicast
80 group Address in this IGMP control block
82 @param[in] IgmpCtrl The IGMP control block to search from.
83 @param[in] Address The multicast address to search.
85 @return NULL if the multicast address isn't in the IGMP control block. Otherwise
86 the point to the IGMP_GROUP which contains the status of multicast group
92 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
99 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
100 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
102 if (Group
->Address
== Address
) {
112 Count the number of IP4 multicast groups that are mapped to the
113 same MAC address. Several IP4 multicast address may be mapped to
114 the same MAC address.
116 @param[in] IgmpCtrl The IGMP control block to search in.
117 @param[in] Mac The MAC address to search.
119 @return The number of the IP4 multicast group that mapped to the same
125 IN IGMP_SERVICE_DATA
*IgmpCtrl
,
126 IN EFI_MAC_ADDRESS
*Mac
135 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
136 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
138 if (NET_MAC_EQUAL (&Group
->Mac
, Mac
, sizeof (EFI_MAC_ADDRESS
))) {
148 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.
150 @param[in] IpSb The IP4 service instance that requests the
152 @param[in] Dst The destinaton to send to.
153 @param[in] Type The IGMP message type, such as IGMP v1 membership
155 @param[in] Group The group address in the IGMP message head.
157 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
158 @retval EFI_SUCCESS The IGMP message is successfully send.
159 @retval Others Failed to send the IGMP message.
164 IN IP4_SERVICE
*IpSb
,
175 // Allocate a net buffer to hold the message
177 Packet
= NetbufAlloc (IP4_MAX_HEADLEN
+ sizeof (IGMP_HEAD
));
179 if (Packet
== NULL
) {
180 return EFI_OUT_OF_RESOURCES
;
184 // Fill in the IGMP and IP header, then transmit the message
186 NetbufReserve (Packet
, IP4_MAX_HEADLEN
);
188 Igmp
= (IGMP_HEAD
*) NetbufAllocSpace (Packet
, sizeof (IGMP_HEAD
), FALSE
);
190 return EFI_OUT_OF_RESOURCES
;
194 Igmp
->MaxRespTime
= 0;
196 Igmp
->Group
= HTONL (Group
);
197 Igmp
->Checksum
= (UINT16
) (~NetblockChecksum ((UINT8
*) Igmp
, sizeof (IGMP_HEAD
)));
200 Head
.Protocol
= IP4_PROTO_IGMP
;
204 Head
.Src
= IP4_ALLZERO_ADDRESS
;
211 (UINT8
*) &mRouteAlertOption
,
221 Send an IGMP membership report. Depends on whether the server is
222 v1 or v2, it will send either a V1 or V2 membership report.
224 @param[in] IpSb The IP4 service instance that requests the
226 @param[in] Group The group address to report.
228 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message.
229 @retval EFI_SUCCESS The IGMP report message is successfully send.
230 @retval Others Failed to send the report.
235 IN IP4_SERVICE
*IpSb
,
239 if (IpSb
->IgmpCtrl
.Igmpv1QuerySeen
!= 0) {
240 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V1_MEMBERSHIP_REPORT
, Group
);
242 return Ip4SendIgmpMessage (IpSb
, Group
, IGMP_V2_MEMBERSHIP_REPORT
, Group
);
248 Join the multicast group on behalf of this IP4 child
250 @param[in] IpInstance The IP4 child that wants to join the group.
251 @param[in] Address The group to join.
253 @retval EFI_SUCCESS Successfully join the multicast group.
254 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources.
255 @retval Others Failed to join the multicast group.
260 IN IP4_PROTOCOL
*IpInstance
,
264 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
266 IGMP_SERVICE_DATA
*IgmpCtrl
;
270 IpSb
= IpInstance
->Service
;
271 IgmpCtrl
= &IpSb
->IgmpCtrl
;
275 // If the IP service already is a member in the group, just
276 // increase the refernce count and return.
278 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
286 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,
287 // send a report, then direct MNP to receive the multicast.
289 Group
= AllocatePool (sizeof (IGMP_GROUP
));
292 return EFI_OUT_OF_RESOURCES
;
295 Group
->Address
= Address
;
297 Group
->DelayTime
= IGMP_UNSOLICIATED_REPORT
;
298 Group
->ReportByUs
= TRUE
;
300 Status
= Ip4GetMulticastMac (Mnp
, Address
, &Group
->Mac
);
302 if (EFI_ERROR (Status
)) {
306 Status
= Ip4SendIgmpReport (IpSb
, Address
);
308 if (EFI_ERROR (Status
)) {
312 Status
= Mnp
->Groups (Mnp
, TRUE
, &Group
->Mac
);
314 if (EFI_ERROR (Status
) && (Status
!= EFI_ALREADY_STARTED
)) {
318 InsertHeadList (&IgmpCtrl
->Groups
, &Group
->Link
);
328 Leave the IP4 multicast group on behalf of IpInstance.
330 @param[in] IpInstance The IP4 child that wants to leave the group
332 @param[in] Address The group address to leave.
334 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group.
335 @retval EFI_SUCCESS Successfully leave the multicast group.
336 @retval Others Failed to leave the multicast group.
341 IN IP4_PROTOCOL
*IpInstance
,
345 EFI_MANAGED_NETWORK_PROTOCOL
*Mnp
;
347 IGMP_SERVICE_DATA
*IgmpCtrl
;
351 IpSb
= IpInstance
->Service
;
352 IgmpCtrl
= &IpSb
->IgmpCtrl
;
355 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
358 return EFI_NOT_FOUND
;
362 // If more than one instance is in the group, decrease
363 // the RefCnt then return.
365 if (--Group
->RefCnt
> 0) {
370 // If multiple IP4 group addresses are mapped to the same
371 // multicast MAC address, don't configure the MNP to leave
374 if (Ip4FindMac (IgmpCtrl
, &Group
->Mac
) == 1) {
375 Status
= Mnp
->Groups (Mnp
, FALSE
, &Group
->Mac
);
377 if (EFI_ERROR (Status
) && (Status
!= EFI_NOT_FOUND
)) {
383 // Send a leave report if the membership is reported by us
384 // and we are talking IGMPv2.
386 if (Group
->ReportByUs
&& IgmpCtrl
->Igmpv1QuerySeen
== 0) {
387 Ip4SendIgmpMessage (IpSb
, IP4_ALLROUTER_ADDRESS
, IGMP_LEAVE_GROUP
, Group
->Address
);
390 RemoveEntryList (&Group
->Link
);
398 Handle the received IGMP message for the IP4 service instance.
400 @param[in] IpSb The IP4 service instance that received the message.
401 @param[in] Head The IP4 header of the received message.
402 @param[in] Packet The IGMP message, without IP4 header.
404 @retval EFI_INVALID_PARAMETER The IGMP message is malformatted.
405 @retval EFI_SUCCESS The IGMP message is successfully processed.
410 IN IP4_SERVICE
*IpSb
,
415 IGMP_SERVICE_DATA
*IgmpCtrl
;
421 IgmpCtrl
= &IpSb
->IgmpCtrl
;
424 // Must checksum over the whole packet, later IGMP version
425 // may employ message longer than 8 bytes. IP's header has
426 // already been trimmed off.
428 if ((Packet
->TotalSize
< sizeof (Igmp
)) || (NetbufChecksum (Packet
) != 0)) {
430 return EFI_INVALID_PARAMETER
;
434 // Copy the packet in case it is fragmented
436 NetbufCopy (Packet
, 0, sizeof (IGMP_HEAD
), (UINT8
*)&Igmp
);
439 case IGMP_MEMBERSHIP_QUERY
:
441 // If MaxRespTime is zero, it is most likely that we are
442 // talking to a V1 router
444 if (Igmp
.MaxRespTime
== 0) {
445 IgmpCtrl
->Igmpv1QuerySeen
= IGMP_V1ROUTER_PRESENT
;
446 Igmp
.MaxRespTime
= 100;
450 // Igmp is ticking once per second but MaxRespTime is in
451 // the unit of 100ms.
453 Igmp
.MaxRespTime
/= 10;
454 Address
= NTOHL (Igmp
.Group
);
456 if (Address
== IP4_ALLSYSTEM_ADDRESS
) {
460 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
461 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
464 // If address is all zero, all the memberships will be reported.
465 // otherwise only one is reported.
467 if ((Address
== IP4_ALLZERO_ADDRESS
) || (Address
== Group
->Address
)) {
469 // If the timer is pending, only update it if the time left
470 // is longer than the MaxRespTime. TODO: randomize the DelayTime.
472 if ((Group
->DelayTime
== 0) || (Group
->DelayTime
> Igmp
.MaxRespTime
)) {
473 Group
->DelayTime
= MAX (1, Igmp
.MaxRespTime
);
480 case IGMP_V1_MEMBERSHIP_REPORT
:
481 case IGMP_V2_MEMBERSHIP_REPORT
:
482 Address
= NTOHL (Igmp
.Group
);
483 Group
= Ip4FindGroup (IgmpCtrl
, Address
);
485 if ((Group
!= NULL
) && (Group
->DelayTime
> 0)) {
486 Group
->DelayTime
= 0;
487 Group
->ReportByUs
= FALSE
;
499 The periodical timer function for IGMP. It does the following
501 1. Decrease the Igmpv1QuerySeen to make it possible to refresh
502 the IGMP server type.
503 2. Decrease the report timer for each IGMP group in "delaying
506 @param[in] IpSb The IP4 service instance that is ticking.
514 IGMP_SERVICE_DATA
*IgmpCtrl
;
518 IgmpCtrl
= &IpSb
->IgmpCtrl
;
520 if (IgmpCtrl
->Igmpv1QuerySeen
> 0) {
521 IgmpCtrl
->Igmpv1QuerySeen
--;
525 // Decrease the report timer for each IGMP group in "delaying member"
527 NET_LIST_FOR_EACH (Entry
, &IgmpCtrl
->Groups
) {
528 Group
= NET_LIST_USER_STRUCT (Entry
, IGMP_GROUP
, Link
);
529 ASSERT (Group
->DelayTime
>= 0);
531 if (Group
->DelayTime
> 0) {
534 if (Group
->DelayTime
== 0) {
535 Ip4SendIgmpReport (IpSb
, Group
->Address
);
536 Group
->ReportByUs
= TRUE
;
544 Add a group address to the array of group addresses.
545 The caller should make sure that no duplicated address
546 existed in the array. Although the function doesn't
547 assume the byte order of the both Source and Addr, the
548 network byte order is used by the caller.
550 @param[in] Source The array of group addresses to add to.
551 @param[in] Count The number of group addresses in the Source.
552 @param[in] Addr The IP4 multicast address to add.
554 @return NULL if failed to allocate memory for the new groups,
555 otherwise the new combined group addresses.
567 Groups
= AllocatePool (sizeof (IP4_ADDR
) * (Count
+ 1));
569 if (Groups
== NULL
) {
573 CopyMem (Groups
, Source
, Count
* sizeof (IP4_ADDR
));
574 Groups
[Count
] = Addr
;
581 Remove a group address from the array of group addresses.
582 Although the function doesn't assume the byte order of the
583 both Groups and Addr, the network byte order is used by
586 @param Groups The array of group addresses to remove from.
587 @param Count The number of group addresses in the Groups.
588 @param Addr The IP4 multicast address to remove.
590 @return The nubmer of group addresses in the Groups after remove.
591 It is Count if the Addr isn't in the Groups.
596 IN OUT IP4_ADDR
*Groups
,
603 for (Index
= 0; Index
< Count
; Index
++) {
604 if (Groups
[Index
] == Addr
) {
609 while (Index
< Count
- 1) {
610 Groups
[Index
] = Groups
[Index
+ 1];