Skip to content

Commit

Permalink
Moves more functions to FreeRTOS_Sockets.c
Browse files Browse the repository at this point in the history
Sends IGMPv2 Leave Group messages whenever the last socket subscribed to a group drops that membership.
Adds ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL for debug purposes when there is no IGMP/MLD querier
  • Loading branch information
Emil Popov committed Nov 13, 2023
1 parent 05873a1 commit 9b911f6
Show file tree
Hide file tree
Showing 7 changed files with 288 additions and 255 deletions.
274 changes: 41 additions & 233 deletions source/FreeRTOS_IGMP.c

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions source/FreeRTOS_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,12 +475,12 @@ static void prvProcessIPEventsAndTimers( void )
case eSocketOptDropMembership:
{
MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData;
( void ) vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
break;
}

case eIGMPEvent:
( void ) vHandleIGMP_Event();
case eMulticastTimerEvent:
vHandleIGMP_Event();
break;
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0) */

Expand Down Expand Up @@ -688,7 +688,7 @@ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint )
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 12 ] = 0xFFU;
( void ) memcpy( &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 13 ], &pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ], 3 );

if( pdTRUE != xAddIGMPReportToList( pxMRD ) )
if( pdTRUE != xAddMulticastReportToList( pxMRD ) )
{
vPortFree( pxMRD );
pxMRD = NULL;
Expand Down
4 changes: 2 additions & 2 deletions source/FreeRTOS_IP_Timers.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ void vCheckNetworkTimers( void )
/* Is it time to send any IGMP reports? */
if( prvIPTimerCheck( &xIGMPTimer ) != pdFALSE )
{
( void ) xSendIGMPEvent();
( void ) xSendMulticastTimerEvent();
}
}
#endif /* ipconfigSUPPORT_IP_MULTICAST != 0 */
Expand Down Expand Up @@ -448,7 +448,7 @@ void vARPTimerReload( TickType_t xTime )
*/
void vIGMPTimerReload( TickType_t xIgmpTickTime )
{
prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( ipIGMP_TIMER_PERIOD_MS ) );
prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( igmpMULTICAST_EVENT_PERIOD_MS ) );
}
#endif /* ipconfigSUPPORT_IP_MULTICAST */
/*-----------------------------------------------------------*/
Expand Down
202 changes: 202 additions & 0 deletions source/FreeRTOS_Sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ static int32_t prvSendTo_ActualSend( const FreeRTOS_Socket_t * pxSocket,
int32_t lOptionName,
const void * pvOptionValue,
size_t uxOptionLength );
static void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket );
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

/*-----------------------------------------------------------*/
Expand Down Expand Up @@ -6612,4 +6613,205 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket )

return xReturn;
}

/**
* @brief Adds or drops a multicast group to/from a socket.
*
* @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for.
* @param[in] bAction: MUST be eSocketOptAddMembership or eSocketOptDropMembership.
*/
void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction,
uint8_t bAction )
{
uint8_t MCastMacBytes[ 6 ];
FreeRTOS_Socket_t * pxSocket = pxMulticastAction->pxSocket;
uint8_t bFreeMatchedItem = pdFALSE;
NetworkInterface_t * pxNetIf = pxMulticastAction->pxInterface;
BaseType_t bReportItemConsumed = pdFALSE;

configASSERT( pxSocket != NULL );

/* Note: This function is only called with eSocketOptDropMembership or eSocketOptAddMembership*/

/* This TCP stack does NOT support sockets subscribing to more than one multicast group.
* If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the
* IGMP/MLD reports corresponding to that group address. */
prvDropMulticastMembership( pxSocket );

if( eSocketOptAddMembership == bAction )
{
/* Store the multicast address. */
( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastAction->xMulticastGroup ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) );

if( pxSocket->bits.bIsIPv6 == pdFALSE )
{
vSetMultiCastIPv4MacAddress( pxMulticastAction->xMulticastGroup.ulIP_IPv4, MCastMacBytes );
}
else
{
vSetMultiCastIPv6MacAddress( &( pxMulticastAction->xMulticastGroup.xIP_IPv6 ), MCastMacBytes );
}

/* Inform the network driver */
if( pxNetIf )
{
if( pxNetIf->pfAddMulticastMAC != NULL )
{
pxNetIf->pfAddMulticastMAC( MCastMacBytes );
}
}
else
{
/* pxNetIf is NULL. For IPv4 that means "use all interfaces". For IPv6, that means "use the default multicast interface"
* FreeRTOS+TCP does not have the notion of default multicast interface so for now, subscribe on all. */
for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) )
{
if( pxNetIf->pfAddMulticastMAC != NULL )
{
pxNetIf->pfAddMulticastMAC( MCastMacBytes );
}
}
}

/* Remember which interface(s) this socket is subscribed on. */
pxSocket->u.xUDP.pxMulticastNetIf = pxMulticastAction->pxInterface;

/* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report
* for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist.
* If such an IGMP/MLD report is already present in the list, we will increment it's socket
* count and free the report we have here. In either case, the MulticastAction_t that we were
* passed, no longer needs to hold a reference to this multicast report. */
do
{
if( pxMulticastAction->pxMCastReportData == NULL )
{
break;
}

if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIs_IPv6 == pdTRUE )
{
/* RFC2710 end of section section 5 and RFC3810 section 6:
* ff02::1 is a special case and we do not send reports for it. */
static const struct xIPv6_Address FreeRTOS_in6addr_allnodes = { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } };

if( memcmp( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, FreeRTOS_in6addr_allnodes.ucBytes, sizeof( IPv6_Address_t ) ) == 0 )
{
break;
}

/* RFC2710 end of section section 5 and RFC3810 section 6:
* Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local).
* Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/
if( ( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] & 0x0FU ) <= 1 )
{
break;
}
}
else
{
/* RFC2236 end of section 6:
* 224.0.0.1 is a special case and we do not send reports for it. */
if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == igmpIGMP_IP_ADDR )
{
break;
}
}

bReportItemConsumed = xAddMulticastReportToList( pxMulticastAction->pxMCastReportData );
} while( pdFALSE );

/* If the report was a special case address or was not consumed by
* xAddMulticastReportToList(), free the multicast report. */
if( bReportItemConsumed == pdFALSE )
{
vPortFree( pxMulticastAction->pxMCastReportData );
pxMulticastAction->pxMCastReportData = NULL;
}
}

/* Free the message that was sent to us. */
vPortFree( pxMulticastAction );
}

static void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
{
uint8_t MCastMacBytes[ 6 ];
UBaseType_t uxLeaveGroup = pdFALSE_UNSIGNED;
NetworkInterface_t * pxNetIf = pxSocket->u.xUDP.pxMulticastNetIf;

if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED )
{
/* IPv6 */
if( xIsIPv6AllowedMulticast( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ) ) )
{
uxLeaveGroup = pdTRUE_UNSIGNED;

if( pxNetIf )
{
if( pxNetIf->pfRemoveMulticastMAC != NULL )
{
vSetMultiCastIPv6MacAddress( &( pxSocket->u.xUDP.xMulticastAddress.xIP_IPv6 ), MCastMacBytes );
pxNetIf->pfRemoveMulticastMAC( MCastMacBytes );
}
}
else
{
}
}
else
{
/* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valid multicast group
* or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group.
* Do nothing. */
}
}
else
{
/* IPv4 */
if( xIsIPv4Multicast( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 ) )
{
uxLeaveGroup = pdTRUE_UNSIGNED;

vSetMultiCastIPv4MacAddress( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4, MCastMacBytes );

if( pxNetIf )
{
if( pxNetIf->pfRemoveMulticastMAC != NULL )
{
pxNetIf->pfRemoveMulticastMAC( MCastMacBytes );
}
}
else
{
/* pxNetIf is NULL. For IPv4 that means "all interfaces", so unsubscribe from all. */
for( pxNetIf = FreeRTOS_FirstNetworkInterface(); pxNetIf != NULL; pxNetIf = FreeRTOS_NextNetworkInterface( pxNetIf ) )
{
if( pxNetIf->pfRemoveMulticastMAC != NULL )
{
pxNetIf->pfRemoveMulticastMAC( MCastMacBytes );
}
}
}
}
else
{
/* Whatever is stored in pxSocket->u.xUDP.xMulticastAddress is not a valid multicast group
* or prvDropMulticastMembership was called for a socket that is not still subscribed to a multicast group.
* Do nothing. */
}
}

if( uxLeaveGroup == pdTRUE_UNSIGNED )
{
/* Locate the IGMP/MLD report for this group. Decrement its socket count and
* if it becomes zero, remove it from the list and free it. */
vRemoveMulticastReportFromList( &( pxSocket->u.xUDP.xMulticastAddress ), ( UBaseType_t ) pxSocket->bits.bIsIPv6 );
}

/* Invalidate the multicast group address to prevent erroneous matches if someone calls
* FREERTOS_SO_IP_DROP_MEMBERSHIP multiple times. */
memset( &pxSocket->u.xUDP.xMulticastAddress, 0x00, sizeof( pxSocket->u.xUDP.xMulticastAddress ) );
pxSocket->u.xUDP.pxMulticastNetIf = NULL; /* not really needed, but just looks cleaner when debugging. */
}

#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
18 changes: 18 additions & 0 deletions source/include/FreeRTOSIPConfigDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -3321,6 +3321,24 @@

/*---------------------------------------------------------------------------*/

/*
* ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL
*
* Type: BaseType_t
* Unit: count of igmpMULTICAST_EVENT_PERIOD_MS
*
* When set to -1, no periodic unsolicited multicast reports are sent out.
* This is the correct behavior. For debug purposes, set to > 0 to cause
* periodic sending of multicast reports even if there are no IGMP/MLD
* queries heard. Example: 150 = 15.0 seconds.
* Note: Maybe remove that ?
*/
#ifndef ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL
#define ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL ( -1 )
#endif

