Regarding DHCP Functionality, IP Address is not allocated

Hi,
I was enabling the DHCP functionality in our code. I observed that in file FreeRTOS_DHCP.c
the DHCP state from “vDHCPProcessEndPoint()” function is not changing.

  1. When the DHCP event (“eDHCPEvent”) is generated from “prvIPTask( void *pvParameters )” function.
    vDHCPProcess()” function is called and then after that the xDHCPData.eDHCPState is checked and I have observed that it’s countinously on eWaitingOffer state.

  2. Following is the code snippet for eWaiting offer case from vDHCPProcessEndPoint function.

case eWaitingOffer :

  xGivingUp = pdFALSE;

  /* Look for offers coming in. */
  if( ( xDoCheck != pdFALSE ) && ( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxEndPoint ) == pdPASS ) )
  {
  #if( ipconfigUSE_DHCP_HOOK != 0 )
    /* Ask the user if a DHCP request is required. */
    eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, pxEndPoint->xDHCPData.ulOfferedIPAddress );
    if( eAnswer == eDHCPContinue )
  #endif  /* ipconfigUSE_DHCP_HOOK */
    {
      /* An offer has been made, the user wants to continue,
      generate the request. */
      pxEndPoint->xDHCPData.xDHCPTxTime = xTaskGetTickCount();
      pxEndPoint->xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
      prvSendDHCPRequest( pxEndPoint );
      pxEndPoint->xDHCPData.eDHCPState = eWaitingAcknowledge;
      break;
    }
  #if( ipconfigUSE_DHCP_HOOK != 0 )
    if( eAnswer == eDHCPUseDefaults )
    {
      memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );
      pxEndPoint->ulIPAddress = pxEndPoint->ulDefaultIPAddress;  /* Use this address in case DHCP has failed. */
    }
    /* 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() - pxEndPoint->xDHCPData.xDHCPTxTime ) > pxEndPoint->xDHCPData.xDHCPTxPeriod )
  {
    /* Increase the time period, and if it has not got to the
    point of giving up - send another discovery. */
    pxEndPoint->xDHCPData.xDHCPTxPeriod <<= 1;

    if( pxEndPoint->xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
    {
      pxEndPoint->xDHCPData.ulTransactionId++;
      pxEndPoint->xDHCPData.xDHCPTxTime = xTaskGetTickCount();
      pxEndPoint->xDHCPData.xUseBroadcast = !pxEndPoint->xDHCPData.xUseBroadcast;
      prvSendDHCPDiscover( pxEndPoint );
      FreeRTOS_printf( ( "vDHCPProcess[%02x-%02x]: timeout %lu ticks\n",
        pxEndPoint->xMACAddress.ucBytes[ 4 ],
        pxEndPoint->xMACAddress.ucBytes[ 5 ],
        pxEndPoint->xDHCPData.xDHCPTxPeriod ) );
    }
    else
    {
      FreeRTOS_printf( ( "vDHCPProcess[%02x-%02x]: giving up %lu > %lu ticks\n",
        pxEndPoint->xMACAddress.ucBytes[ 4 ],
        pxEndPoint->xMACAddress.ucBytes[ 5 ],
        pxEndPoint->xDHCPData.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();

        /* Setting an IP address manually so set to not using
        leased address mode. */
        pxEndPoint->xDHCPData.eDHCPState = eGetLinkLayerAddress;
      }
      #else
      {
        xGivingUp = pdTRUE;
      }
      #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
    }
  }
  break;
  1. This should change its state form “eWaitingOffer” to “eWaintingAcknowledge” inorder to work. I have also debugged the code. But yet not able to solve it can you help?

Thanks and Regards
Prince

Hello Prince,

Yes, the code should proceed to eWaitingAcknowledge state once it receives offer and sends the reply.

Have you noticed what is the path the code follows? If you are using the DHCP hook then is the xApplicationDHCPHook not returning the correct value?

What I am trying to say here is that it would be extremely helpful if we can have more information since there are numerous things which can go wrong in this case. Would you mind stepping through the code to see what exactly is happening?

Also, if possible, can you also attach a Wireshark capture here? It will be quite helpful in debugging.

Thanks,
Aniruddha

Hello Aniruddha,
Thanks for replying. I have gone through the path that code follows. DHCP hook is enabled and xApplicationDHCPHook is returning the eReturn = eDHCPContinue; .

xApplicationDHCPHook is called from case : eWaitingSendFirstDiscover
and case : eWaitingOffer inside vDHCPProcessEndPoint() function

  1. When xApplicationDHCPHook is called from case : eWaitingSendFirstDiscover it returns eReturn = eDHCPContinue

case eWaitingSendFirstDiscover :

        /* Ask the user if a DHCP discovery is required. */

    #if( ipconfigUSE_DHCP_HOOK != 0 )

        **eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, pxEndPoint->ulDefaultIPAddress );**

        if( eAnswer == eDHCPContinue )

    #endif  /* ipconfigUSE_DHCP_HOOK */

        {

            /* Initial state.  Create the DHCP socket, timer, etc. if they

            have not already been created. */

            prvInitialiseDHCP( pxEndPoint );

            /* Put 'ulIPAddress' to zero to indicate that the end-point is down. */

            pxEndPoint->ulIPAddress = 0UL;

            /* Send the first discover request. */

            if( xDHCPSocket != NULL )

            {

                pxEndPoint->xDHCPData.xDHCPTxTime = xTaskGetTickCount();

                prvSendDHCPDiscover( pxEndPoint );

                pxEndPoint->xDHCPData.eDHCPState = eWaitingOffer;

            }

        }

    #if( ipconfigUSE_DHCP_HOOK != 0 )

        else

        {

            if( eAnswer == eDHCPUseDefaults )

            {

                memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );

            }

            /* The user indicates that the DHCP process does not continue. */

            xGivingUp = pdTRUE;

        }

    #endif  /* ipconfigUSE_DHCP_HOOK */

        break; 
  1. After that it changes its state from eWaitingSendFirstDiscover to eWaitingOffer and now after it goes in case : eWaitingOffer the following if condition is not executed. Due to which the xApplicationDHCPHook is not called again as it stucks there and execute the else if part mentioned below.

