From aa7f9f0ce1668faf62e30be57e144aab2f3a7beb Mon Sep 17 00:00:00 2001 From: Emil Popov Date: Mon, 8 Apr 2024 07:16:36 -0400 Subject: [PATCH 1/2] Adds NetIf functions for filtering MAC addresses (#1065) * Adds ipconfigMAC_FILTERING that when enabled, adds two MAC manipulation functions to NetworkInterface_t Adds documentation and design notes for the MAC filtering functions of the SAME70 network driver. 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 MAC addresses are being received by the hardware. This implementation utilizes the 4 specific match registers and the 64bit hash match register that are 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() Adds 'U' to some uint8_t[] initializers * Removes ipconfigMAC_FILTERING Rewrites the comment describing the MAC filtering functions. * Adds checks for ipconfigUSE_IPv6 Fixes a copy/paste bug that was allowing pfRemoveAllowedMAC() to be called without being checked for non-NULL * Updates the unit tests for better coverage. Thanks @htibosch * Converts the indexing variables to `size_t xIndex` and avoids inline for() declarations in DriverSAM/NetworkInterface.c Consolidates the solicited-node MAC and MLD management into a single function. Calls the new solicited-node address management function on network UP/DOWN events. * Update some comments Rewrites the generation of the solicited-node multicast IPv6 address. Thanks @htibosch Sprits the allocation and NULL check when allocating an MLD report. Thanks @AniruddhaKanhere * Moves vManageSolicitedNodeAddress() from FreeRTOS_IP_Utils.c to FreeRTOS_IPv6_Utils.c Changes some indexing variables prefix to "ux" * Adds a macros for easy checking if a MAC address is unicast or multicast. Improves the readability of the SAME70 hash register code by adding a bunch of macros and defines Moves all hash register macros anad variables to the top of NetworkInterface.c where they belong. * Adds xNetworkInterface * parameters to the MAC filtering functions as requested by @HTRamsey * Updates the DriverSAM network inteface to include the new MAC filter function parameters. * more renaming * Adds overflow check when incrementing the specific match register counters. Thanks @HTRamsey * Adds proper casting when converting byte arrays to uint32_t registers. Thanks @htibosch. * Exposes pcLOCAL_ALL_NODES_MULTICAST_IP so that it can be re-used by the user * Fixes an array initializer that was not constant at compile time. Removes code that was not supposed to be in this PR * Uncrustify: triggered by comment. * Fix unit tests * Fix formatting --------- Co-authored-by: Emil Popov Co-authored-by: Tony Josi Co-authored-by: Aniruddha Kanhere <60444055+AniruddhaKanhere@users.noreply.github.com> Co-authored-by: GitHub Action --- source/FreeRTOS_IP.c | 8 + source/FreeRTOS_IP_Utils.c | 9 + source/FreeRTOS_IPv6_Utils.c | 67 +++ source/FreeRTOS_ND.c | 7 +- source/include/FreeRTOS_IP_Utils.h | 1 + source/include/FreeRTOS_IPv6_Utils.h | 3 + source/include/FreeRTOS_ND.h | 2 + source/include/FreeRTOS_Routing.h | 40 ++ source/include/NetworkInterface.h | 3 + .../DriverSAM/NetworkInterface.c | 390 ++++++++++++++++-- test/unit-test/CMakeLists.txt | 2 +- .../unit-test/FreeRTOS_IP/FreeRTOS_IP_utest.c | 162 ++++++++ test/unit-test/FreeRTOS_IP/ut.cmake | 1 + .../FreeRTOS_IP_DiffConfig2/ut.cmake | 1 + .../FreeRTOS_IP_Utils_utest.c | 190 +++++++++ .../FreeRTOS_IPv6_Utils_utest.c | 227 ++++++++++ test/unit-test/FreeRTOS_IPv6_Utils/ut.cmake | 1 + 17 files changed, 1064 insertions(+), 50 deletions(-) diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index f34776454..a43e65f32 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -624,6 +624,14 @@ TaskHandle_t FreeRTOS_GetIPTaskHandle( void ) */ void vIPNetworkUpCalls( struct xNetworkEndPoint * pxEndPoint ) { + if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* IPv6 end-points have a solicited-node address that needs extra housekeeping. */ + #if ( ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) ) + vManageSolicitedNodeAddress( pxEndPoint, pdTRUE ); + #endif + } + pxEndPoint->bits.bEndPointUp = pdTRUE_UNSIGNED; #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) diff --git a/source/FreeRTOS_IP_Utils.c b/source/FreeRTOS_IP_Utils.c index 459d6ee64..3c27eb8bc 100644 --- a/source/FreeRTOS_IP_Utils.c +++ b/source/FreeRTOS_IP_Utils.c @@ -835,6 +835,15 @@ void prvProcessNetworkDownEvent( struct xNetworkInterface * pxInterface ) { /* The bit 'bEndPointUp' stays low until vIPNetworkUpCalls() is called. */ pxEndPoint->bits.bEndPointUp = pdFALSE_UNSIGNED; + + if( pxEndPoint->bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* IPv6 end-points have a solicited-node address that needs extra housekeeping. */ + #if ( ipconfigIS_ENABLED( ipconfigUSE_IPv6 ) ) + vManageSolicitedNodeAddress( pxEndPoint, pdFALSE ); + #endif + } + #if ( ipconfigUSE_NETWORK_EVENT_HOOK == 1 ) { if( pxEndPoint->bits.bCallDownHook != pdFALSE_UNSIGNED ) diff --git a/source/FreeRTOS_IPv6_Utils.c b/source/FreeRTOS_IPv6_Utils.c index 9f498649f..f35995bb5 100644 --- a/source/FreeRTOS_IPv6_Utils.c +++ b/source/FreeRTOS_IPv6_Utils.c @@ -285,6 +285,73 @@ size_t usGetExtensionHeaderLength( const uint8_t * pucEthernetBuffer, } /*-----------------------------------------------------------*/ +/** + * @brief Every IPv6 end-point has a solicited node multicast address and a corresponding + * multicast MAC address. The IPv6 solicited node address also has an MLD reports associated + * with it. This function manages both the MAC address and the MLD report associated with + * the end-point's solicited-node address. On network UP, this function registers the MAC address + * with the network driver's filter and creates and MLD report for the UPv6 multicast address. + * On network DOWN, the function unregisters the MAC address and removes the MLD report. + * This is a "convenience" function that keeps all these tasks under one roof for easier maintenance. + * + * @param[in] pxEndPoint The end-point for which a network up/down event is being handled. + * @param[in] xNetworkGoingUp pdTRUE when the network goes UP, pdFALSE when the network goes DOWN. + */ +void vManageSolicitedNodeAddress( const struct xNetworkEndPoint * pxEndPoint, + BaseType_t xNetworkGoingUp ) +{ + IPv6_Type_t xAddressType; + MACAddress_t xMACAddress; + + configASSERT( pxEndPoint != NULL ); + configASSERT( pxEndPoint->pxNetworkInterface != NULL ); + + /* do{}while(0) to allow for the use of break statements */ + do + { + /* During the very first network DOWN event, pxEndPoint->ipv6_settings does not yet hold the proper address and + * therefore the calculated MAC address will be incorrect. Nothing bad will happen though, because the address + * type check below will kick us out before the call to pfRemoveAllowedMAC(). Without the check below, the network + * driver ends up being called once to register 33:33:FF:00:00:00 and that MAC never gets unregistered. */ + + /* Solicited-node multicast addresses only apply to normal unicast non-loopback addresses. */ + xAddressType = xIPv6_GetIPType( &( pxEndPoint->ipv6_settings.xIPAddress ) ); + + if( ( xAddressType != eIPv6_LinkLocal ) && ( xAddressType != eIPv6_SiteLocal ) && ( xAddressType != eIPv6_Global ) ) + { + /* The address of this end-point is something other than a normal unicast address... Maybe it's the + * loopback address or maybe this is an error scenario. In any case, there is no corresponding + * solicited-node multicast address that we need to manage. Do nothing.*/ + break; + } + + /* Calculate the multicast MAC that corresponds to this endpoint's IPv6 address. */ + xMACAddress.ucBytes[ 0 ] = ipMULTICAST_MAC_ADDRESS_IPv6_0; + xMACAddress.ucBytes[ 1 ] = ipMULTICAST_MAC_ADDRESS_IPv6_0; + xMACAddress.ucBytes[ 2 ] = 0xFFU; + 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 ]; + + /* Update the network driver filter */ + if( xNetworkGoingUp == pdTRUE ) + { + if( pxEndPoint->pxNetworkInterface->pfAddAllowedMAC != NULL ) + { + pxEndPoint->pxNetworkInterface->pfAddAllowedMAC( pxEndPoint->pxNetworkInterface, xMACAddress.ucBytes ); + } + } + else + { + if( pxEndPoint->pxNetworkInterface->pfRemoveAllowedMAC != NULL ) + { + pxEndPoint->pxNetworkInterface->pfRemoveAllowedMAC( pxEndPoint->pxNetworkInterface, xMACAddress.ucBytes ); + } + } + } while( pdFALSE ); +} +/*-----------------------------------------------------------*/ + /* *INDENT-OFF* */ #endif /* ( ipconfigUSE_IPv6 != 0 ) */ /* *INDENT-ON* */ diff --git a/source/FreeRTOS_ND.c b/source/FreeRTOS_ND.c index 278448b29..d5c55e478 100644 --- a/source/FreeRTOS_ND.c +++ b/source/FreeRTOS_ND.c @@ -74,12 +74,9 @@ #define ndMAX_CACHE_AGE_BEFORE_NEW_ND_SOLICITATION ( 3U ) /** @brief All nodes on the local network segment: IP address. */ - /* 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 */ + const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ] = { 0xffU, 0x02U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U, 0x01U }; /* 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 ] = { 0x33U, 0x33U, 0x00U, 0x00U, 0x00U, 0x01U }; /** @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/FreeRTOS_IP_Utils.h b/source/include/FreeRTOS_IP_Utils.h index 37b9bb4d3..301f14905 100644 --- a/source/include/FreeRTOS_IP_Utils.h +++ b/source/include/FreeRTOS_IP_Utils.h @@ -109,6 +109,7 @@ void vPreCheckConfigs( void ); */ void prvProcessNetworkDownEvent( struct xNetworkInterface * pxInterface ); + /* *INDENT-OFF* */ #ifdef __cplusplus } /* extern "C" */ diff --git a/source/include/FreeRTOS_IPv6_Utils.h b/source/include/FreeRTOS_IPv6_Utils.h index 34f23e985..db80071d4 100644 --- a/source/include/FreeRTOS_IPv6_Utils.h +++ b/source/include/FreeRTOS_IPv6_Utils.h @@ -66,6 +66,9 @@ size_t usGetExtensionHeaderLength( const uint8_t * pucEthernetBuffer, size_t uxBufferLength, uint8_t * pucProtocol ); +void vManageSolicitedNodeAddress( const struct xNetworkEndPoint * pxEndPoint, + BaseType_t xNetworkGoingUp ); + /* *INDENT-OFF* */ #ifdef __cplusplus } /* extern "C" */ diff --git a/source/include/FreeRTOS_ND.h b/source/include/FreeRTOS_ND.h index bdbabdec7..d0f168531 100644 --- a/source/include/FreeRTOS_ND.h +++ b/source/include/FreeRTOS_ND.h @@ -204,6 +204,8 @@ void FreeRTOS_PrintNDCache( void ); #endif + extern const uint8_t pcLOCAL_ALL_NODES_MULTICAST_IP[ ipSIZE_OF_IPv6_ADDRESS ]; + 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 f6dbd1465..4a68bb319 100644 --- a/source/include/FreeRTOS_Routing.h +++ b/source/include/FreeRTOS_Routing.h @@ -55,6 +55,10 @@ /* Return true as long as the LinkStatus on the PHY is present. */ typedef BaseType_t ( * GetPhyLinkStatusFunction_t ) ( struct xNetworkInterface * pxDescriptor ); +/* Functions that manipulate what MAC addresses are received by this interface */ + typedef void ( * NetworkInterfaceMACFilterFunction_t ) ( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddressBytes ); + /** @brief These NetworkInterface access functions are collected in a struct: */ typedef struct xNetworkInterface { @@ -63,6 +67,42 @@ 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. */ + + /* + * pfAddAllowedMAC and pfRemoveAllowedMAC form the network driver's address filtering API. + * The network stack uses these functions to alter which MAC addresses will be received. + * The MAC addresses passed to the functions can be unicast or multicast. It is important + * to note that the stack may call these functions multiple times for the the same MAC address. + * For example, if two sockets subscribe to the same multicast group, pfAddAllowedMAC() + * will be called twice with the same MAC address. The network driver is responsible for + * keeping track of these calls. The network driver should continue receiving that + * particular MAC address until pfRemoveAllowedMAC() is called the same number of times. + * + * Most EMAC hardware nowadays can filter frames based on both specific MAC address matching + * and hash matching. Specific address matching is ideal because as the name suggests, + * only frames with the exact MAC address are received. Usually however, the number of + * specific MAC addresses is limited ( to 4 in many cases ) and is sometimes not enough for + * all the MAC addresses that the network stack needs to receive. + * Hash matching is usually based around a 64-bit hash table. For every incoming frame, + * the EMAC calculates a hash value (mod 64) of the destination MAC address. + * The hash value is looked up in the 64-bit hash table and if that bit is set, the frame is + * received. If the bit is clear, the frame is dropped. With hash matching, multiple + * MAC addresses are represented by a single bit. It is the responsibility of the network + * driver to manage both the hash address matching and specific address matching capabilities + * of the EMAC hardware. + * A quick and dirty implementation option is to receive all MAC addresses and set both + * pfAddAllowedMAC and pfRemoveAllowedMAC to NULL. This results in an interface running + * in promiscuous mode and the entire burden of MAC filtering falls on the network stack. + * For a more realistic implementation, check out + * "portable/NetworkInterface/DriverSAM/NetworkInterface.c" It demonstrates the use of both + * specific and hash address matching as well as keeping count of how many time the + * individual registers/bits have been used. That implementation's init functions also + * demonstrates the use of prvAddAllowedMACAddress() function to register all end-point's + * MAC addresses whether the endpoints used the same or different MAC addresses. + */ + NetworkInterfaceMACFilterFunction_t pfAddAllowedMAC; + NetworkInterfaceMACFilterFunction_t pfRemoveAllowedMAC; + struct { uint32_t diff --git a/source/include/NetworkInterface.h b/source/include/NetworkInterface.h index a9c804cd8..50b354ca9 100644 --- a/source/include/NetworkInterface.h +++ b/source/include/NetworkInterface.h @@ -52,6 +52,9 @@ void vNetworkInterfaceAllocateRAMToBuffers( NetworkBufferDescriptor_t pxNetworkB BaseType_t xGetPhyLinkStatus( struct xNetworkInterface * pxInterface ); +#define MAC_IS_MULTICAST( pucMACAddressBytes ) ( ( pucMACAddressBytes[ 0 ] & 1U ) != 0U ) +#define MAC_IS_UNICAST( pucMACAddressBytes ) ( ( pucMACAddressBytes[ 0 ] & 1U ) == 0U ) + /* *INDENT-OFF* */ #ifdef __cplusplus } /* extern "C" */ diff --git a/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c index c022f4cb3..6619f6afb 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" @@ -200,9 +201,40 @@ NetworkInterface_t * pxSAM_FillInterfaceDescriptor( BaseType_t xEMACIndex, */ static void hand_tx_errors( void ); +/*-----------------------------------------------------------*/ + +/* + * GMAC hash match register definitions and macros and variables + * 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. Those counters allow us to keep track + * of how many times each bit has been referenced by a call to prvAddAllowedMACAddress(). + * 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. + */ + +#define MULTICAST_HASH_IS_ENABLED( pGMAC ) ( ( pGMAC->GMAC_NCFGR & GMAC_NCFGR_MTIHEN ) != 0U ) +#define MULTICAST_HASH_IS_DISABLED( pGMAC ) ( ( pGMAC->GMAC_NCFGR & GMAC_NCFGR_MTIHEN ) == 0U ) +#define MULTICAST_HASH_ENABLE( pGMAC ) pGMAC->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN +#define MULTICAST_HASH_DISABLE( pGMAC ) pGMAC->GMAC_NCFGR &= ~( GMAC_NCFGR_MTIHEN ) +#define UNICAST_HASH_IS_ENABLED( pGMAC ) ( ( pGMAC->GMAC_NCFGR & GMAC_NCFGR_UNIHEN ) != 0U ) +#define UNICAST_HASH_IS_DISABLED( pGMAC ) ( ( pGMAC->GMAC_NCFGR & GMAC_NCFGR_UNIHEN ) == 0U ) +#define UNICAST_HASH_ENABLE( pGMAC ) pGMAC->GMAC_NCFGR |= GMAC_NCFGR_UNIHEN +#define UNICAST_HASH_DISABLE( pGMAC ) pGMAC->GMAC_NCFGR &= ~( GMAC_NCFGR_UNIHEN ) + +#define GMAC_ADDRESS_HASH_BITS ( 64U ) +#define GMAC_ADDRESS_HASH_MASK ( GMAC_ADDRESS_HASH_BITS - 1U ) + +static uint64_t prvAddressHashBitMask = 0U; +static uint8_t prvAddressHashCounters[ GMAC_ADDRESS_HASH_BITS ] = { 0U }; +static uint8_t prvSpecificMatchCounters[ GMACSA_NUMBER ] = { 0U }; + /* 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 prvAddAllowedMACAddress( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ); +static void prvRemoveAllowedMACAddress( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ); /* Checks IP queue, buffers, and semaphore and logs diagnostic info if configured */ static void vCheckBuffersAndQueue( void ); @@ -521,6 +553,8 @@ NetworkInterface_t * pxSAM_FillInterfaceDescriptor( BaseType_t xEMACIndex, pxInterface->pfInitialise = prvSAM_NetworkInterfaceInitialise; pxInterface->pfOutput = prvSAM_NetworkInterfaceOutput; pxInterface->pfGetPhyLinkStatus = prvSAM_GetPhyLinkStatus; + pxInterface->pfAddAllowedMAC = prvAddAllowedMACAddress; + pxInterface->pfRemoveAllowedMAC = prvRemoveAllowedMACAddress; FreeRTOS_AddNetworkInterface( pxInterface ); @@ -676,12 +710,10 @@ static BaseType_t prvSAM_NetworkInterfaceOutput( NetworkInterface_t * pxInterfac static BaseType_t prvGMACInit( NetworkInterface_t * pxInterface ) { NetworkEndPoint_t * pxEndPoint; + size_t uxIndex; gmac_options_t gmac_option; - pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface ); - configASSERT( pxEndPoint != NULL ); - gmac_enable_management( GMAC, true ); /* Enable further GMAC maintenance. */ GMAC->GMAC_NCR |= GMAC_NCR_MPE; @@ -691,7 +723,9 @@ static BaseType_t prvGMACInit( NetworkInterface_t * pxInterface ) /* Note that 'gmac_option.uc_copy_all_frame' is false, do not copy all frames. * And 'gmac_option.uc_no_boardcast' is false, meaning that broadcast is received. * 'boardcast' is a typo. */ - memcpy( gmac_option.uc_mac_addr, pxEndPoint->xMACAddress.ucBytes, sizeof( gmac_option.uc_mac_addr ) ); + + /* Note that the gmac_option holds the MAC address that will be enabled for reception. + * Leave the MAC as zeros. It will be handled properly further down.*/ gs_gmac_dev.p_hw = GMAC; gmac_dev_init( GMAC, &gs_gmac_dev, &gmac_option ); @@ -699,45 +733,63 @@ static BaseType_t prvGMACInit( NetworkInterface_t * pxInterface ) NVIC_SetPriority( GMAC_IRQn, configMAC_INTERRUPT_PRIORITY ); NVIC_EnableIRQ( GMAC_IRQn ); - /* Clear the hash table for multicast MAC addresses. - * OR set both to ~0H to receive all multicast packets. */ - GMAC->GMAC_HRB = 0U; /* Hash Register Bottom. */ - GMAC->GMAC_HRT = 0U; /* Hash Register Top. */ + /* The call to gmac_dev_init() above writes gmac_option.uc_mac_addr to the first + * MAC address register by calling gmac_set_address( GMAC, 0, p_opt->uc_mac_addr ) + * This driver however uses the prvAddAllowedMACAddress() to manipulate the specific match + * and hash match registers, so undo the setting of the first specific MAC register. */ + + /* Disable all specific MAC address match registers */ + for( uxIndex = 0; uxIndex < GMACSA_NUMBER; uxIndex++ ) + { + /* Writing the bottom register disable this specific MAC register. */ + GMAC->GMAC_SA[ uxIndex ].GMAC_SAB = 0; + } + + /* Clear the hash table for unicast/multicast MAC addresses. */ + gmac_set_hash( GMAC, 0, 0 ); - /* gmac_enable_multicast_hash() sets the wrong bit, don't use it. */ - /* gmac_enable_multicast_hash( GMAC, pdTRUE ); */ - /* set Multicast Hash Enable. */ - GMAC->GMAC_NCFGR |= GMAC_NCFGR_MTIHEN; + /* gmac_enable_multicast_hash() sets the wrong bit, don't use it. + * There is also no equivalent for unicasts, so manipulate the bits directly. + * For now, disable both multicast and unicast hash matching. + * The appropriate bits will be set later */ + MULTICAST_HASH_DISABLE( GMAC ); + UNICAST_HASH_DISABLE( GMAC ); - #if ( ipconfigUSE_LLMNR == 1 ) + /* Go through all end-points of the interface and add their MAC addresses to the GMAC. */ + for( pxEndPoint = FreeRTOS_FirstEndPoint( pxInterface ); + pxEndPoint != NULL; + pxEndPoint = FreeRTOS_NextEndPoint( pxInterface, pxEndPoint ) ) { - prvAddMulticastMACAddress( xLLMNR_MacAddress.ucBytes ); + prvAddAllowedMACAddress( pxInterface, pxEndPoint->xMACAddress.ucBytes ); } - #endif /* ipconfigUSE_LLMNR */ - #if ( ipconfigUSE_IPv6 != 0 ) + #if ( ipconfigIS_ENABLED( ipconfigUSE_IPv4 ) ) + #if ( ipconfigUSE_LLMNR == ipconfigENABLE ) + prvAddAllowedMACAddress( pxInterface, xLLMNR_MacAddress.ucBytes ); + #endif /* ipconfigUSE_LLMNR */ + + #if ( ipconfigUSE_MDNS == ipconfigENABLE ) + prvAddAllowedMACAddress( pxInterface, xMDNS_MacAddress.ucBytes ); + #endif /* ipconfigUSE_MDNS */ + #endif /* ipconfigIS_ENABLED( ipconfigUSE_IPv4 */ + + #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 */ + prvAddAllowedMACAddress( pxInterface, pcLOCAL_ALL_NODES_MULTICAST_MAC ); + + #if ( ipconfigUSE_LLMNR == ipconfigENABLE ) { - prvAddMulticastMACAddress( xLLMNR_MacAddressIPv6.ucBytes ); + prvAddAllowedMACAddress( pxInterface, 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 ); - } + prvAddAllowedMACAddress( pxInterface, xMDNS_MACAddressIPv6.ucBytes ); } + #endif /* ipconfigUSE_MDNS */ } #endif /* ipconfigUSE_IPv6 */ @@ -796,29 +848,279 @@ 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 registers. Internally, this function + * keeps track of the 4 specific MAC address register as well as the hash match register. + * It keeps a counter for how many times the specific registers have been used as well as 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 prvRemoveAllowedMACAddress 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 prvRemoveAllowedMACAddress for + * the other MAC causes the counter to reach 0 and the bit to get cleared. Same logic applies to + * the specific match registers. + * + * @param[in] pucMacAddress: A pointer to the multicast MAC Address in question. + */ +static void prvAddAllowedMACAddress( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) { - uint32_t ulMask; - uint16_t usIndex; + /* Design rationale and implementation details: + * Most network infrastructure ( both wired and wireless ) will do a pretty good job in + * limiting how much unicast MAC traffic we receive. In general, we will only be sent + * unicast frames that are destined to MAC addresses that we send from. Because of this, + * the EMAC doesn't need to do heavy filtering for unicasts. + * On the other hand, most networking hardware ( both wired and wireless ) will happily + * send us all traffic that has a multicast destination MAC. It is therefor much more + * important to filter out unnecessary multicast traffic. Ideally, we could explicitly + * allow all multicast MAC addresses that the TCP stack requests, however, this EMAC controller + * only has 4 specific MAC match registers. Beyond that, we can use the hash match register to + * match unicast and/or multicast addresses. Every bit in the hash match register corresponds + * to millions of MAC addresses, so the usage of the hash register should be kept to a minimum. + * As a side note, setting all bits in the hash register is somewhat equivalent to + * promiscuous mode and is therefor not very useful. + * Due to the filtering nature of network infrastructure, It would make sense to use the + * specific match registers for matching multicasts and use the hash match register for unicasts + * because they are already well filtered. Due to the EMAC hardware limitations, such approach + * is only feasible if very few ( no more than 4 ) multicast MAC addresses need to be received. + * + * TCP stack multicast requirements notes: + * - IPv4 only, no IGMP, no LLMNR, no mDNS - 0 multicasts required. + * - IPv4 only: IGMP 224.0.0.1, LLMNR 224.0.0.252, mDNS 224.0.0.251 - total 3 multicasts required. + * - IPv6 only, no MLD, no LLMNR, no mDNS - 1 multicast required per IPv6 endpoint for the + * solicited node multicast address + all nodes ff02::1 - total of at least 2 multicasts required. + * - IPv4+v6 with IGMP, LLMNR and mDNS = 6 + 1 for every IPv6 end-point - 7+ multicasts required. + * + * This implementation is optimized for use cases where a lot of multicasts and very few unicast + * MAC addresses need to be received. This implementation prioritizes the use of the exact match + * register and once used up, begins utilizing the hash match register. It is highly recommended + * that all the end-points of an interface use the same unicast MAC. If for some reason, the TCP + * stack adds too many unicast MAC addresses, the hash register will be used for both unicasts + * and multicasts which will result in a lot of bits being set and consequently very poor + * multicast filtering. + * One limitation of this implementation is that once it enables unicast or multicast hash + * matching, it will never disable them. This should be acceptable for a fairly static system + * and was chosen because it minimizes RAM usage by not keeping a list of all MAC addresses + * that the TCP stack has requested. One optimization that this implementation makes is as follows: + * If the MAC being added is already covered by the hash match register and that specific + * matching is already enabled, we add that new MAC to the hash register even if there is an + * empty specific match register available. The reasoning is that the hash match register is already + * allowing this new MAC address to be received and there is no reason to waste a specific + * match register. + * The implementation keep counts of how many times the individual hash bits and specific match + * registers have been used. */ + + /* Note: Only called from the IPTask, so no thread-safety is required. */ + uint8_t ucHashBit; + size_t uxIndex, uxEmptyIndex; + + uint32_t ulSAB, ulSAT; + + configASSERT( pucMacAddress != NULL ); + configASSERT( pxInterface != NULL ); /* Not used, but the stack should not be sending us NULL parameters. */ + + ucHashBit = prvGenerateCRC16( pucMacAddress ); + FreeRTOS_debug_printf( "prvAddAllowedMACAddress: pxIf %p, %02X-%02X-%02X-%02X-%02X-%02X hash-bit %u", + pxInterface, + pucMacAddress[ 0 ], pucMacAddress[ 1 ], pucMacAddress[ 2 ], pucMacAddress[ 3 ], pucMacAddress[ 4 ], pucMacAddress[ 5 ], + ucHashBit ); + + /* Calculate what the specific match registers would look like for this MAC address so that + * we can check if this MAC address is already present in one of the specific match registers. */ + ulSAB = ( ( ( uint32_t ) pucMacAddress[ 3 ] ) << 24 ) | + ( ( ( uint32_t ) pucMacAddress[ 2 ] ) << 16 ) | + ( ( ( uint32_t ) pucMacAddress[ 1 ] ) << 8 ) | + ( ( uint32_t ) pucMacAddress[ 0 ] ); + ulSAT = ( ( ( uint32_t ) pucMacAddress[ 5 ] ) << 8 ) | ( ( uint32_t ) pucMacAddress[ 4 ] ); + + /* Always try to find a match within the specific match registers first. */ + uxEmptyIndex = GMACSA_NUMBER; + + for( uxIndex = 0; uxIndex < GMACSA_NUMBER; uxIndex++ ) + { + if( prvSpecificMatchCounters[ uxIndex ] > 0U ) + { + /* This specific match register is being used. Check if the address is the same. */ + if( ( ulSAB == GMAC->GMAC_SA[ uxIndex ].GMAC_SAB ) && ( ulSAT == GMAC->GMAC_SA[ uxIndex ].GMAC_SAT ) ) + { + /* Exact match! Increment the counter and leave. As with the hash counters, make sure we don't overflow. */ + if( prvSpecificMatchCounters[ uxIndex ] < UINT8_MAX ) + { + prvSpecificMatchCounters[ uxIndex ]++; + } - usIndex = prvGenerateCRC16( ucMacAddress ); + /* FreeRTOS_debug_printf("prvAddAllowedMACAddress: EXACT MATCH at %u, new counter %u", uxIndex, prvSpecificMatchCounters[ uxIndex ] ); */ + break; + } + else + { + /* No match. Do nothing. */ + } + } + else + { + /* This specific match register is empty. + * Keep track of the first empty register in case we need to use it. */ + if( uxEmptyIndex >= GMACSA_NUMBER ) + { + uxEmptyIndex = uxIndex; + } + else + { + /* We have already found an empty specific match register. Do nothing. */ + } + } + } /* for( uxIndex = 0; uxIndex < GMACSA_NUMBER; uxIndex++ ) */ - ulMask = 1U << ( usIndex % 32 ); + /* do{}while(0) to allow the use of break statements. */ + do + { + if( uxIndex < GMACSA_NUMBER ) + { + /* An exact match was found in the for(;;) loop above. Do nothing. */ + break; + } - if( usIndex < 32U ) + /* No exact match found in the specific match registers. + * Is one of them empty so we can add the MAC there? */ + if( uxEmptyIndex < GMACSA_NUMBER ) + { + /* There is an empty slot in the specific match registers. Using this empty slot should + * be a priority, except if the hash register already covers the MAC address we were given + * and the type of address we were given ( unicast/multicast ). If any of those are not + * met, simply add to the empty slot we found in the specific match registers. */ + if( ( ( MAC_IS_MULTICAST( pucMacAddress ) ) && ( MULTICAST_HASH_IS_DISABLED( GMAC ) ) ) || + ( ( MAC_IS_UNICAST( pucMacAddress ) ) && ( UNICAST_HASH_IS_DISABLED( GMAC ) ) ) || + ( prvAddressHashCounters[ ucHashBit ] == 0U ) /* hash matching doesn't cover this address yet. */ ) + { + /* In all cases above, simply add the MAC address to the empty specific match register. */ + gmac_set_address( GMAC, uxEmptyIndex, pucMacAddress ); + prvSpecificMatchCounters[ uxEmptyIndex ] = 1U; + /* FreeRTOS_debug_printf("prvAddAllowedMACAddress: ADD at %u, new counter %u", uxEmptyIndex, prvSpecificMatchCounters[ uxEmptyIndex ] ); */ + break; + } + } + + /* If we reach here, we need to add the MAC address we were given to the hash match register. */ + /* 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( prvAddressHashCounters[ ucHashBit ] == 0 ) + { + /* This bin counter is zero, so this is the first time we are registering a MAC with this hash + * and we need to update the hash register. */ + prvAddressHashBitMask |= ( uint64_t ) ( ( uint64_t ) 1 << ucHashBit ); + gmac_set_hash64( GMAC, prvAddressHashBitMask ); + } + + /* Increment the counter, but make sure we don't overflow it. */ + if( prvAddressHashCounters[ ucHashBit ] < UINT8_MAX ) + { + prvAddressHashCounters[ ucHashBit ]++; + } + + /* Make sure the unicast or multicast hash enable bits are set properly. */ + if( ( MAC_IS_UNICAST( pucMacAddress ) ) && ( UNICAST_HASH_IS_DISABLED( GMAC ) ) ) + { + /* This is the first ever unicast address added to the hash register. Enable unicast matching. */ + UNICAST_HASH_ENABLE( GMAC ); + } + else if( ( MAC_IS_MULTICAST( pucMacAddress ) ) && ( MULTICAST_HASH_IS_DISABLED( GMAC ) ) ) + { + /* This is the first ever multicast address added to the hash register. Enable multicast matching. */ + MULTICAST_HASH_ENABLE( GMAC ); + } + else + { + /* The proper unicast / multicast matching is already enabled. Do nothing. */ + } + } while( pdFALSE ); +} + +/** + * @brief Removes a MAC address from the GMAC's specific match or hash match registers. + * + * @param[in] pucMacAddress: A pointer to the multicast MAC Address in question. + */ +static void prvRemoveAllowedMACAddress( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) +{ + /* Note: Only called from the IPTask, so no thread-safety is required. */ + uint8_t ucHashBit; + uint32_t ulSAB, ulSAT; + size_t uxIndex; + + configASSERT( pucMacAddress != NULL ); + configASSERT( pxInterface != NULL ); /* Not used, but the stack should not be sending us NULL parameters. */ + + ucHashBit = prvGenerateCRC16( pucMacAddress ); + FreeRTOS_debug_printf( "prvRemoveAllowedMACAddress: pxIf %p, %02X-%02X-%02X-%02X-%02X-%02X hash-bit %u", + pxInterface, + pucMacAddress[ 0 ], pucMacAddress[ 1 ], pucMacAddress[ 2 ], pucMacAddress[ 3 ], pucMacAddress[ 4 ], pucMacAddress[ 5 ], + ucHashBit ); + + /* Calculate what the specific match registers would look like for this MAC address so that + * we can check if this MAC address is already present in one of the specific match registers. */ + ulSAB = ( ( ( uint32_t ) pucMacAddress[ 3 ] ) << 24 ) | + ( ( ( uint32_t ) pucMacAddress[ 2 ] ) << 16 ) | + ( ( ( uint32_t ) pucMacAddress[ 1 ] ) << 8 ) | + ( ( uint32_t ) pucMacAddress[ 0 ] ); + ulSAT = ( ( ( uint32_t ) pucMacAddress[ 5 ] ) << 8 ) | ( ( uint32_t ) pucMacAddress[ 4 ] ); + + /* Check the specific match registers first. */ + for( uxIndex = 0; uxIndex < GMACSA_NUMBER; uxIndex++ ) { - /* 0 .. 31 */ - GMAC->GMAC_HRB |= ulMask; + if( ( prvSpecificMatchCounters[ uxIndex ] > 0U ) && ( ulSAB == GMAC->GMAC_SA[ uxIndex ].GMAC_SAB ) && ( ulSAT == GMAC->GMAC_SA[ uxIndex ].GMAC_SAT ) ) + { + /* Exact match! Decrement the counter unless it's maxed out. */ + if( prvSpecificMatchCounters[ uxIndex ] < UINT8_MAX ) + { + prvSpecificMatchCounters[ uxIndex ]--; + } + + if( prvSpecificMatchCounters[ uxIndex ] == 0 ) + { + /* FreeRTOS_debug_printf("prvAddAllowedMACAddress: EXACT MATCH at %u, INDEX DISABLED", uxIndex ); */ + /* This specific match register counter is now zero. Disable it by writing it in reverse order. */ + GMAC->GMAC_SA[ uxIndex ].GMAC_SAT = 0U; /* This is not needed, just clears out the top register.*/ + GMAC->GMAC_SA[ uxIndex ].GMAC_SAB = 0U; /* Writing the bottom register disables this specific match register. */ + } + else + { + /* FreeRTOS_debug_printf("prvAddAllowedMACAddress: EXACT MATCH at %u, new counter %u", uxIndex, prvSpecificMatchCounters[ uxIndex ] ); */ + } + + break; + } + } + + if( uxIndex >= GMACSA_NUMBER ) + { + /* The MAC address was not found amongst the specific match registers, check the hash register. */ + if( prvAddressHashCounters[ ucHashBit ] > 0 ) + { + /* 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[ ucHashBit ] < UINT8_MAX ) + { + prvAddressHashCounters[ ucHashBit ]--; + + if( 0 == prvAddressHashCounters[ ucHashBit ] ) + { + uint64_t hash = ( uint64_t ) ( ( uint64_t ) 1 << ucHashBit ); + prvAddressHashBitMask &= ~hash; + gmac_set_hash64( GMAC, prvAddressHashBitMask ); + } + } + } } else { - /* 32 .. 63 */ - GMAC->GMAC_HRT |= ulMask; + /* Nothing left to do. */ } } /*-----------------------------------------------------------*/ @@ -914,7 +1216,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 ) { diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index b93680075..253cf45b2 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -311,7 +311,7 @@ include( ${UNIT_TEST_DIR}/FreeRTOS_Routing_ConfigCompatibleWithSingle/ut.cmake ) add_custom_target( coverage COMMAND ${CMAKE_COMMAND} -P ${MODULE_ROOT_DIR}/test/unit-test/cmock/coverage.cmake DEPENDS cmock unity -FreeRTOS_ARP_utest + FreeRTOS_ARP_utest FreeRTOS_ARP_DataLenLessThanMinPacket_utest FreeRTOS_BitConfig_utest FreeRTOS_DHCP_utest diff --git a/test/unit-test/FreeRTOS_IP/FreeRTOS_IP_utest.c b/test/unit-test/FreeRTOS_IP/FreeRTOS_IP_utest.c index 1fa06d924..d7c5af405 100644 --- a/test/unit-test/FreeRTOS_IP/FreeRTOS_IP_utest.c +++ b/test/unit-test/FreeRTOS_IP/FreeRTOS_IP_utest.c @@ -48,6 +48,7 @@ #include "mock_FreeRTOS_IP_Private.h" #include "mock_FreeRTOS_IPv4_Private.h" #include "mock_FreeRTOS_IP_Utils.h" +#include "mock_FreeRTOS_IPv6_Utils.h" #include "mock_FreeRTOS_IP_Timers.h" #include "mock_FreeRTOS_TCP_IP.h" #include "mock_FreeRTOS_ICMP.h" @@ -93,6 +94,18 @@ const IPv6_Address_t xIPAddressTen = { 0x20, 0x01, 0x12, 0x34, 0x56, 0x78, 0x00, /* MAC Address for endpoint. */ const uint8_t ucMACAddress[ ipMAC_ADDRESS_LENGTH_BYTES ] = { 0xab, 0xcd, 0xef, 0x11, 0x22, 0x33 }; +/* This variable will be set when pfAddAllowedMAC() is called by vIPNetworkUpCalls(). */ +static BaseType_t xMACFunctionCalled; + +/* An implementation of pfAddAllowedMAC(). */ +static void pfAddAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ); + +/* Testing all situations for vIPNetworkUpCalls() to increase coverage. */ +#define ipHAS_METHOD 0x01U +#define ipHAS_INTERFACE 0x02U +#define ipHAS_IPV6 0x04U + /* ============================ Unity Fixtures ============================ */ /*! called before each test case */ @@ -158,6 +171,7 @@ void test_vIPNetworkUpCalls( void ) NetworkEndPoint_t xEndPoint = { 0 }; xEndPoint.bits.bEndPointUp = pdFALSE; + xEndPoint.bits.bIPv6 = pdFALSE; vApplicationIPNetworkEventHook_Multi_Expect( eNetworkUp, &xEndPoint ); vDNSInitialise_Expect(); @@ -3957,3 +3971,151 @@ void test_uxIPHeaderSizeSocket_IPv6() xReturn = uxIPHeaderSizeSocket( &xSocket ); TEST_ASSERT_EQUAL( ipSIZE_OF_IPv6_HEADER, xReturn ); } + +static void pfAddAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) +{ + xMACFunctionCalled = pdTRUE; +} + +static BaseType_t xNetworkInterfaceInitialise_returnTrue( NetworkInterface_t * xInterface ) +{ + return pdTRUE; +} + +/** + * @brief test_prvProcessNetworkDownEvent_Fail + * To validate if prvProcessNetworkDownEvent skips hook and DHCP + * when bCallDownHook & bWantDHCP are both disabled. + */ +static void prvIPNetworkUpCalls_Generic( const uint8_t * pucAddress, + IPv6_Type_t eType, + UBaseType_t uxSetMembers ) +{ + NetworkInterface_t xInterface = { 0 }; + NetworkEndPoint_t xEndPoint = { 0 }; + BaseType_t xMACAddExpected = pdFALSE; + + xInterfaces[ 0 ].pfInitialise = &xNetworkInterfaceInitialise_returnTrue; + xInterfaces[ 0 ].pxEndPoint = &xEndPoint; + + xEndPoint.bits.bEndPointUp = pdFALSE; + xEndPoint.bits.bCallDownHook = pdFALSE_UNSIGNED; + xEndPoint.bits.bWantDHCP = pdFALSE_UNSIGNED; + memcpy( xEndPoint.ipv6_settings.xIPAddress.ucBytes, pucAddress, ipSIZE_OF_IPv6_ADDRESS ); + + if( ( uxSetMembers & ipHAS_INTERFACE ) != 0U ) + { + xEndPoint.pxNetworkInterface = &xInterface; + } + + if( ( uxSetMembers & ipHAS_IPV6 ) != 0U ) + { + xEndPoint.bits.bIPv6 = pdTRUE_UNSIGNED; + } + + xMACFunctionCalled = pdFALSE; + + if( ( ( eType == eIPv6_LinkLocal ) || ( eType == eIPv6_SiteLocal ) || ( eType == eIPv6_Global ) ) && + ( xInterface.pfAddAllowedMAC != NULL ) && + ( xEndPoint.pxNetworkInterface != NULL ) && + ( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) ) + { + xMACAddExpected = pdTRUE; + } + + if( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* The vManageSolicitedNodeAddress() function is mocked. */ + vManageSolicitedNodeAddress_Expect( &xEndPoint, pdTRUE ); + } + + vApplicationIPNetworkEventHook_Multi_Expect( eNetworkUp, &xEndPoint ); + vDNSInitialise_Expect(); + vARPTimerReload_Expect( pdMS_TO_TICKS( 10000 ) ); + + vIPNetworkUpCalls( &xEndPoint ); + + TEST_ASSERT_EQUAL( pdTRUE, xEndPoint.bits.bEndPointUp ); +} + +void test_prvIPNetworkUpCalls_LinkLocal() +{ + /* Use the local-link address fe80::7009 */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0x80U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + /* Test all combinations of what might go wrong. */ + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_INTERFACE ); +} + +void test_prvIPNetworkUpCalls_Global() +{ + /* Use the local-link address "2001:0470:ed44::7009" */ + static const uint8_t ucAddress[ 16 ] = + { + 0x20U, 0x01U, + 0x04U, 0x70U, + 0xEDU, 0x44U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 | ipHAS_METHOD | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 | ipHAS_METHOD ); +} + +void test_prvIPNetworkUpCalls_SiteLocal() +{ + /* Use the local-link address "fec0::7009" */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0xC0U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 | ipHAS_METHOD | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 | ipHAS_METHOD ); +} + +void test_prvIPNetworkUpCalls_Multicast() +{ + /* Use the multicast address "ff02::fb", + * just for the coverage of vIPNetworkUpCalls(). */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFFU, 0x02U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0xFBU + }; + + prvIPNetworkUpCalls_Generic( ucAddress, eIPv6_Multicast, ipHAS_IPV6 | ipHAS_METHOD | ipHAS_INTERFACE ); +} diff --git a/test/unit-test/FreeRTOS_IP/ut.cmake b/test/unit-test/FreeRTOS_IP/ut.cmake index b2a33dac1..16dbceec5 100644 --- a/test/unit-test/FreeRTOS_IP/ut.cmake +++ b/test/unit-test/FreeRTOS_IP/ut.cmake @@ -23,6 +23,7 @@ list(APPEND mock_list "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IPv4_Private.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IP_Timers.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IP_Utils.h" + "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IPv6_Utils.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_ARP.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_ICMP.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_DNS.h" diff --git a/test/unit-test/FreeRTOS_IP_DiffConfig2/ut.cmake b/test/unit-test/FreeRTOS_IP_DiffConfig2/ut.cmake index 2ef4a847f..4e31ae3ec 100644 --- a/test/unit-test/FreeRTOS_IP_DiffConfig2/ut.cmake +++ b/test/unit-test/FreeRTOS_IP_DiffConfig2/ut.cmake @@ -20,6 +20,7 @@ list(APPEND mock_list "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IPv4_Private.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IP_Timers.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IP_Utils.h" + "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IPv6_Utils.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_ARP.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_ICMP.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_DNS.h" diff --git a/test/unit-test/FreeRTOS_IP_Utils/FreeRTOS_IP_Utils_utest.c b/test/unit-test/FreeRTOS_IP_Utils/FreeRTOS_IP_Utils_utest.c index 14fa15118..aac452fca 100644 --- a/test/unit-test/FreeRTOS_IP_Utils/FreeRTOS_IP_Utils_utest.c +++ b/test/unit-test/FreeRTOS_IP_Utils/FreeRTOS_IP_Utils_utest.c @@ -80,6 +80,18 @@ extern uint16_t prvGetChecksumFromPacket( const struct xPacketSummary * pxSet ); extern void prvSetChecksumInPacket( const struct xPacketSummary * pxSet, uint16_t usChecksum ); +/* This variable will be set when pfRemoveAllowedMAC() is called by prvProcessNetworkDownEvent(). */ +static BaseType_t xMACFunctionCalled; + +/* An implementation of pfRemoveAllowedMAC() */ +static void pfRemoveAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ); + +/* Testing all situations for prvProcessNetworkDownEvent() to increase coverage. */ +#define ipHAS_METHOD 0x01U +#define ipHAS_INTERFACE 0x02U +#define ipHAS_IPV6 0x04U + /* ============================== Test Cases ============================== */ /** @@ -606,6 +618,13 @@ void test_prvProcessNetworkDownEvent_PassDHCPv6( void ) vIPSetARPTimerEnableState_Expect( pdFALSE ); FreeRTOS_FirstEndPoint_IgnoreAndReturn( &xEndPoint ); + + if( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* The vManageSolicitedNodeAddress() function is mocked. */ + vManageSolicitedNodeAddress_Expect( &xEndPoint, pdFALSE ); + } + FreeRTOS_NextEndPoint_IgnoreAndReturn( NULL ); FreeRTOS_ClearARP_ExpectAnyArgs(); @@ -636,6 +655,13 @@ void test_prvProcessNetworkDownEvent_PassRA( void ) vIPSetARPTimerEnableState_Expect( pdFALSE ); FreeRTOS_FirstEndPoint_IgnoreAndReturn( &xEndPoint ); + + if( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* The vManageSolicitedNodeAddress() function is mocked. */ + vManageSolicitedNodeAddress_Expect( &xEndPoint, pdFALSE ); + } + FreeRTOS_NextEndPoint_IgnoreAndReturn( NULL ); FreeRTOS_ClearARP_ExpectAnyArgs(); @@ -668,6 +694,13 @@ void test_prvProcessNetworkDownEvent_PassStaticIP( void ) vIPSetARPTimerEnableState_Expect( pdFALSE ); FreeRTOS_FirstEndPoint_IgnoreAndReturn( &xEndPoint ); + + if( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* The vManageSolicitedNodeAddress() function is mocked. */ + vManageSolicitedNodeAddress_Expect( &xEndPoint, pdFALSE ); + } + FreeRTOS_NextEndPoint_IgnoreAndReturn( NULL ); FreeRTOS_ClearARP_ExpectAnyArgs(); @@ -3046,6 +3079,163 @@ void test_prvSetChecksumInPacket_IPv6UnhandledProtocol() prvSetChecksumInPacket( &xSet, 0 ); } +static void pfRemoveAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) +{ + xMACFunctionCalled = pdTRUE; +} + +/** + * @brief test_prvProcessNetworkDownEvent_Fail + * To validate if prvProcessNetworkDownEvent skips hook and DHCP + * when bCallDownHook & bWantDHCP are both disabled. + */ +static void prvProcessNetworkDownEvent_Generic( const uint8_t * pucAddress, + IPv6_Type_t eType, + UBaseType_t uxSetMembers ) +{ + NetworkInterface_t xInterface = { 0 }; + NetworkEndPoint_t xEndPoint = { 0 }; + BaseType_t xMACRemoveExpected = pdFALSE; + + xCallEventHook = pdFALSE; + xInterfaces[ 0 ].pfInitialise = &xNetworkInterfaceInitialise_returnTrue; + xInterfaces[ 0 ].pxEndPoint = &xEndPoint; + + xEndPoint.bits.bEndPointUp = pdTRUE; + xEndPoint.bits.bCallDownHook = pdFALSE_UNSIGNED; + xEndPoint.bits.bWantDHCP = pdFALSE_UNSIGNED; + memcpy( xEndPoint.ipv6_settings.xIPAddress.ucBytes, pucAddress, ipSIZE_OF_IPv6_ADDRESS ); + + if( ( uxSetMembers & ipHAS_INTERFACE ) != 0U ) + { + xEndPoint.pxNetworkInterface = &xInterface; + } + + if( ( uxSetMembers & ipHAS_IPV6 ) != 0U ) + { + xEndPoint.bits.bIPv6 = pdTRUE_UNSIGNED; + } + + xMACFunctionCalled = pdFALSE; + + if( ( ( eType == eIPv6_LinkLocal ) || ( eType == eIPv6_SiteLocal ) || ( eType == eIPv6_Global ) ) && + ( xInterface.pfRemoveAllowedMAC != NULL ) && + ( xEndPoint.pxNetworkInterface != NULL ) && + ( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) ) + { + xMACRemoveExpected = pdTRUE; + } + + vIPSetARPTimerEnableState_Expect( pdFALSE ); + + FreeRTOS_FirstEndPoint_IgnoreAndReturn( &xEndPoint ); + + if( xEndPoint.bits.bIPv6 == pdTRUE_UNSIGNED ) + { + /* The vManageSolicitedNodeAddress() function is mocked. */ + vManageSolicitedNodeAddress_Expect( &xEndPoint, pdFALSE ); + } + + FreeRTOS_NextEndPoint_IgnoreAndReturn( NULL ); + + + FreeRTOS_ClearARP_Expect( &xEndPoint ); + + vIPNetworkUpCalls_Expect( &xEndPoint ); + + prvProcessNetworkDownEvent( &xInterfaces[ 0 ] ); + + /* See if pfRemoveAllowedMAC() was called when it has to. */ + TEST_ASSERT_EQUAL( xMACRemoveExpected, xMACFunctionCalled ); +} + +void test_prvProcessNetworkDownEvent_LinkLocal() +{ + /* Use the local-link address fe80::7009 */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0x80U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + /* Test all combinations of what might go wrong. */ + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_IPV6 ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_LinkLocal, ipHAS_INTERFACE ); +} + +void test_prvProcessNetworkDownEvent_Global() +{ + /* Use the local-link address "2001:0470:ed44::7009" */ + static const uint8_t ucAddress[ 16 ] = + { + 0x20U, 0x01U, + 0x04U, 0x70U, + 0xEDU, 0x44U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Global, ipHAS_IPV6 ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Global, ipHAS_INTERFACE ); +} + +void test_prvProcessNetworkDownEvent_SiteLocal() +{ + /* Use the local-link address "fec0::7009" */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0xC0U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_IPV6 ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_SiteLocal, ipHAS_INTERFACE ); +} + +void test_prvProcessNetworkDownEvent_Multicast() +{ + /* Use the multicast address "ff02::fb", + * just for the coverage of prvProcessNetworkDownEvent(). */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFFU, 0x02U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0xFBU + }; + + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Multicast, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Multicast, ipHAS_IPV6 | ipHAS_INTERFACE ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Multicast, ipHAS_IPV6 ); + prvProcessNetworkDownEvent_Generic( ucAddress, eIPv6_Multicast, ipHAS_INTERFACE ); +} + /** * @brief test_eGetDHCPState * To validate if eGetDHCPState returns expected diff --git a/test/unit-test/FreeRTOS_IPv6_Utils/FreeRTOS_IPv6_Utils_utest.c b/test/unit-test/FreeRTOS_IPv6_Utils/FreeRTOS_IPv6_Utils_utest.c index 8eaf9f7ff..12b731ab0 100644 --- a/test/unit-test/FreeRTOS_IPv6_Utils/FreeRTOS_IPv6_Utils_utest.c +++ b/test/unit-test/FreeRTOS_IPv6_Utils/FreeRTOS_IPv6_Utils_utest.c @@ -44,6 +44,7 @@ #include "mock_FreeRTOS_IP.h" #include "mock_FreeRTOS_IPv6.h" +#include "mock_FreeRTOS_Routing.h" #include "FreeRTOS_IPv6_Utils.h" @@ -642,3 +643,229 @@ void test_usGetExtensionHeaderLength_InvalidHeader( void ) TEST_ASSERT_EQUAL( pxNetworkBuffer->xDataLength, xReturn ); } + +/* This variable will be set when pfAddAllowedMAC() is called by vIPNetworkUpCalls(). */ +static BaseType_t xMACAddFunctionCalled; +static BaseType_t xMACRemoveFunctionCalled; + +static void pfAddAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) +{ + xMACAddFunctionCalled = pdTRUE; +} + +static void pfRemoveAllowedMAC( struct xNetworkInterface * pxInterface, + const uint8_t * pucMacAddress ) +{ + xMACRemoveFunctionCalled = pdTRUE; +} + +/** + * @brief Check for assert if endpoint is NULL + */ +void test_vManageSolicitedNodeAddress_NoEndPoint_CatchAssert( void ) +{ + catch_assert( vManageSolicitedNodeAddress( NULL, pdTRUE ) ); +} + + +/** + * @brief Check for assert if interface is NULL + */ +void test_vManageSolicitedNodeAddress_NoInterface_CatchAssert( void ) +{ + NetworkEndPoint_t xEndPoint = { 0 }; + + catch_assert( vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ) ); +} + + +/** + * @brief Validate the case were the network is going up + */ +void test_vManageSolicitedNodeAddress_NetworkGoingUp( void ) +{ + NetworkInterface_t xInterface = { 0 }; + NetworkEndPoint_t xEndPoint = { 0 }; + BaseType_t xMACAddExpected = pdFALSE; + /* Use the local-link address fe80::7009 */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0x80U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + + /* Happy path eIPv6_LinkLocal */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xEndPoint.bits.bEndPointUp = pdFALSE; + xEndPoint.bits.bCallDownHook = pdFALSE_UNSIGNED; + xEndPoint.bits.bWantDHCP = pdFALSE_UNSIGNED; + xEndPoint.bits.bIPv6 = pdTRUE; + xEndPoint.pxNetworkInterface = &xInterface; + memcpy( xEndPoint.ipv6_settings.xIPAddress.ucBytes, ucAddress, ipSIZE_OF_IPv6_ADDRESS ); + + xMACAddFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_LinkLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACAddFunctionCalled ); + + /* No MAC address add handler */ + xInterface.pfAddAllowedMAC = NULL; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACAddFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_LinkLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ); + + TEST_ASSERT_EQUAL( pdFALSE, xMACAddFunctionCalled ); + + /* Happy path eIPv6_SiteLocal */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACAddFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_SiteLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACAddFunctionCalled ); + + /* Happy path eIPv6_Global */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACAddFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_Global ); + + vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACAddFunctionCalled ); + + /* Unhappy path eIPv6_Loopback */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACAddFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_Loopback ); + + vManageSolicitedNodeAddress( &xEndPoint, pdTRUE ); + + TEST_ASSERT_EQUAL( pdFALSE, xMACAddFunctionCalled ); +} + +/** + * @brief Validate the case were the network is going down + */ +void test_vManageSolicitedNodeAddress_NetworkGoingDown( void ) +{ + NetworkInterface_t xInterface = { 0 }; + NetworkEndPoint_t xEndPoint = { 0 }; + BaseType_t xMACAddExpected = pdFALSE; + /* Use the local-link address fe80::7009 */ + static const uint8_t ucAddress[ 16 ] = + { + 0xFEU, 0x80U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x00U, 0x00U, + 0x70U, 0x09U + }; + + + /* Happy path eIPv6_LinkLocal */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xEndPoint.bits.bEndPointUp = pdFALSE; + xEndPoint.bits.bCallDownHook = pdFALSE_UNSIGNED; + xEndPoint.bits.bWantDHCP = pdFALSE_UNSIGNED; + xEndPoint.bits.bIPv6 = pdTRUE; + xEndPoint.pxNetworkInterface = &xInterface; + memcpy( xEndPoint.ipv6_settings.xIPAddress.ucBytes, ucAddress, ipSIZE_OF_IPv6_ADDRESS ); + + xMACRemoveFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_LinkLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdFALSE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACRemoveFunctionCalled ); + + /* No MAC address add handler */ + xInterface.pfAddAllowedMAC = NULL; + xInterface.pfRemoveAllowedMAC = NULL; + xInterface.pxEndPoint = &xEndPoint; + + xMACRemoveFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_LinkLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdFALSE ); + + TEST_ASSERT_EQUAL( pdFALSE, xMACRemoveFunctionCalled ); + + /* Happy path eIPv6_SiteLocal */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACRemoveFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_SiteLocal ); + + vManageSolicitedNodeAddress( &xEndPoint, pdFALSE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACRemoveFunctionCalled ); + + /* Happy path eIPv6_Global */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACRemoveFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_Global ); + + vManageSolicitedNodeAddress( &xEndPoint, pdFALSE ); + + TEST_ASSERT_EQUAL( pdTRUE, xMACRemoveFunctionCalled ); + + /* Unhappy path eIPv6_Loopback */ + xInterface.pfAddAllowedMAC = &pfAddAllowedMAC; + xInterface.pfRemoveAllowedMAC = &pfRemoveAllowedMAC; + xInterface.pxEndPoint = &xEndPoint; + + xMACRemoveFunctionCalled = pdFALSE; + + xIPv6_GetIPType_ExpectAndReturn( &xEndPoint.ipv6_settings.xIPAddress, eIPv6_Loopback ); + + vManageSolicitedNodeAddress( &xEndPoint, pdFALSE ); + + TEST_ASSERT_EQUAL( pdFALSE, xMACRemoveFunctionCalled ); +} diff --git a/test/unit-test/FreeRTOS_IPv6_Utils/ut.cmake b/test/unit-test/FreeRTOS_IPv6_Utils/ut.cmake index 6e9f105f5..16cf08c42 100644 --- a/test/unit-test/FreeRTOS_IPv6_Utils/ut.cmake +++ b/test/unit-test/FreeRTOS_IPv6_Utils/ut.cmake @@ -18,6 +18,7 @@ list(APPEND mock_list "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IPv6.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_ICMP.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_Sockets.h" + "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_Routing.h" "${CMAKE_BINARY_DIR}/Annexed_TCP/FreeRTOS_IP_Utils.h" ) From 1bc37d3e901dc585217877f0adbf0c9f456c6c34 Mon Sep 17 00:00:00 2001 From: Tony Josi Date: Mon, 8 Apr 2024 20:51:13 +0530 Subject: [PATCH 2/2] Fix inconsistent use of ipconfigCOMPATIBLE_WITH_SINGLE & ipconfigIPv4_BACKWARD_COMPATIBLE (#1134) --- source/FreeRTOS_IP.c | 2 +- .../portable/NetworkInterface/DriverSAM/NetworkInterface.c | 4 ++-- .../portable/NetworkInterface/MPS2_AN385/NetworkInterface.c | 4 ++-- .../portable/NetworkInterface/MPS3_AN552/NetworkInterface.c | 4 ++-- .../portable/NetworkInterface/MPS4_CS315/NetworkInterface.c | 4 ++-- source/portable/NetworkInterface/NXP1060/NetworkInterface.c | 6 +++--- .../portable/NetworkInterface/STM32Fxx/NetworkInterface.c | 2 +- .../portable/NetworkInterface/STM32Hxx/NetworkInterface.c | 2 +- source/portable/NetworkInterface/WinPCap/NetworkInterface.c | 4 ++-- source/portable/NetworkInterface/Zynq/NetworkInterface.c | 4 ++-- source/portable/NetworkInterface/esp32/NetworkInterface.c | 4 ++-- .../NetworkInterface/loopback/loopbackNetworkInterface.c | 6 +++--- .../NetworkInterface/pic32mzef/NetworkInterface_eth.c | 4 ++-- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/source/FreeRTOS_IP.c b/source/FreeRTOS_IP.c index a43e65f32..5c82bd1fe 100644 --- a/source/FreeRTOS_IP.c +++ b/source/FreeRTOS_IP.c @@ -924,7 +924,7 @@ void * FreeRTOS_GetUDPPayloadBuffer_Multi( size_t uxRequestedSizeBytes, /* IF the following function should be declared in the NetworkInterface.c * linked in the project. */ - pxFillInterfaceDescriptor( 0, &( xInterfaces[ 0 ] ) ); + ( void ) pxFillInterfaceDescriptor( 0, &( xInterfaces[ 0 ] ) ); FreeRTOS_FillEndPoint( &( xInterfaces[ 0 ] ), &( xEndPoints[ 0 ] ), ucIPAddress, ucNetMask, ucGatewayAddress, ucDNSServerAddress, ucMACAddress ); #if ( ipconfigUSE_DHCP != 0 ) { diff --git a/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c b/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c index 6619f6afb..c787bbd0b 100644 --- a/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c +++ b/source/portable/NetworkInterface/DriverSAM/NetworkInterface.c @@ -522,7 +522,7 @@ static BaseType_t prvSAM_NetworkInterfaceInitialise( NetworkInterface_t * pxInte } /*-----------------------------------------------------------*/ -#if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -530,7 +530,7 @@ static BaseType_t prvSAM_NetworkInterfaceInitialise( NetworkInterface_t * pxInte NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxSAM_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxSAM_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c b/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c index 951d33266..1c55f82db 100644 --- a/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c +++ b/source/portable/NetworkInterface/MPS2_AN385/NetworkInterface.c @@ -398,7 +398,7 @@ static BaseType_t xMPS2_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -406,7 +406,7 @@ static BaseType_t xMPS2_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxMPS2_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxMPS2_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/MPS3_AN552/NetworkInterface.c b/source/portable/NetworkInterface/MPS3_AN552/NetworkInterface.c index 836f4e3b0..ac68594ac 100644 --- a/source/portable/NetworkInterface/MPS3_AN552/NetworkInterface.c +++ b/source/portable/NetworkInterface/MPS3_AN552/NetworkInterface.c @@ -507,7 +507,7 @@ static BaseType_t xLAN91C111_GetPhyLinkStatus( NetworkInterface_t * pxInterface /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialise the interface and end-point @@ -515,7 +515,7 @@ static BaseType_t xLAN91C111_GetPhyLinkStatus( NetworkInterface_t * pxInterface NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxLAN91C111_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxLAN91C111_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/MPS4_CS315/NetworkInterface.c b/source/portable/NetworkInterface/MPS4_CS315/NetworkInterface.c index 9fc319f96..ac68594ac 100644 --- a/source/portable/NetworkInterface/MPS4_CS315/NetworkInterface.c +++ b/source/portable/NetworkInterface/MPS4_CS315/NetworkInterface.c @@ -507,7 +507,7 @@ static BaseType_t xLAN91C111_GetPhyLinkStatus( NetworkInterface_t * pxInterface /*-----------------------------------------------------------*/ -#if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialise the interface and end-point @@ -515,7 +515,7 @@ static BaseType_t xLAN91C111_GetPhyLinkStatus( NetworkInterface_t * pxInterface NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxLAN91C111_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxLAN91C111_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/NXP1060/NetworkInterface.c b/source/portable/NetworkInterface/NXP1060/NetworkInterface.c index 6eee8a78f..4d5097425 100644 --- a/source/portable/NetworkInterface/NXP1060/NetworkInterface.c +++ b/source/portable/NetworkInterface/NXP1060/NetworkInterface.c @@ -269,7 +269,7 @@ NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ); /*-----------------------------------------------------------*/ -#if ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -277,10 +277,10 @@ NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxNXP1060_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxNXP1060_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } -#endif /* ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) */ +#endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) */ /*-----------------------------------------------------------*/ NetworkInterface_t * pxNXP1060_FillInterfaceDescriptor( BaseType_t xEMACIndex, diff --git a/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c b/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c index 8d2ed7f48..64e8071f5 100644 --- a/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c +++ b/source/portable/NetworkInterface/STM32Fxx/NetworkInterface.c @@ -353,7 +353,7 @@ BaseType_t xNetworkInterfaceOutput( NetworkInterface_t * pxInterface, return xSTM32F_NetworkInterfaceOutput( pxInterface, pxBuffer, bReleaseAfterSend ); } -#if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point diff --git a/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c b/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c index e0cc4f036..4456d517e 100644 --- a/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c +++ b/source/portable/NetworkInterface/STM32Hxx/NetworkInterface.c @@ -535,7 +535,7 @@ static BaseType_t xSTM32H_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) } /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point diff --git a/source/portable/NetworkInterface/WinPCap/NetworkInterface.c b/source/portable/NetworkInterface/WinPCap/NetworkInterface.c index c3e4a6d3b..e7f8975a8 100644 --- a/source/portable/NetworkInterface/WinPCap/NetworkInterface.c +++ b/source/portable/NetworkInterface/WinPCap/NetworkInterface.c @@ -347,7 +347,7 @@ static BaseType_t xWinPcap_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) } /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. @@ -356,7 +356,7 @@ static BaseType_t xWinPcap_GetPhyLinkStatus( NetworkInterface_t * pxInterface ) NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxWinPcap_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxWinPcap_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/Zynq/NetworkInterface.c b/source/portable/NetworkInterface/Zynq/NetworkInterface.c index cc1873bcb..25dfd8e0f 100644 --- a/source/portable/NetworkInterface/Zynq/NetworkInterface.c +++ b/source/portable/NetworkInterface/Zynq/NetworkInterface.c @@ -523,7 +523,7 @@ static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface ) } /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -531,7 +531,7 @@ static BaseType_t xZynqGetPhyLinkStatus( NetworkInterface_t * pxInterface ) NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxZynq_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxZynq_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/esp32/NetworkInterface.c b/source/portable/NetworkInterface/esp32/NetworkInterface.c index b4c8b068e..452fad6fc 100644 --- a/source/portable/NetworkInterface/esp32/NetworkInterface.c +++ b/source/portable/NetworkInterface/esp32/NetworkInterface.c @@ -60,7 +60,7 @@ NetworkInterface_t * pxESP32_Eth_FillInterfaceDescriptor( BaseType_t xEMACIndex, /*-----------------------------------------------------------*/ -#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -68,7 +68,7 @@ NetworkInterface_t * pxESP32_Eth_FillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxESP32_Eth_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxESP32_Eth_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif diff --git a/source/portable/NetworkInterface/loopback/loopbackNetworkInterface.c b/source/portable/NetworkInterface/loopback/loopbackNetworkInterface.c index 466c6e47d..cf66a62ff 100644 --- a/source/portable/NetworkInterface/loopback/loopbackNetworkInterface.c +++ b/source/portable/NetworkInterface/loopback/loopbackNetworkInterface.c @@ -75,7 +75,7 @@ static BaseType_t prvLoopback_Initialise( NetworkInterface_t * pxInterface ) } /*-----------------------------------------------------------*/ -#if ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) +#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -83,10 +83,10 @@ static BaseType_t prvLoopback_Initialise( NetworkInterface_t * pxInterface ) NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxLoopback_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxLoopback_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } -#endif /* ( ipconfigCOMPATIBLE_WITH_SINGLE != 0 ) */ +#endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) */ /*-----------------------------------------------------------*/ NetworkInterface_t * pxLoopback_FillInterfaceDescriptor( BaseType_t xEMACIndex, diff --git a/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c b/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c index c9500c5b1..df35ed880 100644 --- a/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c +++ b/source/portable/NetworkInterface/pic32mzef/NetworkInterface_eth.c @@ -219,7 +219,7 @@ }; #endif /* (PIC32_MAC_DEBUG_COMMANDS != 0) */ - #if defined( ipconfigIPv4_BACKWARD_COMPATIBLE ) && ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) + #if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 0 ) /* Do not call the following function directly. It is there for downward compatibility. * The function FreeRTOS_IPInit() will call it to initialice the interface and end-point @@ -227,7 +227,7 @@ NetworkInterface_t * pxFillInterfaceDescriptor( BaseType_t xEMACIndex, NetworkInterface_t * pxInterface ) { - pxPIC32_Eth_FillInterfaceDescriptor( xEMACIndex, pxInterface ); + return pxPIC32_Eth_FillInterfaceDescriptor( xEMACIndex, pxInterface ); } #endif