2828 * @brief Implements the optional IGMP functionality of the FreeRTOS+TCP network stack.
2929 */
3030
31- /* ToDo List ( remove the items below as progress is made )
31+ /* ToDo List ( remove the items below as progress is made )
3232 * - Rename this file
3333 * - netif: netif-pointer in the setsockopt struct, null means "all interfaces"
3434 * - Check task to task multicast ( maybe the network driver can handle to loop
3535 * - Rework the sockopt structures to be the same and use IP_Address_t or IPv46_Address_t
3636 * - Write a demo and add to https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS-Plus/Demo
37- * - Sockets cannot handle v4 and v6 at the same time, so enforce this for setsockopt multicast calls
37+ * - Sockets cannot handle v4 and v6 at the same time, so enforce this for setsockopt multicast calls
38+ * - Documentation: Caution about calling FREERTOS_SO_IP_ADD_MEMBERSHIP followed by FREERTOS_SO_IP_DROP_MEMBERSHIP
39+ * in close succession. The DROP may fail because the IP task hasn't handled the ADD yet.
40+ * - Documentation: The values used for FREERTOS_SO_IP_ADD_MEMBERSHIP and FREERTOS_SO_IP_DROP_MEMBERSHIP
41+ * must be exactly the same. This includes the interface pointer!
3842 * Topics to discuss over email or in a conference call:
3943 * - Integration with other hardware. For now, only SAME70 target has the proper functions for receive multicasts.
4044 * - Is task to task multicast really needed? In order to get that feature, we need code that handles every outgoing
4145 * multicast as if it were an incoming packet and possibly duplicates it. I don't think this functionality is
42- * really needed and this may only be needed if we want a send/receive demo to work on a single device. Maybe
46+ * really needed and this may only be needed if we want a send/receive demo to work on a single device. Maybe
4347 * it's better to have a demo that sends to multicast_A and receives multicast_B and then have a PC-based
44- * python script that does the opposite to complete the demo application.
48+ * python script that does the opposite to complete the demo application.
4549 */
4650
4751/* Standard includes. */
@@ -654,7 +658,7 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
654658{
655659 uint8_t MCastMacBytes [ 6 ];
656660 UBaseType_t uxLeaveGroup = pdFALSE_UNSIGNED ;
657- NetworkInterface_t * pxNetIf = ( pxSocket -> pxEndPoint != NULL && pxSocket -> pxEndPoint -> pxNetworkInterface != NULL ) ? pxSocket -> pxEndPoint -> pxNetworkInterface : NULL ;
661+ NetworkInterface_t * pxNetIf = pxSocket -> u . xUDP . pxMulticastNetIf ;
658662
659663 if ( pxSocket -> bits .bIsIPv6 == pdTRUE_UNSIGNED )
660664 {
@@ -725,7 +729,10 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
725729 prvRemoveMulticastReportFromList ( & ( pxSocket -> u .xUDP .xMulticastAddress ), ( UBaseType_t ) pxSocket -> bits .bIsIPv6 );
726730 }
727731
732+ /* Invalidate the multicast group address to prevent erroneous matches if someone calls
733+ * FREERTOS_SO_IP_DROP_MEMBERSHIP multiple times. */
728734 memset ( & pxSocket -> u .xUDP .xMulticastAddress , 0x00 , sizeof ( pxSocket -> u .xUDP .xMulticastAddress ) );
735+ pxSocket -> u .xUDP .pxMulticastNetIf = NULL ; /* not really needed, but just looks cleaner when debugging. */
729736}
730737
731738/**
@@ -734,21 +741,22 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
734741 * @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for.
735742 * @param[in] bAction: eSocketOptAddMembership or eSocketOptDropMembership.
736743 */
737- void vModifyMulticastMembership ( MCastGroupDesc_t * pxMulticastGroup ,
744+ void vModifyMulticastMembership ( MulticastAction_t * pxMulticastAction ,
738745 uint8_t bAction )
739746{
740- if ( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
741- {
742- return ;
743- }
744-
745747 uint8_t MCastMacBytes [ 6 ];
746- FreeRTOS_Socket_t * pxSocket = pxMulticastGroup -> pxSocket ;
748+ FreeRTOS_Socket_t * pxSocket = pxMulticastAction -> pxSocket ;
747749 uint8_t bFreeMatchedItem = pdFALSE ;
748- NetworkInterface_t * pxNetIf = ( pxSocket -> pxEndPoint != NULL && pxSocket -> pxEndPoint -> pxNetworkInterface != NULL ) ? pxSocket -> pxEndPoint -> pxNetworkInterface : NULL ;
750+ NetworkInterface_t * pxNetIf = pxMulticastAction -> pxInterface ;
751+ BaseType_t bReportItemConsumed = pdFALSE ;
749752
750753 configASSERT ( pxSocket != NULL );
751754
755+ if ( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
756+ {
757+ return ;
758+ }
759+
752760 /* This TCP stack does NOT support sockets subscribing to more than one multicast group.
753761 * If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the
754762 * IGMP/MLD reports corresponding to that group address. */
@@ -757,15 +765,15 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
757765 if ( eSocketOptAddMembership == bAction )
758766 {
759767 /* Store the multicast address. */
760- ( void ) memcpy ( & ( pxSocket -> u .xUDP .xMulticastAddress ), & ( pxMulticastGroup -> xMulticastGroup . xIPAddress ), sizeof ( pxSocket -> u .xUDP .xMulticastAddress ) );
768+ ( void ) memcpy ( & ( pxSocket -> u .xUDP .xMulticastAddress ), & ( pxMulticastAction -> xMulticastGroup ), sizeof ( pxSocket -> u .xUDP .xMulticastAddress ) );
761769
762- if ( pxMulticastGroup -> xMulticastGroup . xIs_IPv6 == pdFALSE )
770+ if ( pxSocket -> bits . bIsIPv6 == pdFALSE )
763771 {
764- vSetMultiCastIPv4MacAddress ( pxMulticastGroup -> xMulticastGroup . xIPAddress .ulIP_IPv4 , MCastMacBytes );
772+ vSetMultiCastIPv4MacAddress ( pxMulticastAction -> xMulticastGroup .ulIP_IPv4 , MCastMacBytes );
765773 }
766774 else
767775 {
768- vSetMultiCastIPv6MacAddress ( & ( pxMulticastGroup -> xMulticastGroup . xIPAddress .xIP_IPv6 ), MCastMacBytes );
776+ vSetMultiCastIPv6MacAddress ( & ( pxMulticastAction -> xMulticastGroup .xIP_IPv6 ), MCastMacBytes );
769777 }
770778
771779 /* Inform the network driver */
@@ -789,30 +797,65 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
789797 }
790798 }
791799
800+ /* Remember which interface(s) this socket is subscribed on. */
801+ pxSocket -> u .xUDP .pxMulticastNetIf = pxMulticastAction -> pxInterface ;
802+
792803 /* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report
793804 * for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist.
794805 * If such an IGMP/MLD report is already present in the list, we will increment it's socket
795- * count and free the report we have here. In either case, the MCastGroupDesc_t that we were
796- * passed, no longer needs to hold a reference to this IGMP report. */
797- if ( pxMulticastGroup -> pxMCastReportData )
806+ * count and free the report we have here. In either case, the MulticastAction_t that we were
807+ * passed, no longer needs to hold a reference to this multicast report. */
808+ do
798809 {
799- /* ToDo: Add and exception for ff02::1 If someone subscribes to it, do not add report. */
810+ if ( pxMulticastAction -> pxMCastReportData == NULL )
811+ {
812+ break ;
813+ }
800814
801- BaseType_t bReportItemConsumed = xAddIGMPReportToList ( pxMulticastGroup -> pxMCastReportData );
815+ if ( pxMulticastAction -> pxMCastReportData -> xMCastGroupAddress .xIs_IPv6 == pdTRUE )
816+ {
817+ /* RFC2710 end of section section 5 and RFC3810 section 6:
818+ * ff02::1 is a special case and we do not send reports for it. */
819+ static const struct xIPv6_Address FreeRTOS_in6addr_allnodes = { { 0xff , 0x02 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 } };
802820
803- if ( pdTRUE != bReportItemConsumed )
821+ if ( memcmp ( pxMulticastAction -> pxMCastReportData -> xMCastGroupAddress .xIPAddress .xIP_IPv6 .ucBytes , FreeRTOS_in6addr_allnodes .ucBytes , sizeof ( IPv6_Address_t ) ) == 0 )
822+ {
823+ break ;
824+ }
825+
826+ /* RFC2710 end of section section 5 and RFC3810 section 6:
827+ * Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local).
828+ * Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/
829+ if ( ( pxMulticastAction -> pxMCastReportData -> xMCastGroupAddress .xIPAddress .xIP_IPv6 .ucBytes [ 1 ] & 0x0FU ) <= 1 )
830+ {
831+ break ;
832+ }
833+ }
834+ else
804835 {
805- /* If adding to the list did not consume the item that we sent, that means a duplicate
806- * was found and its socket count was incremented instead of adding the item we sent.
807- * Free the item that was passed to us. */
808- vPortFree ( pxMulticastGroup -> pxMCastReportData );
809- pxMulticastGroup -> pxMCastReportData = NULL ;
836+ /* RFC2236 end of section 6:
837+ * 224.0.0.1 is a special case and we do not send reports for it. */
838+ if ( pxMulticastAction -> pxMCastReportData -> xMCastGroupAddress .xIPAddress .ulIP_IPv4 == ipIGMP_IP_ADDR )
839+ {
840+ break ;
841+ }
810842 }
843+
844+ bReportItemConsumed = xAddIGMPReportToList ( pxMulticastAction -> pxMCastReportData );
845+ } while ( pdFALSE );
846+
847+ /* If the report either a special case address or was not consumed by xAddIGMPReportToList() because there was
848+ * a duplicate found and its socket count was incremented instead of adding the report to the global list.
849+ * In either case, free the multicast report. */
850+ if ( bReportItemConsumed == pdFALSE )
851+ {
852+ vPortFree ( pxMulticastAction -> pxMCastReportData );
853+ pxMulticastAction -> pxMCastReportData = NULL ;
811854 }
812855 }
813856
814857 /* Free the message that was sent to us. */
815- vPortFree ( pxMulticastGroup );
858+ vPortFree ( pxMulticastAction );
816859}
817860
818861static portBASE_TYPE xSendIGMP ( uint32_t uiBlockTime ,
0 commit comments