Skip to content

Commit

Permalink
Begins processing MLD Queries and schedules MLD v1 reports in the amo…
Browse files Browse the repository at this point in the history
…unt of time requested by the query.

Rewrites prvScheduleIgmpReports to work for both IPv4 IGMP an IPv6 MLD reports
Sends unsolicited IGMP and MLD reports on network-up events.
Simplifies the randomization of the multicast report random send times.
  • Loading branch information
Emil Popov committed Oct 24, 2023
1 parent 24c55f6 commit 11ffd69
Show file tree
Hide file tree
Showing 10 changed files with 480 additions and 315 deletions.
610 changes: 370 additions & 240 deletions source/FreeRTOS_IGMP.c

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion source/FreeRTOS_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,17 @@ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint )
}
}
}
}
} /* if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) */

/* Reschedule all multicast reports associated with this end-point.
* /* Note: countdown is in increments of ipIGMP_TIMER_PERIOD_MS. It's a good idea to spread out all reports a little.
* 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,
* they will soon ask us for reports anyways, so sending these unsolicited reports is not required. It simply enhances the user
* experience by shortening the time it takes before we begin receiving the multicasts that we care for. */
/* _EP_: vRescheduleAllMulticastReports() is NOT declared in header files because I don't want to expose it to the user */
extern void vRescheduleAllMulticastReports( NetworkEndPoint_t * pxEndPoint,
BaseType_t xMaxCountdown );
vRescheduleAllMulticastReports( pxEndPoint, 5 );
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

#if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 )
Expand Down
20 changes: 20 additions & 0 deletions source/FreeRTOS_ND.c
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,26 @@
break;
#endif /* ( ipconfigUSE_RA != 0 ) */

#if ( ipconfigSUPPORT_IP_MULTICAST == 1 )
case ipICMP_MULTICAST_LISTENER_QUERY:
case ipICMP_MULTICAST_LISTENER_REPORT_V1:
case ipICMP_MULTICAST_LISTENER_REPORT_V2:

/* Note: prvProcessIPPacket() stripped the extension headers, so this packet struct is defined without them and they cannot be checked.
* per RFC, MLD packets must use the RouterAlert option in a Hop By Hop extension header. */
/* All MLD packets are at least as large as a v1 query packet. */
uxNeededSize = ( size_t ) ( ipSIZE_OF_ETH_HEADER + ipSIZE_OF_IPv6_HEADER + ipSIZE_OF_ICMPv6_HEADER );

if( uxNeededSize > pxNetworkBuffer->xDataLength )
{
FreeRTOS_printf( ( "Too small\n" ) );
break;
}

vProcessMLDPacket( pxNetworkBuffer );
break;
#endif /* if ( ipconfigSUPPORT_IP_MULTICAST == 1 ) */

default:
/* All possible values are included here above. */
break;
Expand Down
32 changes: 14 additions & 18 deletions source/FreeRTOS_Sockets.c
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ Socket_t FreeRTOS_socket( BaseType_t xDomain,

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
pxSocket->u.xUDP.xMulticastTTL = ipconfigMULTICAST_DEFAULT_TTL;
vListInitialise( &pxSocket->u.xUDP.xMulticastGroupsList );
memset( &( pxSocket->u.xUDP.xMulticastAddress ), 0, sizeof( pxSocket->u.xUDP.xMulticastAddress ) );
#endif
}

Expand Down Expand Up @@ -2161,12 +2161,7 @@ void * vSocketClose( FreeRTOS_Socket_t * pxSocket )
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
if( pxSocket->ucProtocol == ipPROTOCOL_UDP )
{
/* Un-register all multicast groups that might have been added. */
while( listCURRENT_LIST_LENGTH( &( pxSocket->u.xUDP.xMulticastGroupsList ) ) > 0U )
{
MCastGroupDesc_t * pMCD = ( MCastGroupDesc_t * ) listGET_OWNER_OF_HEAD_ENTRY( &( pxSocket->u.xUDP.xMulticastGroupsList ) );
( void ) vModifyMulticastMembership( pMCD, eSocketOptDropMembership );
}
prvDropMulticastMembership( pxSocket );
}
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

