Skip to content

Commit 2b08654

Browse files
author
Emil Popov
committed
Adds support for receiving IPv4 and IPv6 multicast groups
Modifies eConsiderFrameForProcessing() to allow all multicast ethernet frames when ipconfigSUPPORT_IP_MULTICAST is enabled and ipconfigETHERNET_DRIVER_FILTERS_FRAME_TYPES is disabled. 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 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 pxSocket->u.xUDP.pxMulticastNetIf that specifies the interface on which a sockets wants to receive multicasts. 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 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. The MLD report defines are different for transmitted and received packets because the stack strips the optional headers from received MLD packets. 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. On network down, stops receiving the MAC address that corresponds to the solicited node multicast IPv6 address. This balances out the "network-up" calls that allow that MAC address. Removes the explicit broadcast MAC check in eConsiderFrameForProcessing. Broadcasts are a form of multicasts and will be received when ipconfigSUPPORT_IP_MULTICAST is enabled. Adds ipconfigSUPPORT_IP_MULTICAST to enable/disable all the functionality described above. Adds ipconfigPERIODIC_MULTICAST_REPORT_INTERVAL for debug purposes when there is no IGMP/MLD querier Moves the registration of the IGMP multicast MAC to the network driver init code. Adds a Multicast Todo list to help keep me on track.
1 parent 1c54ccc commit 2b08654

23 files changed

+2188
-65
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 ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
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 /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
8792
}
8893

8994
return xSocket;

source/FreeRTOS_DNS_Parser.c

+30
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,26 @@
896896
}
897897

898898
xUDPPacket_IPv6->xUDPHeader.usLength = FreeRTOS_htons( ( uint16_t ) lNetLength + ipSIZE_OF_UDP_HEADER );
899+
900+
if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipMDNS_PORT ) )
901+
{
902+
/* RFC6762, section 11 */
903+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 255U;
904+
}
905+
else if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
906+
{
907+
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */
908+
909+
/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
910+
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
911+
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
912+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 1U;
913+
}
914+
else
915+
{
916+
xUDPPacket_IPv6->xIPHeader.ucHopLimit = ipconfigUDP_TIME_TO_LIVE;
917+
}
918+
899919
vFlip_16( pxUDPHeader->usSourcePort, pxUDPHeader->usDestinationPort );
900920
uxDataLength = ( size_t ) lNetLength + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER;
901921
}
@@ -911,8 +931,18 @@
911931
/* HT:endian: should not be translated, copying from packet to packet */
912932
if( pxIPHeader->ulDestinationIPAddress == ipMDNS_IP_ADDRESS )
913933
{
934+
/* RFC6762, section 11 */
914935
pxIPHeader->ucTimeToLive = ipMDNS_TIME_TO_LIVE;
915936
}
937+
else if( pxUDPHeader->usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
938+
{
939+
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */
940+
941+
/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
942+
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
943+
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
944+
pxIPHeader->ucTimeToLive = 1;
945+
}
916946
else
917947
{
918948
pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;

source/FreeRTOS_IP.c

+78-22
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 ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
63+
#include "FreeRTOS_IGMP.h"
64+
#endif /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
6265

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

466+
#if ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
467+
case eSocketOptAddMembership:
468+
case eSocketOptDropMembership:
469+
{
470+
MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData;
471+
vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
472+
break;
473+
}
474+
475+
case eMulticastTimerEvent:
476+
vIPMulticast_HandleTimerEvent();
477+
break;
478+
#endif /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
479+
463480
default:
464481
/* Should not get here. */
465482
break;
@@ -519,6 +536,11 @@ static void prvIPTask_Initialise( void )
519536
}
520537
#endif /* ( ( ipconfigUSE_DNS_CACHE != 0 ) && ( ipconfigUSE_DNS != 0 ) ) */
521538

539+
/* Init the list that will hold scheduled IGMP reports. */
540+
#if ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
541+
( void ) vIPMulticast_Init();
542+
#endif /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
543+
522544
/* Initialisation is complete and events can now be processed. */
523545
xIPTaskInitialised = pdTRUE;
524546
}
@@ -632,6 +654,16 @@ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint )
632654
#endif
633655
}
634656

657+
#if ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
658+
659+
/* Reschedule all multicast reports associated with this end-point.
660+
* Note: countdown is in increments of ipIGMP_TIMER_PERIOD_MS. It's a good idea to spread out all reports a little.
661+
* 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,
662+
* they will soon ask us for reports anyways, so sending these unsolicited reports is not required. It simply enhances the user
663+
* experience by shortening the time it takes before we begin receiving the multicasts that we care for. */
664+
vRescheduleAllMulticastReports( pxEndPoint->pxNetworkInterface, 5 );
665+
#endif /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
666+
635667
pxEndPoint->bits.bEndPointUp = pdTRUE_UNSIGNED;
636668

637669
#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
@@ -1321,6 +1353,7 @@ void FreeRTOS_ReleaseUDPPayloadBuffer( void const * pvBuffer )
13211353
pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT;
13221354
pxNetworkBuffer->xIPAddress.ulIP_IPv4 = ulIPAddress;
13231355
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
1356+
pxNetworkBuffer->ucMaximumHops = ipconfigICMP_TIME_TO_LIVE;
13241357
/* xDataLength is the size of the total packet, including the Ethernet header. */
13251358
pxNetworkBuffer->xDataLength = uxTotalLength;
13261359

