1
0
mirror of https://github.com/FreeRTOS/FreeRTOS-Plus-TCP synced 2025-10-20 13:23:44 +08:00
Files
FreeRTOS-Plus-TCP/source/FreeRTOS_DHCP.c
Monika Singh b23fa86ac4 Add and fix -Wconversion errors (#980)
* Fix Wconverstion

* Enable Wconversion warning

* Add fix

* Fix MISRA

* Fix coverity

* Add comments
2023-07-27 15:56:36 +05:30

1692 lines
71 KiB
C

/*
* FreeRTOS+TCP <DEVELOPMENT BRANCH>
* Copyright (C) 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* SPDX-License-Identifier: MIT
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://aws.amazon.com/freertos
* http://www.FreeRTOS.org
*/
/**
* @file FreeRTOS_DHCP.c
* @brief Implements the Dynamic Host Configuration Protocol for the FreeRTOS+TCP network stack.
*/
/* Standard includes. */
#include <stdint.h>
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* FreeRTOS+TCP includes. */
#include "FreeRTOS_IP.h"
#include "FreeRTOS_Sockets.h"
#include "FreeRTOS_IP_Private.h"
#include "FreeRTOS_UDP_IP.h"
#include "FreeRTOS_DHCP.h"
#include "FreeRTOS_ARP.h"
#include "FreeRTOS_IP_Timers.h"
/* Exclude the entire file if DHCP is not enabled. */
#if ( ipconfigUSE_DHCP != 0 )
#include "NetworkInterface.h"
#include "NetworkBufferManagement.h"
#include "FreeRTOS_Routing.h"
/* The following define is temporary and serves to make the /single source
* code more similar to the /multi version. */
#define EP_DHCPData pxEndPoint->xDHCPData /**< Temporary define to make /single source similar to /multi version. */
#define EP_IPv4_SETTINGS pxEndPoint->ipv4_settings /**< Temporary define to make /single source similar to /multi version. */
/** @brief The UDP socket used for all incoming and outgoing DHCP traffic. */
_static Socket_t xDHCPv4Socket;
#if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
/* Define the Link Layer IP address: 169.254.x.x */
#define LINK_LAYER_ADDRESS_0 169
#define LINK_LAYER_ADDRESS_1 254
/* Define the netmask used: 255.255.0.0 */
#define LINK_LAYER_NETMASK_0 255
#define LINK_LAYER_NETMASK_1 255
#define LINK_LAYER_NETMASK_2 0
#define LINK_LAYER_NETMASK_3 0
#endif
/*-----------------------------------------------------------*/
/**
* @brief The number of end-points that are making use of the UDP-socket.
*/
static BaseType_t xDHCPSocketUserCount = 0;
/*
* Generate a DHCP discover message and send it on the DHCP socket.
*/
static BaseType_t prvSendDHCPDiscover( NetworkEndPoint_t * pxEndPoint );
/*
* Interpret message received on the DHCP socket.
*/
static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType,
NetworkEndPoint_t * pxEndPoint );
/*
* Generate a DHCP request packet, and send it on the DHCP socket.
*/
static BaseType_t prvSendDHCPRequest( NetworkEndPoint_t * pxEndPoint );
/*
* Prepare to start a DHCP transaction. This initialises some state variables
* and creates the DHCP socket if necessary.
*/
static void prvInitialiseDHCP( NetworkEndPoint_t * pxEndPoint );
/*
* Creates the part of outgoing DHCP messages that are common to all outgoing
* DHCP messages.
*/
static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress,
BaseType_t xOpcode,
const uint8_t * const pucOptionsArray,
size_t * pxOptionsArraySize,
const NetworkEndPoint_t * pxEndPoint );
/*
* Create the DHCP socket, if it has not been created already.
*/
_static void prvCreateDHCPSocket( const NetworkEndPoint_t * pxEndPoint );
/*
* Close the DHCP socket, only when not in use anymore (i.e. xDHCPSocketUserCount = 0).
*/
static void prvCloseDHCPSocket( const NetworkEndPoint_t * pxEndPoint );
static void vDHCPProcessEndPoint( BaseType_t xReset,
BaseType_t xDoCheck,
NetworkEndPoint_t * pxEndPoint );
static BaseType_t xHandleWaitingOffer( NetworkEndPoint_t * pxEndPoint,
BaseType_t xDoCheck );
static void vHandleWaitingAcknowledge( NetworkEndPoint_t * pxEndPoint,
BaseType_t xDoCheck );
static BaseType_t xHandleWaitingFirstDiscover( NetworkEndPoint_t * pxEndPoint );
static void prvHandleWaitingeLeasedAddress( NetworkEndPoint_t * pxEndPoint );
static void vProcessHandleOption( NetworkEndPoint_t * pxEndPoint,
ProcessSet_t * pxSet,
BaseType_t xExpectedMessageType );
static BaseType_t xProcessCheckOption( ProcessSet_t * pxSet );
/*-----------------------------------------------------------*/
/**
* @brief Check whether a given socket is the DHCP socket or not.
*
* @param[in] xSocket The socket to be checked.
*
* @return If the socket given as parameter is the DHCP socket - return
* pdTRUE, else pdFALSE.
*/
BaseType_t xIsDHCPSocket( const ConstSocket_t xSocket )
{
BaseType_t xReturn;
if( xDHCPv4Socket == xSocket )
{
xReturn = pdTRUE;
}
else
{
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Returns the current state of a DHCP process.
*
* @param[in] pxEndPoint the end-point which is going through the DHCP process.
*/
eDHCPState_t eGetDHCPState( const struct xNetworkEndPoint * pxEndPoint )
{
/* Note that EP_DHCPData is defined as "pxEndPoint->xDHCPData". */
return EP_DHCPData.eDHCPState;
}
/*-----------------------------------------------------------*/
/**
* @brief Process the DHCP state machine based on current state.
*
* @param[in] xReset Is the DHCP state machine starting over? pdTRUE/pdFALSE.
* @param[in] pxEndPoint The end-point for which the DHCP state machine should
* make one cycle.
*/
void vDHCPProcess( BaseType_t xReset,
struct xNetworkEndPoint * pxEndPoint )
{
BaseType_t xDoProcess = pdTRUE;
/* The function is called by the IP-task, so pxEndPoint
* should be non-NULL. */
configASSERT( pxEndPoint != NULL );
configASSERT( pxEndPoint->bits.bIPv6 == 0 );
/* Is DHCP starting over? */
if( xReset != pdFALSE )
{
EP_DHCPData.eDHCPState = eInitialWait;
}
if( ( EP_DHCPData.eDHCPState != EP_DHCPData.eExpectedState ) && ( xReset == pdFALSE ) )
{
/* When the DHCP event was generated, the DHCP client was
* in a different state. Therefore, ignore this event. */
FreeRTOS_debug_printf( ( "DHCP wrong state: expect: %d got: %d : ignore\n",
EP_DHCPData.eExpectedState, EP_DHCPData.eDHCPState ) );
}
else if( xDHCPv4Socket != NULL ) /* If there is a socket, check for incoming messages first. */
{
/* No need to initialise 'pucUDPPayload', it just looks nicer. */
uint8_t * pucUDPPayload = NULL;
const DHCPMessage_IPv4_t * pxDHCPMessage;
int32_t lBytes;
while( xDHCPv4Socket != NULL )
{
BaseType_t xRecvFlags = FREERTOS_ZERO_COPY + FREERTOS_MSG_PEEK;
NetworkEndPoint_t * pxIterator = NULL;
/* Peek the next UDP message. */
lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &( pucUDPPayload ), 0, xRecvFlags, NULL, NULL );
if( lBytes < ( ( int32_t ) sizeof( DHCPMessage_IPv4_t ) ) )
{
if( ( lBytes < 0 ) && ( lBytes != -pdFREERTOS_ERRNO_EAGAIN ) )
{
FreeRTOS_printf( ( "vDHCPProcess: FreeRTOS_recvfrom returns %d\n", ( int ) lBytes ) );
}
break;
}
/* Map a DHCP structure onto the received data. */
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
pxDHCPMessage = ( ( const DHCPMessage_IPv4_t * ) pucUDPPayload );
/* Sanity check. */
if( ( pxDHCPMessage->ulDHCPCookie == dhcpCOOKIE ) && ( pxDHCPMessage->ucOpcode == dhcpREPLY_OPCODE ) )
{
pxIterator = pxNetworkEndPoints;
/* Find the end-point with given transaction ID. */
while( pxIterator != NULL )
{
if( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( pxIterator->xDHCPData.ulTransactionId ) )
{
break;
}
pxIterator = pxIterator->pxNext;
}
}
if( ( pxIterator != NULL ) && ( pxIterator->xDHCPData.eDHCPState == eLeasedAddress ) )
{
/* No DHCP messages are expected while in eLeasedAddress state. */
pxIterator = NULL;
}
if( pxIterator != NULL )
{
/* The second parameter pdTRUE tells to check for a UDP message. */
vDHCPProcessEndPoint( pdFALSE, pdTRUE, pxIterator );
if( pxEndPoint == pxIterator )
{
xDoProcess = pdFALSE;
}
}
else
{
/* Target not found, fetch the message and delete it. */
/* PAss the address of a pointer pucUDPPayload, because zero-copy is used. */
lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &( pucUDPPayload ), 0, FREERTOS_ZERO_COPY, NULL, NULL );
if( ( lBytes > 0 ) && ( pucUDPPayload != NULL ) )
{
/* Remove it now, destination not found. */
FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload );
FreeRTOS_printf( ( "vDHCPProcess: Removed a %d-byte message: target not found\n", ( int ) lBytes ) );
}
}
}
}
else
{
/* do nothing, coverity happy */
}
if( xDoProcess != pdFALSE )
{
/* Process the end-point, but do not expect incoming packets. */
vDHCPProcessEndPoint( xReset, pdFALSE, pxEndPoint );
}
}
/**
* @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingOffer'.
* If there is a reply, it will be examined, if there is a time-out, there may be a new
* new attempt, or it will give up.
* @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server
* @param[in] xDoCheck When true, the function must handle any replies.
* @return It returns pdTRUE in case the DHCP process must be given up.
*/
static BaseType_t xHandleWaitingOffer( NetworkEndPoint_t * pxEndPoint,
BaseType_t xDoCheck )
{
BaseType_t xGivingUp = pdFALSE;
#if ( ipconfigUSE_DHCP_HOOK != 0 )
eDHCPCallbackAnswer_t eAnswer;
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE != 1 )
IP_Address_t xIPAddress;
#endif
#endif
/* Look for offers coming in. */
if( xDoCheck != pdFALSE )
{
if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxEndPoint ) == pdPASS )
{
#if ( ipconfigUSE_DHCP_HOOK != 0 )
/* Ask the user if a DHCP request is required. */
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, EP_DHCPData.ulOfferedIPAddress );
#else /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */
xIPAddress.ulIP_IPv4 = EP_DHCPData.ulOfferedIPAddress;
eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreRequest, pxEndPoint, &xIPAddress );
#endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */
if( eAnswer == eDHCPContinue )
#endif /* ipconfigUSE_DHCP_HOOK */
{
/* An offer has been made, the user wants to continue,
* generate the request. */
if( prvSendDHCPRequest( pxEndPoint ) == pdPASS )
{
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
EP_DHCPData.eDHCPState = eWaitingAcknowledge;
}
else
{
/* Either the creation of a message buffer failed, or sendto().
* Try again in the next cycle. */
FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/1.\n" ) );
EP_DHCPData.eDHCPState = eSendDHCPRequest;
}
}
#if ( ipconfigUSE_DHCP_HOOK != 0 )
else
{
if( eAnswer == eDHCPUseDefaults )
{
( void ) memcpy( &( pxEndPoint->ipv4_settings ), &( pxEndPoint->ipv4_defaults ), sizeof( pxEndPoint->ipv4_settings ) );
}
/* The user indicates that the DHCP process does not continue. */
xGivingUp = pdTRUE;
}
#endif /* ipconfigUSE_DHCP_HOOK */
}
}
/* Is it time to send another Discover? */
else if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod )
{
/* It is time to send another Discover. Increase the time
* period, and if it has not got to the point of giving up - send
* another discovery. */
EP_DHCPData.xDHCPTxPeriod <<= 1;
if( EP_DHCPData.xDHCPTxPeriod <= ( ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) )
{
if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE )
{
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
if( EP_DHCPData.xUseBroadcast != pdFALSE )
{
EP_DHCPData.xUseBroadcast = pdFALSE;
}
else
{
EP_DHCPData.xUseBroadcast = pdTRUE;
}
if( prvSendDHCPDiscover( pxEndPoint ) == pdPASS )
{
FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", EP_DHCPData.xDHCPTxPeriod ) );
}
else
{
/* Either the creation of a message buffer failed, or sendto().
* Try again in the next cycle. */
FreeRTOS_debug_printf( ( "Send failed during eWaitingOffer/2.\n" ) );
EP_DHCPData.eDHCPState = eInitialWait;
}
}
else
{
FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) );
}
}
else
{
FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", EP_DHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) );
#if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
{
/* Only use a fake Ack if the default IP address == 0x00
* and the link local addressing is used. Start searching
* a free LinkLayer IP-address. Next state will be
* 'eGetLinkLayerAddress'. */
prvPrepareLinkLayerIPLookUp( pxEndPoint );
/* Setting an IP address manually so set to not using
* leased address mode. */
EP_DHCPData.eDHCPState = eGetLinkLayerAddress;
}
#else
{
xGivingUp = pdTRUE;
}
#endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
}
}
else
{
/* There was no DHCP reply, there was no time-out, just keep on waiting. */
}
return xGivingUp;
}
/*-----------------------------------------------------------*/
/**
* @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingAcknowledge'.
* If there is a reply, it will be examined, if there is a time-out, there may be a new
* new attempt, or it will give up.
* After the acknowledge, the leasing of an IP-address will start.
* @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server
* @param[in] xDoCheck When true, the function must handle any replies.
*/
static void vHandleWaitingAcknowledge( NetworkEndPoint_t * pxEndPoint,
BaseType_t xDoCheck )
{
if( xDoCheck == pdFALSE )
{
/* Is it time to send another Discover? */
if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod )
{
/* Increase the time period, and if it has not got to the
* point of giving up - send another request. */
EP_DHCPData.xDHCPTxPeriod <<= 1;
if( EP_DHCPData.xDHCPTxPeriod <= ( TickType_t ) ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
{
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
if( prvSendDHCPRequest( pxEndPoint ) == pdPASS )
{
/* The message is sent. Stay in state 'eWaitingAcknowledge'. */
}
else
{
/* Either the creation of a message buffer failed, or sendto().
* Try again in the next cycle. */
FreeRTOS_debug_printf( ( "Send failed during eWaitingAcknowledge.\n" ) );
EP_DHCPData.eDHCPState = eSendDHCPRequest;
}
}
else
{
/* Give up, start again. */
EP_DHCPData.eDHCPState = eInitialWait;
}
}
}
else if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK, pxEndPoint ) == pdPASS )
{
FreeRTOS_debug_printf( ( "vDHCPProcess: acked %xip\n", ( unsigned int ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) );
/* DHCP completed. The IP address can now be used, and the
* timer set to the lease timeout time. */
EP_IPv4_SETTINGS.ulIPAddress = EP_DHCPData.ulOfferedIPAddress;
/* Setting the 'local' broadcast address, something like
* '192.168.1.255'. */
EP_IPv4_SETTINGS.ulBroadcastAddress = EP_DHCPData.ulOfferedIPAddress | ~( EP_IPv4_SETTINGS.ulNetMask );
EP_DHCPData.eDHCPState = eLeasedAddress;
/* _HT_ This macro must be removed later.
* It is enough to set 'EP_IPv4_SETTINGS.ulIPAddress'. */
*ipLOCAL_IP_ADDRESS_POINTER = EP_IPv4_SETTINGS.ulIPAddress;
iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress );
/* DHCP failed, the default configured IP-address will be used
* Now call vIPNetworkUpCalls() to send the network-up event and
* start the ARP timer. */
vIPNetworkUpCalls( pxEndPoint );
/* Close socket to ensure packets don't queue on it. */
prvCloseDHCPSocket( pxEndPoint );
if( EP_DHCPData.ulLeaseTime == 0U )
{
EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME;
}
else if( EP_DHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME )
{
EP_DHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME;
}
else
{
/* The lease time is already valid. */
}
/* Check for clashes. */
vARPSendGratuitous();
vDHCP_RATimerReload( ( struct xNetworkEndPoint * ) pxEndPoint, EP_DHCPData.ulLeaseTime );
}
else
{
/* There are no replies yet. */
}
}
/**
* @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eWaitingSendFirstDiscover'.
* If will send a DISCOVER message to a DHCP server, and move to the next status 'eWaitingOffer'.
* @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server
* @return xGivingUp: when pdTRUE, there was a fatal error and the process can not continue;
*/
static BaseType_t xHandleWaitingFirstDiscover( NetworkEndPoint_t * pxEndPoint )
{
BaseType_t xGivingUp = pdFALSE;
/* Ask the user if a DHCP discovery is required. */
#if ( ipconfigUSE_DHCP_HOOK != 0 )
#if ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 )
eDHCPCallbackAnswer_t eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, pxEndPoint->ipv4_defaults.ulIPAddress );
#else /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */
IP_Address_t xIPAddress;
eDHCPCallbackAnswer_t eAnswer;
xIPAddress.ulIP_IPv4 = pxEndPoint->ipv4_defaults.ulIPAddress;
eAnswer = xApplicationDHCPHook_Multi( eDHCPPhasePreDiscover, pxEndPoint, &xIPAddress );
#endif /* ( ipconfigIPv4_BACKWARD_COMPATIBLE == 1 ) */
if( eAnswer == eDHCPContinue )
#endif /* ipconfigUSE_DHCP_HOOK */
{
/* See if prvInitialiseDHCP() has creates a socket. */
if( xDHCPv4Socket == NULL )
{
xGivingUp = pdTRUE;
}
else
{
/* Put 'ulIPAddress' to zero to indicate that the end-point is down. */
EP_IPv4_SETTINGS.ulIPAddress = 0U;
/* Send the first discover request. */
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
if( prvSendDHCPDiscover( pxEndPoint ) == pdPASS )
{
EP_DHCPData.eDHCPState = eWaitingOffer;
}
else
{
/* Either the creation of a message buffer failed, or sendto().
* Try again in the next cycle. */
FreeRTOS_debug_printf( ( "Send failed during eWaitingSendFirstDiscover\n" ) );
}
}
}
#if ( ipconfigUSE_DHCP_HOOK != 0 )
else
{
if( eAnswer == eDHCPUseDefaults )
{
( void ) memcpy( &( pxEndPoint->ipv4_settings ), &( pxEndPoint->ipv4_defaults ), sizeof( pxEndPoint->ipv4_settings ) );
}
/* The user indicates that the DHCP process does not continue. */
xGivingUp = pdTRUE;
}
#endif /* ipconfigUSE_DHCP_HOOK */
return xGivingUp;
}
/*-----------------------------------------------------------*/
/**
* @brief Called by vDHCPProcessEndPoint(), this function handles the state 'eLeasedAddress'.
* If waits until the lease must be renewed, and then send a new request.
* @param[in] pxEndPoint The end-point that is getting an IP-address from a DHCP server
*/
static void prvHandleWaitingeLeasedAddress( NetworkEndPoint_t * pxEndPoint )
{
if( FreeRTOS_IsEndPointUp( pxEndPoint ) != 0 )
{
/* Resend the request at the appropriate time to renew the lease. */
prvCreateDHCPSocket( pxEndPoint );
if( xDHCPv4Socket != NULL )
{
uint32_t ulID = 0U;
if( xApplicationGetRandomNumber( &( ulID ) ) != pdFALSE )
{
EP_DHCPData.ulTransactionId = ulID;
}
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
if( prvSendDHCPRequest( pxEndPoint ) == pdPASS )
{
/* The packet was sent successfully, wait for an acknowledgement. */
EP_DHCPData.eDHCPState = eWaitingAcknowledge;
}
else
{
/* The packet was not sent, try sending it later. */
EP_DHCPData.eDHCPState = eSendDHCPRequest;
FreeRTOS_debug_printf( ( "Send failed eLeasedAddress.\n" ) );
}
/* From now on, we should be called more often */
vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD );
}
}
else
{
/* See PR #53 on github/freertos/freertos */
FreeRTOS_printf( ( "DHCP: lease time finished but network is down\n" ) );
vDHCP_RATimerReload( ( struct xNetworkEndPoint * ) pxEndPoint, pdMS_TO_TICKS( 5000U ) );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Process the DHCP state machine based on current state.
*
* @param[in] xReset Is the DHCP state machine starting over? pdTRUE/pdFALSE.
* @param[in] xDoCheck true when an incoming message is to be expected, and
* prvProcessDHCPReplies() will be called.
* @param[in] pxEndPoint The end-point for which the DHCP state machine should
* make one cycle.
*/
static void vDHCPProcessEndPoint( BaseType_t xReset,
BaseType_t xDoCheck,
NetworkEndPoint_t * pxEndPoint )
{
BaseType_t xGivingUp = pdFALSE;
configASSERT( pxEndPoint != NULL );
/* Is DHCP starting over? */
if( xReset != pdFALSE )
{
EP_DHCPData.eDHCPState = eInitialWait;
}
if( ( EP_DHCPData.eDHCPState != EP_DHCPData.eExpectedState ) && ( xReset == pdFALSE ) )
{
/* When the DHCP event was generated, the DHCP client was
* in a different state. Therefore, ignore this event. */
FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: wrong state: expect: %d got: %d : ignore\n",
EP_DHCPData.eExpectedState, EP_DHCPData.eDHCPState ) );
}
else
{
{
static eDHCPState_t eLastState = eNotUsingLeasedAddress;
if( eLastState != EP_DHCPData.eDHCPState )
{
eLastState = EP_DHCPData.eDHCPState;
FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: enter %d\n", EP_DHCPData.eDHCPState ) );
}
}
switch( EP_DHCPData.eDHCPState )
{
case eInitialWait:
/* Initial state. Create the DHCP socket, timer, etc. if they
* have not already been created. */
/* Initial state. Create the DHCP socket, timer, etc. if they
* have not already been created. */
prvInitialiseDHCP( pxEndPoint );
EP_DHCPData.eDHCPState = eWaitingSendFirstDiscover;
break;
case eWaitingSendFirstDiscover:
xGivingUp = xHandleWaitingFirstDiscover( pxEndPoint );
break;
case eSendDHCPRequest:
if( prvSendDHCPRequest( pxEndPoint ) == pdPASS )
{
/* Send succeeded, go to state 'eWaitingAcknowledge'. */
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
EP_DHCPData.eDHCPState = eWaitingAcknowledge;
}
else
{
/* Either the creation of a message buffer failed, or sendto().
* Try again in the next cycle. */
FreeRTOS_debug_printf( ( "Send failed during eSendDHCPRequest.\n" ) );
}
break;
case eWaitingOffer:
xGivingUp = xHandleWaitingOffer( pxEndPoint, xDoCheck );
break;
case eWaitingAcknowledge:
vHandleWaitingAcknowledge( pxEndPoint, xDoCheck );
break;
#if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
case eGetLinkLayerAddress:
if( ( xTaskGetTickCount() - EP_DHCPData.xDHCPTxTime ) > EP_DHCPData.xDHCPTxPeriod )
{
if( xARPHadIPClash == pdFALSE )
{
/* ARP OK. proceed. */
iptraceDHCP_SUCCEDEED( EP_DHCPData.ulOfferedIPAddress );
/* Auto-IP succeeded, the default configured IP-address will
* be used. Now call vIPNetworkUpCalls() to send the
* network-up event and start the ARP timer. */
vIPNetworkUpCalls( pxEndPoint );
EP_DHCPData.eDHCPState = eNotUsingLeasedAddress;
}
else
{
/* ARP clashed - try another IP address. */
prvPrepareLinkLayerIPLookUp( pxEndPoint );
/* Setting an IP address manually so set to not using leased
* address mode. */
EP_DHCPData.eDHCPState = eGetLinkLayerAddress;
}
}
break;
#endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
case eLeasedAddress:
prvHandleWaitingeLeasedAddress( pxEndPoint );
break;
case eNotUsingLeasedAddress:
vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
break;
default:
/* Lint: all options are included. */
break;
}
{
static eDHCPState_t eLastState = eNotUsingLeasedAddress;
if( eLastState != EP_DHCPData.eDHCPState )
{
eLastState = EP_DHCPData.eDHCPState;
FreeRTOS_debug_printf( ( "vDHCPProcessEndPoint: exit %d\n", EP_DHCPData.eDHCPState ) );
}
}
if( xGivingUp != pdFALSE )
{
/* xGivingUp became true either because of a time-out, or because
* xApplicationDHCPHook() returned another value than 'eDHCPContinue',
* meaning that the conversion is cancelled from here. */
/* Revert to static IP address. */
taskENTER_CRITICAL();
{
EP_IPv4_SETTINGS.ulIPAddress = pxEndPoint->ipv4_defaults.ulIPAddress;
iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( pxEndPoint->ipv4_defaults.ulIPAddress );
}
taskEXIT_CRITICAL();
EP_DHCPData.eDHCPState = eNotUsingLeasedAddress;
vIPSetDHCP_RATimerEnableState( pxEndPoint, pdFALSE );
/* DHCP failed, the default configured IP-address will be used. Now
* call vIPNetworkUpCalls() to send the network-up event and start the ARP
* timer. */
vIPNetworkUpCalls( pxEndPoint );
/* Close socket to ensure packets don't queue on it. */
prvCloseDHCPSocket( pxEndPoint );
}
}
}
/*-----------------------------------------------------------*/
/**
* @brief Close the DHCP socket, but only when there are no other end-points
* using it.
* @param[in] pxEndPoint The end-point that stops using the socket.
*/
static void prvCloseDHCPSocket( const NetworkEndPoint_t * pxEndPoint )
{
( void ) pxEndPoint;
if( ( xDHCPv4Socket != NULL ) && ( xDHCPSocketUserCount > 0 ) )
{
xDHCPSocketUserCount--;
if( xDHCPSocketUserCount == 0 )
{
/* This modules runs from the IP-task. Use the internal
* function 'vSocketClose()` to close the socket. */
( void ) vSocketClose( xDHCPv4Socket );
xDHCPv4Socket = NULL;
}
}
else
{
/* Strange: there is a socket, but there are no users. */
}
FreeRTOS_printf( ( "prvCloseDHCPSocket[%02x-%02x]: %s, user count %d\n",
pxEndPoint->xMACAddress.ucBytes[ 4 ],
pxEndPoint->xMACAddress.ucBytes[ 5 ],
( xDHCPv4Socket != NULL ) ? "open" : "closed",
( int ) xDHCPSocketUserCount ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Create a DHCP socket with the defined timeouts. The same socket
* will be shared among all end-points that need DHCP.
*/
_static void prvCreateDHCPSocket( const NetworkEndPoint_t * pxEndPoint )
{
struct freertos_sockaddr xAddress;
BaseType_t xReturn;
TickType_t xTimeoutTime = ( TickType_t ) 0;
if( xDHCPv4Socket == NULL ) /* Create the socket, if it has not already been created. */
{
xDHCPv4Socket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
configASSERT( xSocketValid( xDHCPv4Socket ) == pdTRUE );
/* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
/* coverity[misra_c_2012_rule_11_4_violation] */
if( xSocketValid( xDHCPv4Socket ) == pdTRUE )
{
/* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the
* context of the IP task. */
( void ) FreeRTOS_setsockopt( xDHCPv4Socket, 0, FREERTOS_SO_RCVTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) );
( void ) FreeRTOS_setsockopt( xDHCPv4Socket, 0, FREERTOS_SO_SNDTIMEO, &( xTimeoutTime ), sizeof( TickType_t ) );
/* Bind to the standard DHCP client port. */
xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT_IPv4;
xReturn = vSocketBind( xDHCPv4Socket, &xAddress, sizeof( xAddress ), pdFALSE );
xDHCPSocketUserCount = 1;
FreeRTOS_printf( ( "DHCP-socket[%02x-%02x]: DHCP Socket Create\n",
pxEndPoint->xMACAddress.ucBytes[ 4 ],
pxEndPoint->xMACAddress.ucBytes[ 5 ] ) );
if( xReturn != 0 )
{
/* Binding failed, close the socket again. */
prvCloseDHCPSocket( pxEndPoint );
}
}
else
{
/* Change to NULL for easier testing. */
xDHCPv4Socket = NULL;
}
}
else
{
xDHCPSocketUserCount++;
}
FreeRTOS_printf( ( "prvCreateDHCPSocket[%02x-%02x]: %s, user count %d\n",
pxEndPoint->xMACAddress.ucBytes[ 4 ],
pxEndPoint->xMACAddress.ucBytes[ 5 ],
( xDHCPv4Socket != NULL ) ? "open" : "closed",
( int ) xDHCPSocketUserCount ) );
}
/*-----------------------------------------------------------*/
/**
* @brief Initialise the DHCP state machine by creating DHCP socket and
* begin the transaction.
*
* @param[in] pxEndPoint The end-point that needs DHCP.
*/
static void prvInitialiseDHCP( NetworkEndPoint_t * pxEndPoint )
{
/* Initialise the parameters that will be set by the DHCP process. Per
* https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random
* value chosen by the client. */
/* Check for random number generator API failure. */
if( xApplicationGetRandomNumber( &( EP_DHCPData.ulTransactionId ) ) != pdFALSE )
{
EP_DHCPData.xUseBroadcast = 0;
EP_DHCPData.ulOfferedIPAddress = 0U;
EP_DHCPData.ulDHCPServerAddress = 0U;
EP_DHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
/* Create the DHCP socket if it has not already been created. */
prvCreateDHCPSocket( pxEndPoint );
FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) );
vDHCP_RATimerReload( pxEndPoint, dhcpINITIAL_TIMER_PERIOD );
}
else
{
FreeRTOS_debug_printf( ( "prvInitialiseDHCP: failed to generate a random Transaction ID\n" ) );
}
}
/*-----------------------------------------------------------*/
/**
* @brief Called by prvProcessDHCPReplies(), which walks through an array of DHCP options,
* this function will check a single option.
* @param[in] pxEndPoint The end-point that needs an IP-address.
* @param[in] pxSet A set of variables that describe the parsing process.
* @param[in] xExpectedMessageType The type of message expected in the
* dhcpIPv4_MESSAGE_TYPE_OPTION_CODE option.
*/
static void vProcessHandleOption( NetworkEndPoint_t * pxEndPoint,
ProcessSet_t * pxSet,
BaseType_t xExpectedMessageType )
{
/* Option-specific handling. */
switch( pxSet->ucOptionCode )
{
case dhcpIPv4_MESSAGE_TYPE_OPTION_CODE:
if( pxSet->pucByte[ pxSet->uxIndex ] == ( uint8_t ) xExpectedMessageType )
{
/* The message type is the message type the
* state machine is expecting. */
pxSet->ulProcessed++;
}
else
{
if( pxSet->pucByte[ pxSet->uxIndex ] == ( uint8_t ) dhcpMESSAGE_TYPE_NACK )
{
if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK )
{
/* Start again. */
EP_DHCPData.eDHCPState = eInitialWait;
}
}
/* Stop processing further options. */
pxSet->uxLength = 0;
}
break;
case dhcpIPv4_SUBNET_MASK_OPTION_CODE:
if( pxSet->uxLength == sizeof( uint32_t ) )
{
EP_IPv4_SETTINGS.ulNetMask = pxSet->ulParameter;
}
break;
case dhcpIPv4_GATEWAY_OPTION_CODE:
/* The DHCP server may send more than 1 gateway addresses. */
if( pxSet->uxLength >= sizeof( uint32_t ) )
{
/* ulProcessed is not incremented in this case
* because the gateway is not essential. */
EP_IPv4_SETTINGS.ulGatewayAddress = pxSet->ulParameter;
}
break;
case dhcpIPv4_DNS_SERVER_OPTIONS_CODE:
/* ulProcessed is not incremented in this case
* because the DNS server is not essential. Only the
* first DNS server address is taken. */
if( pxSet->uxLength >= sizeof( uint32_t ) )
{
size_t uxSourceIndex;
size_t uxTargetIndex = 0;
size_t uxDNSCount = pxSet->uxLength / sizeof( uint32_t );
size_t uxByteIndex = pxSet->uxIndex;
void * pvCopyDest = &( pxSet->ulParameter );
/* Just to try-out for CBMC. */
if( uxDNSCount > ipconfigENDPOINT_DNS_ADDRESS_COUNT )
{
uxDNSCount = ipconfigENDPOINT_DNS_ADDRESS_COUNT;
}
for( uxSourceIndex = 0U; uxSourceIndex < uxDNSCount; uxSourceIndex++ )
{
const void * pvCopySource = &( pxSet->pucByte[ uxByteIndex ] );
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxSet->ulParameter ) );
if( ( pxSet->ulParameter != FREERTOS_INADDR_ANY ) && ( pxSet->ulParameter != ipBROADCAST_IP_ADDRESS ) )
{
EP_IPv4_SETTINGS.ulDNSServerAddresses[ uxTargetIndex ] = pxSet->ulParameter;
uxTargetIndex++;
/* uxDNSCount <= ipconfigENDPOINT_DNS_ADDRESS_COUNT , hence check is removed. */
}
uxByteIndex += sizeof( uint32_t );
}
/* Clear the remaining entries. */
while( uxTargetIndex < ipconfigENDPOINT_DNS_ADDRESS_COUNT )
{
EP_IPv4_SETTINGS.ulDNSServerAddresses[ uxTargetIndex ] = 0U;
uxTargetIndex++;
}
/* For the next lookup, start using the first DNS entry. */
EP_IPv4_SETTINGS.ucDNSIndex = 0U;
}
break;
case dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE:
if( pxSet->uxLength == sizeof( uint32_t ) )
{
if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER )
{
/* Offers state the replying server. */
pxSet->ulProcessed++;
EP_DHCPData.ulDHCPServerAddress = pxSet->ulParameter;
}
else
{
/* The ack must come from the expected server. */
if( EP_DHCPData.ulDHCPServerAddress == pxSet->ulParameter )
{
pxSet->ulProcessed++;
}
}
}
break;
case dhcpIPv4_LEASE_TIME_OPTION_CODE:
if( pxSet->uxLength == sizeof( EP_DHCPData.ulLeaseTime ) )
{
/* ulProcessed is not incremented in this case
* because the lease time is not essential. */
/* The DHCP parameter is in seconds, convert
* to host-endian format. */
EP_DHCPData.ulLeaseTime = FreeRTOS_ntohl( pxSet->ulParameter );
/* Divide the lease time by two to ensure a renew
* request is sent before the lease actually expires. */
EP_DHCPData.ulLeaseTime >>= 1U;
/* Multiply with configTICK_RATE_HZ to get clock ticks. */
EP_DHCPData.ulLeaseTime = ( uint32_t ) configTICK_RATE_HZ * ( uint32_t ) EP_DHCPData.ulLeaseTime;
}
break;
default:
/* Not interested in this field. */
break;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Check whether the DHCP response from the server has all valid
* invariant parameters and valid (non broadcast and non localhost)
* IP address being assigned to the device.
*
* @param[in] pxDHCPMessage The DHCP message.
*
* @return pdPASS if the DHCP response has correct parameters; pdFAIL otherwise.
*/
static BaseType_t prvIsValidDHCPResponse( const DHCPMessage_IPv4_t * pxDHCPMessage )
{
BaseType_t xReturn = pdPASS;
if( ( pxDHCPMessage->ulDHCPCookie != ( uint32_t ) dhcpCOOKIE ) ||
( pxDHCPMessage->ucOpcode != ( uint8_t ) dhcpREPLY_OPCODE ) ||
( pxDHCPMessage->ucAddressType != ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET ) ||
( pxDHCPMessage->ucAddressLength != ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH ) ||
( ( FreeRTOS_ntohl( pxDHCPMessage->ulYourIPAddress_yiaddr ) & 0xFFU ) == 0xFFU ) ||
( ( ( pxDHCPMessage->ulYourIPAddress_yiaddr & 0x7FU ) ^ 0x7FU ) == 0x00U ) )
{
/* Invalid cookie OR
* Unexpected opcode OR
* Incorrect address type OR
* Incorrect address length OR
* The DHCP server is trying to assign a broadcast address to the device OR
* The DHCP server is trying to assign a localhost address to the device. */
xReturn = pdFAIL;
}
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Check an incoming DHCP option.
*
* @param[in] pxSet A set of variables needed to parse the DHCP reply.
*
* @return pdPASS: 1 when the option must be analysed, 0 when the option
* must be skipped, and -1 when parsing must stop.
*/
static BaseType_t xProcessCheckOption( ProcessSet_t * pxSet )
{
BaseType_t xResult = -1;
do
{
if( pxSet->ucOptionCode == ( uint8_t ) dhcpOPTION_END_BYTE )
{
/* Ready, the last byte has been seen.
* Return -1 so that the parsing will stop. */
break;
}
if( pxSet->ucOptionCode == ( uint8_t ) dhcpIPv4_ZERO_PAD_OPTION_CODE )
{
/* The value zero is used as a pad byte,
* it is not followed by a length byte. */
pxSet->uxIndex++;
/* Return zero to skip this option. */
xResult = 0;
break;
}
/* Stop if the response is malformed. */
if( ( pxSet->uxIndex + 1U ) >= pxSet->uxPayloadDataLength )
{
/* The length byte is missing, stop parsing. */
break;
}
/* Fetch the length byte. */
pxSet->uxLength = ( size_t ) pxSet->pucByte[ pxSet->uxIndex + 1U ];
pxSet->uxIndex = pxSet->uxIndex + 2U;
if( !( ( ( pxSet->uxIndex + pxSet->uxLength ) - 1U ) < pxSet->uxPayloadDataLength ) )
{
/* There are not as many bytes left as there should be. */
break;
}
/* In most cases, a 4-byte network-endian parameter follows,
* just get it once here and use later. */
if( pxSet->uxLength >= sizeof( pxSet->ulParameter ) )
{
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
const void * pvCopySource = &( pxSet->pucByte[ pxSet->uxIndex ] );
void * pvCopyDest = &( pxSet->ulParameter );
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( pxSet->ulParameter ) );
/* 'uxIndex' will be increased at the end of this loop. */
}
else
{
pxSet->ulParameter = 0;
}
/* Confirm uxIndex is still a valid index after adjustments to uxIndex above */
if( !( pxSet->uxIndex < pxSet->uxPayloadDataLength ) )
{
break;
}
/* Return 1 so that the option will be processed. */
xResult = 1;
/* Try to please CBMC with a break statement here. */
break;
} while( ipFALSE_BOOL );
return xResult;
}
/*-----------------------------------------------------------*/
/**
* @brief Process the DHCP replies.
*
* @param[in] xExpectedMessageType The type of the message the DHCP state machine is expecting.
* Messages of different type will be dropped.
* @param[in] pxEndPoint The end-point to whom the replies are addressed.
*
* @return pdPASS: if DHCP options are received correctly; pdFAIL: Otherwise.
*/
static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType,
NetworkEndPoint_t * pxEndPoint )
{
uint8_t * pucUDPPayload;
int32_t lBytes;
const DHCPMessage_IPv4_t * pxDHCPMessage;
BaseType_t xReturn = pdFALSE;
const uint32_t ulMandatoryOptions = 2U; /* DHCP server address, and the correct DHCP message type must be present in the options. */
ProcessSet_t xSet;
( void ) memset( &( xSet ), 0, sizeof( xSet ) );
/* Passing the address of a pointer (pucUDPPayload) because FREERTOS_ZERO_COPY is used. */
lBytes = FreeRTOS_recvfrom( xDHCPv4Socket, &pucUDPPayload, 0U, FREERTOS_ZERO_COPY, NULL, NULL );
if( lBytes > 0 )
{
/* Map a DHCP structure onto the received data. */
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayload );
/* Sanity check. */
if( lBytes < ( int32_t ) sizeof( DHCPMessage_IPv4_t ) )
{
/* Not enough bytes. */
}
else if( prvIsValidDHCPResponse( pxDHCPMessage ) == pdFAIL )
{
/* Invalid values in DHCP response. */
}
else if( ( pxDHCPMessage->ulTransactionID != FreeRTOS_htonl( EP_DHCPData.ulTransactionId ) ) )
{
/* Transaction ID does not match. */
}
else /* Looks like a valid DHCP response, with the same transaction ID. */
{
if( memcmp( pxDHCPMessage->ucClientHardwareAddress,
pxEndPoint->xMACAddress.ucBytes,
sizeof( MACAddress_t ) ) != 0 )
{
/* Target MAC address doesn't match. */
}
else
{
/* None of the essential options have been processed yet. */
xSet.ulProcessed = 0U;
/* Walk through the options until the dhcpOPTION_END_BYTE byte
* is found, taking care not to walk off the end of the options. */
xSet.pucByte = &( pucUDPPayload[ sizeof( DHCPMessage_IPv4_t ) ] );
xSet.uxIndex = 0;
xSet.uxPayloadDataLength = ( ( size_t ) lBytes ) - sizeof( DHCPMessage_IPv4_t );
while( xSet.uxIndex < xSet.uxPayloadDataLength )
{
BaseType_t xResult;
xSet.ucOptionCode = xSet.pucByte[ xSet.uxIndex ];
xResult = xProcessCheckOption( &( xSet ) );
if( xResult > 0 )
{
vProcessHandleOption( pxEndPoint, &( xSet ), xExpectedMessageType );
}
if( xResult != 0 )
{
if( ( xSet.uxLength == 0U ) || ( xResult < 0 ) )
{
break;
}
xSet.uxIndex += xSet.uxLength;
}
}
/* Were all the mandatory options received? */
if( xSet.ulProcessed >= ulMandatoryOptions )
{
/* HT:endian: used to be network order */
EP_DHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr;
FreeRTOS_printf( ( "vDHCPProcess: offer %xip for MAC address %02x-%02x\n",
( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ),
pxEndPoint->xMACAddress.ucBytes[ 4 ],
pxEndPoint->xMACAddress.ucBytes[ 5 ] ) );
xReturn = pdPASS;
}
}
}
if( pucUDPPayload != NULL )
{
FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayload );
}
} /* if( lBytes > 0 ) */
return xReturn;
}
/*-----------------------------------------------------------*/
/**
* @brief Create a partial DHCP message by filling in all the 'constant' fields.
*
* @param[out] pxAddress Address to be filled in.
* @param[out] xOpcode Opcode to be filled in the packet. Will always be 'dhcpREQUEST_OPCODE'.
* @param[in] pucOptionsArray The options to be added to the packet.
* @param[in,out] pxOptionsArraySize Byte count of the options. Its value might change.
* @param[in] pxEndPoint The end-point for which the request will be sent.
*
* @return Ethernet buffer of the partially created DHCP packet.
*/
static uint8_t * prvCreatePartDHCPMessage( struct freertos_sockaddr * pxAddress,
BaseType_t xOpcode,
const uint8_t * const pucOptionsArray,
size_t * pxOptionsArraySize,
const NetworkEndPoint_t * pxEndPoint )
{
DHCPMessage_IPv4_t * pxDHCPMessage;
size_t uxRequiredBufferSize = sizeof( DHCPMessage_IPv4_t ) + *pxOptionsArraySize;
const NetworkBufferDescriptor_t * pxNetworkBuffer;
uint8_t * pucUDPPayloadBuffer = NULL;
#if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
const char * pucHostName = pcApplicationHostnameHook();
size_t uxNameLength = 0;
if( pucHostName != NULL )
{
uxNameLength = strlen( pucHostName );
}
uint8_t * pucPtr;
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
/* Two extra bytes for option code and length. */
uxRequiredBufferSize += ( 2U + uxNameLength );
#endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */
/* Obtain a network buffer with the required amount of storage. It doesn't make much sense
* to use a time-out here, because that would cause the IP-task to wait for itself. */
pxNetworkBuffer = pxGetNetworkBufferWithDescriptor( sizeof( UDPPacket_t ) + uxRequiredBufferSize, 0U );
if( pxNetworkBuffer != NULL )
{
/* Leave space for the UDP header. */
pucUDPPayloadBuffer = &( pxNetworkBuffer->pucEthernetBuffer[ ipUDP_PAYLOAD_OFFSET_IPv4 ] );
/* MISRA Ref 11.3.1 [Misaligned access] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
/* coverity[misra_c_2012_rule_11_3_violation] */
pxDHCPMessage = ( ( DHCPMessage_IPv4_t * ) pucUDPPayloadBuffer );
{
uint8_t * pucIPType;
/* Store the IP type at a known location.
* Later the type must be known to translate
* a payload- to a network buffer.
*/
pucIPType = pucUDPPayloadBuffer - ipUDP_PAYLOAD_IP_TYPE_OFFSET;
*pucIPType = ipTYPE_IPv4;
}
/* Most fields need to be zero. */
( void ) memset( pxDHCPMessage, 0x00, sizeof( DHCPMessage_IPv4_t ) );
/* Create the message. */
pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode;
pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( EP_DHCPData.ulTransactionId );
pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE;
if( EP_DHCPData.xUseBroadcast != pdFALSE )
{
pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST;
}
else
{
pxDHCPMessage->usFlags = 0U;
}
( void ) memcpy( &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
/* Copy in the const part of the options options. */
( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), pucOptionsArray, *pxOptionsArraySize );
#if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
{
/* With this option, the hostname can be registered as well which makes
* it easier to lookup a device in a router's list of DHCP clients. */
/* Point to where the OPTION_END was stored to add data. */
pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1U ) ] );
pucPtr[ 0U ] = dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE;
pucPtr[ 1U ] = ( uint8_t ) uxNameLength;
/*
* Use helper variables for memcpy() to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
if( pucHostName != NULL )
{
pvCopySource = pucHostName;
pvCopyDest = &pucPtr[ 2U ];
( void ) memcpy( pvCopyDest, pvCopySource, uxNameLength );
}
pucPtr[ 2U + uxNameLength ] = ( uint8_t ) dhcpOPTION_END_BYTE;
*pxOptionsArraySize += ( size_t ) ( 2U + uxNameLength );
}
#endif /* if ( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) */
/* Map in the client identifier. */
( void ) memcpy( &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ),
pxEndPoint->xMACAddress.ucBytes, sizeof( MACAddress_t ) );
/* Set the addressing. */
pxAddress->sin_address.ulIP_IPv4 = ipBROADCAST_IP_ADDRESS;
pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT_IPv4;
pxAddress->sin_family = FREERTOS_AF_INET4;
}
return pucUDPPayloadBuffer;
}
/*-----------------------------------------------------------*/
/**
* @brief Create and send a DHCP request message through the DHCP socket.
*
* @param[in] pxEndPoint The end-point for which the request will be sent.
*/
static BaseType_t prvSendDHCPRequest( NetworkEndPoint_t * pxEndPoint )
{
BaseType_t xResult = pdFAIL;
uint8_t * pucUDPPayloadBuffer;
struct freertos_sockaddr xAddress;
static const uint8_t ucDHCPRequestOptions[] =
{
/* Do not change the ordering without also changing
* dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and
* dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */
dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */
dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */
dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */
dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */
dhcpOPTION_END_BYTE
};
size_t uxOptionsLength = sizeof( ucDHCPRequestOptions );
/* memcpy() helper variables for MISRA Rule 21.15 compliance*/
const void * pvCopySource;
void * pvCopyDest;
/* MISRA doesn't like uninitialised structs. */
( void ) memset( &( xAddress ), 0, sizeof( xAddress ) );
pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress,
( BaseType_t ) dhcpREQUEST_OPCODE,
ucDHCPRequestOptions,
&( uxOptionsLength ),
pxEndPoint );
/* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
/* coverity[misra_c_2012_rule_11_4_violation] */
if( ( xSocketValid( xDHCPv4Socket ) == pdTRUE ) && ( pucUDPPayloadBuffer != NULL ) )
{
/* Copy in the IP address being requested. */
/*
* Use helper variables for memcpy() source & dest to remain
* compliant with MISRA Rule 21.15. These should be
* optimized away.
*/
pvCopySource = &EP_DHCPData.ulOfferedIPAddress;
pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ];
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulOfferedIPAddress ) );
/* Copy in the address of the DHCP server being used. */
pvCopySource = &EP_DHCPData.ulDHCPServerAddress;
pvCopyDest = &pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ];
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulDHCPServerAddress ) );
FreeRTOS_debug_printf( ( "vDHCPProcess: reply %xip\n", ( unsigned ) FreeRTOS_ntohl( EP_DHCPData.ulOfferedIPAddress ) ) );
iptraceSENDING_DHCP_REQUEST();
xDHCPv4Socket->pxEndPoint = pxEndPoint;
if( FreeRTOS_sendto( xDHCPv4Socket, pucUDPPayloadBuffer, sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength, FREERTOS_ZERO_COPY, &xAddress, ( socklen_t ) sizeof( xAddress ) ) == 0 )
{
/* The packet was not successfully queued for sending and must be
* returned to the stack. */
FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
}
else
{
xResult = pdPASS;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
/**
* @brief Create and send a DHCP discover packet through the DHCP socket.
*
* @param[in] pxEndPoint the end-point for which the discover message will be sent.
*
* @return: pdPASS if the DHCP discover message was sent successfully, pdFAIL otherwise.
*/
static BaseType_t prvSendDHCPDiscover( NetworkEndPoint_t * pxEndPoint )
{
BaseType_t xResult = pdFAIL;
uint8_t * pucUDPPayloadBuffer;
struct freertos_sockaddr xAddress;
static const uint8_t ucDHCPDiscoverOptions[] =
{
/* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */
dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */
dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 7, 1, 0, 0, 0, 0, 0, 0, /* Client identifier. */
dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */
dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE, 3, dhcpIPv4_SUBNET_MASK_OPTION_CODE, dhcpIPv4_GATEWAY_OPTION_CODE, dhcpIPv4_DNS_SERVER_OPTIONS_CODE, /* Parameter request option. */
dhcpOPTION_END_BYTE
};
size_t uxOptionsLength = sizeof( ucDHCPDiscoverOptions );
( void ) memset( &( xAddress ), 0, sizeof( xAddress ) );
pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress,
( BaseType_t ) dhcpREQUEST_OPCODE,
ucDHCPDiscoverOptions,
&( uxOptionsLength ),
pxEndPoint );
/* MISRA Ref 11.4.1 [Socket error and integer to pointer conversion] */
/* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-114 */
/* coverity[misra_c_2012_rule_11_4_violation] */
if( ( xSocketValid( xDHCPv4Socket ) == pdTRUE ) && ( pucUDPPayloadBuffer != NULL ) )
{
const void * pvCopySource;
void * pvCopyDest;
FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) );
iptraceSENDING_DHCP_DISCOVER();
if( pxEndPoint->xDHCPData.ulPreferredIPAddress != 0U )
{
/* Fill in the IPv4 address. */
pvCopySource = &( pxEndPoint->xDHCPData.ulPreferredIPAddress );
pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] );
( void ) memcpy( pvCopyDest, pvCopySource, sizeof( EP_DHCPData.ulPreferredIPAddress ) );
}
else
{
/* Remove option-50 from the list because it is not used. */
size_t uxCopyLength;
/* Exclude this line from branch coverage as the not-taken condition will never happen unless the code is modified */
configASSERT( uxOptionsLength > ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ) ); /* LCOV_EXCL_BR_LINE */
uxCopyLength = uxOptionsLength - ( dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE );
pvCopySource = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET + dhcpOPTION_50_SIZE ] );
pvCopyDest = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpOPTION_50_OFFSET ] );
( void ) memmove( pvCopyDest, pvCopySource, uxCopyLength );
/* Send 6 bytes less than foreseen. */
uxOptionsLength -= dhcpOPTION_50_SIZE;
}
xDHCPv4Socket->pxEndPoint = pxEndPoint;
if( FreeRTOS_sendto( xDHCPv4Socket,
pucUDPPayloadBuffer,
sizeof( DHCPMessage_IPv4_t ) + uxOptionsLength,
FREERTOS_ZERO_COPY,
&( xAddress ),
( socklen_t ) sizeof( xAddress ) ) == 0 )
{
/* The packet was not successfully queued for sending and must be
* returned to the stack. */
FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
}
else
{
xResult = pdTRUE;
}
}
return xResult;
}
/*-----------------------------------------------------------*/
#if ( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
/**
* @brief When DHCP has failed, the code can assign a Link-Layer address, and check if
* another device already uses the IP-address.
*
* param[in] pxEndPoint The end-point that wants to obtain a link-layer address.
*/
void prvPrepareLinkLayerIPLookUp( NetworkEndPoint_t * pxEndPoint )
{
uint8_t ucLinkLayerIPAddress[ 2 ];
uint32_t ulNumbers[ 2 ];
/* After DHCP has failed to answer, prepare everything to start
* trying-out LinkLayer IP-addresses, using the random method. */
EP_DHCPData.xDHCPTxTime = xTaskGetTickCount();
( void ) xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) );
( void ) xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) );
ucLinkLayerIPAddress[ 0 ] = ( uint8_t ) ( 1 + ( ulNumbers[ 0 ] % 0xFDU ) ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */
ucLinkLayerIPAddress[ 1 ] = ( uint8_t ) ( 1 + ( ulNumbers[ 1 ] % 0xFDU ) ); /* get value 1..254 for IP-address 4th byte of IP address to try. */
EP_IPv4_SETTINGS.ulGatewayAddress = 0U;
/* prepare xDHCPData with data to test. */
EP_DHCPData.ulOfferedIPAddress =
FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] );
EP_DHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */
EP_IPv4_SETTINGS.ulNetMask =
FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 );
/* DHCP completed. The IP address can now be used, and the
* timer set to the lease timeout time. */
EP_IPv4_SETTINGS.ulIPAddress = EP_DHCPData.ulOfferedIPAddress;
/* Setting the 'local' broadcast address, something like 192.168.1.255' */
EP_IPv4_SETTINGS.ulBroadcastAddress = ( EP_DHCPData.ulOfferedIPAddress & EP_IPv4_SETTINGS.ulNetMask ) | ~EP_IPv4_SETTINGS.ulNetMask;
/* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */
prvCloseDHCPSocket( pxEndPoint );
xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) );
EP_DHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000U + ( ulNumbers[ 0 ] & 0x3ffU ) ); /* do ARP test every (3 + 0-1024mS) seconds. */
xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */
vARPSendGratuitous();
}
#endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
/*-----------------------------------------------------------*/
#endif /* ipconfigUSE_DHCP != 0 */