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 */