Expand Down Expand Up @@ -6466,7 +6461,9 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket )

case FREERTOS_SO_IP_ADD_MEMBERSHIP:
{
if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) )
if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) ||
( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) ||
( pxSocket->bits.bIsIPv6 != pdFALSE ) )
{
break; /* will return -pdFREERTOS_ERRNO_EINVAL */
}
Expand Down Expand Up @@ -6496,22 +6493,20 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket )
break;
}

/* Store this whole option in case we are later called to drop this membership.
* This data also needs to be stored so that incoming packets can be filtered properly.
* This can be done more efficiently, but storing the whole struct will be more portable
* in the multi-interface, dual-stack case.*/
/* Store this whole option so we can pass it to the IP task. */
( void ) memcpy( ( void * ) &pxMCG->mreq, pvOptionValue, uxOptionLength );
listSET_LIST_ITEM_OWNER( &( pxMCG->xListItem ), ( void * ) pxMCG );
pxMCG->pxSocket = pxSocket;
pxMCG->xMulticastGroup.xIs_IPv6 = pdFALSE;
pxMCG->xMulticastGroup.xIPAddress.ulIP_IPv4 = pMReq->imr_multiaddr.s_addr;

/* Init the IGMP report description. It needs to hold the same membership information
* and it will eventually be added to the IGMP list. */
* and it will eventually be added to the IGMP list.
* Note: fileds like xNumSockets and ucCountDown don't need to be initialized. They will
* be set to their proper values if this reports is added to the global list. */
pxMRD->xMCastGroupAddress.xIs_IPv6 = pdFALSE_UNSIGNED;
pxMRD->xMCastGroupAddress.xIPAddress.ulIP_IPv4 = pMReq->imr_multiaddr.s_addr;
pxMRD->pxEndPoint = NULL;
listSET_LIST_ITEM_OWNER( &( pxMRD->xListItem ), ( void * ) pxMRD );
pxMRD->xNumSockets = 0;
pxMRD->ucCountDown = 0;
/* Quick and dirty assignment of end-point. This will probably have to be re-designed and re-done. */
pxMRD->pxEndPoint = ( pxSocket->bits.bIsIPv6 ) ? FreeRTOS_FirstEndPoint_IPv6( FreeRTOS_FirstNetworkInterface() ) : FreeRTOS_FirstEndPoint( FreeRTOS_FirstNetworkInterface() );

Expand All @@ -6537,7 +6532,9 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket )

case FREERTOS_SO_IP_DROP_MEMBERSHIP:
{
if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) || ( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) )
if( ( pxSocket->ucProtocol != ( uint8_t ) FREERTOS_IPPROTO_UDP ) ||
( uxOptionLength != sizeof( struct freertos_ip_mreq ) ) ||
( pxSocket->bits.bIsIPv6 != pdFALSE ) )
{
break; /* will return -pdFREERTOS_ERRNO_EINVAL */
}
Expand All @@ -6563,7 +6560,6 @@ void * pvSocketGetSocketID( const ConstSocket_t xSocket )
* This can be done more efficiently, but storing the whole struct will be more portable
* in the multi-interface, dual-stack case.*/
( void ) memcpy( ( void * ) &pxMCG->mreq, pvOptionValue, uxOptionLength );
listSET_LIST_ITEM_OWNER( &( pxMCG->xListItem ), ( void * ) pxMCG );
pxMCG->pxSocket = pxSocket;

