Skip to content

Commit

Permalink
Adds a ucMaximumHops field to NetworkBufferDescriptor_t and assigns i…
Browse files Browse the repository at this point in the history
…t 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
  • Loading branch information
Emil Popov committed Nov 8, 2023
1 parent c5ad83a commit 985b14b
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 336 deletions.
30 changes: 30 additions & 0 deletions source/FreeRTOS_DNS_Parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,26 @@
}

xUDPPacket_IPv6->xUDPHeader.usLength = FreeRTOS_htons( ( uint16_t ) lNetLength + ipSIZE_OF_UDP_HEADER );

if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipMDNS_PORT ) )
{
/* RFC6762, section 11 */
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 255U;
}
else if( xUDPPacket_IPv6->xUDPHeader.usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
{
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */

/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
xUDPPacket_IPv6->xIPHeader.ucHopLimit = 1U;
}
else
{
xUDPPacket_IPv6->xIPHeader.ucHopLimit = ipconfigUDP_TIME_TO_LIVE;
}

vFlip_16( pxUDPHeader->usSourcePort, pxUDPHeader->usDestinationPort );
uxDataLength = ( size_t ) lNetLength + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_UDP_HEADER + ipSIZE_OF_ETH_HEADER;
}
Expand All @@ -951,8 +971,18 @@
/* HT:endian: should not be translated, copying from packet to packet */
if( pxIPHeader->ulDestinationIPAddress == ipMDNS_IP_ADDRESS )
{
/* RFC6762, section 11 */
pxIPHeader->ucTimeToLive = ipMDNS_TIME_TO_LIVE;
}
else if( pxUDPHeader->usDestinationPort == FreeRTOS_ntohs( ipLLMNR_PORT ) )
{
/* LLMNR: RFC4795 section 2.5 recommends UDP requests and responses use TTL of 255 */

/* Theoretically, LLMNR replies can go "off-link" and create a DDoS scenario. That should be preventable
* by settings our rely's TTL/HopLimit to 1. Please note that in certain situations ( I think unicast
* responses), Wireshark flags some LLMNR packets that have TTL of 1 as too low. */
pxIPHeader->ucTimeToLive = 1;
}
else
{
pxIPHeader->ulDestinationIPAddress = pxIPHeader->ulSourceIPAddress;
Expand Down
101 changes: 72 additions & 29 deletions source/FreeRTOS_IGMP.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,24 @@
* @brief Implements the optional IGMP functionality of the FreeRTOS+TCP network stack.
*/

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

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

if( pxSocket->bits.bIsIPv6 == pdTRUE_UNSIGNED )
{
Expand Down Expand Up @@ -725,7 +729,10 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
prvRemoveMulticastReportFromList( &( pxSocket->u.xUDP.xMulticastAddress ), ( UBaseType_t ) pxSocket->bits.bIsIPv6 );
}

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

/**
Expand All @@ -734,21 +741,22 @@ void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket )
* @param[in] pxMulticastGroup: The multicast group descriptor. Also holds the socket that this call is for.
* @param[in] bAction: eSocketOptAddMembership or eSocketOptDropMembership.
*/
void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
void vModifyMulticastMembership( MulticastAction_t * pxMulticastAction,
uint8_t bAction )
{
if( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
{
return;
}

uint8_t MCastMacBytes[ 6 ];
FreeRTOS_Socket_t * pxSocket = pxMulticastGroup->pxSocket;
FreeRTOS_Socket_t * pxSocket = pxMulticastAction->pxSocket;
uint8_t bFreeMatchedItem = pdFALSE;
NetworkInterface_t * pxNetIf = ( pxSocket->pxEndPoint != NULL && pxSocket->pxEndPoint->pxNetworkInterface != NULL ) ? pxSocket->pxEndPoint->pxNetworkInterface : NULL;
NetworkInterface_t * pxNetIf = pxMulticastAction->pxInterface;
BaseType_t bReportItemConsumed = pdFALSE;

configASSERT( pxSocket != NULL );

if( ( eSocketOptAddMembership != bAction ) && ( eSocketOptDropMembership != bAction ) )
{
return;
}

/* This TCP stack does NOT support sockets subscribing to more than one multicast group.
* If the socket is already subscribed to a multicast group, we need to unsubscribe it and remove the
* IGMP/MLD reports corresponding to that group address. */
Expand All @@ -757,15 +765,15 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
if( eSocketOptAddMembership == bAction )
{
/* Store the multicast address. */
( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastGroup->xMulticastGroup.xIPAddress ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) );
( void ) memcpy( &( pxSocket->u.xUDP.xMulticastAddress ), &( pxMulticastAction->xMulticastGroup ), sizeof( pxSocket->u.xUDP.xMulticastAddress ) );

if( pxMulticastGroup->xMulticastGroup.xIs_IPv6 == pdFALSE )
if( pxSocket->bits.bIsIPv6 == pdFALSE )
{
vSetMultiCastIPv4MacAddress( pxMulticastGroup->xMulticastGroup.xIPAddress.ulIP_IPv4, MCastMacBytes );
vSetMultiCastIPv4MacAddress( pxMulticastAction->xMulticastGroup.ulIP_IPv4, MCastMacBytes );
}
else
{
vSetMultiCastIPv6MacAddress( &( pxMulticastGroup->xMulticastGroup.xIPAddress.xIP_IPv6 ), MCastMacBytes );
vSetMultiCastIPv6MacAddress( &( pxMulticastAction->xMulticastGroup.xIP_IPv6 ), MCastMacBytes );
}

/* Inform the network driver */
Expand All @@ -789,30 +797,65 @@ void vModifyMulticastMembership( MCastGroupDesc_t * pxMulticastGroup,
}
}

/* Remember which interface(s) this socket is subscribed on. */
pxSocket->u.xUDP.pxMulticastNetIf = pxMulticastAction->pxInterface;

/* Since we've added a multicast group to this socket, we need to prepare an IGMP/MLD report
* for when we receive an IGMP/MLD query. Keep in mind that such a report might already exist.
* If such an IGMP/MLD report is already present in the list, we will increment it's socket
* count and free the report we have here. In either case, the MCastGroupDesc_t that we were
* passed, no longer needs to hold a reference to this IGMP report. */
if( pxMulticastGroup->pxMCastReportData )
* count and free the report we have here. In either case, the MulticastAction_t that we were
* passed, no longer needs to hold a reference to this multicast report. */
do
{
/* ToDo: Add and exception for ff02::1 If someone subscribes to it, do not add report. */
if( pxMulticastAction->pxMCastReportData == NULL )
{
break;
}

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

if( pdTRUE != bReportItemConsumed )
if( memcmp( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes, FreeRTOS_in6addr_allnodes.ucBytes, sizeof( IPv6_Address_t ) ) == 0 )
{
break;
}

/* RFC2710 end of section section 5 and RFC3810 section 6:
* Never send reports for multicast scopes of: 0 (reserved) or 1 (node-local).
* Note: the address was already checked to be a valid multicast in FreeRTOS_setsockopt()*/
if( ( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.xIP_IPv6.ucBytes[ 1 ] & 0x0FU ) <= 1 )
{
break;
}
}
else
{
/* If adding to the list did not consume the item that we sent, that means a duplicate
* was found and its socket count was incremented instead of adding the item we sent.
* Free the item that was passed to us. */
vPortFree( pxMulticastGroup->pxMCastReportData );
pxMulticastGroup->pxMCastReportData = NULL;
/* RFC2236 end of section 6:
* 224.0.0.1 is a special case and we do not send reports for it. */
if( pxMulticastAction->pxMCastReportData->xMCastGroupAddress.xIPAddress.ulIP_IPv4 == ipIGMP_IP_ADDR )
{
break;
}
}

bReportItemConsumed = xAddIGMPReportToList( pxMulticastAction->pxMCastReportData );
} while( pdFALSE );

/* If the report either a special case address or was not consumed by xAddIGMPReportToList() because there was
* a duplicate found and its socket count was incremented instead of adding the report to the global list.
* In either case, free the multicast report. */
if( bReportItemConsumed == pdFALSE )
{
vPortFree( pxMulticastAction->pxMCastReportData );
pxMulticastAction->pxMCastReportData = NULL;
}
}

/* Free the message that was sent to us. */
vPortFree( pxMulticastGroup );
vPortFree( pxMulticastAction );
}

static portBASE_TYPE xSendIGMP( uint32_t uiBlockTime,
Expand Down
5 changes: 3 additions & 2 deletions source/FreeRTOS_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ static void prvProcessIPEventsAndTimers( void )
case eSocketOptAddMembership:
case eSocketOptDropMembership:
{
MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) xReceivedEvent.pvData;
( void ) vModifyMulticastMembership( pxMCG, xReceivedEvent.eEventType );
MulticastAction_t * pxMCA = ( MulticastAction_t * ) xReceivedEvent.pvData;
( void ) vModifyMulticastMembership( pxMCA, xReceivedEvent.eEventType );
break;
}

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

Expand Down
3 changes: 2 additions & 1 deletion source/FreeRTOS_ND.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
/* MISRA Ref 8.9.1 [File scoped variables] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-89 */
/* coverity[misra_c_2012_rule_8_9_violation] */
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 */
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 */
/** @brief All nodes on the local network segment: MAC address. */
static const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 };

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

Expand Down
Loading

0 comments on commit 985b14b

Please sign in to comment.