@@ -1477,34 +1510,50 @@ eFrameProcessingResult_t eConsiderFrameForProcessing( const uint8_t * const pucE
14771510
/* The packet was directed to this node - process it. */
14781511
eReturn = eProcessBuffer;
14791512
}
1480-
else if( memcmp( xBroadcastMACAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
1481-
{
1482-
/* The packet was a broadcast - process it. */
1483-
eReturn = eProcessBuffer;
1484-
}
1485-
else
1486-
#if ( ( ipconfigUSE_LLMNR == 1 ) && ( ipconfigUSE_DNS != 0 ) )
1487-
if( memcmp( xLLMNR_MacAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
1513+
1514+
#if ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) )
1515+
1516+
/*
1517+
* With ipconfigSUPPORT_IP_MULTICAST enabled, FreeRTOS+TCP needs access to all
1518+
* multicast packets. It is too early to filter them out here because we don't
1519+
* know which socket needs which multicast address. Another thing to consider is
1520+
* that unless this function returns eProcessBuffer, eApplicationProcessCustomFrameHook()
1521+
* will not be called, so handling custom multicast frames would be impossible.
1522+
* Note that the broadcast MAC is a type of multicast so the multicast check covers it.
1523+
*/
1524+
else if( MAC_IS_MULTICAST( pxEthernetHeader->xDestinationAddress.ucBytes ) )
14881525
{
1489-
/* The packet is a request for LLMNR - process it. */
14901526
eReturn = eProcessBuffer;
14911527
}
1492-
else
1493-
#endif /* ipconfigUSE_LLMNR */
1494-
#if ( ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_DNS != 0 ) )
1495-
if( memcmp( xMDNS_MacAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
1528+
#else /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
1529+
else if( memcmp( xBroadcastMACAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
14961530
{
1497-
/* The packet is a request for MDNS - process it. */
1531+
/* The packet was a broadcast - process it. */
14981532
eReturn = eProcessBuffer;
14991533
}
1500-
else
1501-
#endif /* ipconfigUSE_MDNS */
1502-
if( ( pxEthernetHeader->xDestinationAddress.ucBytes[ 0 ] == ipMULTICAST_MAC_ADDRESS_IPv6_0 ) &&
1503-
( pxEthernetHeader->xDestinationAddress.ucBytes[ 1 ] == ipMULTICAST_MAC_ADDRESS_IPv6_1 ) )
1504-
{
1505-
/* The packet is a request for LLMNR - process it. */
1506-
eReturn = eProcessBuffer;
1507-
}
1534+
#if ( ( ipconfigUSE_LLMNR == 1 ) && ( ipconfigUSE_DNS != 0 ) )
1535+
else if( memcmp( xLLMNR_MacAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
1536+
{
1537+
/* The packet is a request for LLMNR - process it. */
1538+
eReturn = eProcessBuffer;
1539+
}
1540+
#endif /* ipconfigUSE_LLMNR */
1541+
#if ( ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_DNS != 0 ) )
1542+
else if( memcmp( xMDNS_MacAddress.ucBytes, pxEthernetHeader->xDestinationAddress.ucBytes, sizeof( MACAddress_t ) ) == 0 )
1543+
{
1544+
/* The packet is a request for MDNS - process it. */
1545+
eReturn = eProcessBuffer;
1546+
}
1547+
#endif /* ipconfigUSE_MDNS */
1548+
#if ( ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) )
1549+
else if( ( pxEthernetHeader->xDestinationAddress.ucBytes[ 0 ] == ipMULTICAST_MAC_ADDRESS_IPv6_0 ) &&
1550+
( pxEthernetHeader->xDestinationAddress.ucBytes[ 1 ] == ipMULTICAST_MAC_ADDRESS_IPv6_1 ) )
1551+
{
1552+
/* The packet is an IPv6 multicast - process it. */
1553+
eReturn = eProcessBuffer;
1554+
}
1555+
#endif /* ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) */
1556+
#endif /* ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) */
15081557
else
15091558
{
15101559
/* The packet was not a broadcast, or for this node, just release
@@ -2009,6 +2058,13 @@ static eFrameProcessingResult_t prvProcessIPPacket( const IPPacket_t * pxIPPacke
20092058
break;
20102059
#endif /* ( ipconfigUSE_IPv6 != 0 ) */
20112060

2061+
#if ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) && ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) )
2062+
case ipPROTOCOL_IGMP:
2063+
/* The IP packet contained an IGMP frame. */
2064+
eReturn = eProcessIGMPPacket( pxNetworkBuffer );
2065+
break;
2066+
#endif /* ( ipconfigIS_ENABLED( ipconfigSUPPORT_IP_MULTICAST ) && ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) ) */
2067+
20122068
case ipPROTOCOL_UDP:
20132069
/* The IP packet contained a UDP frame. */
20142070

0 commit comments

Comments
 (0)