Skip to content

Commit

Permalink
Adds ipconfigMULTICAST_MAC_FILTERING that when enabled, adds two MAC …
Browse files Browse the repository at this point in the history
…manupulation functions to NetworkInterface_t

Exposes pcLOCAL_ALL_NODES_MULTICAST_MAC[ ipMAC_ADDRESS_LENGTH_BYTES ] so that network drivers can add it to the received multicast addresses during initialization.
Adds functions to the SAME70 network driver that allow modification of what multicast MAC addresses are being received by the hardware. This implementation utilizes the 64bit hash match register that is present in the SAME70/V71 microcontrollers.
Registes the mDNS address when the SAME70 network driver is initialized.
Moves the registering of the solicited-node multicast address from the network driver to vIPNetworkUpCalls()
  • Loading branch information
Emil Popov committed Dec 20, 2023
1 parent f42d53a commit 37e45b0
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 34 deletions.
30 changes: 30 additions & 0 deletions source/FreeRTOS_IP.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 )
Expand Down
4 changes: 2 additions & 2 deletions source/FreeRTOS_ND.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
15 changes: 15 additions & 0 deletions source/include/FreeRTOSIPConfigDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
1 change: 1 addition & 0 deletions source/include/FreeRTOS_ND.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */


Expand Down
27 changes: 27 additions & 0 deletions source/include/FreeRTOS_Routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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
Expand Down
143 changes: 111 additions & 32 deletions source/portable/NetworkInterface/DriverSAM/NetworkInterface.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );

Expand Down Expand Up @@ -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 */

Expand Down Expand Up @@ -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;
Expand All @@ -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 );
}
}
}
}
}
/*-----------------------------------------------------------*/
Expand Down Expand Up @@ -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 )
{
Expand Down

0 comments on commit 37e45b0

Please sign in to comment.