Skip to content

Commit de1c4c0

Browse files
author
Emil Popov
committed
Adds support for receiving IPv4 and IPv6 multicast groups
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 add/drop membership as well as change the transmit TTL of multicasts. Makes all 3 multicast socket options (add/drop/ttl) work with both IPv4 and IPv6 Adds a ucMaximumHops field to NetworkBufferDescriptor_t and assigns it to the proper TTL/HopLimit value based on what packet is being sent. Adds a NetworkInterface_t * to the socket struct to keep track of which network interface(s) should receive multicasts. Adds exceptions so that we don't send multicast reports for 224.0.0.1, ff02::1, as well as anything with IPv6 multicast scope of 0 or 1 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 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 (+3 squashed commit) Improves the SAME70 driver to handle adding/removing multicast MAC addresses Adds a Multicast ToDo list to help keep me on track.
1 parent c263e33 commit de1c4c0

23 files changed

+2136
-73
lines changed

source/FreeRTOS_DNS_Networking.c

Lines changed: 5 additions & 0 deletions
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_DNS_Parser.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,26 @@
936936
}
937937

938938
xUDPPacket_IPv6->xUDPHeader.usLength = FreeRTOS_htons( ( uint16_t ) lNetLength + ipSIZE_OF_UDP_HEADER );
939+
940+
if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipMDNS_PORT ) )
941+
{
942+
/* RFC6762, section 11 */
943+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 255U;
944+
}
945+
else if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
946+
{
947+
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */
948+
949+
/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
950+
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
951+
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
952+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 1U;
953+
}
954+
else
955+
{
956+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = ipconfigUDP_TIME_TO_LIVE;
957+
}
958+
939959
vFlip_16( pxUDPHeader->usSourcePort, pxUDPHeader->usDestinationPort );
940960
uxDataLength = ( size_t ) lNetLength + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER;
941961
}
@@ -951,8 +971,18 @@
951971
/* HT:endian: should not be translated, copying from packet to packet */
952972
if( pxIPHeader->ulDestinationIPAddress == ipMDNS_IP_ADDRESS )
953973
{
974+
/* RFC6762, section 11 */
954975
pxIPHeader->ucTimeToLive = ipMDNS_TIME_TO_LIVE;
955976
}
977+
else if( pxUDPHeader->usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
978+
{
979+
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */
980+
981+
/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
982+
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
983+
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
984+
pxIPHeader->ucTimeToLive = 1;
985+
}
956986
else
957987
{
958988
pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;

source/FreeRTOS_IP.c

Lines changed: 98 additions & 0 deletions
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+
MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData;
478+
vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
479+
break;
480+
}
481+
482+
case eMulticastTimerEvent:
483+
vIPMulticast_HandleTimerEvent();
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 ) vIPMulticast_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->pxInterface = pxEndPoint->pxNetworkInterface;
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 != xAddMulticastReportToList( 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( NetworkInterface_t * pxInterface,
722+
BaseType_t xMaxCountdown );
723+
vRescheduleAllMulticastReports( pxEndPoint->pxNetworkInterface, 5 );
724+
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
725+
636726
#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
637727
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
638728
{
@@ -1310,6 +1400,7 @@ void FreeRTOS_SetEndPointConfiguration( const uint32_t * pulIPAddress,
13101400
pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT;
13111401
pxNetworkBuffer->xIPAddress.ulIP_IPv4 = ulIPAddress;
13121402
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
1403+
pxNetworkBuffer->ucMaximumHops = ipconfigICMP_TIME_TO_LIVE;
13131404
/* xDataLength is the size of the total packet, including the Ethernet header. */
13141405
pxNetworkBuffer->xDataLength = uxTotalLength;
13151406

@@ -1976,6 +2067,13 @@ static eFrameProcessingResult_t prvProcessIPPacket( const IPPacket_t * pxIPPacke
19762067
break;
19772068
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
19782069

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

0 commit comments

Comments
 (0)