/*---------------------------------------------------------------------------*/

/*
* ipconfigMULTICAST_DEFAULT_TTL
*
Expand Down
26 changes: 11 additions & 15 deletions source/include/FreeRTOS_IGMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,26 +46,26 @@
#include "event_groups.h"


/** @brief IGMP times events at 100ms. */
#define ipIGMP_TIMER_PERIOD_MS ( 100U )
/** @brief IGMP times events at 100ms. Use this interval for MLD as well. */
#define igmpMULTICAST_EVENT_PERIOD_MS ( 100U )

/* IGMP protocol definitions. */
#define ipIGMP_MEMBERSHIP_QUERY ( ( uint8_t ) 0x11U ) /**< IGMP membership query. */
#define ipIGMP_MEMBERSHIP_REPORT_V1 ( ( uint8_t ) 0x12U ) /**< IGMP v1 and v2 membership report. */
#define ipIGMP_MEMBERSHIP_REPORT_V2 ( ( uint8_t ) 0x16U ) /**< IGMP v1 and v2 membership report. */
#define ipIGMP_MEMBERSHIP_REPORT_V3 ( ( uint8_t ) 0x22U ) /**< IGMP v3 membership report. */
#define ipIGMP_LEAVE_GROUP ( ( uint8_t ) 0x17U ) /**< IGMP leave group */
#define igmpIGMP_MEMBERSHIP_QUERY ( ( uint8_t ) 0x11U ) /**< IGMP membership query. */
#define igmpIGMP_MEMBERSHIP_REPORT_V1 ( ( uint8_t ) 0x12U ) /**< IGMP v1 and v2 membership report. */
#define igmpIGMP_MEMBERSHIP_REPORT_V2 ( ( uint8_t ) 0x16U ) /**< IGMP v1 and v2 membership report. */
#define igmpIGMP_MEMBERSHIP_REPORT_V3 ( ( uint8_t ) 0x22U ) /**< IGMP v3 membership report. */
#define igmpIGMP_LEAVE_GROUP ( ( uint8_t ) 0x17U ) /**< IGMP leave group */

#if ( ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN )
#define ipIGMP_IP_ADDR 0xE0000001UL
#define igmpIGMP_IP_ADDR 0xE0000001UL
#else
#define ipIGMP_IP_ADDR 0x010000E0UL
#define igmpIGMP_IP_ADDR 0x010000E0UL
#endif /* ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN */

#include "pack_struct_start.h"
struct xIGMP_HEADER
{
uint8_t ucTypeOfMessage; /**< The IGMP type 0 + 1 = 1 */
uint8_t ucMessageType; /**< The IGMP type 0 + 1 = 1 */
uint8_t ucMaxResponseTime; /**< Maximum time (sec) for responses. 1 + 1 = 2 */
uint16_t usChecksum; /**< The checksum of whole IGMP packet 2 + 2 = 4 */
uint32_t ulGroupAddress; /**< The multicast group address 4 + 4 = 8 */
Expand All @@ -84,14 +84,10 @@
typedef struct xIGMP_PACKET IGMPPacket_t;

void vIGMP_Init( void );
void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction,
uint8_t bAction );
BaseType_t xSendIGMPEvent( void );
void vHandleIGMP_Event( void );
BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry );
BaseType_t xAddMulticastReportToList( MCastReportData_t * pNewEntry );
eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer );
void vProcessMLDPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer );
void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ); /* move to Sockets.c as static */


#ifdef __cplusplus
Expand Down
11 changes: 10 additions & 1 deletion source/include/FreeRTOS_IP_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ typedef enum
eSocketSetDeleteEvent, /*13: A socket set must be deleted. */
eSocketOptAddMembership, /*14: Register a UDP socket to a multicast group. */
eSocketOptDropMembership, /*15: Unregister a UDP socket from a multicast group. */
eIGMPEvent, /*16: */
eMulticastTimerEvent, /*16: Handles the sending of scheduled IGMP/MLD multicast reports. */
} eIPEvent_t;

/**
Expand Down Expand Up @@ -892,6 +892,15 @@ BaseType_t xIsCallingFromIPTask( void );

#endif /* ipconfigSUPPORT_SELECT_FUNCTION */

#if ( ipconfigSUPPORT_IP_MULTICAST == 1 )
struct xMCastGroupDesc;
void vModifyMulticastMembership( struct xMCastGroupDesc * pxMulticastAction,
uint8_t bAction );
void vRemoveMulticastReportFromList( IP_Address_t * pxMulticastAddress,
UBaseType_t xIsIPv6 );
BaseType_t xSendMulticastTimerEvent( void );
#endif /* ipconfigSUPPORT_IP_MULTICAST */

/* Send the network-up event and start the ARP timer. */
void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint );

Expand Down

0 comments on commit 9b911f6

Please sign in to comment.