Skip to content

Commit ee52085

Browse files
author
Emil Popov
committed
Adds support for receiving IPv4 and IPv6 multicast group
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
1 parent 1474378 commit ee52085

22 files changed

+2048
-50
lines changed

source/FreeRTOS_DNS_Networking.c

+5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
* going to be '0' i.e. success. Thus, return value is discarded */
8585
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_SNDTIMEO, &( uxWriteTimeOut_ticks ), sizeof( TickType_t ) );
8686
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_RCVTIMEO, &( uxReadTimeOut_ticks ), sizeof( TickType_t ) );
87+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
88+
/* Since this socket may be used for LLMNR or mDNS, set the multicast TTL to 1. */
89+
uint8_t ucMulticastTTL = 1;
90+
( void ) FreeRTOS_setsockopt( xSocket, 0, FREERTOS_SO_IP_MULTICAST_TTL, &( ucMulticastTTL ), sizeof( ucMulticastTTL ) );
91+
#endif
8792
}
8893

8994
return xSocket;

source/FreeRTOS_IGMP.c

+1,007
Large diffs are not rendered by default.

source/FreeRTOS_IP.c

+97
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@
5959
#include "FreeRTOS_DNS.h"
6060
#include "FreeRTOS_Routing.h"
6161
#include "FreeRTOS_ND.h"
62+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
63+
#include "FreeRTOS_IGMP.h"
64+
#endif
6265

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

473+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
474+
case eSocketOptAddMembership:
475+
case eSocketOptDropMembership:
476+
{
477+
MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) xReceivedEvent.pvData;
478+
( void ) vModifyMulticastMembership( pxMCG, xReceivedEvent.eEventType );
479+
break;
480+
}
481+
482+
case eIGMPEvent:
483+
( void ) vHandleIGMP_Event();
484+
break;
485+
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0) */
486+
470487
default:
471488
/* Should not get here. */
472489
break;
@@ -526,6 +543,11 @@ static void prvIPTask_Initialise( void )
526543
}
527544
#endif /* ( ( ipconfigUSE_DNS_CACHE != 0 ) && ( ipconfigUSE_DNS != 0 ) ) */
528545

