Skip to content

Commit 985b14b

Browse files
author
Emil Popov
committed
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 Makes all 3 multicast socket options work with both IPv4 and IPv6
1 parent c5ad83a commit 985b14b

13 files changed

+270
-336
lines changed

source/FreeRTOS_DNS_Parser.c

+30
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_IGMP.c

+72-29
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,24 @@
2828
* @brief Implements the optional IGMP functionality of the FreeRTOS+TCP network stack.
2929
*/
3030

31-
/* ToDo List ( remove the items below as progress is made )
31+
/* ToDo List ( remove the items below as progress is made )
3232
* - Rename this file
3333
* - netif: netif-pointer in the setsockopt struct, null means "all interfaces"
3434
* - Check task to task multicast ( maybe the network driver can handle to loop
3535
* - Rework the sockopt structures to be the same and use IP_Address_t or IPv46_Address_t
3636
* - Write a demo and add to https://github.com/FreeRTOS/FreeRTOS/tree/main/FreeRTOS-Plus/Demo
37-
* - Sockets cannot handle v4 and v6 at the same time, so enforce this for setsockopt multicast calls
37+
* - Sockets cannot handle v4 and v6 at the same time, so enforce this for setsockopt multicast calls
38+
* - Documentation: Caution about calling FREERTOS_SO_IP_ADD_MEMBERSHIP followed by FREERTOS_SO_IP_DROP_MEMBERSHIP
39+
* in close succession. The DROP may fail because the IP task hasn't handled the ADD yet.
40+
* - Documentation: The values used for FREERTOS_SO_IP_ADD_MEMBERSHIP and FREERTOS_SO_IP_DROP_MEMBERSHIP
41+
* must be exactly the same. This includes the interface pointer!
3842
* Topics to discuss over email or in a conference call:
3943
* - Integration with other hardware. For now, only SAME70 target has the proper functions for receive multicasts.
4044
* - Is task to task multicast really needed? In order to get that feature, we need code that handles every outgoing
4145
* multicast as if it were an incoming packet and possibly duplicates it. I don't think this functionality is
42-
* really needed and this may only be needed if we want a send/receive demo to work on a single device. Maybe
46+
* really needed and this may only be needed if we want a send/receive demo to work on a single device. Maybe
4347
* it's better to have a demo that sends to multicast_A and receives multicast_B and then have a PC-based
44-
* python script that does the opposite to complete the demo application.
48+
* python script that does the opposite to complete the demo application.
4549
*/
4650

4751
/* Standard includes. */
@@ -654,7 +658,7 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
654658
{
655659
uint8_t MCastMacBytes[ 6 ];
656660
UBaseType_t uxLeaveGroup = pdFALSE_UNSIGNED;
657-
NetworkInterface_t * pxNetIf = ( pxSocket->pxEndPoint != NULL && pxSocket->pxEndPoint->pxNetworkInterface != NULL ) ? pxSocket->pxEndPoint->pxNetworkInterface : NULL;
661+
NetworkInterface_t * pxNetIf = pxSocket->u.xUDP.pxMulticastNetIf;
658662

659663
if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED )
660664
{
@@ -725,7 +729,10 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
725729
prvRemoveMulticastReportFromList( &( pxSocket->u.xUDP.xMulticastAddress ), ( UBaseType_t ) pxSocket->bits.bIsIPv6 );
726730
}
727731

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

731738
/**
@@ -734,21 +741,22 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
734741
* @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for.
735742
* @param[in] bAction: eSocketOptAddMembership or eSocketOptDropMembership.
736743
*/
737-
void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
744+
void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction,
738745
uint8_t bAction )
739746
{
740-
if( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
741-
{
742-
return;
743-
}
744-
745747
uint8_t MCastMacBytes[ 6 ];
746-
FreeRTOS_Socket_t * pxSocket = pxMulticastGroup->pxSocket;
748+
FreeRTOS_Socket_t * pxSocket = pxMulticastAction->pxSocket;
747749
uint8_t bFreeMatchedItem = pdFALSE;
748-
NetworkInterface_t * pxNetIf = ( pxSocket->pxEndPoint != NULL && pxSocket->pxEndPoint->pxNetworkInterface != NULL ) ? pxSocket->pxEndPoint->pxNetworkInterface : NULL;
750+
NetworkInterface_t * pxNetIf = pxMulticastAction->pxInterface;
751+
BaseType_t bReportItemConsumed = pdFALSE;
749752

750753
configASSERT( pxSocket != NULL );
751754

755+
if( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
756+
{
757+
return;
758+
}
759+
752760
/* This TCP stack does NOT support sockets subscribing to more than one multicast group.
753761
* If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the
754762
* IGMP/MLD reports corresponding to that group address. */
@@ -757,15 +765,15 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
757765
if( eSocketOptAddMembership == bAction )
758766
{
759767
/* Store the multicast address. */
760-
( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastGroup->xMulticastGroup.xIPAddress ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) );
768+
( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastAction->xMulticastGroup ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) );
761769

762-
if( pxMulticastGroup->xMulticastGroup.xIs_IPv6 == pdFALSE )
770+
if( pxSocket->bits.bIsIPv6 == pdFALSE )
763771
{
764-
vSetMultiCastIPv4MacAddress( pxMulticastGroup->xMulticastGroup.xIPAddress.ulIP_IPv4, MCastMacBytes );
772+
vSetMultiCastIPv4MacAddress( pxMulticastAction->xMulticastGroup.ulIP_IPv4, MCastMacBytes );
765773
}
766774
else
767775
{
768-
vSetMultiCastIPv6MacAddress( &( pxMulticastGroup->xMulticastGroup.xIPAddress.xIP_IPv6 ), MCastMacBytes );
776+
vSetMultiCastIPv6MacAddress( &( pxMulticastAction->xMulticastGroup.xIP_IPv6 ), MCastMacBytes );
769777
}
770778

771779
/* Inform the network driver */
@@ -789,30 +797,65 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
789797
}
790798
}
791799

