Skip to content

Commit

Permalink
Adds support for receiving IPv4 and IPv6 multicast group
Browse files Browse the repository at this point in the history
Adds parsing of IGMP and MLD queries.
Sends IGMPv2 and MLDv1 reports on a schedule that is updated based on received IGMP/MLD queries.
Sends unsolicited IGMP and MLD reports on network-up events and on add-membership socket option.
Adds 2 function pointers to the network interface struct that handle adding and removing  multicast MAC addresses.
Adds pxSocket->u.xUDP.xMulticastTTL that can be used for both IPv4 and IPv6
Adds pxSocket->u.xUDP.xMulticastAddress that can be used for both IPv4 and IPv6
Adds socket option defines to cover IPv4, IPv6 as well as source-specific multicast
Adds defines for MLD packets like the Multicast Listener Query and Report
Generates an MLD report for the solicited-node multicast addresses corresponding to all unicast IPv6 addresses

Improves the SAME70 driver to handle adding/removing muticast MAC addresses
  • Loading branch information
Emil Popov committed Oct 25, 2023
1 parent 1474378 commit ee52085
Show file tree
Hide file tree
Showing 22 changed files with 2,048 additions and 50 deletions.
5 changes: 5 additions & 0 deletions source/FreeRTOS_DNS_Networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
* going to be '0' i.e. success. Thus, return value is discarded */
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, &( uxWriteTimeOut_ticks ), sizeof( TickType_t ) );
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &( uxReadTimeOut_ticks ), sizeof( TickType_t ) );
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
/* Since this socket may be used for LLMNR or mDNS, set the multicast TTL to 1. */
uint8_t ucMulticastTTL = 1;
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_IP_MULTICAST_TTL, &( ucMulticastTTL ), sizeof( ucMulticastTTL ) );
#endif
}

return xSocket;
Expand Down
1,007 changes: 1,007 additions & 0 deletions source/FreeRTOS_IGMP.c

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions source/FreeRTOS_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
#include "FreeRTOS_DNS.h"
#include "FreeRTOS_Routing.h"
#include "FreeRTOS_ND.h"
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
#include "FreeRTOS_IGMP.h"
#endif

/** @brief Time delay between repeated attempts to initialise the network hardware. */
#ifndef ipINITIALISATION_RETRY_DELAY
Expand Down Expand Up @@ -467,6 +470,20 @@ static void prvProcessIPEventsAndTimers( void )
/* xQueueReceive() returned because of a normal time-out. */
break;

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
case eSocketOptAddMembership:
case eSocketOptDropMembership:
{
MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) xReceivedEvent.pvData;
( void ) vModifyMulticastMembership( pxMCG, xReceivedEvent.eEventType );
break;
}

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

default:
/* Should not get here. */
break;
Expand Down Expand Up @@ -526,6 +543,11 @@ static void prvIPTask_Initialise( void )
}
#endif /* ( ( ipconfigUSE_DNS_CACHE != 0 ) && ( ipconfigUSE_DNS != 0 ) ) */

/* Init the list that will hold scheduled IGMP reports. */
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
( void ) vIGMP_Init();
#endif

/* Initialisation is complete and events can now be processed. */
xIPTaskInitialised = pdTRUE;
}
Expand Down Expand Up @@ -631,8 +653,76 @@ TaskHandle_t FreeRTOS_GetIPTaskHandle( void )
*/
void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint )
{
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
MCastReportData_t * pxMRD;
IPv6_Type_t xAddressType;
MACAddress_t xMACAddress;
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

pxEndPoint->bits.bEndPointUp = pdTRUE_UNSIGNED;

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED )
{
/* Now that the network is up, pxEndPoint->ipv6_settings should hold the actual address of this
* end-point. For unicast addresses, generate the solicited-node multicast address that corresponds
* to the address and generate an MLD report for it.
* ToDo: Figure out what the proper place is to remove multicast addresses that are no longer valid. For
* example when a DHCPv6 lease expires. */
xAddressType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) );

if( ( xAddressType == eIPv6_LinkLocal ) || ( xAddressType == eIPv6_SiteLocal ) || ( xAddressType == eIPv6_Global ) )
{
if( NULL != ( pxMRD = ( MCastReportData_t * ) pvPortMalloc( sizeof( MCastReportData_t ) ) ) )
{
listSET_LIST_ITEM_OWNER( &( pxMRD->xListItem ), ( void * ) pxMRD );
pxMRD->pxEndPoint = pxEndPoint;
pxMRD->xMCastGroupAddress.xIs_IPv6 = pdTRUE_UNSIGNED;

/* Generate the solicited-node multicast address in the form of
* ff02::1:ffnn:nnnn, where nn:nnnn are the last 3 bytes of the IPv6 address. */
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ] = 0xFFU;
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] = 0x02U;
( void ) memset( &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 2 ], 0x00, 9 );
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 11 ] = 0x01U;
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 ) )
{
vPortFree( pxMRD );
pxMRD = NULL;
}
else
{
/* The report was consumed, therefore it was added to the list. Tell the network
* driver to begin receiving the associated MAC address */
if( pxEndPoint->pxNetworkInterface && ( pxEndPoint->pxNetworkInterface->pfAddMulticastMAC != NULL ) )
{
xMACAddress.ucBytes[ 0 ] = 0x33;
xMACAddress.ucBytes[ 1 ] = 0x33;
xMACAddress.ucBytes[ 2 ] = 0xFF;
xMACAddress.ucBytes[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
xMACAddress.ucBytes[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
xMACAddress.ucBytes[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
pxEndPoint->pxNetworkInterface->pfAddMulticastMAC( xMACAddress.ucBytes );
}
}
}
}
} /* 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 )
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
{
Expand Down Expand Up @@ -1976,6 +2066,13 @@ static eFrameProcessingResult_t prvProcessIPPacket( const IPPacket_t * pxIPPacke
break;
#endif /* ( ipconfigUSE_IPv6 != 0 ) */

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
case ipPROTOCOL_IGMP:
/* The IP packet contained an IGMP frame. */
eReturn = eProcessIGMPPacket( pxNetworkBuffer );
break;
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