if( ( xDoCheck != pdFALSE ) && ( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxEndPoint ) == pdPASS ) )

case eWaitingOffer :

        xGivingUp = pdFALSE;

        /* Look for offers coming in. */

        if( ( xDoCheck != pdFALSE ) && ( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxEndPoint ) == pdPASS ) )

        {

        #if( ipconfigUSE_DHCP_HOOK != 0 )

            /* Ask the user if a DHCP request is required. */

            eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, pxEndPoint->xDHCPData.ulOfferedIPAddress );

            if( eAnswer == eDHCPContinue )

        #endif  /* ipconfigUSE_DHCP_HOOK */

            {

                /* An offer has been made, the user wants to continue,

                generate the request. */

                pxEndPoint->xDHCPData.xDHCPTxTime = xTaskGetTickCount();

                pxEndPoint->xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;

                prvSendDHCPRequest( pxEndPoint );

                pxEndPoint->xDHCPData.eDHCPState = eWaitingAcknowledge;

                break;

            }

        #if( ipconfigUSE_DHCP_HOOK != 0 )

            if( eAnswer == eDHCPUseDefaults )

            {

                memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );

                pxEndPoint->ulIPAddress = pxEndPoint->ulDefaultIPAddress;   /* Use this address in case DHCP has failed. */

            }

            /* 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() - pxEndPoint->xDHCPData.xDHCPTxTime ) > pxEndPoint->xDHCPData.xDHCPTxPeriod )**

        {

            /* Increase the time period, and if it has not got to the

            point of giving up - send another discovery. */

            pxEndPoint->xDHCPData.xDHCPTxPeriod <<= 1;

            if( pxEndPoint->xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )

            {

                pxEndPoint->xDHCPData.ulTransactionId++;

                pxEndPoint->xDHCPData.xDHCPTxTime = xTaskGetTickCount();

                pxEndPoint->xDHCPData.xUseBroadcast = !pxEndPoint->xDHCPData.xUseBroadcast;

                prvSendDHCPDiscover( pxEndPoint );

                FreeRTOS_printf( ( "vDHCPProcess[%02x-%02x]: timeout %lu ticks\n",

                    pxEndPoint->xMACAddress.ucBytes[ 4 ],

                    pxEndPoint->xMACAddress.ucBytes[ 5 ],

                    pxEndPoint->xDHCPData.xDHCPTxPeriod ) );

            }

            else

            {

                FreeRTOS_printf( ( "vDHCPProcess[%02x-%02x]: giving up %lu > %lu ticks\n",

                    pxEndPoint->xMACAddress.ucBytes[ 4 ],

                    pxEndPoint->xMACAddress.ucBytes[ 5 ],

                    pxEndPoint->xDHCPData.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();

                    /* Setting an IP address manually so set to not using

                    leased address mode. */

                    pxEndPoint->xDHCPData.eDHCPState = eGetLinkLayerAddress;

                }

                #else

                {

                    xGivingUp = pdTRUE;

                }

                #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */

            }

        }

        break;
  1. Now I have observed that in vDHCPProcess() function the lBytes is calculated from FreeRTOS_recvfrom() function and then the condition ( lBytes <= 0 ) is checked. Here the value of lBytes is always -11 i.e
    pdFREERTOS_ERRNO_EWOULDBLOCK
    . So it never complete the for loop and break from the condition ( lBytes <= 0 ).