800+
/* Remember which interface(s) this socket is subscribed on. */
801+
pxSocket->u.xUDP.pxMulticastNetIf = pxMulticastAction->pxInterface;
802+
792803
/* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report
793804
* for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist.
794805
* If such an IGMP/MLD report is already present in the list, we will increment it's socket
795-
* count and free the report we have here. In either case, the MCastGroupDesc_t that we were
796-
* passed, no longer needs to hold a reference to this IGMP report. */
797-
if( pxMulticastGroup->pxMCastReportData )
806+
* count and free the report we have here. In either case, the MulticastAction_t that we were
807+
* passed, no longer needs to hold a reference to this multicast report. */
808+
do
798809
{
799-
/* ToDo: Add and exception for ff02::1 If someone subscribes to it, do not add report. */
810+
if( pxMulticastAction->pxMCastReportData == NULL )
811+
{
812+
break;
813+
}
800814

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

803-
if( pdTRUE != bReportItemConsumed )
821+
if( memcmp( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, FreeRTOS_in6addr_allnodes.ucBytes, sizeof( IPv6_Address_t ) ) == 0 )
822+
{
823+
break;
824+
}
825+
826+
/* RFC2710 end of section section 5 and RFC3810 section 6:
827+
* Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local).
828+
* Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/
829+
if( ( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] & 0x0FU ) <= 1 )
830+
{
831+
break;
832+
}
833+
}
834+
else
804835
{
805-
/* If adding to the list did not consume the item that we sent, that means a duplicate
806-
* was found and its socket count was incremented instead of adding the item we sent.
807-
* Free the item that was passed to us. */
808-
vPortFree( pxMulticastGroup->pxMCastReportData );
809-
pxMulticastGroup->pxMCastReportData = NULL;
836+
/* RFC2236 end of section 6:
837+
* 224.0.0.1 is a special case and we do not send reports for it. */
838+
if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == ipIGMP_IP_ADDR )
839+
{
840+
break;
841+
}
810842
}
843+
844+
bReportItemConsumed = xAddIGMPReportToList( pxMulticastAction->pxMCastReportData );
845+
} while( pdFALSE );
846+
847+
/* If the report either a special case address or was not consumed by xAddIGMPReportToList() because there was
848+
* a duplicate found and its socket count was incremented instead of adding the report to the global list.
849+
* In either case, free the multicast report. */
850+
if( bReportItemConsumed == pdFALSE )
851+
{
852+
vPortFree( pxMulticastAction->pxMCastReportData );
853+
pxMulticastAction->pxMCastReportData = NULL;
811854
}
812855
}
813856

814857
/* Free the message that was sent to us. */
815-
vPortFree( pxMulticastGroup );
858+
vPortFree( pxMulticastAction );
816859
}
817860

818861
static portBASE_TYPE xSendIGMP( uint32_t uiBlockTime,

source/FreeRTOS_IP.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,8 @@ static void prvProcessIPEventsAndTimers( void )
474474
case eSocketOptAddMembership:
475475
case eSocketOptDropMembership:
476476
{
477-
MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) xReceivedEvent.pvData;
478-
( void ) vModifyMulticastMembership( pxMCG, xReceivedEvent.eEventType );
477+
MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData;
478+
( void ) vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
479479
break;
480480
}
481481

@@ -1400,6 +1400,7 @@ void FreeRTOS_SetEndPointConfiguration( const uint32_t * pulIPAddress,
14001400
pxNetworkBuffer->pucEthernetBuffer[ ipSOCKET_OPTIONS_OFFSET ] = FREERTOS_SO_UDPCKSUM_OUT;
14011401
pxNetworkBuffer->xIPAddress.ulIP_IPv4 = ulIPAddress;
14021402
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
1403+
pxNetworkBuffer->ucMaximumHops = ipconfigICMP_TIME_TO_LIVE;
14031404
/* xDataLength is the size of the total packet, including the Ethernet header. */
14041405
pxNetworkBuffer->xDataLength = uxTotalLength;
14051406

source/FreeRTOS_ND.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
/* MISRA Ref 8.9.1 [File scoped variables] */
7878
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
7979
/* coverity[misra_c_2012_rule_8_9_violation] */
80-
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; /* ff02:1 */
80+
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; /* ff02::1 */
8181
/** @brief All nodes on the local network segment: MAC address. */
8282
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };
8383

@@ -801,6 +801,7 @@
801801
( void ) memcpy( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, pxIPAddress->ucBytes, ipSIZE_OF_IPv6_ADDRESS );
802802
/* Let vProcessGeneratedUDPPacket() know that this is an ICMP packet. */
803803
pxNetworkBuffer->usPort = ipPACKET_CONTAINS_ICMP_DATA;
804+
pxNetworkBuffer->ucMaximumHops = ipconfigICMP_TIME_TO_LIVE;
804805
/* 'uxPacketLength' is initialised due to the flow of the program. */
805806
pxNetworkBuffer->xDataLength = uxPacketLength;
806807

0 commit comments

Comments
 (0)