/* When dropping memberships, we don't need an IGMP report descriptor. */
Expand Down
59 changes: 26 additions & 33 deletions source/FreeRTOS_UDP_IPv4.c
Original file line number Diff line number Diff line change
Expand Up @@ -374,44 +374,37 @@ BaseType_t xProcessReceivedUDPPacket_IPv4( NetworkBufferDescriptor_t * pxNetwork

*pxIsWaitingForARPResolution = pdFALSE;

#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )

/* If this incoming packet is a multicast, we may have a socket for the port, but we still need
* to ensure the socket is subscribed to that particular multicast group. Note: Since this stack
* does not support port reusing, we don't have to worry about two UDP sockets bound to the exact same
* local port, but subscribed to different multicast groups. If this was allowed, this check
* would have to be moved to the pxUDPSocketLookup() function itself. */
if( ( pxSocket != NULL ) && ( xIsIPv4Multicast( pxUDPPacket->xIPHeader.ulDestinationIPAddress ) ) )
do
{
if( pxSocket != NULL )
{
/* Note: There is no need for vTaskSuspendAll/xTaskResumeAll here because only the IP Task
* touches the socket's multicast groups list directly. */
const ListItem_t * pxIterator;
const ListItem_t * xEnd = listGET_END_MARKER( &( pxSocket->u.xUDP.xMulticastGroupsList ) );

for( pxIterator = ( const ListItem_t * ) listGET_NEXT( xEnd );
pxIterator != ( const ListItem_t * ) xEnd;
pxIterator = ( const ListItem_t * ) listGET_NEXT( pxIterator ) )
{
MCastGroupDesc_t * pxMCG = ( MCastGroupDesc_t * ) listGET_LIST_ITEM_OWNER( pxIterator );
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )

if( pxMCG->mreq.imr_multiaddr.s_addr == pxUDPPacket->xIPHeader.ulDestinationIPAddress )
/* If this incoming packet is a multicast, we may have a socket for the port, but we still need
* to ensure the socket is subscribed to that particular multicast group. Note: Since this stack
* does not support port reusing, we don't have to worry about two UDP sockets bound to the exact same
* local port, but subscribed to different multicast groups. If this was allowed, this check
* would have to be moved to the pxUDPSocketLookup() function itself. */
if( xIsIPv4Multicast( pxUDPPacket->xIPHeader.ulDestinationIPAddress ) )
{
break;
if( pxSocket->u.xUDP.xMulticastAddress.ulIP_IPv4 != pxUDPPacket->xIPHeader.ulDestinationIPAddress )
{
/* This socket is not subscribed to this multicast group. Nullify the result from pxUDPSocketLookup().
* Setting the socket to NULL is not strictly necessary. Leave here for clarity and insurance. */
pxSocket = NULL;
/* return pdFAIL so the buffer can be released */
xReturn = pdFAIL;
/* Do not continue parsing */
break;
}
}
}

if( pxIterator == ( const ListItem_t * ) xEnd )
{
/* This socket is not subscribed to this multicast group. Nullify the result from pxUDPSocketLookup() */
pxSocket = NULL;
}
}
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */
else
{
/* The incoming packet is not a multicast and we already know it
* matches this socket's port number, so just proceed */
}
#endif /* ( ipconfigSUPPORT_IP_MULTICAST != 0 ) */

do
{
if( pxSocket != NULL )
{
if( ( pxEndpoint != NULL ) && ( pxEndpoint->ipv4_settings.ulIPAddress != 0U ) )
{
if( xCheckRequiresARPResolution( pxNetworkBuffer ) == pdTRUE )
Expand Down
4 changes: 2 additions & 2 deletions source/include/FreeRTOS_DNS.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ uint32_t ulDNSHandlePacket( const NetworkBufferDescriptor_t * pxNetworkBuffer );

#if ( ipconfigUSE_MDNS == 1 ) && ( ipconfigUSE_IPv6 != 0 )

/* The MDNS IPv6 address is ff02::1:3 */
/* The MDNS IPv6 address is ff02::fb */
extern const IPv6_Address_t ipMDNS_IP_ADDR_IPv6;

/* The MDNS IPv6 MAC address is 33:33:00:01:00:03 */
/* The MDNS IPv6 MAC address is 33:33:00:00:00:fb */
extern const MACAddress_t xMDNS_MACAddressIPv6;
#endif /* ipconfigUSE_MDNS */

Expand Down
8 changes: 4 additions & 4 deletions source/include/FreeRTOS_IGMP.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,13 @@
#define ipIGMP_IP_ADDR 0x010000E0UL
#endif /* ipconfigBYTE_ORDER == pdFREERTOS_BIG_ENDIAN */


#include "pack_struct_start.h"
struct xIGMP_HEADER
{
uint8_t ucTypeOfMessage; /**< The IGMP type 0 + 1 = 1 */
uint8_t ucMaxResponseTime; /**< Maximum time (sec) for responses. 1 + 1 = 2 */
uint16_t usChecksum; /**< The checksum of whole IGMP packet 2 + 2 = 4 */
uint32_t uiGroupAddress; /**< The multicast group address 4 + 4 = 8 */
uint32_t ulGroupAddress; /**< The multicast group address 4 + 4 = 8 */
}
#include "pack_struct_end.h"
typedef struct xIGMP_HEADER IGMPHeader_t;
Expand All @@ -89,16 +88,17 @@
uint8_t bAction );
BaseType_t xSendIGMPEvent( void );
void vHandleIGMP_Event( void );
void vRemoveIGMPReportFromList( struct freertos_ip_mreq * pMCastGroup );
BaseType_t xAddIGMPReportToList( MCastReportData_t * pNewEntry );
eFrameProcessingResult_t eProcessIGMPPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer );
void vProcessMLDPacket( NetworkBufferDescriptor_t * const pxNetworkBuffer );
BaseType_t xSendMulticastListenerReport_v1( const IPv6_Address_t * pxGroupAddress,
NetworkEndPoint_t * pxEndPoint,
TickType_t uxBlockTimeTicks );
void prvDropMulticastMembership( FreeRTOS_Socket_t * pxSocket ); /* move to Sockets.c as static */


#ifdef __cplusplus
} /* extern "C" */
} /* extern "C" */
#endif

