From 11ffd695ec8bb526d83c18b21f8ba91e526283e0 Mon Sep 17 00:00:00 2001 From: Emil Popov Date: Tue, 24 Oct 2023 15:12:26 -0400 Subject: [PATCH] Begins processing MLD Queries and schedules MLD v1 reports in the amount of time requested by the query. Rewrites prvScheduleIgmpReports to work for both IPv4 IGMP an IPv6 MLD reports Sends unsolicited IGMP and MLD reports on network-up events. Simplifies the randomization of the multicast report random send times. --- source/FreeRTOS_IGMP.c | 610 +++++++++++++++---------- source/FreeRTOS_IP.c | 12 +- source/FreeRTOS_ND.c | 20 + source/FreeRTOS_Sockets.c | 32 +- source/FreeRTOS_UDP_IPv4.c | 59 ++- source/include/FreeRTOS_DNS.h | 4 +- source/include/FreeRTOS_IGMP.h | 8 +- source/include/FreeRTOS_IP_Private.h | 20 +- source/include/FreeRTOS_IP_Utils.h | 4 +- source/include/FreeRTOS_IPv6_Private.h | 26 +- 10 files changed, 480 insertions(+), 315 deletions(-) diff --git a/source/FreeRTOS_IGMP.c b/source/FreeRTOS_IGMP.c index 76a21e29a7..442931728e 100644 --- a/source/FreeRTOS_IGMP.c +++ b/source/FreeRTOS_IGMP.c @@ -68,14 +68,18 @@ static portBASE_TYPE xSendIGMP( uint32_t uiBlockTime, uint8_t ucIgmpRespTime, uint32_t uiMulticastGroup_NBO, NetworkEndPoint_t * pxEndPoint ); -void prvScheduleIgmpReports( uint32_t uiGroupAddress, - uint8_t ucMaxRespTime ); +void prvScheduleMulticastReports( BaseType_t xIsIPv6, + void * pvGroupAddress, + uint16_t usMaxRespTime ); + +static void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, + UBaseType_t xIsIPv6 ); /*-----------------------------------------------------------*/ void vIGMP_Init( void ) { - MACAddress_t IGMP_MacAddress; + MACAddress_t xIGMP_MacAddress; NetworkInterface_t * pxInterface; MCastReportData_t * pxMRD; @@ -85,17 +89,17 @@ void vIGMP_Init( void ) ( void ) vIgmpSnooping_Initialize(); #endif - /* Calculate multicast MAC address that belongs to the IP-address 224.0.0.1. */ - vSetMultiCastIPv4MacAddress( ipIGMP_IP_ADDR, &IGMP_MacAddress ); + /* Calculate multicast MAC address that belongs to the IP-address 224.0.0.1 */ + vSetMultiCastIPv4MacAddress( ipIGMP_IP_ADDR, &xIGMP_MacAddress ); for( pxInterface = FreeRTOS_FirstNetworkInterface(); pxInterface != NULL; pxInterface = FreeRTOS_NextNetworkInterface( pxInterface ) ) { - /* Tell the interface (the EMAC) to allow IGMP_MacAddress. */ if( pxInterface->pfAddMulticastMAC != NULL ) { - pxInterface->pfAddMulticastMAC( IGMP_MacAddress.ucBytes ); + /* Tell the interface (the EMAC) to allow IGMP_MacAddress. */ + pxInterface->pfAddMulticastMAC( xIGMP_MacAddress.ucBytes ); } } @@ -204,7 +208,7 @@ eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const p extern uint32_t uiNumIGMP_Queries; uiNumIGMP_Queries++; - prvScheduleIgmpReports( pxIGMPPacket->xIGMPHeader.uiGroupAddress, pxIGMPPacket->xIGMPHeader.ucMaxResponseTime ); + prvScheduleMulticastReports( pdFALSE, ( void * ) &( pxIGMPPacket->xIGMPHeader.ulGroupAddress ), ( uint16_t ) pxIGMPPacket->xIGMPHeader.ucMaxResponseTime ); } #if ( ipconfigIGMP_SNOOPING != 0 ) @@ -215,108 +219,172 @@ eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const p return eReturn; } -void prvScheduleIgmpReports( uint32_t uiGroupAddress, - uint8_t ucMaxRespTime ) +void vProcessMLDPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ) { - if( !xIGMP_ScheduleList.uxNumberOfItems ) + /* Note: pxNetworkBuffer->xDataLength was already checked to be >= sizeof( MLDv1_Rx_Packet_t ) */ + const MLDv1_Rx_Packet_t * pxMLD_Packet = ( ( const MLDv1_Tx_Packet_t * ) pxNetworkBuffer->pucEthernetBuffer ); + + if( pxMLD_Packet->xMLD.ucTypeOfMessage == ipICMP_MULTICAST_LISTENER_QUERY ) { - return; + prvScheduleMulticastReports( pdTRUE, ( void * ) ( &pxMLD_Packet->xMLD.xGroupAddress.ucBytes[ 0 ] ), FreeRTOS_ntohs( pxMLD_Packet->xMLD.usMaxResponseDelay ) ); } - /* Schedule reports at random times withing the required response time. - * Instead of calling xApplicationGetRandomNumber() multiple times until we get - * an acceptable value lower than ucMaxRespTime, - * let's calculate a mask that guarantees we can get a random number and mask it - * out such that it is less than ucMaxRespTime. */ + #if ( ipconfigIGMP_SNOOPING != 0 ) + extern eFrameProcessingResult_t eApplicationIgmpFrameReceivedHook( NetworkBufferDescriptor_t * pxNetworkBuffer ); + ( void ) eApplicationIgmpFrameReceivedHook( pxNetworkBuffer ); + #endif /* ( ipconfigIGMP_SNOOPING != 0 ) */ +} + +void vRescheduleAllMulticastReports( NetworkEndPoint_t * pxEndPoint, + BaseType_t xMaxCountdown ) +{ + const ListItem_t * pxIterator; + const ListItem_t * xEnd; + uint32_t ulRandom; + MCastReportData_t * pxMRD; - /* Prepare a fake random number in case the random generator fails. */ - uint32_t uiNonRandomCounter = 1; + DebugPrintf_internal( "vRescheduleAllMulticastReports %u", xMaxCountdown ); + xMaxCountdown = min( 1, xMaxCountdown ); + xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); - /* Sanity enforcement. */ - ucMaxRespTime = max( 2, ucMaxRespTime ); - ucMaxRespTime--; - /* Now we can safely search for random numbers between 1 and ucMaxRespTime which is 1 or more. */ - - /* Find the next power of 2 that is larger than ucMaxRespTime. The algorithm for 32 bit values is described below: - * n--; // 1101 1101 --> 1101 1100 - * n |= n >> 1; // 1101 1100 | 0110 1110 = 1111 1110 - * n |= n >> 2; // 1111 1110 | 0011 1111 = 1111 1111 - * n |= n >> 4; // ... - * n |= n >> 8; - * n |= n >> 16; // 1111 1111 | 1111 1111 = 1111 1111 - * n++; // 1111 1111 --> 1 0000 0000 - * In our case, we don't need the ++ at the end as we need a mask-type value. Since we are skipping the ++ though, - * we have to check for zeros again. */ - uint32_t RandMask = ucMaxRespTime; - RandMask |= RandMask >> 1; - RandMask |= RandMask >> 2; - RandMask |= RandMask >> 4; - - if( 0 == RandMask ) + for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); + pxIterator != ( const ListItem_t * ) xEnd; + pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) { - RandMask = 1; - } + pxMRD = ( MCastReportData_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); + + if( ( pxEndPoint == pxMRD->pxEndPoint ) || ( pxMRD->pxEndPoint == NULL ) ) + { + if( ( pxMRD->xCountDown < 0 ) || ( pxMRD->xCountDown >= xMaxCountdown ) ) + { + if( xApplicationGetRandomNumber( &( ulRandom ) ) == pdFALSE ) + { + ulRandom = 0; + } - /* Go through the list of IGMP reports */ + /* Generate a random countdown between 0 and usMaxCountdown - 1. */ + pxMRD->xCountDown = ulRandom % xMaxCountdown; + } + } + } /* for(;;) iterating over xIGMP_ScheduleList */ +} - /* and schedule the reports. Note, the IGMP event is set at 100ms - * which corresponds to the increment used in ucMaxRespTime. - * pxMRD->ucCountDown holds a count in increments of the IGMP event time, so 12 = 1200ms = 1.2s */ +void prvScheduleMulticastReports( BaseType_t xIsIPv6, + void * pvGroupAddress, + uint16_t usMaxRespTime ) +{ const ListItem_t * pxIterator; - const ListItem_t * xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); + const ListItem_t * xEnd; + uint32_t ulRandom; MCastReportData_t * pxMRD; + const IPv6_Address_t * pxIPv6_GroupAddress; + const uint32_t * plIPv4_GroupAddress; + uint16_t usMaxCountdown; + static uint32_t ulNonRandomCounter = 0; /* In case the random number generator fails have a "not so random number" ready. */ + + /* Sanity enforcement. */ + + /* Go through the list of IGMP reports and schedule them. Note, the IGMP event is set at 100ms + * pxMRD->usMaxRespTime is in milliseconds */ + if( xIsIPv6 ) + { + usMaxCountdown = usMaxRespTime / ipIGMP_TIMER_PERIOD_MS; + } + else + { + usMaxCountdown = usMaxRespTime; + } + + usMaxCountdown = max( 1, usMaxCountdown ); + + xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); pxIterator != ( const ListItem_t * ) xEnd; pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) { + BaseType_t xReschedule = pdFALSE; pxMRD = ( MCastReportData_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); - /* Continue parsing if we are dealing with a general query, or if we are servicing a group-specific - * query and this report matches the group-specific query's destination address*/ - if( ( uiGroupAddress == 0U ) || ( uiGroupAddress == pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 ) ) + /* Make sure IP versions are the same. */ + if( xIsIPv6 != pxMRD->xMCastGroupAddress.xIs_IPv6 ) { - /* This report needs to be scheduled for sending. Remember that it may already be scheduled. - * pxMRD->ucCountDown of zero means the report is not scheduled to be sent. If a report is scheduled, and it's - * scheduled time is before ucMaxRespTime, there is nothing to be done. If a - * report is scheduled past ucMaxRespTime, or not scheduled at all, we need - * to schedule it for a random time between 0 and ucMaxRespTime. */ - if( ( pxMRD->ucCountDown == 0 ) || ( pxMRD->ucCountDown >= ucMaxRespTime ) ) - { - uint32_t uiRandom; + continue; + } - if( xApplicationGetRandomNumber( &( uiRandom ) ) == pdFALSE ) - { - pxMRD->ucCountDown = uiNonRandomCounter++; + if( xIsIPv6 ) + { + /* IPv6 MLD */ + pxIPv6_GroupAddress = ( const IPv6_Address_t * ) pvGroupAddress; + BaseType_t i, xIsUnspecified = pdTRUE; - if( uiNonRandomCounter > ucMaxRespTime ) - { - uiNonRandomCounter = 1; - } - } - else + for( i = 0; i < ipSIZE_OF_IPv6_ADDRESS; i++ ) + { + if( pxIPv6_GroupAddress->ucBytes[ i ] != 0 ) { - uiRandom &= RandMask; + xIsUnspecified = pdFALSE; + break; + } + } - if( 0 == uiRandom ) - { - uiRandom = 1; - } + /* Continue if this a general query, or if we are servicing a group-specific + * query and this report matches the group-specific query's destination address*/ + if( ( xIsUnspecified == pdTRUE ) || + ( 0 == memcmp( pxIPv6_GroupAddress->ucBytes, pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) ) ) + { + xReschedule = pdTRUE; + } + } + else + { + /* IPv4 IGMP*/ + plIPv4_GroupAddress = ( const uint32_t * ) pvGroupAddress; - while( uiRandom > ucMaxRespTime ) - { - uiRandom -= ucMaxRespTime; - } + /* Continue if this a general query, or if we are servicing a group-specific + * query and this report matches the group-specific query's destination address*/ + if( ( *plIPv4_GroupAddress == 0U ) || ( *plIPv4_GroupAddress == pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 ) ) + { + xReschedule = pdTRUE; + /* IGMP maximum response time is stored in single byte. Every count represents 0.1s */ + usMaxCountdown = min( 255, usMaxCountdown ); + } + } - pxMRD->ucCountDown = ( uint8_t ) uiRandom; + if( xReschedule ) + { + /* This report needs to be scheduled for sending. Remember that it may already be scheduled. + * Negative pxMRD->xCountDown means the report is not scheduled to be sent. If a report is scheduled, and it's + * scheduled time is before usMaxCountdown, there is nothing to be done. If a + * report is scheduled for later than usMaxCountdown, or not scheduled at all, we need + * to schedule it for a random time between 0 and usMaxCountdown - 1. */ + if( ( pxMRD->xCountDown < 0 ) || ( pxMRD->xCountDown >= usMaxCountdown ) ) + { + if( xApplicationGetRandomNumber( &( ulRandom ) ) == pdFALSE ) + { + /* The world is ending, our random number generator has failed. Use a not very random up-counter. */ + ulRandom = ulNonRandomCounter; + ulNonRandomCounter++; } + + /* Generate a random countdown between 0 and usMaxCountdown - 1. */ + pxMRD->xCountDown = ulRandom % usMaxCountdown; + DebugPrintf_internal( "prvScheduleMulticastReports %s, ucMaxRespTime %u, usMaxCountdown %u, xCountDown %u", ( xIsIPv6 ) ? "IPv6" : "IPv4", usMaxRespTime, usMaxCountdown, pxMRD->xCountDown ); + } + else + { + /* This report is currently scheduled to be sent earlier than usMaxCountdown. + * Do nothing. */ } } } /* for(;;) iterating over xIGMP_ScheduleList */ } - void vHandleIGMP_Event( void ) { + /* _EP_: Investigate if sending reports from 0.0.0.0 and :: is actually forbidden or if there + * is anything wrong with it. It may be just fine, in which case we don't need to the IP checks below. + * I have the suspicion that as far as those reports go, nobody cares about our source IP. These + * are only used to identify how many receivers there are for every multicast group and possibly which + * switch ports have receivers... So our IP being 0.0.0.0 or :: may not be a problem at all. */ extern uint32_t uiNumIGMP_Events; uiNumIGMP_Events++; @@ -332,23 +400,25 @@ void vHandleIGMP_Event( void ) { pxMRD = ( MCastReportData_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); - /* Only decrement down to one. Decrementing to zero is handled later. */ - if( pxMRD->ucCountDown > 1 ) + /* Decrement down to 0. Decrementing from 0 to -1 triggers the sending of the scheduled report. */ + if( pxMRD->xCountDown > 0 ) { - pxMRD->ucCountDown--; + pxMRD->xCountDown--; } - - if( pxMRD->ucCountDown == 1 ) + else if( pxMRD->xCountDown == 0 ) { pxEndPoint = pxMRD->pxEndPoint; /* If the end-point is null, the report is for all interfaces. */ if( pxEndPoint == NULL ) { + /* Go through all interfaces. */ for( pxInterface = FreeRTOS_FirstNetworkInterface(); pxInterface != NULL; pxInterface = FreeRTOS_NextNetworkInterface( pxInterface ) ) { + /* For every end-point of the current interface, pick the first one that is + * usable as an outgoing end-point for the current multicast report. */ for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface ); pxEndPoint != NULL; pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) ) @@ -359,33 +429,44 @@ void vHandleIGMP_Event( void ) continue; } - if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) - { - /* IPv6 MLD Reports. */ - xSendMulticastListenerReport_v1( &pxMRD->xMCastGroupAddress.xIPAddress, pxEndPoint, 10 ); - - /* Test only: Reschedule send in 15 seconds */ - pxMRD->ucCountDown = 150 + ( ( ( ( uint32_t ) pxEndPoint ) >> 4 ) & 0xF ); - continue; - } - else + if( pxEndPoint->bits.bEndPointUp ) { - /* This report is for an IPv4 group */ - - /* Make sure the end-point has an IP address */ - if( pxEndPoint->ipv4_settings.ulIPAddress != 0 ) + if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) { - pxMRD->ucCountDown = 0; - xSendIGMP( 0, ipIGMP_MEMBERSHIP_REPORT_V2, 0, pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4, pxEndPoint ); - - /* Test only: Reschedule send in 15 seconds */ - pxMRD->ucCountDown = 150 + ( ( ( ( uint32_t ) pxEndPoint ) >> 4 ) & 0xF ); + /* IPv6 MLD Reports. */ + { + uint8_t EndPointAddressString[ ( 45 + 1 ) ] = { 0 }; + uint8_t MCastGroupAddressString[ ( 45 + 1 ) ] = { 0 }; + FreeRTOS_inet_ntop6( ( void * ) &pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 0 ], EndPointAddressString, ( 45 + 1 ) ); + FreeRTOS_inet_ntop6( ( void * ) &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], MCastGroupAddressString, ( 45 + 1 ) ); + + DebugPrintf_internal( "SENDING_1 REPORT FOR %s, EndPoint %s", MCastGroupAddressString, EndPointAddressString ); + } + pxMRD->xCountDown = -1; + xSendMulticastListenerReport_v1( &pxMRD->xMCastGroupAddress.xIPAddress, pxEndPoint, 10 ); + break; } else { - /* EndPoint has no IP yet. */ + /* This report is for an IPv4 group */ + + /* Make sure the end-point has an IP address */ + if( pxEndPoint->ipv4_settings.ulIPAddress != 0 ) + { + pxMRD->xCountDown = -1; + xSendIGMP( 0, ipIGMP_MEMBERSHIP_REPORT_V2, 0, pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4, pxEndPoint ); +/* uint8_t * pIP = &pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4; */ +/* uint8_t * pEpIP = &pxEndPoint->ipv4_settings.ulIPAddress; */ +/* DebugPrintf_internal( "SENDING REPORT FOR %u.%u.%u.%u, on EndPoint %u.%u.%u.%u", pIP[ 0 ], pIP[ 1 ], pIP[ 2 ], pIP[ 3 ], pEpIP[ 0 ], pEpIP[ 1 ], pEpIP[ 2 ], pEpIP[ 3 ] ); */ + + break; + } + else + { + /* EndPoint has no IP yet. */ + } } - } + } /* if( pxEndPoint->bits.bEndPointUp ) */ } } } @@ -395,10 +476,16 @@ void vHandleIGMP_Event( void ) if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) { /* IPv6 MLD Reports. */ + { + uint8_t EndPointAddressString[ ( 45 + 1 ) ] = { 0 }; + uint8_t MCastGroupAddressString[ ( 45 + 1 ) ] = { 0 }; + FreeRTOS_inet_ntop6( ( void * ) &pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 0 ], EndPointAddressString, ( 45 + 1 ) ); + FreeRTOS_inet_ntop6( ( void * ) &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], MCastGroupAddressString, ( 45 + 1 ) ); + DebugPrintf_internal( "SENDING_2 REPORT FOR %s, on EXPLICIT EndPoint %s", MCastGroupAddressString, EndPointAddressString ); + } + pxMRD->xCountDown = -1; xSendMulticastListenerReport_v1( &pxMRD->xMCastGroupAddress.xIPAddress, pxEndPoint, 10 ); - /* Test only: Reschedule send in 15 seconds */ - pxMRD->ucCountDown = 150 + ( ( ( ( uint32_t ) pxEndPoint ) >> 4 ) & 0xF ); continue; } else @@ -407,16 +494,21 @@ void vHandleIGMP_Event( void ) /* Make sure the end-point has an IP address */ if( pxEndPoint->ipv4_settings.ulIPAddress != 0 ) { - pxMRD->ucCountDown = 0; + pxMRD->xCountDown = -1; xSendIGMP( 0, ipIGMP_MEMBERSHIP_REPORT_V2, 0, pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4, pxEndPoint ); - - /* Test only: Reschedule send in 15 seconds */ - pxMRD->ucCountDown = 150 + ( ( ( ( uint32_t ) pxEndPoint ) >> 4 ) & 0xF ); +/* uint8_t * pIP = &pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4; */ +/* uint8_t * pEpIP = &pxEndPoint->ipv4_settings.ulIPAddress; */ +/* DebugPrintf_internal( "SENDING REPORT FOR %u.%u.%u.%u, on EXPLICIT EndPoint %u.%u.%u.%u ", pIP[ 0 ], pIP[ 1 ], pIP[ 2 ], pIP[ 3 ], pEpIP[ 0 ], pEpIP[ 1 ], pEpIP[ 2 ], pEpIP[ 3 ] ); */ } } } } - } + else + { + /* ( pxMRD->xCountDown < 0 ) + * Do nothing. */ + } + } /* for(;;) loop iterating over the list of multicast reports */ #if ( ipconfigIGMP_SNOOPING != 0 ) extern void vApplicationIgmpEventHook( void ); @@ -448,10 +540,9 @@ BaseType_t xSendIGMPEvent( void ) * * @param[in] pMCastGroup: The multicast group descriptor the specifies the group and interface. */ -void vRemoveIGMPReportFromList( struct freertos_ip_mreq * pMCastGroup ) +void prvRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress, + UBaseType_t uxIsIPv6 ) { - configASSERT( pMCastGroup != NULL ); - const ListItem_t * pxIterator; const ListItem_t * xEnd = listGET_END_MARKER( &xIGMP_ScheduleList ); MCastReportData_t * pxIRD; @@ -464,25 +555,33 @@ void vRemoveIGMPReportFromList( struct freertos_ip_mreq * pMCastGroup ) if( pxIRD->xMCastGroupAddress.xIs_IPv6 == pdTRUE_UNSIGNED ) { - /* ToDo: handle IPv6 */ - continue; + if( 0 == memcmp( pxIRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, pxMulticastAddress->xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) ) + { + break; + } } - - if( pxIRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == pMCastGroup->imr_multiaddr.s_addr ) + else { - /* Found a match. */ - if( pxIRD->xNumSockets > 0 ) + if( pxIRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == pxMulticastAddress->ulIP_IPv4 ) { - pxIRD->xNumSockets--; + break; } + } + } - if( 0 == pxIRD->xNumSockets ) - { - ( void ) uxListRemove( &pxIRD->xListItem ); - vPortFree( pxIRD ); - } + if( pxIterator != xEnd ) + { + /* Found a match. */ + if( pxIRD->xNumSockets > 0 ) + { + pxIRD->xNumSockets--; + } - break; + if( 0 == pxIRD->xNumSockets ) + { + /* 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 ); } } } @@ -502,16 +601,18 @@ BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ) configASSERT( pNewEntry != NULL ); { - uint8_t EndPointAddressString[ ( 45 + 1 ) ] = { 0 }; + uint8_t ucMCastGroupAddressString[ ( 45 + 1 ) ] = { 0 }; if( pNewEntry->xMCastGroupAddress.xIs_IPv6 == pdTRUE_UNSIGNED ) { - FreeRTOS_inet_ntop6( ( void * ) &pNewEntry->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], EndPointAddressString, ( 45 + 1 ) ); + FreeRTOS_inet_ntop6( ( void * ) &pNewEntry->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ], ucMCastGroupAddressString, ( 45 + 1 ) ); } else { - FreeRTOS_inet_ntop4( ( void * ) &pNewEntry->xMCastGroupAddress.xIPAddress.ulIP_IPv4, EndPointAddressString, ( 45 + 1 ) ); + FreeRTOS_inet_ntop4( ( void * ) &pNewEntry->xMCastGroupAddress.xIPAddress.ulIP_IPv4, ucMCastGroupAddressString, ( 45 + 1 ) ); } + + DebugPrintf_internal( "xAddIGMPReportToList %s", ucMCastGroupAddressString ); } for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); @@ -549,8 +650,8 @@ BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ) /* Schedule an unsolicited report to quickly inform IGMP snooping switches that we want * to receive this multicast group. During power up, our IP may be 0.0.0.0 and we don't * want to send IGMP reports with an IP of 0.0.0.0. To get around this, the - * vHandleIGMP_Event code will keep ucCountDown at 1 until the IP becomes non-zero. */ - pNewEntry->ucCountDown = 1; + * vHandleIGMP_Event code will keep xCountDown at 0 until the IP becomes non-zero. */ + pNewEntry->xCountDown = 0; vListInsertEnd( &xIGMP_ScheduleList, &( pNewEntry->xListItem ) ); } @@ -558,6 +659,60 @@ 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->pxEndPoint != NULL && pxSocket->pxEndPoint->pxNetworkInterface != NULL ) ? pxSocket->pxEndPoint->pxNetworkInterface : NULL; + + if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED ) + { + /* IPv6 */ + if( xIsIPv6AllowedMulticast( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ) ) ) + { + uxLeaveGroup = pdTRUE_UNSIGNED; + + if( pxNetIf ) + { + vSetMultiCastIPv6MacAddress( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ), MCastMacBytes ); + pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); + } + } + else + { + /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valud multicast group . + * Do nothing. */ + } + } + else + { + /* IPv4 */ + if( xIsIPv4Multicast( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 ) ) + { + uxLeaveGroup = pdTRUE_UNSIGNED; + + if( pxNetIf ) + { + vSetMultiCastIPv4MacAddress( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4, MCastMacBytes ); + pxNetIf->pfRemoveMulticastMAC( MCastMacBytes ); + } + } + else + { + /* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valud 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 ); + } +} + /** * @brief Adds or drops a multicast group to/from a socket. * @@ -567,132 +722,103 @@ BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ) void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup, uint8_t bAction ) { + DebugPrintf_internal( "vModifyMulticastMembership" ); + if( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) ) { return; } + uint8_t MCastMacBytes[ 6 ]; FreeRTOS_Socket_t * pxSocket = pxMulticastGroup->pxSocket; - uint8_t bFreeInputItem = pdTRUE; uint8_t bFreeMatchedItem = pdFALSE; NetworkInterface_t * pxNetIf = ( pxSocket->pxEndPoint != NULL && pxSocket->pxEndPoint->pxNetworkInterface != NULL ) ? pxSocket->pxEndPoint->pxNetworkInterface : NULL; - /* Go through the list of registered groups and try to locate the group that - * we are being asked to add or remove. This check prevents adding duplicates.*/ - const ListItem_t * pxIterator; - const ListItem_t * xEnd = listGET_END_MARKER( &( pxSocket->u.xUDP.xMulticastGroupsList ) ); - MCastGroupDesc_t * pxMCG; + configASSERT( pxSocket != NULL ); - for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); - pxIterator != ( const ListItem_t * ) xEnd; - pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) + /* 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 ) { - pxMCG = ( MCastGroupDesc_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); + /* Store the multicast address. We do not need the bit that specifies whether this is IPv4 or IPv6 */ + ( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastGroup->xMulticastGroup.xIPAddress ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); - if( pxMCG->mreq.imr_multiaddr.s_addr == pxMulticastGroup->mreq.imr_multiaddr.s_addr ) + if( pxMulticastGroup->xMulticastGroup.xIs_IPv6 == pdFALSE ) { - /* Found a match. If we need to remove this address, go ahead. - * If we need to add it, it's already there, so just free the the descriptor to prevent memory leaks. */ - if( eSocketOptDropMembership == bAction ) - { - ( void ) uxListRemove( &pxMCG->xListItem ); - - /* Defer freeing this list item because when called from vSocketClose, this matching item - * is the same as our input parameter item, and we need the input parameter item further - * down when informing the network interface driver. */ - bFreeMatchedItem = pdTRUE; - - if( pxMulticastGroup == pxMCG ) - { - bFreeInputItem = pdFALSE; - } - } - - break; + vSetMultiCastIPv4MacAddress( pxMulticastGroup->mreq.imr_multiaddr.s_addr, MCastMacBytes ); } - } - - if( eSocketOptAddMembership == bAction ) - { - if( pxIterator == xEnd ) + else { - /* We are adding an item and we couldn't find an identical one. Simply add it. */ - vListInsertEnd( &( pxSocket->u.xUDP.xMulticastGroupsList ), &( pxMulticastGroup->xListItem ) ); - /* Inform the network driver */ - uint8_t MCastDestMacBytes[ 6 ]; - vSetMultiCastIPv4MacAddress( pxMulticastGroup->mreq.imr_multiaddr.s_addr, MCastDestMacBytes ); + vSetMultiCastIPv6MacAddress( &( pxMulticastGroup->xMulticastGroup.xIPAddress.xIP_IPv6 ), MCastMacBytes ); + } - if( pxNetIf ) + /* Inform the network driver */ + if( pxNetIf ) + { + DebugPrintf_internal( "MULTICAST ADD MAC %02X-%02X-%02X-%02X-%02X-%02X, pxSocket 0x%08X, EndPoint 0x%08X, pxNetIf 0x%08X", + MCastMacBytes[ 0 ], + MCastMacBytes[ 1 ], + MCastMacBytes[ 2 ], + MCastMacBytes[ 3 ], + MCastMacBytes[ 4 ], + MCastMacBytes[ 5 ], + pxSocket, + pxSocket->pxEndPoint, + pxNetIf + ); + pxNetIf->pfAddMulticastMAC( MCastMacBytes ); + } + else + { + /* pxNetIf is NULL. For IPv4 that means "use all interfaces". For IPv6, that means "use the default multicast interface" */ + if( pxMulticastGroup->xMulticastGroup.xIs_IPv6 == pdTRUE ) { - pxNetIf->pfAddMulticastMAC( MCastDestMacBytes ); + DebugPrintf_internal( "IPv6 FIX MEEEEEEEEEEEEEEEEEee" ); } - - bFreeInputItem = pdFALSE; - - /* Since we've added a multicast group to this socket, we need to prepare an IGMP report - * for when we receive an IGMP query. Keep in mind that such a report might already exist. - * If such an IGMP 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 MCastGroupDesc_t that we were - * passed, no longer needs to hold a reference to this IGMP report. */ - if( pxMulticastGroup->pxIGMPReportDesc ) + else { - BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMulticastGroup->pxIGMPReportDesc ); - - if( pdTRUE != bReportItemConsumed ) + for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) ) { - /* If adding to the list did not consume the item that we sent, that means a duplicate - * was found and its socket count was incremented instead of adding the item we sent. - * Free the item that was passed to us. */ - vPortFree( pxMulticastGroup->pxIGMPReportDesc ); - pxMulticastGroup->pxIGMPReportDesc = NULL; + DebugPrintf_internal( "MULTICAST ADD MAC %02X-%02X-%02X-%02X-%02X-%02X TO ALL IFS, pxSocket 0x%08X, pxNetIf 0x%08X", + MCastMacBytes[ 0 ], + MCastMacBytes[ 1 ], + MCastMacBytes[ 2 ], + MCastMacBytes[ 3 ], + MCastMacBytes[ 4 ], + MCastMacBytes[ 5 ], + pxSocket, + pxNetIf + ); + pxNetIf->pfAddMulticastMAC( MCastMacBytes ); } } } - else + + /* Since we've added a multicast group to this socket, we need to prepare an IGMP report + * for when we receive an IGMP query. Keep in mind that such a report might already exist. + * If such an IGMP 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 MCastGroupDesc_t that we were + * passed, no longer needs to hold a reference to this IGMP report. */ + if( pxMulticastGroup->pxIGMPReportDesc ) { - /* Adding, but found duplicate. No need to inform the network driver. Simply free - * the MCastReportData_t */ - if( pxMulticastGroup->pxIGMPReportDesc ) + BaseType_t bReportItemConsumed = xAddIGMPReportToList( pxMulticastGroup->pxIGMPReportDesc ); + + if( pdTRUE != bReportItemConsumed ) { + /* If adding to the list did not consume the item that we sent, that means a duplicate + * was found and its socket count was incremented instead of adding the item we sent. + * Free the item that was passed to us. */ vPortFree( pxMulticastGroup->pxIGMPReportDesc ); pxMulticastGroup->pxIGMPReportDesc = NULL; } } } - else - { - if( pxIterator == xEnd ) - { - /* Removing, but no match. No need to inform the network driver. */ - } - else - { - /* Removing and found a match. */ - /* Inform the network driver */ - uint8_t xMCastDestMacBytes[ 6 ]; - vSetMultiCastIPv4MacAddress( pxMulticastGroup->mreq.imr_multiaddr.s_addr, xMCastDestMacBytes ); - - if( pxNetIf ) - { - pxNetIf->pfRemoveMulticastMAC( xMCastDestMacBytes ); - } - - /* Lastly, locate the IGMP report for this group. Decrement its socket count and - * if it becomes zero, remove it from the list and free it. */ - vRemoveIGMPReportFromList( &( pxMCG->mreq ) ); - } - } /* Free the message that was sent to us. */ - if( bFreeInputItem ) - { - vPortFree( pxMulticastGroup ); - } - - if( bFreeMatchedItem ) - { - vPortFree( pxMCG ); - } + vPortFree( pxMulticastGroup ); } static portBASE_TYPE xSendIGMP( uint32_t uiBlockTime, @@ -723,7 +849,7 @@ static portBASE_TYPE xSendIGMP( uint32_t uiBlockTime, pxIGMPPacket->xIGMPHeader.ucTypeOfMessage = ucIgmpMsgType; pxIGMPPacket->xIGMPHeader.ucMaxResponseTime = ucIgmpRespTime; - pxIGMPPacket->xIGMPHeader.uiGroupAddress = uiMulticastGroup_NBO; + pxIGMPPacket->xIGMPHeader.ulGroupAddress = uiMulticastGroup_NBO; pxIPHeader->ulDestinationIPAddress = uiMulticastGroup_NBO; pxIPHeader->ulSourceIPAddress = pxEndPoint->ipv4_settings.ulIPAddress; @@ -798,7 +924,7 @@ BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddres TickType_t uxBlockTimeTicks ) { NetworkBufferDescriptor_t * pxNetworkBuffer = NULL; - MLDReportPacket_v1_t * pxMLRPacket; + MLDv1_Tx_Packet_t * pxMLRPacket; BaseType_t xReturn = pdFAIL; /* Quick and dirty, use whatever endpoint we were given, or find the link-local end-point. @@ -825,19 +951,19 @@ BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddres /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ - pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( MLDReportPacket_v1_t ), uxBlockTimeTicks ); + pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( MLDv1_Tx_Packet_t ), uxBlockTimeTicks ); if( pxNetworkBuffer != NULL ) { - pxNetworkBuffer->pxEndPoint = NULL; + pxNetworkBuffer->pxEndPoint = pxEndPoint; configASSERT( pxEndPoint->pxNetworkInterface != NULL ); /* MISRA Ref 11.3.1 [Misaligned access] */ /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */ /* coverity[misra_c_2012_rule_11_3_violation] */ - pxMLRPacket = ( ( MLDReportPacket_v1_t * ) pxNetworkBuffer->pucEthernetBuffer ); - pxNetworkBuffer->xDataLength = sizeof( MLDReportPacket_v1_t ); + pxMLRPacket = ( ( MLDv1_Tx_Packet_t * ) pxNetworkBuffer->pucEthernetBuffer ); + pxNetworkBuffer->xDataLength = sizeof( MLDv1_Tx_Packet_t ); /* MLD Reports version 1 are sent to the MAC address corresponding to the multicast address. */ vSetMultiCastIPv6MacAddress( pxGroupAddress, &pxMLRPacket->xEthernetHeader.xDestinationAddress ); @@ -863,11 +989,11 @@ BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddres pxMLRPacket->xRAOption.xPadding.ucType = ipHOP_BY_HOP_PadN; pxMLRPacket->xRAOption.xPadding.ucLength = 0; /* in multiples of 8 octets, not counting the first 8 */ - pxMLRPacket->xICMPv6_MLDReport.ucTypeOfMessage = ipICMP_MULTICAST_LISTENER_REPORT_V1; - pxMLRPacket->xICMPv6_MLDReport.ucTypeOfService = 0; - pxMLRPacket->xICMPv6_MLDReport.usMaxResponseDelay = FreeRTOS_htons( 0 ); - pxMLRPacket->xICMPv6_MLDReport.usReserved = FreeRTOS_htons( 0 ); - ( void ) memcpy( pxMLRPacket->xICMPv6_MLDReport.xGroupAddress.ucBytes, pxGroupAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS ); + pxMLRPacket->xMLD.ucTypeOfMessage = ipICMP_MULTICAST_LISTENER_REPORT_V1; + pxMLRPacket->xMLD.ucTypeOfService = 0; + pxMLRPacket->xMLD.usMaxResponseDelay = FreeRTOS_htons( 0 ); + pxMLRPacket->xMLD.usReserved = FreeRTOS_htons( 0 ); + ( void ) memcpy( pxMLRPacket->xMLD.xGroupAddress.ucBytes, pxGroupAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS ); #if ( ipconfigDRIVER_INCLUDED_TX_IP_CHECKSUM == 0 ) { @@ -878,16 +1004,20 @@ BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddres { /* Many EMAC peripherals will only calculate the ICMP checksum * correctly if the field is nulled beforehand. */ - pxMLRPacket->xICMPv6_MLDReport.usChecksum = 0; + pxMLRPacket->xMLD.usChecksum = 0; } #endif - ( void ) pxEndPoint->pxNetworkInterface->pfOutput( pxEndPoint->pxNetworkInterface, pxNetworkBuffer, pdTRUE ); - xReturn = pdPASS; + if( pxEndPoint->pxNetworkInterface->pfOutput != NULL ) + { + ( void ) pxEndPoint->pxNetworkInterface->pfOutput( pxEndPoint->pxNetworkInterface, pxNetworkBuffer, pdTRUE ); + xReturn = pdPASS; + } } } else { + DebugPrintf_internal( "NO BUFFERS" ); /* Either no proper end-point found, or allocating the network buffer failed. */ } diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index db9ad52ec3..a124f590fa 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -716,7 +716,17 @@ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint ) } } } - } + } /* if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) */ + + /* Reschedule all multicast reports associated with this end-point. + * /* Note: countdown is in increments of ipIGMP_TIMER_PERIOD_MS. It's a good idea to spread out all reports a little. + * 200 to 500ms ( xMaxCountdown of 2 - 5 ) should be a good happy medium. If the network we just connected to has a IGMP/MLD querier, + * they will soon ask us for reports anyways, so sending these unsolicited reports is not required. It simply enhances the user + * experience by shortening the time it takes before we begin receiving the multicasts that we care for. */ + /* _EP_: vRescheduleAllMulticastReports() is NOT declared in header files because I don't want to expose it to the user */ + extern void vRescheduleAllMulticastReports( NetworkEndPoint_t * pxEndPoint, + BaseType_t xMaxCountdown ); + vRescheduleAllMulticastReports( pxEndPoint, 5 ); #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index 26e07341db..2b2c9d9285 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -1112,6 +1112,26 @@ break; #endif /* ( ipconfigUSE_RA != 0 ) */ + #if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) + case ipICMP_MULTICAST_LISTENER_QUERY: + case ipICMP_MULTICAST_LISTENER_REPORT_V1: + case ipICMP_MULTICAST_LISTENER_REPORT_V2: + + /* Note: prvProcessIPPacket() stripped the extension headers, so this packet struct is defined without them and they cannot be checked. + * per RFC, MLD packets must use the RouterAlert option in a Hop By Hop extension header. */ + /* All MLD packets are at least as large as a v1 query packet. */ + uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_ICMPv6_HEADER ); + + if( uxNeededSize > pxNetworkBuffer->xDataLength ) + { + FreeRTOS_printf( ( "Too small\n" ) ); + break; + } + + vProcessMLDPacket( pxNetworkBuffer ); + break; + #endif /* if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) */ + default: /* All possible values are included here above. */ break; diff --git a/source/FreeRTOS_Sockets.c b/source/FreeRTOS_Sockets.c index 12fa86c7f9..2d0743cd8e 100644 --- a/source/FreeRTOS_Sockets.c +++ b/source/FreeRTOS_Sockets.c @@ -751,7 +751,7 @@ Socket_t FreeRTOS_socket( BaseType_t xDomain, #if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) pxSocket->u.xUDP.xMulticastTTL = ipconfigMULTICAST_DEFAULT_TTL; - vListInitialise( &pxSocket->u.xUDP.xMulticastGroupsList ); + memset( &( pxSocket->u.xUDP.xMulticastAddress ), 0, sizeof( pxSocket->u.xUDP.xMulticastAddress ) ); #endif } @@ -2161,12 +2161,7 @@ void * vSocketClose( FreeRTOS_Socket_t * pxSocket ) #if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) if( pxSocket->ucProtocol == ipPROTOCOL_UDP ) { - /* Un-register all multicast groups that might have been added. */ - while( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xMulticastGroupsList ) ) > 0U ) - { - MCastGroupDesc_t * pMCD = ( MCastGroupDesc_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xMulticastGroupsList ) ); - ( void ) vModifyMulticastMembership( pMCD, eSocketOptDropMembership ); - } + prvDropMulticastMembership( pxSocket ); } #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ @@ -6466,7 +6461,9 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket ) case FREERTOS_SO_IP_ADD_MEMBERSHIP: { - if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) ) + if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || + ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) || + ( pxSocket->bits.bIsIPv6 != pdFALSE ) ) { break; /* will return -pdFREERTOS_ERRNO_EINVAL */ } @@ -6496,22 +6493,20 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket ) break; } - /* Store this whole option in case we are later called to drop this membership. - * This data also needs to be stored so that incoming packets can be filtered properly. - * This can be done more efficiently, but storing the whole struct will be more portable - * in the multi-interface, dual-stack case.*/ + /* Store this whole option so we can pass it to the IP task. */ ( void ) memcpy( ( void * ) &pxMCG->mreq, pvOptionValue, uxOptionLength ); - listSET_LIST_ITEM_OWNER( &( pxMCG->xListItem ), ( void * ) pxMCG ); pxMCG->pxSocket = pxSocket; + pxMCG->xMulticastGroup.xIs_IPv6 = pdFALSE; + pxMCG->xMulticastGroup.xIPAddress.ulIP_IPv4 = pMReq->imr_multiaddr.s_addr; /* Init the IGMP report description. It needs to hold the same membership information - * and it will eventually be added to the IGMP list. */ + * and it will eventually be added to the IGMP list. + * Note: fileds like xNumSockets and ucCountDown don't need to be initialized. They will + * be set to their proper values if this reports is added to the global list. */ pxMRD->xMCastGroupAddress.xIs_IPv6 = pdFALSE_UNSIGNED; pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 = pMReq->imr_multiaddr.s_addr; pxMRD->pxEndPoint = NULL; listSET_LIST_ITEM_OWNER( &( pxMRD->xListItem ), ( void * ) pxMRD ); - pxMRD->xNumSockets = 0; - pxMRD->ucCountDown = 0; /* Quick and dirty assignment of end-point. This will probably have to be re-designed and re-done. */ pxMRD->pxEndPoint = ( pxSocket->bits.bIsIPv6 ) ? FreeRTOS_FirstEndPoint_IPv6( FreeRTOS_FirstNetworkInterface() ) : FreeRTOS_FirstEndPoint( FreeRTOS_FirstNetworkInterface() ); @@ -6537,7 +6532,9 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket ) case FREERTOS_SO_IP_DROP_MEMBERSHIP: { - if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) ) + if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || + ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) || + ( pxSocket->bits.bIsIPv6 != pdFALSE ) ) { break; /* will return -pdFREERTOS_ERRNO_EINVAL */ } @@ -6563,7 +6560,6 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket ) * This can be done more efficiently, but storing the whole struct will be more portable * in the multi-interface, dual-stack case.*/ ( void ) memcpy( ( void * ) &pxMCG->mreq, pvOptionValue, uxOptionLength ); - listSET_LIST_ITEM_OWNER( &( pxMCG->xListItem ), ( void * ) pxMCG ); pxMCG->pxSocket = pxSocket; /* When dropping memberships, we don't need an IGMP report descriptor. */ diff --git a/source/FreeRTOS_UDP_IPv4.c b/source/FreeRTOS_UDP_IPv4.c index 27557437dd..f51243ebdb 100644 --- a/source/FreeRTOS_UDP_IPv4.c +++ b/source/FreeRTOS_UDP_IPv4.c @@ -374,44 +374,37 @@ BaseType_t xProcessReceivedUDPPacket_IPv4( NetworkBufferDescriptor_t * pxNetwork *pxIsWaitingForARPResolution = pdFALSE; - #if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) - - /* If this incoming packet is a multicast, we may have a socket for the port, but we still need - * to ensure the socket is subscribed to that particular multicast group. Note: Since this stack - * does not support port reusing, we don't have to worry about two UDP sockets bound to the exact same - * local port, but subscribed to different multicast groups. If this was allowed, this check - * would have to be moved to the pxUDPSocketLookup() function itself. */ - if( ( pxSocket != NULL ) && ( xIsIPv4Multicast( pxUDPPacket->xIPHeader.ulDestinationIPAddress ) ) ) + do + { + if( pxSocket != NULL ) { - /* Note: There is no need for vTaskSuspendAll/xTaskResumeAll here because only the IP Task - * touches the socket's multicast groups list directly. */ - const ListItem_t * pxIterator; - const ListItem_t * xEnd = listGET_END_MARKER( &( pxSocket->u.xUDP.xMulticastGroupsList ) ); - - for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd ); - pxIterator != ( const ListItem_t * ) xEnd; - pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) ) - { - MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) listGET_LIST_ITEM_OWNER( pxIterator ); + #if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) - if( pxMCG->mreq.imr_multiaddr.s_addr == pxUDPPacket->xIPHeader.ulDestinationIPAddress ) + /* If this incoming packet is a multicast, we may have a socket for the port, but we still need + * to ensure the socket is subscribed to that particular multicast group. Note: Since this stack + * does not support port reusing, we don't have to worry about two UDP sockets bound to the exact same + * local port, but subscribed to different multicast groups. If this was allowed, this check + * would have to be moved to the pxUDPSocketLookup() function itself. */ + if( xIsIPv4Multicast( pxUDPPacket->xIPHeader.ulDestinationIPAddress ) ) { - break; + if( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 != pxUDPPacket->xIPHeader.ulDestinationIPAddress ) + { + /* This socket is not subscribed to this multicast group. Nullify the result from pxUDPSocketLookup(). + * Setting the socket to NULL is not strictly necessary. Leave here for clarity and insurance. */ + pxSocket = NULL; + /* return pdFAIL so the buffer can be released */ + xReturn = pdFAIL; + /* Do not continue parsing */ + break; + } } - } - - if( pxIterator == ( const ListItem_t * ) xEnd ) - { - /* This socket is not subscribed to this multicast group. Nullify the result from pxUDPSocketLookup() */ - pxSocket = NULL; - } - } - #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ + else + { + /* The incoming packet is not a multicast and we already know it + * matches this socket's port number, so just proceed */ + } + #endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */ - do - { - if( pxSocket != NULL ) - { if( ( pxEndpoint != NULL ) && ( pxEndpoint->ipv4_settings.ulIPAddress != 0U ) ) { if( xCheckRequiresARPResolution( pxNetworkBuffer ) == pdTRUE ) diff --git a/source/include/FreeRTOS_DNS.h b/source/include/FreeRTOS_DNS.h index 67dc7f6d5b..533c523dcd 100644 --- a/source/include/FreeRTOS_DNS.h +++ b/source/include/FreeRTOS_DNS.h @@ -67,10 +67,10 @@ uint32_t ulDNSHandlePacket( const NetworkBufferDescriptor_t * pxNetworkBuffer ); #if ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_IPv6 != 0 ) -/* The MDNS IPv6 address is ff02::1:3 */ +/* The MDNS IPv6 address is ff02::fb */ extern const IPv6_Address_t ipMDNS_IP_ADDR_IPv6; -/* The MDNS IPv6 MAC address is 33:33:00:01:00:03 */ +/* The MDNS IPv6 MAC address is 33:33:00:00:00:fb */ extern const MACAddress_t xMDNS_MACAddressIPv6; #endif /* ipconfigUSE_MDNS */ diff --git a/source/include/FreeRTOS_IGMP.h b/source/include/FreeRTOS_IGMP.h index 9f57c21576..82c748ca80 100644 --- a/source/include/FreeRTOS_IGMP.h +++ b/source/include/FreeRTOS_IGMP.h @@ -62,14 +62,13 @@ #define ipIGMP_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 ucMaxResponseTime; /**< Maximum time (sec) for responses. 1 + 1 = 2 */ uint16_t usChecksum; /**< The checksum of whole IGMP packet 2 + 2 = 4 */ - uint32_t uiGroupAddress; /**< The multicast group address 4 + 4 = 8 */ + uint32_t ulGroupAddress; /**< The multicast group address 4 + 4 = 8 */ } #include "pack_struct_end.h" typedef struct xIGMP_HEADER IGMPHeader_t; @@ -89,16 +88,17 @@ uint8_t bAction ); BaseType_t xSendIGMPEvent( void ); void vHandleIGMP_Event( void ); - void vRemoveIGMPReportFromList( struct freertos_ip_mreq * pMCastGroup ); BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry ); eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); + void vProcessMLDPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer ); BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddress, NetworkEndPoint_t * pxEndPoint, TickType_t uxBlockTimeTicks ); + void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ); /* move to Sockets.c as static */ #ifdef __cplusplus -} /* extern "C" */ + } /* extern "C" */ #endif #endif /* FREERTOS_IP_PRIVATE_H */ diff --git a/source/include/FreeRTOS_IP_Private.h b/source/include/FreeRTOS_IP_Private.h index 200115aba0..44cd23a393 100644 --- a/source/include/FreeRTOS_IP_Private.h +++ b/source/include/FreeRTOS_IP_Private.h @@ -648,16 +648,16 @@ typedef struct UDPSOCKET FOnUDPSent_t pxHandleSent; /**< Function pointer to handle the events after a successful send. */ #endif /* ipconfigUSE_CALLBACKS */ #if ( ipconfigSUPPORT_IP_MULTICAST != 0 ) - List_t xMulticastGroupsList; - BaseType_t xMulticastTTL; /**< Allows for multicast sockets to use a different TTL value to limit the scope of the multicast packet. Usually set to 1. - * Note that the options are of different sizes for IPv4 and IPv6. - * Example: - * uint8_t ttl = 1; - * FreeRTOS_setsockopt( MCastSendSock, 0, FREERTOS_SO_IP_MULTICAST_TTL, ( void * ) &ttl, sizeof( ttl ) ); - * IPv6 Example: - * BaseType_t hops = 1; - * FreeRTOS_setsockopt( MCastSendSock_v6, 0, FREERTOS_SO_IPV6_MULTICAST_HOPS, ( void * ) &hops, sizeof( hops ) ); - */ + IP_Address_t xMulticastAddress; /**< Holds the multicast group address that the socket may have subscribed to receive. */ + BaseType_t xMulticastTTL; /**< Allows for multicast sockets to use a different TTL value to limit the scope of the multicast packet. Usually set to 1. + * Note that the options are of different sizes for IPv4 and IPv6. + * Example: + * uint8_t ttl = 1; + * FreeRTOS_setsockopt( MCastSendSock, 0, FREERTOS_SO_IP_MULTICAST_TTL, ( void * ) &ttl, sizeof( ttl ) ); + * IPv6 Example: + * BaseType_t hops = 1; + * FreeRTOS_setsockopt( MCastSendSock_v6, 0, FREERTOS_SO_IPV6_MULTICAST_HOPS, ( void * ) &hops, sizeof( hops ) ); + */ #endif } IPUDPSocket_t; diff --git a/source/include/FreeRTOS_IP_Utils.h b/source/include/FreeRTOS_IP_Utils.h index b31298c23b..937e2f7fb7 100644 --- a/source/include/FreeRTOS_IP_Utils.h +++ b/source/include/FreeRTOS_IP_Utils.h @@ -73,14 +73,14 @@ ListItem_t xListItem; /**< List item for adding to the global list of reports. */ NetworkEndPoint_t * pxEndPoint; /**< The end-point whose source address will be send for sending this report. NULL to send on the first end-point of every interface. */ BaseType_t xNumSockets; /**< The number of sockets that are subscribed to this multicast group. */ - uint8_t ucCountDown; + BaseType_t xCountDown; } MCastReportData_t; /** @brief The structure to hold a "descriptor" for a multicast group that a socket has registered to. */ typedef struct xMCastGroupDesc { + IPv46_Address_t xMulticastGroup; /**< Holds the IPv4/IPv6 multicast group address */ struct freertos_ip_mreq mreq; /**< Struct for storing the original mreq structure that was sent to setsockopts() */ - struct xLIST_ITEM xListItem; /**< List struct. */ FreeRTOS_Socket_t * pxSocket; MCastReportData_t * pxIGMPReportDesc; /**< Optional. used to hold the allocated IGMP report descriptor while passing from user code to the IP Task. */ } MCastGroupDesc_t; diff --git a/source/include/FreeRTOS_IPv6_Private.h b/source/include/FreeRTOS_IPv6_Private.h index e1461c5b1e..b0f1926496 100644 --- a/source/include/FreeRTOS_IPv6_Private.h +++ b/source/include/FreeRTOS_IPv6_Private.h @@ -236,7 +236,7 @@ typedef struct xICMPRouterSolicitation_IPv6 ICMPRouterSolicitation_IPv6_t; typedef struct xIP_HOP_BY_HOP_EXT_ROUTER_ALERT_IPv6 IPHopByHopExtRouterAlert_IPv6_t; #include "pack_struct_start.h" - struct xICMPv6_MLD_REPORTv1 + struct xICMPv6_MLDv1 { uint8_t ucTypeOfMessage; /**< The message type. 0 + 1 = 1 */ uint8_t ucTypeOfService; /**< Type of service. 1 + 1 = 2 */ @@ -246,17 +246,33 @@ typedef struct xICMPRouterSolicitation_IPv6 ICMPRouterSolicitation_IPv6_t; IPv6_Address_t xGroupAddress; /**< The IPv6 address. 8 + 16 = 24 */ } #include "pack_struct_end.h" - typedef struct xICMPv6_MLD_REPORTv1 ICMPv6_MLDReportv1_t; + typedef struct xICMPv6_MLDv1 ICMPv6_MLDv1_t; - struct xICMPv6_MLD_REPORTv1_PACKET +/* Note: MLD packets are required to use the Router-Alert option + * in an IPv6 extension header. */ + #include "pack_struct_start.h" + struct xICMPv6_MLDv1_TX_PACKET { EthernetHeader_t xEthernetHeader; /* 0 + 14 = 14 */ IPHeader_IPv6_t xIPHeader; /* 14 + 40 = 54 */ IPHopByHopExtRouterAlert_IPv6_t xRAOption; /* 54 + 8 = 62 */ - ICMPv6_MLDReportv1_t xICMPv6_MLDReport; /* 62 + 24 = 86 */ + ICMPv6_MLDv1_t xMLD; /* 62 + 24 = 86 */ + } + #include "pack_struct_end.h" + typedef struct xICMPv6_MLDv1_TX_PACKET MLDv1_Tx_Packet_t; + +/* This TCP stack strips the extension headers from IPv6 packets, so even though + * MLD packets include a Router-Alert option in an IPv6 extension header, the ICMP + * layer will not see it in the packet prvProcessIPPacket() stripped the extension headers. */ + #include "pack_struct_start.h" + struct xICMPv6_MLDv1_RX_PACKET + { + EthernetHeader_t xEthernetHeader; /* 0 + 14 = 14 */ + IPHeader_IPv6_t xIPHeader; /* 14 + 40 = 54 */ + ICMPv6_MLDv1_t xMLD; /* 54 + 24 = 78 */ } #include "pack_struct_end.h" - typedef struct xICMPv6_MLD_REPORTv1_PACKET MLDReportPacket_v1_t; + typedef struct xICMPv6_MLDv1_RX_PACKET MLDv1_Rx_Packet_t; /** @brief Options that can be sent in a Multicast Listener Report packet. * more info at https://www.rfc-editor.org/rfc/rfc2711#section-2.0 */