28
28
* @brief Implements the optional IGMP functionality of the FreeRTOS+TCP network stack.
29
29
*/
30
30
31
- /* ToDo List ( remove the items below as progress is made )
31
+ /* ToDo List ( remove the items below as progress is made )
32
32
* - Rename this file
33
33
* - netif: netif-pointer in the setsockopt struct, null means "all interfaces"
34
34
* - Check task to task multicast ( maybe the network driver can handle to loop
35
35
* - Rework the sockopt structures to be the same and use IP_Address_t or IPv46_Address_t
36
36
* - 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!
38
42
* Topics to discuss over email or in a conference call:
39
43
* - Integration with other hardware. For now, only SAME70 target has the proper functions for receive multicasts.
40
44
* - Is task to task multicast really needed? In order to get that feature, we need code that handles every outgoing
41
45
* 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
43
47
* 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.
45
49
*/
46
50
47
51
/* Standard includes. */
@@ -654,7 +658,7 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
654
658
{
655
659
uint8_t MCastMacBytes [ 6 ];
656
660
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 ;
658
662
659
663
if ( pxSocket -> bits .bIsIPv6 == pdTRUE_UNSIGNED )
660
664
{
@@ -725,7 +729,10 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
725
729
prvRemoveMulticastReportFromList ( & ( pxSocket -> u .xUDP .xMulticastAddress ), ( UBaseType_t ) pxSocket -> bits .bIsIPv6 );
726
730
}
727
731
732
+ /* Invalidate the multicast group address to prevent erroneous matches if someone calls
733
+ * FREERTOS_SO_IP_DROP_MEMBERSHIP multiple times. */
728
734
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. */
729
736
}
730
737
731
738
/**
@@ -734,21 +741,22 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
734
741
* @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for.
735
742
* @param[in] bAction: eSocketOptAddMembership or eSocketOptDropMembership.
736
743
*/
737
- void vModifyMulticastMembership ( MCastGroupDesc_t * pxMulticastGroup ,
744
+ void vModifyMulticastMembership ( MulticastAction_t * pxMulticastAction ,
738
745
uint8_t bAction )
739
746
{
740
- if ( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
741
- {
742
- return ;
743
- }
744
-
745
747
uint8_t MCastMacBytes [ 6 ];
746
- FreeRTOS_Socket_t * pxSocket = pxMulticastGroup -> pxSocket ;
748
+ FreeRTOS_Socket_t * pxSocket = pxMulticastAction -> pxSocket ;
747
749
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 ;
749
752
750
753
configASSERT ( pxSocket != NULL );
751
754
755
+ if ( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
756
+ {
757
+ return ;
758
+ }
759
+
752
760
/* This TCP stack does NOT support sockets subscribing to more than one multicast group.
753
761
* If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the
754
762
* IGMP/MLD reports corresponding to that group address. */
@@ -757,15 +765,15 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
757
765
if ( eSocketOptAddMembership == bAction )
758
766
{
759
767
/* 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 ) );
761
769
762
- if ( pxMulticastGroup -> xMulticastGroup . xIs_IPv6 == pdFALSE )
770
+ if ( pxSocket -> bits . bIsIPv6 == pdFALSE )
763
771
{
764
- vSetMultiCastIPv4MacAddress ( pxMulticastGroup -> xMulticastGroup . xIPAddress .ulIP_IPv4 , MCastMacBytes );
772
+ vSetMultiCastIPv4MacAddress ( pxMulticastAction -> xMulticastGroup .ulIP_IPv4 , MCastMacBytes );
765
773
}
766
774
else
767
775
{
768
- vSetMultiCastIPv6MacAddress ( & ( pxMulticastGroup -> xMulticastGroup . xIPAddress .xIP_IPv6 ), MCastMacBytes );
776
+ vSetMultiCastIPv6MacAddress ( & ( pxMulticastAction -> xMulticastGroup .xIP_IPv6 ), MCastMacBytes );
769
777
}
770
778
771
779
/* Inform the network driver */
@@ -789,30 +797,65 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
789
797
}
790
798
}
791
799
800
+ /* Remember which interface(s) this socket is subscribed on. */
801
+ pxSocket -> u .xUDP .pxMulticastNetIf = pxMulticastAction -> pxInterface ;
802
+
792
803
/* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report
793
804
* for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist.
794
805
* 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
798
809
{
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
+ }
800
814
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 } };
802
820
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
804
835
{
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
+ }
810
842
}
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 ;
811
854
}
812
855
}
813
856
814
857
/* Free the message that was sent to us. */
815
- vPortFree ( pxMulticastGroup );
858
+ vPortFree ( pxMulticastAction );
816
859
}
817
860
818
861
static portBASE_TYPE xSendIGMP ( uint32_t uiBlockTime ,
0 commit comments