case ipPROTOCOL_UDP:
/* The IP packet contained a UDP frame. */

Expand Down
41 changes: 41 additions & 0 deletions source/FreeRTOS_IP_Timers.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
#include "NetworkBufferManagement.h"
#include "FreeRTOS_Routing.h"
#include "FreeRTOS_DNS.h"
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
#include "FreeRTOS_IGMP.h"
#endif
/*-----------------------------------------------------------*/

/** @brief 'xAllNetworksUp' becomes pdTRUE when all network interfaces are initialised
Expand Down Expand Up @@ -110,6 +113,11 @@ static IPTimer_t xARPTimer;
static IPTimer_t xDNSTimer;
#endif

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
/** @brief IGMP timer. Used for sending scheduled IGMP Reports */
static IPTimer_t xIGMPTimer;
#endif

/** @brief As long as not all networks are up, repeat initialisation by calling the
* xNetworkInterfaceInitialise() function of the interfaces that are not ready. */

Expand Down Expand Up @@ -176,6 +184,15 @@ TickType_t xCalculateSleepTime( void )
}
#endif

#if ( ipconfigSUPPORT_IP_MULTICAST == 1 )
{
if( xIGMPTimer.ulRemainingTime < uxMaximumSleepTime )
{
uxMaximumSleepTime = xIGMPTimer.ulRemainingTime;
}
}
#endif /* ipconfigSUPPORT_IP_MULTICAST */

#if ( ipconfigDNS_USE_CALLBACKS != 0 )
{
if( xDNSTimer.bActive != pdFALSE_UNSIGNED )
Expand Down Expand Up @@ -312,6 +329,16 @@ void vCheckNetworkTimers( void )
vSocketListenNextTime( NULL );
#endif /* ipconfigUSE_TCP == 1 */

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
{
/* Is it time to send any IGMP reports? */
if( prvIPTimerCheck( &xIGMPTimer ) != pdFALSE )
{
( void ) xSendIGMPEvent();
}
}
#endif /* ipconfigSUPPORT_IP_MULTICAST != 0 */

/* Is it time to trigger the repeated NetworkDown events? */
if( xAllNetworksUp == pdFALSE )
{
Expand Down Expand Up @@ -412,6 +439,20 @@ void vARPTimerReload( TickType_t xTime )

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

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )

/**
* @brief Reload the IGMP timer.
*
* @param[in] xIgmpTickTime: The reload value.
*/
void vIGMPTimerReload( TickType_t xIgmpTickTime )
{
prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( ipIGMP_TIMER_PERIOD_MS ) );
}
#endif /* ipconfigSUPPORT_IP_MULTICAST */
/*-----------------------------------------------------------*/

#if ( ipconfigDNS_USE_CALLBACKS != 0 )

/**
Expand Down
3 changes: 2 additions & 1 deletion source/FreeRTOS_IPv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ BaseType_t xIsIPv6AllowedMulticast( const IPv6_Address_t * pxIPAddress )
/**
* @brief Compares 2 IPv6 addresses and checks if the one
* on the left can handle the one on right. Note that 'xCompareIPv6_Address' will also check if 'pxRight' is
* the special unicast address: ff02::1:ffnn:nnnn, where nn:nnnn are
* the special multicast address: ff02::1:ffnn:nnnn, where nn:nnnn are
* the last 3 bytes of the IPv6 address.
*
* @param[in] pxLeft First IP address.
Expand All @@ -410,6 +410,7 @@ BaseType_t xCompareIPv6_Address( const IPv6_Address_t * pxLeft,
( pxRight->ucBytes[ 12 ] == 0xffU ) )
{
/* This is an LLMNR address. */
/* _EP_: I don't think this is LLMNR. Did someone mean to say "the solicited-node multicast address" ? */
xResult = memcmp( &( pxLeft->ucBytes[ 13 ] ), &( pxRight->ucBytes[ 13 ] ), 3 );
}
else
Expand Down
20 changes: 20 additions & 0 deletions source/FreeRTOS_ND.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit ee52085

Please sign in to comment.