#endif /* FREERTOS_IP_PRIVATE_H */
20 changes: 10 additions & 10 deletions source/include/FreeRTOS_IP_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -648,16 +648,16 @@ typedef struct UDPSOCKET
FOnUDPSent_t pxHandleSent; /**< Function pointer to handle the events after a successful send. */
#endif /* ipconfigUSE_CALLBACKS */
#if ( ipconfigSUPPORT_IP_MULTICAST != 0 )
List_t xMulticastGroupsList;
BaseType_t xMulticastTTL; /**< Allows for multicast sockets to use a different TTL value to limit the scope of the multicast packet. Usually set to 1.
* Note that the options are of different sizes for IPv4 and IPv6.
* Example:
* uint8_t ttl = 1;
* FreeRTOS_setsockopt( MCastSendSock, 0, FREERTOS_SO_IP_MULTICAST_TTL, ( void * ) &ttl, sizeof( ttl ) );
* IPv6 Example:
* BaseType_t hops = 1;
* FreeRTOS_setsockopt( MCastSendSock_v6, 0, FREERTOS_SO_IPV6_MULTICAST_HOPS, ( void * ) &hops, sizeof( hops ) );
*/
IP_Address_t xMulticastAddress; /**< Holds the multicast group address that the socket may have subscribed to receive. */
BaseType_t xMulticastTTL; /**< Allows for multicast sockets to use a different TTL value to limit the scope of the multicast packet. Usually set to 1.
* Note that the options are of different sizes for IPv4 and IPv6.
* Example:
* uint8_t ttl = 1;
* FreeRTOS_setsockopt( MCastSendSock, 0, FREERTOS_SO_IP_MULTICAST_TTL, ( void * ) &ttl, sizeof( ttl ) );
* IPv6 Example:
* BaseType_t hops = 1;
* FreeRTOS_setsockopt( MCastSendSock_v6, 0, FREERTOS_SO_IPV6_MULTICAST_HOPS, ( void * ) &hops, sizeof( hops ) );
*/
#endif
} IPUDPSocket_t;