546+
/* Init the list that will hold scheduled IGMP reports. */
547+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
548+
( void ) vIGMP_Init();
549+
#endif
550+
529551
/* Initialisation is complete and events can now be processed. */
530552
xIPTaskInitialised = pdTRUE;
531553
}
@@ -631,8 +653,76 @@ TaskHandle_t FreeRTOS_GetIPTaskHandle( void )
631653
*/
632654
void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint )
633655
{
656+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
657+
MCastReportData_t * pxMRD;
658+
IPv6_Type_t xAddressType;
659+
MACAddress_t xMACAddress;
660+
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
661+
634662
pxEndPoint->bits.bEndPointUp = pdTRUE_UNSIGNED;
635663

664+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
665+
if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED )
666+
{
667+
/* Now that the network is up, pxEndPoint->ipv6_settings should hold the actual address of this
668+
* end-point. For unicast addresses, generate the solicited-node multicast address that corresponds
669+
* to the address and generate an MLD report for it.
670+
* ToDo: Figure out what the proper place is to remove multicast addresses that are no longer valid. For
671+
* example when a DHCPv6 lease expires. */
672+
xAddressType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) );
673+
674+
if( ( xAddressType == eIPv6_LinkLocal ) || ( xAddressType == eIPv6_SiteLocal ) || ( xAddressType == eIPv6_Global ) )
675+
{
676+
if( NULL != ( pxMRD = ( MCastReportData_t * ) pvPortMalloc( sizeof( MCastReportData_t ) ) ) )
677+
{
678+
listSET_LIST_ITEM_OWNER( &( pxMRD->xListItem ), ( void * ) pxMRD );
679+
pxMRD->pxEndPoint = pxEndPoint;
680+
pxMRD->xMCastGroupAddress.xIs_IPv6 = pdTRUE_UNSIGNED;
681+
682+
/* Generate the solicited-node multicast address in the form of
683+
* ff02::1:ffnn:nnnn, where nn:nnnn are the last 3 bytes of the IPv6 address. */
684+
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 0 ] = 0xFFU;
685+
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] = 0x02U;
686+
( void ) memset( &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 2 ], 0x00, 9 );
687+
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 11 ] = 0x01U;
688+
pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 12 ] = 0xFFU;
689+
( void ) memcpy( &pxMRD->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 13 ], &pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ], 3 );
690+
691+
if( pdTRUE != xAddIGMPReportToList( pxMRD ) )
692+
{
693+
vPortFree( pxMRD );
694+
pxMRD = NULL;
695+
}
696+
else
697+
{
698+
/* The report was consumed, therefore it was added to the list. Tell the network
699+
* driver to begin receiving the associated MAC address */
700+
if( pxEndPoint->pxNetworkInterface && ( pxEndPoint->pxNetworkInterface->pfAddMulticastMAC != NULL ) )
701+
{
702+
xMACAddress.ucBytes[ 0 ] = 0x33;
703+
xMACAddress.ucBytes[ 1 ] = 0x33;
704+
xMACAddress.ucBytes[ 2 ] = 0xFF;
705+
xMACAddress.ucBytes[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ];
706+
xMACAddress.ucBytes[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ];
707+
xMACAddress.ucBytes[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ];
708+
pxEndPoint->pxNetworkInterface->pfAddMulticastMAC( xMACAddress.ucBytes );
709+
}
710+
}
711+
}
712+
}
713+
} /* if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) */
714+
715+
/* Reschedule all multicast reports associated with this end-point.
716+
* /* Note: countdown is in increments of ipIGMP_TIMER_PERIOD_MS. It's a good idea to spread out all reports a little.
717+
* 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,
718+
* they will soon ask us for reports anyways, so sending these unsolicited reports is not required. It simply enhances the user
719+
* experience by shortening the time it takes before we begin receiving the multicasts that we care for. */
720+
/* _EP_: vRescheduleAllMulticastReports() is NOT declared in header files because I don't want to expose it to the user */
721+
extern void vRescheduleAllMulticastReports( NetworkEndPoint_t * pxEndPoint,
722+
BaseType_t xMaxCountdown );
723+
vRescheduleAllMulticastReports( pxEndPoint, 5 );
724+
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
725+
636726
#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
637727
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
638728
{
@@ -1976,6 +2066,13 @@ static eFrameProcessingResult_t prvProcessIPPacket( const IPPacket_t * pxIPPacke
19762066
break;
19772067
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
19782068

2069+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
2070+
case ipPROTOCOL_IGMP:
2071+
/* The IP packet contained an IGMP frame. */
2072+
eReturn = eProcessIGMPPacket( pxNetworkBuffer );
2073+
break;
2074+
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
2075+
19792076
case ipPROTOCOL_UDP:
19802077
/* The IP packet contained a UDP frame. */
19812078

source/FreeRTOS_IP_Timers.c

+41
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
#include "NetworkBufferManagement.h"
5656
#include "FreeRTOS_Routing.h"
5757
#include "FreeRTOS_DNS.h"
58+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
59+
#include "FreeRTOS_IGMP.h"
60+
#endif
5861
/*-----------------------------------------------------------*/
5962

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

116+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
117+
/** @brief IGMP timer. Used for sending scheduled IGMP Reports */
118+
static IPTimer_t xIGMPTimer;
119+
#endif
120+
113121
/** @brief As long as not all networks are up, repeat initialisation by calling the
114122
* xNetworkInterfaceInitialise() function of the interfaces that are not ready. */
115123

@@ -176,6 +184,15 @@ TickType_t xCalculateSleepTime( void )
176184
}
177185
#endif
178186

187+
#if ( ipconfigSUPPORT_IP_MULTICAST == 1 )
188+
{
189+
if( xIGMPTimer.ulRemainingTime < uxMaximumSleepTime )
190+
{
191+
uxMaximumSleepTime = xIGMPTimer.ulRemainingTime;
192+
}
193+
}
194+
#endif /* ipconfigSUPPORT_IP_MULTICAST */
195+
179196
#if ( ipconfigDNS_USE_CALLBACKS != 0 )
180197
{
181198
if( xDNSTimer.bActive != pdFALSE_UNSIGNED )
@@ -312,6 +329,16 @@ void vCheckNetworkTimers( void )
312329
vSocketListenNextTime( NULL );
313330
#endif /* ipconfigUSE_TCP == 1 */
314331

332+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
333+
{
334+
/* Is it time to send any IGMP reports? */
335+
if( prvIPTimerCheck( &xIGMPTimer ) != pdFALSE )
336+
{
337+
( void ) xSendIGMPEvent();
338+
}
339+
}
340+
#endif /* ipconfigSUPPORT_IP_MULTICAST != 0 */
341+
315342
/* Is it time to trigger the repeated NetworkDown events? */
316343
if( xAllNetworksUp == pdFALSE )
317344
{
@@ -412,6 +439,20 @@ void vARPTimerReload( TickType_t xTime )
412439

413440
/*-----------------------------------------------------------*/
414441

442+
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
443+
444+
/**
445+
* @brief Reload the IGMP timer.
446+
*
447+
* @param[in] xIgmpTickTime: The reload value.
448+
*/
449+
void vIGMPTimerReload( TickType_t xIgmpTickTime )
450+
{
451+
prvIPTimerReload( &xIGMPTimer, pdMS_TO_TICKS( ipIGMP_TIMER_PERIOD_MS ) );
452+
}
453+
#endif /* ipconfigSUPPORT_IP_MULTICAST */
454+
/*-----------------------------------------------------------*/
455+
415456
#if ( ipconfigDNS_USE_CALLBACKS != 0 )
416457

417458
/**

source/FreeRTOS_IPv6.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -386,7 +386,7 @@ BaseType_t xIsIPv6AllowedMulticast( const IPv6_Address_t * pxIPAddress )
386386
/**
387387
* @brief Compares 2 IPv6 addresses and checks if the one
388388
* on the left can handle the one on right. Note that 'xCompareIPv6_Address' will also check if 'pxRight' is
389-
* the special unicast address: ff02::1:ffnn:nnnn, where nn:nnnn are
389+
* the special multicast address: ff02::1:ffnn:nnnn, where nn:nnnn are
390390
* the last 3 bytes of the IPv6 address.
391391
*
392392
* @param[in] pxLeft First IP address.
@@ -410,6 +410,7 @@ BaseType_t xCompareIPv6_Address( const IPv6_Address_t * pxLeft,
410410
( pxRight->ucBytes[ 12 ] == 0xffU ) )
411411
{
412412
/* This is an LLMNR address. */
413+
/* _EP_: I don't think this is LLMNR. Did someone mean to say "the solicited-node multicast address" ? */
413414
xResult = memcmp( &( pxLeft->ucBytes[ 13 ] ), &( pxRight->ucBytes[ 13 ] ), 3 );
414415
}
415416
else

source/FreeRTOS_ND.c

+20
Original file line numberDiff line numberDiff line change
@@ -1112,6 +1112,26 @@
11121112
break;
11131113
#endif /* ( ipconfigUSE_RA != 0 ) */
11141114

1115+
#if ( ipconfigSUPPORT_IP_MULTICAST == 1 )
1116+
case ipICMP_MULTICAST_LISTENER_QUERY:
1117+
case ipICMP_MULTICAST_LISTENER_REPORT_V1:
1118+
case ipICMP_MULTICAST_LISTENER_REPORT_V2:
1119+
1120+
/* Note: prvProcessIPPacket() stripped the extension headers, so this packet struct is defined without them and they cannot be checked.
1121+
* per RFC, MLD packets must use the RouterAlert option in a Hop By Hop extension header. */
1122+
/* All MLD packets are at least as large as a v1 query packet. */
1123+
uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_ICMPv6_HEADER );
1124+
1125+
if( uxNeededSize > pxNetworkBuffer->xDataLength )
1126+
{
1127+
FreeRTOS_printf( ( "Too small\n" ) );
1128+
break;
1129+
}
1130+
1131+
vProcessMLDPacket( pxNetworkBuffer );
1132+
break;
1133+
#endif /* if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) */
1134+
11151135
default:
11161136
/* All possible values are included here above. */
11171137
break;

0 commit comments

Comments
 (0)