void vDHCPProcess( BaseType_t xReset, NetworkEndPoint_t *pxEndPoint )

{

    /* If there is a socket, check for incoming messasges first. */

    if( xDHCPSocket != NULL )

    {

    uint8_t *pucUDPPayload;

    DHCPMessage_IPv4_t *pxDHCPMessage;

    BaseType_t lBytes;

        for( ;; )

        {

        NetworkEndPoint_t *pxIterator = NULL;

            /* Peek the next UDP message. */

            lBytes = FreeRTOS_recvfrom( xDHCPSocket, ( void * ) &pucUDPPayload, 0, FREERTOS_ZERO_COPY | FREERTOS_MSG_PEEK, NULL, NULL );

            if( lBytes <= 0 )

            {

                if( ( lBytes < 0 ) && ( lBytes != -pdFREERTOS_ERRNO_EAGAIN ) )

                {

                    FreeRTOS_printf( ( "vDHCPProcess: FreeRTOS_recvfrom returns %d\n", lBytes ) );

                }

                break;

            }

            /* Map a DHCP structure onto the received data. */

            pxDHCPMessage = ( 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 ) )

            {

                pxIterator = NULL;

            }

            if( pxIterator != NULL )

            {

                #if( ipconfigHAS_DEBUG_PRINTF != 0 )

                {

                    FreeRTOS_debug_printf( ( "vDHCPProcess[%02x-%02x] ID %04X: state %s found xReset %d\n",

                        pxIterator->xMACAddress.ucBytes[ 4 ],

                        pxIterator->xMACAddress.ucBytes[ 5 ],

                        pxIterator->xDHCPData.ulTransactionId,

                        pcDHCPStateName( pxIterator->xDHCPData.eDHCPState ) ) );

                }

                #endif /* ipconfigHAS_DEBUG_PRINTF */

                /* The second parameter pdTRUE tells to check for a UDP message. */

                vDHCPProcessEndPoint( pdFALSE, pdTRUE, pxIterator );

                if( pxEndPoint == pxIterator )

                {

                    pxEndPoint = NULL;

                }

            }

            else

            {

                /* Target not found, fetch the message and delete it. */

                lBytes = FreeRTOS_recvfrom( xDHCPSocket, ( void * ) &pucUDPPayload, 0, FREERTOS_ZERO_COPY, NULL, NULL );

                if( lBytes > 0 )

                {

                    /* Remove it now, destination not found. */

                    FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload );

                    FreeRTOS_printf( ( "vDHCPProcess: Removed a %d-byte message: target not found\n", lBytes ) );

                }

            }

        }

    }

    if( pxEndPoint != NULL )

    {

        vDHCPProcessEndPoint( xReset, pdFALSE, pxEndPoint );

    }

}

I will also debug further and will let you know the observation.

Thanks
Prince Tripathi

The state will only change if you receive an offer or time out. Please attach a wireshark trace so we can see if the offer is sent.