Expand Down
4 changes: 2 additions & 2 deletions source/include/FreeRTOS_IP_Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@
ListItem_t xListItem; /**< List item for adding to the global list of reports. */
NetworkEndPoint_t * pxEndPoint; /**< The end-point whose source address will be send for sending this report. NULL to send on the first end-point of every interface. */
BaseType_t xNumSockets; /**< The number of sockets that are subscribed to this multicast group. */
uint8_t ucCountDown;
BaseType_t xCountDown;
} MCastReportData_t;

/** @brief The structure to hold a "descriptor" for a multicast group that a socket has registered to. */
typedef struct xMCastGroupDesc
{
IPv46_Address_t xMulticastGroup; /**< Holds the IPv4/IPv6 multicast group address */
struct freertos_ip_mreq mreq; /**< Struct for storing the original mreq structure that was sent to setsockopts() */
struct xLIST_ITEM xListItem; /**< List struct. */
FreeRTOS_Socket_t * pxSocket;
MCastReportData_t * pxIGMPReportDesc; /**< Optional. used to hold the allocated IGMP report descriptor while passing from user code to the IP Task. */
} MCastGroupDesc_t;
Expand Down
26 changes: 21 additions & 5 deletions source/include/FreeRTOS_IPv6_Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ typedef struct xICMPRouterSolicitation_IPv6 ICMPRouterSolicitation_IPv6_t;
typedef struct xIP_HOP_BY_HOP_EXT_ROUTER_ALERT_IPv6 IPHopByHopExtRouterAlert_IPv6_t;

#include "pack_struct_start.h"
struct xICMPv6_MLD_REPORTv1
struct xICMPv6_MLDv1
{
uint8_t ucTypeOfMessage; /**< The message type. 0 + 1 = 1 */
uint8_t ucTypeOfService; /**< Type of service. 1 + 1 = 2 */
Expand All @@ -246,17 +246,33 @@ typedef struct xICMPRouterSolicitation_IPv6 ICMPRouterSolicitation_IPv6_t;
IPv6_Address_t xGroupAddress; /**< The IPv6 address. 8 + 16 = 24 */
}
#include "pack_struct_end.h"
typedef struct xICMPv6_MLD_REPORTv1 ICMPv6_MLDReportv1_t;
typedef struct xICMPv6_MLDv1 ICMPv6_MLDv1_t;

struct xICMPv6_MLD_REPORTv1_PACKET
/* Note: MLD packets are required to use the Router-Alert option
* in an IPv6 extension header. */
#include "pack_struct_start.h"
struct xICMPv6_MLDv1_TX_PACKET
{
EthernetHeader_t xEthernetHeader; /* 0 + 14 = 14 */
IPHeader_IPv6_t xIPHeader; /* 14 + 40 = 54 */
IPHopByHopExtRouterAlert_IPv6_t xRAOption; /* 54 + 8 = 62 */
ICMPv6_MLDReportv1_t xICMPv6_MLDReport; /* 62 + 24 = 86 */
ICMPv6_MLDv1_t xMLD; /* 62 + 24 = 86 */
}
#include "pack_struct_end.h"
typedef struct xICMPv6_MLDv1_TX_PACKET MLDv1_Tx_Packet_t;

/* This TCP stack strips the extension headers from IPv6 packets, so even though
* MLD packets include a Router-Alert option in an IPv6 extension header, the ICMP
* layer will not see it in the packet prvProcessIPPacket() stripped the extension headers. */
#include "pack_struct_start.h"
struct xICMPv6_MLDv1_RX_PACKET
{
EthernetHeader_t xEthernetHeader; /* 0 + 14 = 14 */
IPHeader_IPv6_t xIPHeader; /* 14 + 40 = 54 */
ICMPv6_MLDv1_t xMLD; /* 54 + 24 = 78 */
}
#include "pack_struct_end.h"
typedef struct xICMPv6_MLD_REPORTv1_PACKET MLDReportPacket_v1_t;
typedef struct xICMPv6_MLDv1_RX_PACKET MLDv1_Rx_Packet_t;

/** @brief Options that can be sent in a Multicast Listener Report packet.
* more info at https://www.rfc-editor.org/rfc/rfc2711#section-2.0 */
Expand Down

0 comments on commit 11ffd69

Please sign in to comment.