diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index f412e914ff..2a16e958a5 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -631,6 +631,36 @@ TaskHandle_t FreeRTOS_GetIPTaskHandle( void ) */ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint ) { + #if ( ipconfigMULTICAST_MAC_FILTERING == ipconfigENABLE ) + IPv6_Type_t xAddressType; + + if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* Now that the network is up, pxEndPoint->ipv6_settings should hold the actual address of this + * end-point. For unicast addresses, generate the solicited-node multicast address that corresponds + * to the address and generate an MLD report for it. + * ToDo: Figure out what the proper place is to remove multicast addresses that are no longer valid. For + * example when a DHCPv6 lease expires, on network down.... etc. */ + xAddressType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ); + + if( ( xAddressType == eIPv6_LinkLocal ) || ( xAddressType == eIPv6_SiteLocal ) || ( xAddressType == eIPv6_Global ) ) + { + /* Tell the network driver to begin receiving this MAC address */ + if( pxEndPoint->pxNetworkInterface && ( pxEndPoint->pxNetworkInterface->pfAddMulticastMAC != NULL ) ) + { + MACAddress_t xMACAddress; + xMACAddress.ucBytes[ 0 ] = 0x33; + xMACAddress.ucBytes[ 1 ] = 0x33; + xMACAddress.ucBytes[ 2 ] = 0xFF; + xMACAddress.ucBytes[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ]; + xMACAddress.ucBytes[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ]; + xMACAddress.ucBytes[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ]; + pxEndPoint->pxNetworkInterface->pfAddMulticastMAC( xMACAddress.ucBytes ); + } + } /* if( xAddressType == ... ) */ + } /* if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) */ + #endif /* ( ipconfigMULTICAST_MAC_FILTERING == ipconfigENABLE ) */ + pxEndPoint->bits.bEndPointUp = pdTRUE_UNSIGNED; #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index dcbf4d6ebd..39a6bda33b 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -77,9 +77,9 @@ /* 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 }; + const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 }; /** @brief See if the MAC-address can be resolved because it is a multi-cast address. */ static eARPLookupResult_t prvMACResolve( const IPv6_Address_t * pxAddressToLookup, diff --git a/source/include/FreeRTOSIPConfigDefaults.h b/source/include/FreeRTOSIPConfigDefaults.h index 70406fa225..e7f5e3fdfd 100644 --- a/source/include/FreeRTOSIPConfigDefaults.h +++ b/source/include/FreeRTOSIPConfigDefaults.h @@ -3318,6 +3318,21 @@ /*---------------------------------------------------------------------------*/ +/* + * ipconfigMULTICAST_MAC_FILTERING + * + * Type: BaseType_t ( ipconfigENABLE | ipconfigDISABLE ) + * + * When set to ipconfigENABLE, this macro will adds two functions + * to the network interface. Those functions allow adding/removing multicast + * MAC addresses to/from the hardware EMAC. It is up to the portable network + * code to implement these as on the specific hardware. */ +#ifndef ipconfigMULTICAST_MAC_FILTERING + #define ipconfigMULTICAST_MAC_FILTERING ipconfigDISABLE +#endif + +/*---------------------------------------------------------------------------*/ + /* Should only be included here, ensures trace config is set first. */ #include "IPTraceMacroDefaults.h" diff --git a/source/include/FreeRTOS_ND.h b/source/include/FreeRTOS_ND.h index bdbabdec71..60cdbd396a 100644 --- a/source/include/FreeRTOS_ND.h +++ b/source/include/FreeRTOS_ND.h @@ -204,6 +204,7 @@ void FreeRTOS_PrintNDCache( void ); #endif + extern const uint8_t pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ]; #endif /* ipconfigUSE_IPv6 != 0 */ diff --git a/source/include/FreeRTOS_Routing.h b/source/include/FreeRTOS_Routing.h index 8b480d8f6b..982844b90a 100644 --- a/source/include/FreeRTOS_Routing.h +++ b/source/include/FreeRTOS_Routing.h @@ -54,6 +54,14 @@ /* Return true as long as the LinkStatus on the PHY is present. */ typedef BaseType_t ( * GetPhyLinkStatusFunction_t ) ( struct xNetworkInterface * pxDescriptor ); + #if ( ipconfigMULTICAST_MAC_FILTERING == ipconfigENABLE ) +/* Adds a multicast MAC address to the list of received MAC addresses for this interface */ + typedef void ( * NetworkInterfaceAddMulticastMACFunction_t ) ( const uint8_t * pucMacAddressBytes ); + +/* Removes a multicast MAC address from the list of MAC addresses that this interface receives */ + typedef void ( * NetworkInterfaceRemoveMulticastMACFunction_t ) ( const uint8_t * pucMacAddressBytes ); + #endif + /** @brief These NetworkInterface access functions are collected in a struct: */ typedef struct xNetworkInterface { @@ -62,6 +70,25 @@ NetworkInterfaceInitialiseFunction_t pfInitialise; /**< This function will be called upon initialisation and repeated until it returns pdPASS. */ NetworkInterfaceOutputFunction_t pfOutput; /**< This function is supposed to send out a packet. */ GetPhyLinkStatusFunction_t pfGetPhyLinkStatus; /**< This function will return pdTRUE as long as the PHY Link Status is high. */ + #if ( ipconfigMULTICAST_MAC_FILTERING == ipconfigENABLE ) + + /* The network driver is responsible for keeping track of which multicast addresses + * need to be received and which are not needed anymore. The stack calls the functions + * below every time a socket subscribes to a multicast group or drops its membership. + * If multiple sockets subscribe to the same multicast IP group address + * or to multiple multicast IP group addresses corresponding to the same MAC address, + * the IP task will call the functions below multiple times for the same multicast MAC address. + * The network driver must keep track of how many times pfAddMulticastMAC() has been called + * for a certain MAC address and only stop receiving that address after pfRemoveMulticastMAC() + * has been called the same number of time for that same MAC address. + * A quick and dirty implementation option is to receive all multicast MAC addresses and + * set both pfAddMulticastMAC and pfRemoveMulticastMAC to NULL + * The optimal way to achieve this functionality depends on the specific hardware EMAC. + * For one implementation option, check out portable/NetworkInterface/DriverSAM/NetworkInterface.c + */ + NetworkInterfaceAddMulticastMACFunction_t pfAddMulticastMAC; + NetworkInterfaceRemoveMulticastMACFunction_t pfRemoveMulticastMAC; + #endif struct { uint32_t diff --git a/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c index 81f654e033..672bb7fba7 100644 --- a/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c +++ b/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c @@ -41,6 +41,7 @@ #include "FreeRTOS_DNS.h" #include "FreeRTOS_ARP.h" #include "FreeRTOS_Routing.h" +#include "FreeRTOS_ND.h" #include "NetworkBufferManagement.h" #include "NetworkInterface.h" @@ -202,7 +203,8 @@ static void hand_tx_errors( void ); /* Functions to set the hash table for multicast addresses. */ static uint16_t prvGenerateCRC16( const uint8_t * pucAddress ); -static void prvAddMulticastMACAddress( const uint8_t * ucMacAddress ); +static void prvAddMulticastMACAddress( const uint8_t * pucMacAddress ); +static void prvRemoveMulticastMACAddress( const uint8_t * pucMacAddress ); /* Checks IP queue, buffers, and semaphore and logs diagnostic info if configured */ static void vCheckBuffersAndQueue( void ); @@ -521,6 +523,10 @@ NetworkInterface_t * pxSAM_FillInterfaceDescriptor( BaseType_t xEMACIndex, pxInterface->pfInitialise = prvSAM_NetworkInterfaceInitialise; pxInterface->pfOutput = prvSAM_NetworkInterfaceOutput; pxInterface->pfGetPhyLinkStatus = prvSAM_GetPhyLinkStatus; + #if ( ipconfigMULTICAST_MAC_FILTERING == ipconfigENABLE ) + pxInterface->pfAddMulticastMAC = prvAddMulticastMACAddress; + pxInterface->pfRemoveMulticastMAC = prvRemoveMulticastMACAddress; + #endif FreeRTOS_AddNetworkInterface( pxInterface ); @@ -718,35 +724,31 @@ static BaseType_t prvGMACInit( NetworkInterface_t * pxInterface ) /* set Multicast Hash Enable. */ GMAC->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN; - #if ( ipconfigUSE_LLMNR == 1 ) - { + #if ( ipconfigUSE_LLMNR == ipconfigENABLE ) prvAddMulticastMACAddress( xLLMNR_MacAddress.ucBytes ); - } #endif /* ipconfigUSE_LLMNR */ - #if ( ipconfigUSE_IPv6 != 0 ) + #if ( ipconfigUSE_MDNS == ipconfigENABLE ) + prvAddMulticastMACAddress( xMDNS_MacAddress.ucBytes ); + #endif /* ipconfigUSE_MDNS */ + + #if ( ipconfigUSE_IPv6 == ipconfigENABLE ) { - NetworkEndPoint_t * pxEndPoint; - #if ( ipconfigUSE_LLMNR == 1 ) + /* Register the Link-Local All-Nodes address */ + /* FF02::1 --> 33-33-00-00-00-01 */ + prvAddMulticastMACAddress( pcLOCAL_ALL_NODES_MULTICAST_MAC ); + + #if ( ipconfigUSE_LLMNR == ipconfigENABLE ) { prvAddMulticastMACAddress( xLLMNR_MacAddressIPv6.ucBytes ); } #endif /* ipconfigUSE_LLMNR */ - for( pxEndPoint = FreeRTOS_FirstEndPoint( pxMyInterface ); - pxEndPoint != NULL; - pxEndPoint = FreeRTOS_NextEndPoint( pxMyInterface, pxEndPoint ) ) + #if ( ipconfigUSE_MDNS == ipconfigENABLE ) { - if( pxEndPoint->bits.bIPv6 != pdFALSE_UNSIGNED ) - { - uint8_t ucMACAddress[ 6 ] = { 0x33, 0x33, 0xff, 0, 0, 0 }; - - ucMACAddress[ 3 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 13 ]; - ucMACAddress[ 4 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 14 ]; - ucMACAddress[ 5 ] = pxEndPoint->ipv6_settings.xIPAddress.ucBytes[ 15 ]; - prvAddMulticastMACAddress( ucMACAddress ); - } + prvAddMulticastMACAddress( xMDNS_MACAddressIPv6.ucBytes ); } + #endif /* ipconfigUSE_MDNS */ } #endif /* ipconfigUSE_IPv6 */ @@ -785,6 +787,18 @@ static BaseType_t prvGMACInit( NetworkInterface_t * pxInterface ) } /*-----------------------------------------------------------*/ +#define GMAC_ADDRESS_HASH_BITS ( 64U ) +#define GMAC_ADDRESS_HASH_MASK ( GMAC_ADDRESS_HASH_BITS - 1 ) + +/* The ATSAM GMAC has a 64 bit register for matching multiple unicast or multicast addresses. +* This implementation keeps a counter for every one of those bits. This allows us to keep track +* of how many times each bit has been referenced by a multicast MAC the the stack wants to receive. +* In order to minimize the memory requirement, the counters are of type uint8_t which limits +* their value to 255. If a counter ever reaches that value, it is never decremented. Reaching this +* limit is extremely unlikely in any system, let alone an embedded one using an ATSAM MCU. */ +static uint8_t prvAddressHashCounters[ GMAC_ADDRESS_HASH_BITS ] = { 0U }; +static uint64_t prvAddressHashBitMask = 0U; + static uint16_t prvGenerateCRC16( const uint8_t * pucAddress ) { uint16_t usSum; @@ -805,29 +819,94 @@ static uint16_t prvGenerateCRC16( const uint8_t * pucAddress ) usSum ^= ( usValues[ 4 ] >> 4 ) ^ ( usValues[ 4 ] << 2 ); usSum ^= ( usValues[ 5 ] >> 2 ) ^ ( usValues[ 5 ] << 4 ); - usSum &= 0x3FU; + usSum &= GMAC_ADDRESS_HASH_MASK; return usSum; } /*-----------------------------------------------------------*/ -static void prvAddMulticastMACAddress( const uint8_t * ucMacAddress ) +/** + * @brief Adds a multicast mac address to the GMAC's hash register. Internally, this function + * keeps track of the hashes of MAC addresses being passed and it also keeps a counter for how many + * times every bit in the GMAC hash register has been hit. This way, if two multicast MAC + * addresses map to the same GMAC hash register bit, the internal counter will hold the value of 2. + * Calling prvRemoveMulticastMACAddress for one of those MACs will only decrement the counter, but + * the bit in the GMAC hash register will remain set until a call to prvRemoveMulticastMACAddress for + * the other MAC causes the counter to reach 0 and the bit to get cleared. + * + * @param[in] pucMacAddress: A pointer to the multicast MAC Address in question. + */ +static void prvAddMulticastMACAddress( const uint8_t * pucMacAddress ) { - uint32_t ulMask; - uint16_t usIndex; + /* Note: Only called from the IPTask, so no thread-safety is required. */ + uint8_t ucBinNumber; - usIndex = prvGenerateCRC16( ucMacAddress ); + if( NULL == pucMacAddress ) + { + return; + } - ulMask = 1U << ( usIndex % 32 ); + FreeRTOS_debug_printf( "prvAddMulticastMACAddress: %02X-%02X-%02X-%02X-%02X-%02X", + pucMacAddress[ 0 ], pucMacAddress[ 1 ], pucMacAddress[ 2 ], pucMacAddress[ 3 ], pucMacAddress[ 4 ], pucMacAddress[ 5 ] ); + ucBinNumber = prvGenerateCRC16( pucMacAddress ); - if( usIndex < 32U ) + /* If the bin counter corresponding to this mac address is already non-zero, */ + /* a multicast address with the same hash has already been added, so there's nothing more to do. */ + if( 0 == prvAddressHashCounters[ ucBinNumber ] ) { - /* 0 .. 31 */ - GMAC->GMAC_HRB |= ulMask; + /* This bin counter is zero, so this is the first time we are registering a MAC with this hash. */ + prvAddressHashBitMask |= ( uint64_t ) ( ( uint64_t ) 1 << ucBinNumber ); + gmac_set_hash64( GMAC, prvAddressHashBitMask ); + gmac_enable_multicast_hash( GMAC, true ); } - else + + /* Increment the counter, but make sure we don't overflow it. */ + if( prvAddressHashCounters[ ucBinNumber ] < UINT8_MAX ) + { + prvAddressHashCounters[ ucBinNumber ]++; + } +} + +/** + * @brief Removes a multicast mac address to the GMAC's hash register. Note that the + * MAC address will actually be removed only if the corresponding hash bit bin counter reaches zero. + * + * @param[in] pucMacAddress: A pointer to the multicast MAC Address in question. + */ +static void prvRemoveMulticastMACAddress( const uint8_t * pucMacAddress ) +{ + /* Note: Only called from the IPTask, so no thread-safety is required. */ + uint8_t ucBinNumber; + + if( NULL == pucMacAddress ) + { + return; + } + + FreeRTOS_debug_printf( "prvRemoveMulticastMACAddress: %02X-%02X-%02X-%02X-%02X-%02X", + pucMacAddress[ 0 ], pucMacAddress[ 1 ], pucMacAddress[ 2 ], pucMacAddress[ 3 ], pucMacAddress[ 4 ], pucMacAddress[ 5 ] ); + ucBinNumber = prvGenerateCRC16( pucMacAddress ); + + if( prvAddressHashCounters[ ucBinNumber ] > 0 ) { - /* 32 .. 63 */ - GMAC->GMAC_HRT |= ulMask; + /* If so many multicasts with the same hash were added that the bin counter was maxed out, */ + /* we don't really know how many times we can decrement before actually unregistering this hash. */ + /* Because of this, if the bin counter ever maxes out, we can never unregister the hash. */ + if( prvAddressHashCounters[ ucBinNumber ] < UINT8_MAX ) + { + prvAddressHashCounters[ ucBinNumber ]--; + + if( 0 == prvAddressHashCounters[ ucBinNumber ] ) + { + uint64_t hash = ( uint64_t ) ( ( uint64_t ) 1 << ucBinNumber ); + prvAddressHashBitMask &= ~hash; + gmac_set_hash64( GMAC, prvAddressHashBitMask ); + + if( 0 == prvAddressHashBitMask ) + { + gmac_enable_multicast_hash( GMAC, false ); + } + } + } } } /*-----------------------------------------------------------*/ @@ -923,7 +1002,7 @@ void vGMACGenerateChecksum( uint8_t * pucBuffer, ProtocolPacket_t * xProtPacket = ( ProtocolPacket_t * ) pucBuffer; /* The SAM4E has problems offloading checksums for transmission. - * The SAME70 does not set the CRC for ICMP packets (ping). */ + * The SAME70 does not set the CRC for ICMP or IGMP packets. */ if( xProtPacket->xICMPPacket.xEthernetHeader.usFrameType == ipIPv4_FRAME_TYPE ) {