From 9b911f660ae4fe750ac5092901a457458239bb1f Mon Sep 17 00:00:00 2001 From: Emil Popov Date: Mon, 13 Nov 2023 15:29:52 -0500 Subject: [PATCH] Moves more functions to FreeRTOS_Sockets.c Sends IGMPv2 Leave Group messages whenever the last socket subscribed to a group drops that membership. Adds ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL for debug purposes when there is no IGMP/MLD querier --- source/FreeRTOS_IGMP.c | 274 ++++------------------ source/FreeRTOS_IP.c | 8 +- source/FreeRTOS_IP_Timers.c | 4 +- source/FreeRTOS_Sockets.c | 202 ++++++++++++++++ source/include/FreeRTOSIPConfigDefaults.h | 18 ++ source/include/FreeRTOS_IGMP.h | 26 +- source/include/FreeRTOS_IP_Private.h | 11 +- 7 files changed, 288 insertions(+), 255 deletions(-) diff --git a/source/FreeRTOS_IGMP.c b/source/FreeRTOS_IGMP.c index 39eb2fb877..a301c9fc5a 100644 --- a/source/FreeRTOS_IGMP.c +++ b/source/FreeRTOS_IGMP.c @@ -70,7 +70,7 @@ /* Exclude the entire file if multicast support is not enabled. */ /* *INDENT-OFF* */ -#if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) +#if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) /* *INDENT-ON* */ @@ -83,8 +83,9 @@ uint16_t usIPv4_IGMP_Identification = 0; /*-----------------------------------------------------------*/ -static BaseType_t prvSendIGMP_Report_v2( IP_Address_t * pxGroupAddress, - NetworkEndPoint_t * pxEndPoint ); +static BaseType_t prvSendIGMPv2( IP_Address_t * pxGroupAddress, + uint8_t ucMessageType, + NetworkEndPoint_t * pxEndPoint ); static BaseType_t prvSendMLD_Report_v1( IP_Address_t * pxGroupAddress, NetworkEndPoint_t * pxEndPoint ); @@ -92,9 +93,6 @@ void prvScheduleMulticastReports( BaseType_t xIsIPv6, void * pvGroupAddress, uint16_t usMaxRespTime ); -static void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, - UBaseType_t xIsIPv6 ); - static void prvSendMulticastReportOnNetworkInterface( MCastReportData_t * pxMRD, NetworkInterface_t * pxInterface ); @@ -114,7 +112,7 @@ void vIGMP_Init( void ) #endif /* Calculate multicast MAC address that belongs to the IP-address 224.0.0.1 */ - vSetMultiCastIPv4MacAddress( ipIGMP_IP_ADDR, &xIGMP_MacAddress ); + vSetMultiCastIPv4MacAddress( igmpIGMP_IP_ADDR, &xIGMP_MacAddress ); for( pxInterface = FreeRTOS_FirstNetworkInterface(); pxInterface != NULL; @@ -134,7 +132,7 @@ void vIGMP_Init( void ) pxMRD->pxInterface = NULL; pxMRD->xMCastGroupAddress.xIs_IPv6 = pdFALSE_UNSIGNED; pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 = ipLLMNR_IP_ADDR; - BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMRD ); + BaseType_t bReportItemConsumed = xAddMulticastReportToList( pxMRD ); if( pdTRUE != bReportItemConsumed ) { @@ -151,7 +149,7 @@ void vIGMP_Init( void ) pxMRD->pxInterface = NULL; pxMRD->xMCastGroupAddress.xIs_IPv6 = pdTRUE_UNSIGNED; memcpy( ( void * ) &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], &ipLLMNR_IP_ADDR_IPv6.ucBytes[ 0 ], sizeof( IPv6_Address_t ) ); - BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMRD ); + BaseType_t bReportItemConsumed = xAddMulticastReportToList( pxMRD ); if( pdTRUE != bReportItemConsumed ) { @@ -170,7 +168,7 @@ void vIGMP_Init( void ) pxMRD->pxInterface = NULL; pxMRD->xMCastGroupAddress.xIs_IPv6 = pdFALSE_UNSIGNED; pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 = ipMDNS_IP_ADDRESS; - BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMRD ); + BaseType_t bReportItemConsumed = xAddMulticastReportToList( pxMRD ); if( pdTRUE != bReportItemConsumed ) { @@ -187,7 +185,7 @@ void vIGMP_Init( void ) pxMRD->pxInterface = NULL; pxMRD->xMCastGroupAddress.xIs_IPv6 = pdTRUE_UNSIGNED; memcpy( ( void * ) &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], &ipMDNS_IP_ADDR_IPv6.ucBytes[ 0 ], sizeof( IPv6_Address_t ) ); - BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMRD ); + BaseType_t bReportItemConsumed = xAddMulticastReportToList( pxMRD ); if( pdTRUE != bReportItemConsumed ) { @@ -199,7 +197,7 @@ void vIGMP_Init( void ) #endif /* ( ipconfigUSE_IPv6 == 1) ) */ #endif /* ( ipconfigUSE_MDNS == 1) */ - vIGMPTimerReload( pdMS_TO_TICKS( ipIGMP_TIMER_PERIOD_MS ) ); + vIGMPTimerReload( pdMS_TO_TICKS( igmpMULTICAST_EVENT_PERIOD_MS ) ); } #if ( ipconfigUSE_IPv4 == 1 ) @@ -223,7 +221,7 @@ void vIGMP_Init( void ) { pxIGMPPacket = ( IGMPPacket_t * ) pxNetworkBuffer->pucEthernetBuffer; - if( ipIGMP_MEMBERSHIP_QUERY == pxIGMPPacket->xIGMPHeader.ucTypeOfMessage ) + if( igmpIGMP_MEMBERSHIP_QUERY == pxIGMPPacket->xIGMPHeader.ucMessageType ) { prvScheduleMulticastReports( pdFALSE, ( void * ) &( pxIGMPPacket->xIGMPHeader.ulGroupAddress ), ( uint16_t ) pxIGMPPacket->xIGMPHeader.ucMaxResponseTime ); } @@ -313,7 +311,7 @@ void prvScheduleMulticastReports( BaseType_t xIsIPv6, * pxMRD->usMaxRespTime is in milliseconds */ if( xIsIPv6 ) { - usMaxCountdown = usMaxRespTime / ipIGMP_TIMER_PERIOD_MS; + usMaxCountdown = usMaxRespTime / igmpMULTICAST_EVENT_PERIOD_MS; } else { @@ -434,37 +432,37 @@ static void prvSendMulticastReportOnNetworkInterface( MCastReportData_t * pxMRD, if( pxEndPoint->bits.bIPv6 == pdTRUE ) { - /* Only send from the link-local end-point. + /* IPv6 MLD report. + * Only send from the link-local end-point. * https://www.rfc-editor.org/rfc/rfc2710#section-3 * https://www.rfc-editor.org/rfc/rfc3810#section-5 */ /* To-do: if possible, allow sending from "::" as well. https://www.rfc-editor.org/rfc/rfc3810#section-5.2.13 allows it. */ if( xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ) == eIPv6_LinkLocal ) { /* IPv6 MLD Reports. */ - pxMRD->xCountDown = 150; /*-1; */ + pxMRD->xCountDown = ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL; prvSendMLD_Report_v1( &( pxMRD->xMCastGroupAddress.xIPAddress ), pxEndPoint ); break; } } else { - /* Both the end-point and the report are IPv4 */ - - /* https://www.rfc-editor.org/rfc/rfc2236#section-10 suggests that reports + /* IPv4 IGMP report. + * https://www.rfc-editor.org/rfc/rfc2236#section-10 suggests that reports * should be discarded unless one can verify that the source IP belongs to the subnet * of the interface. I have no idea what real life hosts follow that recommendation. */ /* For now at least, don't send reports from 0.0.0.0 */ if( pxEndPoint->ipv4_settings.ulIPAddress != 0 ) { - pxMRD->xCountDown = 150; /*-1; */ - prvSendIGMP_Report_v2( &( pxMRD->xMCastGroupAddress.xIPAddress ), pxEndPoint ); + pxMRD->xCountDown = ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL; + prvSendIGMPv2( &( pxMRD->xMCastGroupAddress.xIPAddress ), igmpIGMP_MEMBERSHIP_REPORT_V2, pxEndPoint ); break; } } } /* end-point for(;;) */ /* If we could not find a suitable end-point on this interface pxMRD->xCountDown will be left at 0 - * and the event code will retry in ipIGMP_TIMER_PERIOD_MS milliseconds. */ + * and the event code will retry in igmpMULTICAST_EVENT_PERIOD_MS milliseconds. */ } void vHandleIGMP_Event( void ) @@ -473,7 +471,6 @@ void vHandleIGMP_Event( void ) const ListItem_t * pxIterator; const ListItem_t * xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); MCastReportData_t * pxMRD; - NetworkInterface_t * pxInterface; for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); pxIterator != ( const ListItem_t * ) xEnd; pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) { @@ -490,6 +487,8 @@ void vHandleIGMP_Event( void ) /* If the interface is NULL, the report should be sent on all interfaces. */ if( pxMRD->pxInterface == NULL ) { + NetworkInterface_t * pxInterface; + /* Go through all interfaces. */ for( pxInterface = FreeRTOS_FirstNetworkInterface(); pxInterface != NULL; @@ -501,7 +500,7 @@ void vHandleIGMP_Event( void ) else { /* The report is assigned to a specific interface */ - prvSendMulticastReportOnNetworkInterface( pxMRD, pxInterface ); + prvSendMulticastReportOnNetworkInterface( pxMRD, pxMRD->pxInterface ); } } else @@ -518,18 +517,18 @@ void vHandleIGMP_Event( void ) } /** - * @brief Create a IGMP event. + * @brief Send an IGMP/MLD event. * * @return pdPASS or pdFAIL, depending on whether xSendEventStructToIPTask() * succeeded. */ -BaseType_t xSendIGMPEvent( void ) +BaseType_t xSendMulticastTimerEvent( void ) { IPStackEvent_t xEventMessage; const TickType_t uxDontBlock = 0U; uintptr_t uxOption = 0U; - xEventMessage.eEventType = eIGMPEvent; + xEventMessage.eEventType = eMulticastTimerEvent; xEventMessage.pvData = ( void * ) uxOption; return xSendEventStructToIPTask( &xEventMessage, uxDontBlock ); @@ -541,8 +540,8 @@ BaseType_t xSendIGMPEvent( void ) * * @param[in] pMCastGroup: The multicast group descriptor the specifies the group and interface. */ -void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, - UBaseType_t uxIsIPv6 ) +void vRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, + UBaseType_t uxIsIPv6 ) { const ListItem_t * pxIterator; const ListItem_t * xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); @@ -580,6 +579,14 @@ void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, if( 0 == pxIRD->xNumSockets ) { + if( pxIRD->xMCastGroupAddress.xIs_IPv6 ) + { + } + else + { + prvSendIGMPv2( &( pxIRD->xMCastGroupAddress.xIPAddress ), igmpIGMP_LEAVE_GROUP, FreeRTOS_FirstEndPoint( NULL ) ); + } + /* ToDo: Maybe here and now is the right time to schedule a "Leave" message and free this item when the message gets sent. */ ( void ) uxListRemove( &pxIRD->xListItem ); vPortFree( pxIRD ); @@ -592,7 +599,7 @@ void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, * * @param[in] pNewEntry: The multicast group descriptor to search for. */ -BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ) +BaseType_t xAddMulticastReportToList( MCastReportData_t * pNewEntry ) { const ListItem_t * pxIterator; const ListItem_t * xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); @@ -658,210 +665,11 @@ BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ) return ( bFoundDuplicate == pdTRUE_UNSIGNED ) ? pdFALSE : pdTRUE; } -/* ToDo: move it to Sockets.c and then can really be private. */ -void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ) -{ - uint8_t MCastMacBytes[ 6 ]; - UBaseType_t uxLeaveGroup = pdFALSE_UNSIGNED; - NetworkInterface_t * pxNetIf = pxSocket->u.xUDP.pxMulticastNetIf; - - if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED ) - { - /* IPv6 */ - if( xIsIPv6AllowedMulticast( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ) ) ) - { - uxLeaveGroup = pdTRUE_UNSIGNED; - - if( pxNetIf ) - { - if( pxNetIf->pfRemoveMulticastMAC != NULL ) - { - vSetMultiCastIPv6MacAddress( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ), MCastMacBytes ); - pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); - } - } - else - { - } - } - else - { - /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valud multicast group - * or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group. - * Do nothing. */ - } - } - else - { - /* IPv4 */ - if( xIsIPv4Multicast( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 ) ) - { - uxLeaveGroup = pdTRUE_UNSIGNED; - - vSetMultiCastIPv4MacAddress( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4, MCastMacBytes ); - - if( pxNetIf ) - { - if( pxNetIf->pfRemoveMulticastMAC != NULL ) - { - pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); - } - } - else - { - /* pxNetIf is NULL. For IPv4 that means "all interfaces", so unsubscribe from all. */ - for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) ) - { - if( pxNetIf->pfRemoveMulticastMAC != NULL ) - { - pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); - } - } - } - } - else - { - /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valud multicast group - * or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group. - * Do nothing. */ - } - } - - if( uxLeaveGroup == pdTRUE_UNSIGNED ) - { - /* Locate the IGMP/MLD report for this group. Decrement its socket count and - * if it becomes zero, remove it from the list and free it. */ - prvRemoveMulticastReportFromList( &( pxSocket->u.xUDP.xMulticastAddress ), ( UBaseType_t ) pxSocket->bits.bIsIPv6 ); - } - - /* Invalidate the multicast group address to prevent erroneous matches if someone calls - * FREERTOS_SO_IP_DROP_MEMBERSHIP multiple times. */ - memset( &pxSocket->u.xUDP.xMulticastAddress, 0x00, sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); - pxSocket->u.xUDP.pxMulticastNetIf = NULL; /* not really needed, but just looks cleaner when debugging. */ -} - -/** - * @brief Adds or drops a multicast group to/from a socket. - * - * @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for. - * @param[in] bAction: MUST be eSocketOptAddMembership or eSocketOptDropMembership. - */ -void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction, - uint8_t bAction ) -{ - uint8_t MCastMacBytes[ 6 ]; - FreeRTOS_Socket_t * pxSocket = pxMulticastAction->pxSocket; - uint8_t bFreeMatchedItem = pdFALSE; - NetworkInterface_t * pxNetIf = pxMulticastAction->pxInterface; - BaseType_t bReportItemConsumed = pdFALSE; - - configASSERT( pxSocket != NULL ); - - /* Note: This function is only called with eSocketOptDropMembership or eSocketOptAddMembership*/ - - /* This TCP stack does NOT support sockets subscribing to more than one multicast group. - * If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the - * IGMP/MLD reports corresponding to that group address. */ - prvDropMulticastMembership( pxSocket ); - - if( eSocketOptAddMembership == bAction ) - { - /* Store the multicast address. */ - ( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastAction->xMulticastGroup ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); - - if( pxSocket->bits.bIsIPv6 == pdFALSE ) - { - vSetMultiCastIPv4MacAddress( pxMulticastAction->xMulticastGroup.ulIP_IPv4, MCastMacBytes ); - } - else - { - vSetMultiCastIPv6MacAddress( &( pxMulticastAction->xMulticastGroup.xIP_IPv6 ), MCastMacBytes ); - } - - /* Inform the network driver */ - if( pxNetIf ) - { - if( pxNetIf->pfAddMulticastMAC != NULL ) - { - pxNetIf->pfAddMulticastMAC( MCastMacBytes ); - } - } - else - { - /* pxNetIf is NULL. For IPv4 that means "use all interfaces". For IPv6, that means "use the default multicast interface" - * FreeRTOS+TCP does not have the notion of default multicast interface so for now, subscribe on all. */ - for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) ) - { - if( pxNetIf->pfAddMulticastMAC != NULL ) - { - pxNetIf->pfAddMulticastMAC( MCastMacBytes ); - } - } - } - - /* Remember which interface(s) this socket is subscribed on. */ - pxSocket->u.xUDP.pxMulticastNetIf = pxMulticastAction->pxInterface; - /* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report - * for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist. - * If such an IGMP/MLD report is already present in the list, we will increment it's socket - * count and free the report we have here. In either case, the MulticastAction_t that we were - * passed, no longer needs to hold a reference to this multicast report. */ - do - { - if( pxMulticastAction->pxMCastReportData == NULL ) - { - break; - } - - if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIs_IPv6 == pdTRUE ) - { - /* RFC2710 end of section section 5 and RFC3810 section 6: - * ff02::1 is a special case and we do not send reports for it. */ - static const struct xIPv6_Address FreeRTOS_in6addr_allnodes = { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }; - - if( memcmp( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, FreeRTOS_in6addr_allnodes.ucBytes, sizeof( IPv6_Address_t ) ) == 0 ) - { - break; - } - - /* RFC2710 end of section section 5 and RFC3810 section 6: - * Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local). - * Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/ - if( ( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] & 0x0FU ) <= 1 ) - { - break; - } - } - else - { - /* RFC2236 end of section 6: - * 224.0.0.1 is a special case and we do not send reports for it. */ - if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == ipIGMP_IP_ADDR ) - { - break; - } - } - - bReportItemConsumed = xAddIGMPReportToList( pxMulticastAction->pxMCastReportData ); - } while( pdFALSE ); - - /* If the report either a special case address or was not consumed by xAddIGMPReportToList() because there was - * a duplicate found and its socket count was incremented instead of adding the report to the global list. - * In either case, free the multicast report. */ - if( bReportItemConsumed == pdFALSE ) - { - vPortFree( pxMulticastAction->pxMCastReportData ); - pxMulticastAction->pxMCastReportData = NULL; - } - } - - /* Free the message that was sent to us. */ - vPortFree( pxMulticastAction ); -} -static BaseType_t prvSendIGMP_Report_v2( IP_Address_t * pxGroupAddress, - NetworkEndPoint_t * pxEndPoint ) +static BaseType_t prvSendIGMPv2( IP_Address_t * pxGroupAddress, + uint8_t ucMessageType, + NetworkEndPoint_t * pxEndPoint ) { NetworkBufferDescriptor_t * pxNetworkBuffer; IGMPPacket_t * pxIGMPPacket; @@ -882,7 +690,7 @@ static BaseType_t prvSendIGMP_Report_v2( IP_Address_t * pxGroupAddress, pxIPHeader = &( pxIGMPPacket->xIPHeader ); - pxIGMPPacket->xIGMPHeader.ucTypeOfMessage = ipIGMP_MEMBERSHIP_REPORT_V2; + pxIGMPPacket->xIGMPHeader.ucMessageType = ucMessageType; pxIGMPPacket->xIGMPHeader.ucMaxResponseTime = 0; pxIGMPPacket->xIGMPHeader.ulGroupAddress = pxGroupAddress->ulIP_IPv4; diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index 279f2d7135..5cf7e45031 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -475,12 +475,12 @@ static void prvProcessIPEventsAndTimers( void ) case eSocketOptDropMembership: { MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData; - ( void ) vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType ); + vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType ); break; } - case eIGMPEvent: - ( void ) vHandleIGMP_Event(); + case eMulticastTimerEvent: + vHandleIGMP_Event(); break; #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0) */ @@ -688,7 +688,7 @@ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint ) pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 12 ] = 0xFFU; ( void ) memcpy( &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 13 ], &pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ], 3 ); - if( pdTRUE != xAddIGMPReportToList( pxMRD ) ) + if( pdTRUE != xAddMulticastReportToList( pxMRD ) ) { vPortFree( pxMRD ); pxMRD = NULL; diff --git a/source/FreeRTOS_IP_Timers.c b/source/FreeRTOS_IP_Timers.c index 525da63160..6c3309b090 100644 --- a/source/FreeRTOS_IP_Timers.c +++ b/source/FreeRTOS_IP_Timers.c @@ -334,7 +334,7 @@ void vCheckNetworkTimers( void ) /* Is it time to send any IGMP reports? */ if( prvIPTimerCheck( &xIGMPTimer ) != pdFALSE ) { - ( void ) xSendIGMPEvent(); + ( void ) xSendMulticastTimerEvent(); } } #endif /* ipconfigSUPPORT_IP_MULTICAST != 0 */ @@ -448,7 +448,7 @@ void vARPTimerReload( TickType_t xTime ) */ void vIGMPTimerReload( TickType_t xIgmpTickTime ) { - prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( ipIGMP_TIMER_PERIOD_MS ) ); + prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( igmpMULTICAST_EVENT_PERIOD_MS ) ); } #endif /* ipconfigSUPPORT_IP_MULTICAST */ /*-----------------------------------------------------------*/ diff --git a/source/FreeRTOS_Sockets.c b/source/FreeRTOS_Sockets.c index 29d1e09964..166bec4499 100644 --- a/source/FreeRTOS_Sockets.c +++ b/source/FreeRTOS_Sockets.c @@ -377,6 +377,7 @@ static int32_t prvSendTo_ActualSend( const FreeRTOS_Socket_t * pxSocket, int32_t lOptionName, const void * pvOptionValue, size_t uxOptionLength ); + static void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ); #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ /*-----------------------------------------------------------*/ @@ -6612,4 +6613,205 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket ) return xReturn; } + +/** + * @brief Adds or drops a multicast group to/from a socket. + * + * @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for. + * @param[in] bAction: MUST be eSocketOptAddMembership or eSocketOptDropMembership. + */ + void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction, + uint8_t bAction ) + { + uint8_t MCastMacBytes[ 6 ]; + FreeRTOS_Socket_t * pxSocket = pxMulticastAction->pxSocket; + uint8_t bFreeMatchedItem = pdFALSE; + NetworkInterface_t * pxNetIf = pxMulticastAction->pxInterface; + BaseType_t bReportItemConsumed = pdFALSE; + + configASSERT( pxSocket != NULL ); + + /* Note: This function is only called with eSocketOptDropMembership or eSocketOptAddMembership*/ + + /* This TCP stack does NOT support sockets subscribing to more than one multicast group. + * If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the + * IGMP/MLD reports corresponding to that group address. */ + prvDropMulticastMembership( pxSocket ); + + if( eSocketOptAddMembership == bAction ) + { + /* Store the multicast address. */ + ( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastAction->xMulticastGroup ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); + + if( pxSocket->bits.bIsIPv6 == pdFALSE ) + { + vSetMultiCastIPv4MacAddress( pxMulticastAction->xMulticastGroup.ulIP_IPv4, MCastMacBytes ); + } + else + { + vSetMultiCastIPv6MacAddress( &( pxMulticastAction->xMulticastGroup.xIP_IPv6 ), MCastMacBytes ); + } + + /* Inform the network driver */ + if( pxNetIf ) + { + if( pxNetIf->pfAddMulticastMAC != NULL ) + { + pxNetIf->pfAddMulticastMAC( MCastMacBytes ); + } + } + else + { + /* pxNetIf is NULL. For IPv4 that means "use all interfaces". For IPv6, that means "use the default multicast interface" + * FreeRTOS+TCP does not have the notion of default multicast interface so for now, subscribe on all. */ + for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) ) + { + if( pxNetIf->pfAddMulticastMAC != NULL ) + { + pxNetIf->pfAddMulticastMAC( MCastMacBytes ); + } + } + } + + /* Remember which interface(s) this socket is subscribed on. */ + pxSocket->u.xUDP.pxMulticastNetIf = pxMulticastAction->pxInterface; + + /* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report + * for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist. + * If such an IGMP/MLD report is already present in the list, we will increment it's socket + * count and free the report we have here. In either case, the MulticastAction_t that we were + * passed, no longer needs to hold a reference to this multicast report. */ + do + { + if( pxMulticastAction->pxMCastReportData == NULL ) + { + break; + } + + if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIs_IPv6 == pdTRUE ) + { + /* RFC2710 end of section section 5 and RFC3810 section 6: + * ff02::1 is a special case and we do not send reports for it. */ + static const struct xIPv6_Address FreeRTOS_in6addr_allnodes = { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } }; + + if( memcmp( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, FreeRTOS_in6addr_allnodes.ucBytes, sizeof( IPv6_Address_t ) ) == 0 ) + { + break; + } + + /* RFC2710 end of section section 5 and RFC3810 section 6: + * Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local). + * Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/ + if( ( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] & 0x0FU ) <= 1 ) + { + break; + } + } + else + { + /* RFC2236 end of section 6: + * 224.0.0.1 is a special case and we do not send reports for it. */ + if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == igmpIGMP_IP_ADDR ) + { + break; + } + } + + bReportItemConsumed = xAddMulticastReportToList( pxMulticastAction->pxMCastReportData ); + } while( pdFALSE ); + + /* If the report was a special case address or was not consumed by + * xAddMulticastReportToList(), free the multicast report. */ + if( bReportItemConsumed == pdFALSE ) + { + vPortFree( pxMulticastAction->pxMCastReportData ); + pxMulticastAction->pxMCastReportData = NULL; + } + } + + /* Free the message that was sent to us. */ + vPortFree( pxMulticastAction ); + } + + static void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ) + { + uint8_t MCastMacBytes[ 6 ]; + UBaseType_t uxLeaveGroup = pdFALSE_UNSIGNED; + NetworkInterface_t * pxNetIf = pxSocket->u.xUDP.pxMulticastNetIf; + + if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED ) + { + /* IPv6 */ + if( xIsIPv6AllowedMulticast( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ) ) ) + { + uxLeaveGroup = pdTRUE_UNSIGNED; + + if( pxNetIf ) + { + if( pxNetIf->pfRemoveMulticastMAC != NULL ) + { + vSetMultiCastIPv6MacAddress( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ), MCastMacBytes ); + pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); + } + } + else + { + } + } + else + { + /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valid multicast group + * or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group. + * Do nothing. */ + } + } + else + { + /* IPv4 */ + if( xIsIPv4Multicast( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 ) ) + { + uxLeaveGroup = pdTRUE_UNSIGNED; + + vSetMultiCastIPv4MacAddress( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4, MCastMacBytes ); + + if( pxNetIf ) + { + if( pxNetIf->pfRemoveMulticastMAC != NULL ) + { + pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); + } + } + else + { + /* pxNetIf is NULL. For IPv4 that means "all interfaces", so unsubscribe from all. */ + for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) ) + { + if( pxNetIf->pfRemoveMulticastMAC != NULL ) + { + pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); + } + } + } + } + else + { + /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valid multicast group + * or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group. + * Do nothing. */ + } + } + + if( uxLeaveGroup == pdTRUE_UNSIGNED ) + { + /* Locate the IGMP/MLD report for this group. Decrement its socket count and + * if it becomes zero, remove it from the list and free it. */ + vRemoveMulticastReportFromList( &( pxSocket->u.xUDP.xMulticastAddress ), ( UBaseType_t ) pxSocket->bits.bIsIPv6 ); + } + + /* Invalidate the multicast group address to prevent erroneous matches if someone calls + * FREERTOS_SO_IP_DROP_MEMBERSHIP multiple times. */ + memset( &pxSocket->u.xUDP.xMulticastAddress, 0x00, sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); + pxSocket->u.xUDP.pxMulticastNetIf = NULL; /* not really needed, but just looks cleaner when debugging. */ + } + #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ diff --git a/source/include/FreeRTOSIPConfigDefaults.h b/source/include/FreeRTOSIPConfigDefaults.h index ee84f510d0..35bc72e46f 100644 --- a/source/include/FreeRTOSIPConfigDefaults.h +++ b/source/include/FreeRTOSIPConfigDefaults.h @@ -3321,6 +3321,24 @@ /*---------------------------------------------------------------------------*/ +/* + * ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL + * + * Type: BaseType_t + * Unit: count of igmpMULTICAST_EVENT_PERIOD_MS + * + * When set to -1, no periodic unsolicited multicast reports are sent out. + * This is the correct behavior. For debug purposes, set to > 0 to cause + * periodic sending of multicast reports even if there are no IGMP/MLD + * queries heard. Example: 150 = 15.0 seconds. + * Note: Maybe remove that ? + */ +#ifndef ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL + #define ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL ( -1 ) +#endif + +/*---------------------------------------------------------------------------*/ + /* * ipconfigMULTICAST_DEFAULT_TTL * diff --git a/source/include/FreeRTOS_IGMP.h b/source/include/FreeRTOS_IGMP.h index 5f1ab3ec73..9092613158 100644 --- a/source/include/FreeRTOS_IGMP.h +++ b/source/include/FreeRTOS_IGMP.h @@ -46,26 +46,26 @@ #include "event_groups.h" -/** @brief IGMP times events at 100ms. */ - #define ipIGMP_TIMER_PERIOD_MS ( 100U ) +/** @brief IGMP times events at 100ms. Use this interval for MLD as well. */ + #define igmpMULTICAST_EVENT_PERIOD_MS ( 100U ) /* IGMP protocol definitions. */ - #define ipIGMP_MEMBERSHIP_QUERY ( ( uint8_t ) 0x11U ) /**< IGMP membership query. */ - #define ipIGMP_MEMBERSHIP_REPORT_V1 ( ( uint8_t ) 0x12U ) /**< IGMP v1 and v2 membership report. */ - #define ipIGMP_MEMBERSHIP_REPORT_V2 ( ( uint8_t ) 0x16U ) /**< IGMP v1 and v2 membership report. */ - #define ipIGMP_MEMBERSHIP_REPORT_V3 ( ( uint8_t ) 0x22U ) /**< IGMP v3 membership report. */ - #define ipIGMP_LEAVE_GROUP ( ( uint8_t ) 0x17U ) /**< IGMP leave group */ + #define igmpIGMP_MEMBERSHIP_QUERY ( ( uint8_t ) 0x11U ) /**< IGMP membership query. */ + #define igmpIGMP_MEMBERSHIP_REPORT_V1 ( ( uint8_t ) 0x12U ) /**< IGMP v1 and v2 membership report. */ + #define igmpIGMP_MEMBERSHIP_REPORT_V2 ( ( uint8_t ) 0x16U ) /**< IGMP v1 and v2 membership report. */ + #define igmpIGMP_MEMBERSHIP_REPORT_V3 ( ( uint8_t ) 0x22U ) /**< IGMP v3 membership report. */ + #define igmpIGMP_LEAVE_GROUP ( ( uint8_t ) 0x17U ) /**< IGMP leave group */ #if ( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN ) - #define ipIGMP_IP_ADDR 0xE0000001UL + #define igmpIGMP_IP_ADDR 0xE0000001UL #else - #define ipIGMP_IP_ADDR 0x010000E0UL + #define igmpIGMP_IP_ADDR 0x010000E0UL #endif /* ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN */ #include "pack_struct_start.h" struct xIGMP_HEADER { - uint8_t ucTypeOfMessage; /**< The IGMP type 0 + 1 = 1 */ + uint8_t ucMessageType; /**< The IGMP type 0 + 1 = 1 */ uint8_t ucMaxResponseTime; /**< Maximum time (sec) for responses. 1 + 1 = 2 */ uint16_t usChecksum; /**< The checksum of whole IGMP packet 2 + 2 = 4 */ uint32_t ulGroupAddress; /**< The multicast group address 4 + 4 = 8 */ @@ -84,14 +84,10 @@ typedef struct xIGMP_PACKET IGMPPacket_t; void vIGMP_Init( void ); - void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction, - uint8_t bAction ); - BaseType_t xSendIGMPEvent( void ); void vHandleIGMP_Event( void ); - BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ); + BaseType_t xAddMulticastReportToList( MCastReportData_t * pNewEntry ); eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); void vProcessMLDPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); - void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ); /* move to Sockets.c as static */ #ifdef __cplusplus diff --git a/source/include/FreeRTOS_IP_Private.h b/source/include/FreeRTOS_IP_Private.h index a4411a55c5..17b35961e5 100644 --- a/source/include/FreeRTOS_IP_Private.h +++ b/source/include/FreeRTOS_IP_Private.h @@ -87,7 +87,7 @@ typedef enum eSocketSetDeleteEvent, /*13: A socket set must be deleted. */ eSocketOptAddMembership, /*14: Register a UDP socket to a multicast group. */ eSocketOptDropMembership, /*15: Unregister a UDP socket from a multicast group. */ - eIGMPEvent, /*16: */ + eMulticastTimerEvent, /*16: Handles the sending of scheduled IGMP/MLD multicast reports. */ } eIPEvent_t; /** @@ -892,6 +892,15 @@ BaseType_t xIsCallingFromIPTask( void ); #endif /* ipconfigSUPPORT_SELECT_FUNCTION */ +#if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) + struct xMCastGroupDesc; + void vModifyMulticastMembership( struct xMCastGroupDesc * pxMulticastAction, + uint8_t bAction ); + void vRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, + UBaseType_t xIsIPv6 ); + BaseType_t xSendMulticastTimerEvent( void ); +#endif /* ipconfigSUPPORT_IP_MULTICAST */ + /* Send the network-up event and start the ARP timer. */ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint );