Skip to content

Commit

Permalink
Support caching more than 1 IP address per DNS cache entry (aws#1981)
Browse files Browse the repository at this point in the history
* Support multiple IP addresses per DNS cache entry.
* Add test for multiple IP address DNS results from AWS IoT Core endpoint.
* Re-enable DNS cache on FreeRTOS+TCP platforms
* Configure FreeRTOS+TCP platforms to remember up to 6 DNS answers per cache entry

More information at:

FreeRTOS/FreeRTOS#58
  • Loading branch information
gkwicker authored May 14, 2020
1 parent 0858694 commit bb3c7e6
Show file tree
Hide file tree
Showing 40 changed files with 182 additions and 297 deletions.
57 changes: 57 additions & 0 deletions libraries/abstractions/secure_sockets/test/iot_test_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ typedef struct
#define tcptestNUM_ECHO_CLIENTS ( 2 )
#define tcptestMAX_LOOPS_ECHO_CLIENTS_LOOP ( 10 )

#define dnstestNUM_UNIQUE_IP_ADDRESSES ( 4 )

static void prvThreadSafeDifferentSocketsDifferentTasks( void * pvParameters );
/****************** Unity Test Code *********************************/
size_t xHeapB;
Expand Down Expand Up @@ -805,6 +807,7 @@ TEST_GROUP_RUNNER( Full_TCP )
RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_Recv_Invalid );
RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_htons_HappyCase );
RUN_TEST_CASE( Full_TCP, AFQP_SOCKETS_inet_addr_quick_HappyCase );
RUN_TEST_CASE( Full_TCP, test_dns_multiple_addresses );

#if ( tcptestSECURE_SERVER == 1 )
RUN_TEST_CASE( Full_TCP, AFQP_SECURE_SOCKETS_CloseInvalidParams );
Expand Down Expand Up @@ -2961,6 +2964,60 @@ TEST( Full_TCP, AFQP_SOCKETS_htons_HappyCase )
}
/*-----------------------------------------------------------*/

TEST( Full_TCP, test_dns_multiple_addresses )
{
BaseType_t xResult = pdFAIL;
uint32_t i;
uint32_t j;
uint32_t ulIPAddress;
uint32_t ulUnique;
uint32_t ulNumUniqueIPAddresses = 0;

/* Resolve the AWS IoT Core endpoint, which will have multiple IP addresses */

uint32_t ulIPAddresses[ dnstestNUM_UNIQUE_IP_ADDRESSES ] = { 0UL };

tcptestPRINTF( ( "Starting %s.\r\n", __FUNCTION__ ) );
/*
* Resolve the endpoint to an array of IP addresses. Each subsequent
* call will return one of the addresses which the name resolves to.
*
* NOTE: Resolving addresses can take some time, so allow up to
* 60 seconds to collect all of them.
*/
for( i = 0 ; ( i < 60 ) && ( ulNumUniqueIPAddresses < dnstestNUM_UNIQUE_IP_ADDRESSES ) ; i ++ )
{
ulIPAddress = SOCKETS_GetHostByName( clientcredentialMQTT_BROKER_ENDPOINT );

for( j = 0, ulUnique = 1 ; j < ulNumUniqueIPAddresses ; j++ )
{
if( ulIPAddresses[ j ] == ulIPAddress )
{
ulUnique = 0;
}
}
if( ( ulUnique == 1 ) && ( ulNumUniqueIPAddresses < dnstestNUM_UNIQUE_IP_ADDRESSES ) )
{
ulIPAddresses[ ulNumUniqueIPAddresses++ ] = ulIPAddress;
}
vTaskDelay( 1000 / portTICK_PERIOD_MS );
}
configPRINTF( ( "%s: identified %d different IP addresses for %s.\r\n",
__FUNCTION__,
ulNumUniqueIPAddresses,
clientcredentialMQTT_BROKER_ENDPOINT ) );

/* Require a minimum number of IP addresses for AWS IoT Core endpoints */
if( ulNumUniqueIPAddresses >= dnstestNUM_UNIQUE_IP_ADDRESSES )
{
xResult = pdPASS;
}
TEST_ASSERT_EQUAL_UINT32_MESSAGE( pdPASS, xResult, "Incorrect number of IP addresses per entry" );
tcptestPRINTF( ( "%s complete.\r\n", __FUNCTION__ ) );
}

/*-----------------------------------------------------------*/

TEST( Full_TCP, AFQP_SOCKETS_inet_addr_quick_HappyCase )
{
uint32_t ulPackedIpAddress;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,13 @@ from the FreeRTOSIPConfig.h configuration header file. */
#ifndef ipconfigDNS_CACHE_ENTRIES
#define ipconfigDNS_CACHE_ENTRIES 1
#endif

/* When accessing services which have multiple IP addresses, setting this
greater than 1 can improve reliability by returning different IP address
answers on successive calls to FreeRTOS_gethostbyname(). */
#ifndef ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY 1
#endif
#endif /* ipconfigUSE_DNS_CACHE != 0 */

#ifndef ipconfigCHECK_IP_QUEUE_SPACE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,14 @@ static uint32_t prvGetHostByName( const char *pcHostName,

typedef struct xDNS_CACHE_TABLE_ROW
{
uint32_t ulIPAddress; /* The IP address of an ARP cache entry. */
uint32_t ulIPAddress[ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY]; /* The IP address(es) of an ARP cache entry. */
char pcName[ ipconfigDNS_CACHE_NAME_LENGTH ]; /* The name of the host */
uint32_t ulTTL; /* Time-to-Live (in seconds) from the DNS server. */
uint32_t ulTimeWhenAddedInSeconds;
#if( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
uint8_t ucNumIPAddresses;
uint8_t ucCurrentIPAddress;
#endif
} DNSCacheRow_t;

static DNSCacheRow_t xDNSCache[ ipconfigDNS_CACHE_ENTRIES ];
Expand Down Expand Up @@ -1075,7 +1079,7 @@ uint16_t x, usDataLength, usQuestions;

if( ( pxDNSMessageHeader->usFlags & dnsRX_FLAGS_MASK ) == dnsEXPECTED_RX_FLAGS )
{
for( x = 0; x < pxDNSMessageHeader->usAnswers; x++ )
for( x = 0; ( x < pxDNSMessageHeader->usAnswers ) && ( x < ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ); x++ )
{
pucByte = prvSkipNameField( pucByte,
uxSourceBytesRemaining );
Expand Down Expand Up @@ -1137,7 +1141,11 @@ uint16_t x, usDataLength, usQuestions;

pucByte += sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t );
uxSourceBytesRemaining -= ( sizeof( DNSAnswerRecord_t ) + sizeof( uint32_t ) );
break;

#if( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY <= 1 )
/* One response was received, which is enough for this configuration. */
break;
#endif
}
else if( uxSourceBytesRemaining >= sizeof( DNSAnswerRecord_t ) )
{
Expand Down Expand Up @@ -1471,6 +1479,7 @@ BaseType_t xReturn;
BaseType_t x;
BaseType_t xFound = pdFALSE;
uint32_t ulCurrentTimeSeconds = ( xTaskGetTickCount() / portTICK_PERIOD_MS ) / 1000;
uint32_t ulIPAddressIndex = 0;
static BaseType_t xFreeEntry = 0;
configASSERT(pcName);

Expand All @@ -1490,7 +1499,18 @@ BaseType_t xReturn;
/* Confirm that the record is still fresh. */
if( ulCurrentTimeSeconds < ( xDNSCache[ x ].ulTimeWhenAddedInSeconds + FreeRTOS_ntohl( xDNSCache[ x ].ulTTL ) ) )
{
*pulIP = xDNSCache[ x ].ulIPAddress;
#if( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
/* The ucCurrentIPAddress value increments without bound and will rollover, */
/* modulo it by the number of IP addresses to keep it in range. */
/* Also perform a final modulo by the max number of IP addresses */
/* per DNS cache entry to prevent out-of-bounds access in the event */
/* that ucNumIPAddresses has been corrupted. */
ulIPAddressIndex = ( xDNSCache[ x ].ucCurrentIPAddress %
xDNSCache[ x ].ucNumIPAddresses ) % ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY;

xDNSCache[ x ].ucCurrentIPAddress++;
#endif
*pulIP = xDNSCache[ x ].ulIPAddress[ulIPAddressIndex];
}
else
{
Expand All @@ -1500,7 +1520,16 @@ BaseType_t xReturn;
}
else
{
xDNSCache[ x ].ulIPAddress = *pulIP;
#if( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
if ( xDNSCache[ x ].ucNumIPAddresses < ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY )
{
/* If more answers exist than there are IP address storage slots */
/* they will overwrite entry 0 */

ulIPAddressIndex = xDNSCache[ x ].ucNumIPAddresses++;
}
#endif
xDNSCache[ x ].ulIPAddress[ulIPAddressIndex] = *pulIP;
xDNSCache[ x ].ulTTL = ulTTL;
xDNSCache[ x ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;
}
Expand All @@ -1523,10 +1552,19 @@ BaseType_t xReturn;
{
strcpy( xDNSCache[ xFreeEntry ].pcName, pcName );

xDNSCache[ xFreeEntry ].ulIPAddress = *pulIP;
xDNSCache[ xFreeEntry ].ulIPAddress[0] = *pulIP;
xDNSCache[ xFreeEntry ].ulTTL = ulTTL;
xDNSCache[ xFreeEntry ].ulTimeWhenAddedInSeconds = ulCurrentTimeSeconds;

#if( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY > 1 )
xDNSCache[ xFreeEntry ].ucNumIPAddresses = 1;
xDNSCache[ xFreeEntry ].ucCurrentIPAddress = 0;

/* Initialize all remaining IP addresses in this entry to 0 */
memset( &xDNSCache[ xFreeEntry ].ulIPAddress[ 1 ],
0,
sizeof( xDNSCache[ xFreeEntry ].ulIPAddress[ 1 ] ) *
( ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY - 1 ) );
#endif
xFreeEntry++;

if( xFreeEntry == ipconfigDNS_CACHE_ENTRIES )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,9 @@ extern void vLoggingPrintf( const char * pcFormatString,
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,9 @@ extern void vLoggingPrintf( const char * pcFormatString,
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,9 @@ extern void vLoggingPrintf( const char * pcFormatString,
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 )
#define ipconfigDNS_CACHE_ENTRIES ( 4 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 )
#define ipconfigDNS_CACHE_ENTRIES ( 4 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,9 @@ extern void vLoggingPrintf( const char * pcFormatString,
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 )
#define ipconfigDNS_CACHE_ENTRIES ( 4 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,9 @@
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_CACHE_NAME_LENGTH ( 16 )
#define ipconfigDNS_CACHE_ENTRIES ( 4 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 2 )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,9 @@ extern void vLoggingPrintf( const char * pcFormatString,
* socket has been destroyed, the result will be stored into the cache. The next
* call to FreeRTOS_gethostbyname() will return immediately, without even creating
* a socket.
*
* NOTE: Use caution when enabling the DNS cache and connecting to services which
* use a load balancer. Since the cache only holds a single IP address, if that
* address is out of date, no connections to the host will succeed until the TTL
* expires. See this GitHub issue for more details.
*
* https://github.com/FreeRTOS/FreeRTOS/issues/58
*/
#define ipconfigUSE_DNS_CACHE ( 0 )
#define ipconfigUSE_DNS_CACHE ( 1 )
#define ipconfigDNS_CACHE_ADDRESSES_PER_ENTRY ( 6 )
#define ipconfigDNS_REQUEST_ATTEMPTS ( 10 )

/* The IP stack executes it its own task (although any application task can make
Expand Down
Loading

0 comments on commit bb3c7e6

Please sign in to comment.