X-Git-Url: https://git.sur5r.net/?p=freertos;a=blobdiff_plain;f=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2FFreeRTOS_DHCP.c;fp=FreeRTOS-Plus%2FSource%2FFreeRTOS-Plus-TCP%2FFreeRTOS_DHCP.c;h=930768055483ea21405ab38afbca01fc6aab9168;hp=8871d6563990607693bcf2f52932f197ff4f7cdb;hb=b15dfacb6026af3b0ba697e5753844923b468d2b;hpb=4334233a064299a09d167a497889d3860932a587 diff --git a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_DHCP.c b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_DHCP.c index 8871d6563..930768055 100644 --- a/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_DHCP.c +++ b/FreeRTOS-Plus/Source/FreeRTOS-Plus-TCP/FreeRTOS_DHCP.c @@ -1,1021 +1,1021 @@ -/* - * FreeRTOS+TCP V2.2.0 - * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 - */ - -/* Standard includes. */ -#include - -/* 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_TCP_IP.h" -#include "FreeRTOS_DHCP.h" -#include "FreeRTOS_ARP.h" -#include "NetworkInterface.h" -#include "NetworkBufferManagement.h" - -/* Exclude the entire file if DHCP is not enabled. */ -#if( ipconfigUSE_DHCP != 0 ) - -#if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586u ) - /* DHCP must be able to receive an options field of 312 bytes, the fixed - part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */ - #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP -#endif - -/* Parameter widths in the DHCP packet. */ -#define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH 16 -#define dhcpSERVER_HOST_NAME_LENGTH 64 -#define dhcpBOOT_FILE_NAME_LENGTH 128 - -/* Timer parameters */ -#ifndef dhcpINITIAL_DHCP_TX_PERIOD - #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250 ) ) - #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000 ) ) -#endif - -/* Codes of interest found in the DHCP options field. */ -#define dhcpZERO_PAD_OPTION_CODE ( 0u ) -#define dhcpSUBNET_MASK_OPTION_CODE ( 1u ) -#define dhcpGATEWAY_OPTION_CODE ( 3u ) -#define dhcpDNS_SERVER_OPTIONS_CODE ( 6u ) -#define dhcpDNS_HOSTNAME_OPTIONS_CODE ( 12u ) -#define dhcpREQUEST_IP_ADDRESS_OPTION_CODE ( 50u ) -#define dhcpLEASE_TIME_OPTION_CODE ( 51u ) -#define dhcpMESSAGE_TYPE_OPTION_CODE ( 53u ) -#define dhcpSERVER_IP_ADDRESS_OPTION_CODE ( 54u ) -#define dhcpPARAMETER_REQUEST_OPTION_CODE ( 55u ) -#define dhcpCLIENT_IDENTIFIER_OPTION_CODE ( 61u ) - -/* The four DHCP message types of interest. */ -#define dhcpMESSAGE_TYPE_DISCOVER ( 1 ) -#define dhcpMESSAGE_TYPE_OFFER ( 2 ) -#define dhcpMESSAGE_TYPE_REQUEST ( 3 ) -#define dhcpMESSAGE_TYPE_ACK ( 5 ) -#define dhcpMESSAGE_TYPE_NACK ( 6 ) - -/* Offsets into the transmitted DHCP options fields at which various parameters -are located. */ -#define dhcpCLIENT_IDENTIFIER_OFFSET ( 5 ) -#define dhcpREQUESTED_IP_ADDRESS_OFFSET ( 13 ) -#define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ( 19 ) - -/* Values used in the DHCP packets. */ -#define dhcpREQUEST_OPCODE ( 1 ) -#define dhcpREPLY_OPCODE ( 2 ) -#define dhcpADDRESS_TYPE_ETHERNET ( 1 ) -#define dhcpETHERNET_ADDRESS_LENGTH ( 6 ) - -/* If a lease time is not received, use the default of two days. */ -/* 48 hours in ticks. Can not use pdMS_TO_TICKS() as integer overflow can occur. */ -#define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL ) * configTICK_RATE_HZ ) - -/* Don't allow the lease time to be too short. */ -#define dhcpMINIMUM_LEASE_TIME ( pdMS_TO_TICKS( 60000UL ) ) /* 60 seconds in ticks. */ - -/* Marks the end of the variable length options field in the DHCP packet. */ -#define dhcpOPTION_END_BYTE 0xffu - -/* Offset into a DHCP message at which the first byte of the options is -located. */ -#define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0 ) - -/* Standard DHCP port numbers and magic cookie value. */ -#if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) - #define dhcpCLIENT_PORT 0x4400u - #define dhcpSERVER_PORT 0x4300u - #define dhcpCOOKIE 0x63538263ul - #define dhcpBROADCAST 0x0080u -#else - #define dhcpCLIENT_PORT 0x0044u - #define dhcpSERVER_PORT 0x0043u - #define dhcpCOOKIE 0x63825363ul - #define dhcpBROADCAST 0x8000u -#endif /* ipconfigBYTE_ORDER */ - -#include "pack_struct_start.h" -struct xDHCPMessage -{ - uint8_t ucOpcode; - uint8_t ucAddressType; - uint8_t ucAddressLength; - uint8_t ucHops; - uint32_t ulTransactionID; - uint16_t usElapsedTime; - uint16_t usFlags; - uint32_t ulClientIPAddress_ciaddr; - uint32_t ulYourIPAddress_yiaddr; - uint32_t ulServerIPAddress_siaddr; - uint32_t ulRelayAgentIPAddress_giaddr; - uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ]; - uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ]; - uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ]; - uint32_t ulDHCPCookie; - uint8_t ucFirstOptionByte; -} -#include "pack_struct_end.h" -typedef struct xDHCPMessage DHCPMessage_t; - -/* DHCP state machine states. */ -typedef enum -{ - eWaitingSendFirstDiscover = 0, /* Initial state. Send a discover the first time it is called, and reset all timers. */ - eWaitingOffer, /* Either resend the discover, or, if the offer is forthcoming, send a request. */ - eWaitingAcknowledge, /* Either resend the request. */ - #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) - eGetLinkLayerAddress, /* When DHCP didn't respond, try to obtain a LinkLayer address 168.254.x.x. */ - #endif - eLeasedAddress, /* Resend the request at the appropriate time to renew the lease. */ - eNotUsingLeasedAddress /* DHCP failed, and a default IP address is being used. */ -} eDHCPState_t; - -/* Hold information in between steps in the DHCP state machine. */ -struct xDHCP_DATA -{ - uint32_t ulTransactionId; - uint32_t ulOfferedIPAddress; - uint32_t ulDHCPServerAddress; - uint32_t ulLeaseTime; - /* Hold information on the current timer state. */ - TickType_t xDHCPTxTime; - TickType_t xDHCPTxPeriod; - /* Try both without and with the broadcast flag */ - BaseType_t xUseBroadcast; - /* Maintains the DHCP state machine state. */ - eDHCPState_t eDHCPState; - /* The UDP socket used for all incoming and outgoing DHCP traffic. */ - Socket_t xDHCPSocket; -}; - -typedef struct xDHCP_DATA DHCPData_t; - -#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 - - -/* - * Generate a DHCP discover message and send it on the DHCP socket. - */ -static void prvSendDHCPDiscover( void ); - -/* - * Interpret message received on the DHCP socket. - */ -static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ); - -/* - * Generate a DHCP request packet, and send it on the DHCP socket. - */ -static void prvSendDHCPRequest( void ); - -/* - * Prepare to start a DHCP transaction. This initialises some state variables - * and creates the DHCP socket if necessary. - */ -static void prvInitialiseDHCP( void ); - -/* - * 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 ); - -/* - * Create the DHCP socket, if it has not been created already. - */ -static void prvCreateDHCPSocket( void ); - -/* - * After DHCP has failed to answer, prepare everything to start searching - * for (trying-out) LinkLayer IP-addresses, using the random method: Send - * a gratuitous ARP request and wait if another device responds to it. - */ -#if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) - static void prvPrepareLinkLayerIPLookUp( void ); -#endif - -/*-----------------------------------------------------------*/ - -/* The next DHCP transaction Id to be used. */ -static DHCPData_t xDHCPData; - -/*-----------------------------------------------------------*/ - -BaseType_t xIsDHCPSocket( Socket_t xSocket ) -{ -BaseType_t xReturn; - - if( xDHCPData.xDHCPSocket == xSocket ) - { - xReturn = pdTRUE; - } - else - { - xReturn = pdFALSE; - } - - return xReturn; -} -/*-----------------------------------------------------------*/ - -void vDHCPProcess( BaseType_t xReset ) -{ -BaseType_t xGivingUp = pdFALSE; -#if( ipconfigUSE_DHCP_HOOK != 0 ) - eDHCPCallbackAnswer_t eAnswer; -#endif /* ipconfigUSE_DHCP_HOOK */ - - /* Is DHCP starting over? */ - if( xReset != pdFALSE ) - { - xDHCPData.eDHCPState = eWaitingSendFirstDiscover; - } - - switch( xDHCPData.eDHCPState ) - { - case eWaitingSendFirstDiscover : - /* Ask the user if a DHCP discovery is required. */ - #if( ipconfigUSE_DHCP_HOOK != 0 ) - eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress ); - if( eAnswer == eDHCPContinue ) - #endif /* ipconfigUSE_DHCP_HOOK */ - { - /* Initial state. Create the DHCP socket, timer, etc. if they - have not already been created. */ - prvInitialiseDHCP(); - - /* See if prvInitialiseDHCP() has creates a socket. */ - if( xDHCPData.xDHCPSocket == NULL ) - { - xGivingUp = pdTRUE; - break; - } - - *ipLOCAL_IP_ADDRESS_POINTER = 0UL; - - /* Send the first discover request. */ - if( xDHCPData.xDHCPSocket != NULL ) - { - xDHCPData.xDHCPTxTime = xTaskGetTickCount(); - prvSendDHCPDiscover( ); - 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; - - case eWaitingOffer : - - xGivingUp = pdFALSE; - - /* Look for offers coming in. */ - if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS ) - { - #if( ipconfigUSE_DHCP_HOOK != 0 ) - /* Ask the user if a DHCP request is required. */ - eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, xDHCPData.ulOfferedIPAddress ); - - if( eAnswer == eDHCPContinue ) - #endif /* ipconfigUSE_DHCP_HOOK */ - { - /* An offer has been made, the user wants to continue, - generate the request. */ - xDHCPData.xDHCPTxTime = xTaskGetTickCount(); - xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; - prvSendDHCPRequest( ); - xDHCPData.eDHCPState = eWaitingAcknowledge; - break; - } - - #if( ipconfigUSE_DHCP_HOOK != 0 ) - if( eAnswer == eDHCPUseDefaults ) - { - memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) ); - } - - /* The user indicates that the DHCP process does not continue. */ - xGivingUp = pdTRUE; - #endif /* ipconfigUSE_DHCP_HOOK */ - } - else if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.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. */ - xDHCPData.xDHCPTxPeriod <<= 1; - - if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) - { - if( xApplicationGetRandomNumber( &( xDHCPData.ulTransactionId ) ) != pdFALSE ) - { - xDHCPData.xDHCPTxTime = xTaskGetTickCount( ); - xDHCPData.xUseBroadcast = !xDHCPData.xUseBroadcast; - prvSendDHCPDiscover( ); - FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", xDHCPData.xDHCPTxPeriod ) ); - } - else - { - FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) ); - } - } - else - { - FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", 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. */ - xDHCPData.eDHCPState = eGetLinkLayerAddress; - } - #else - { - xGivingUp = pdTRUE; - } - #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ - } - } - break; - - case eWaitingAcknowledge : - - /* Look for acks coming in. */ - if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS ) - { - FreeRTOS_debug_printf( ( "vDHCPProcess: acked %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); - - /* DHCP completed. The IP address can now be used, and the - timer set to the lease timeout time. */ - *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress; - - /* Setting the 'local' broadcast address, something like - '192.168.1.255'. */ - xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; - xDHCPData.eDHCPState = eLeasedAddress; - - iptraceDHCP_SUCCEDEED( xDHCPData.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( ); - - /* Close socket to ensure packets don't queue on it. */ - vSocketClose( xDHCPData.xDHCPSocket ); - xDHCPData.xDHCPSocket = NULL; - - if( xDHCPData.ulLeaseTime == 0UL ) - { - xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; - } - else if( xDHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) - { - xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME; - } - else - { - /* The lease time is already valid. */ - } - - /* Check for clashes. */ - vARPSendGratuitous(); - vIPReloadDHCPTimer( xDHCPData.ulLeaseTime ); - } - else - { - /* Is it time to send another Discover? */ - if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod ) - { - /* Increase the time period, and if it has not got to the - point of giving up - send another request. */ - xDHCPData.xDHCPTxPeriod <<= 1; - - if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) - { - xDHCPData.xDHCPTxTime = xTaskGetTickCount(); - prvSendDHCPRequest( ); - } - else - { - /* Give up, start again. */ - xDHCPData.eDHCPState = eWaitingSendFirstDiscover; - } - } - } - break; - - #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) - case eGetLinkLayerAddress: - if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod ) - { - if( xARPHadIPClash == pdFALSE ) - { - /* ARP OK. proceed. */ - iptraceDHCP_SUCCEDEED( xDHCPData.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( ); - xDHCPData.eDHCPState = eNotUsingLeasedAddress; - } - else - { - /* ARP clashed - try another IP address. */ - prvPrepareLinkLayerIPLookUp(); - - /* Setting an IP address manually so set to not using leased - address mode. */ - xDHCPData.eDHCPState = eGetLinkLayerAddress; - } - } - break; - #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ - - case eLeasedAddress : - - /* Resend the request at the appropriate time to renew the lease. */ - prvCreateDHCPSocket(); - - if( xDHCPData.xDHCPSocket != NULL ) - { - xDHCPData.xDHCPTxTime = xTaskGetTickCount(); - xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; - prvSendDHCPRequest( ); - xDHCPData.eDHCPState = eWaitingAcknowledge; - - /* From now on, we should be called more often */ - vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD ); - } - break; - - case eNotUsingLeasedAddress: - - vIPSetDHCPTimerEnableState( pdFALSE ); - break; - - default: - break; - } - - 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 canceled from here. */ - - /* Revert to static IP address. */ - taskENTER_CRITICAL(); - { - *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; - iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress ); - } - taskEXIT_CRITICAL(); - - xDHCPData.eDHCPState = eNotUsingLeasedAddress; - vIPSetDHCPTimerEnableState( 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( ); - - /* Test if socket was indeed created. */ - if( xDHCPData.xDHCPSocket != NULL ) - { - /* Close socket to ensure packets don't queue on it. */ - vSocketClose( xDHCPData.xDHCPSocket ); - xDHCPData.xDHCPSocket = NULL; - } - } -} -/*-----------------------------------------------------------*/ - -static void prvCreateDHCPSocket( void ) -{ -struct freertos_sockaddr xAddress; -BaseType_t xReturn; -TickType_t xTimeoutTime = ( TickType_t ) 0; - - /* Create the socket, if it has not already been created. */ - if( xDHCPData.xDHCPSocket == NULL ) - { - xDHCPData.xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); - if( xDHCPData.xDHCPSocket != FREERTOS_INVALID_SOCKET ) - { - - /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the - context of the IP task. */ - FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); - FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); - - /* Bind to the standard DHCP client port. */ - xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT; - xReturn = vSocketBind( xDHCPData.xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE ); - if( xReturn != 0 ) - { - /* Binding failed, close the socket again. */ - vSocketClose( xDHCPData.xDHCPSocket ); - xDHCPData.xDHCPSocket = NULL; - } - } - else - { - /* Change to NULL for easier testing. */ - xDHCPData.xDHCPSocket = NULL; - } - } -} -/*-----------------------------------------------------------*/ - -static void prvInitialiseDHCP( void ) -{ - /* 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( &( xDHCPData.ulTransactionId ) ) != pdFALSE ) - { - xDHCPData.xUseBroadcast = 0; - xDHCPData.ulOfferedIPAddress = 0UL; - xDHCPData.ulDHCPServerAddress = 0UL; - xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; - - /* Create the DHCP socket if it has not already been created. */ - prvCreateDHCPSocket(); - FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) ); - vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD ); - } - else - { - /* There was a problem with the randomiser. */ - } -} -/*-----------------------------------------------------------*/ - -static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ) -{ -uint8_t *pucUDPPayload, *pucLastByte; -struct freertos_sockaddr xClient; -uint32_t xClientLength = sizeof( xClient ); -int32_t lBytes; -DHCPMessage_t *pxDHCPMessage; -uint8_t *pucByte, ucOptionCode, ucLength; -uint32_t ulProcessed, ulParameter; -BaseType_t xReturn = pdFALSE; -const uint32_t ulMandatoryOptions = 2ul; /* DHCP server address, and the correct DHCP message type must be present in the options. */ - - lBytes = FreeRTOS_recvfrom( xDHCPData.xDHCPSocket, ( void * ) &pucUDPPayload, 0ul, FREERTOS_ZERO_COPY, &xClient, &xClientLength ); - - if( lBytes > 0 ) - { - /* Map a DHCP structure onto the received data. */ - pxDHCPMessage = ( DHCPMessage_t * ) ( pucUDPPayload ); - - /* Sanity check. */ - if( ( lBytes >= sizeof( DHCPMessage_t ) ) && - ( pxDHCPMessage->ulDHCPCookie == ( uint32_t ) dhcpCOOKIE ) && - ( pxDHCPMessage->ucOpcode == ( uint8_t ) dhcpREPLY_OPCODE ) && - ( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( xDHCPData.ulTransactionId ) ) ) - { - if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ), - ( void * ) ipLOCAL_MAC_ADDRESS, - sizeof( MACAddress_t ) ) == 0 ) - { - /* None of the essential options have been processed yet. */ - ulProcessed = 0ul; - - /* Walk through the options until the dhcpOPTION_END_BYTE byte - is found, taking care not to walk off the end of the options. */ - pucByte = &( pxDHCPMessage->ucFirstOptionByte ); - /* Maintain a pointer to the last valid byte (i.e. not the first - invalid byte). */ - pucLastByte = pucUDPPayload + lBytes - 1; - - while( pucByte <= pucLastByte ) - { - ucOptionCode = pucByte[ 0 ]; - if( ucOptionCode == dhcpOPTION_END_BYTE ) - { - /* Ready, the last byte has been seen. */ - break; - } - if( ucOptionCode == dhcpZERO_PAD_OPTION_CODE ) - { - /* The value zero is used as a pad byte, - it is not followed by a length byte. */ - pucByte += 1; - continue; - } - - /* Stop if the response is malformed. */ - if( pucByte < pucLastByte ) - { - /* There are at least two bytes left. */ - ucLength = pucByte[ 1 ]; - pucByte += 2; - - if( pucByte + ucLength > pucLastByte ) - { - break; - } - } - else - { - break; - } - - /* In most cases, a 4-byte network-endian parameter follows, - just get it once here and use later. */ - if( ucLength >= sizeof( ulParameter ) ) - { - memcpy( ( void * ) &( ulParameter ), - ( void * ) pucByte, - ( size_t ) sizeof( ulParameter ) ); - } - else - { - ulParameter = 0; - } - - /* Option-specific handling. */ - switch( ucOptionCode ) - { - case dhcpMESSAGE_TYPE_OPTION_CODE : - - if( *pucByte == ( uint8_t ) xExpectedMessageType ) - { - /* The message type is the message type the - state machine is expecting. */ - ulProcessed++; - } - else if( *pucByte == ( uint8_t ) dhcpMESSAGE_TYPE_NACK ) - { - if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK ) - { - /* Start again. */ - xDHCPData.eDHCPState = eWaitingSendFirstDiscover; - } - } - else - { - /* Don't process other message types. */ - } - break; - - case dhcpSUBNET_MASK_OPTION_CODE : - - if( ucLength == sizeof( uint32_t ) ) - { - xNetworkAddressing.ulNetMask = ulParameter; - } - break; - - case dhcpGATEWAY_OPTION_CODE : - - if( ucLength == sizeof( uint32_t ) ) - { - /* ulProcessed is not incremented in this case - because the gateway is not essential. */ - xNetworkAddressing.ulGatewayAddress = ulParameter; - } - break; - - case dhcpDNS_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. */ - xNetworkAddressing.ulDNSServerAddress = ulParameter; - break; - - case dhcpSERVER_IP_ADDRESS_OPTION_CODE : - - if( ucLength == sizeof( uint32_t ) ) - { - if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER ) - { - /* Offers state the replying server. */ - ulProcessed++; - xDHCPData.ulDHCPServerAddress = ulParameter; - } - else - { - /* The ack must come from the expected server. */ - if( xDHCPData.ulDHCPServerAddress == ulParameter ) - { - ulProcessed++; - } - } - } - break; - - case dhcpLEASE_TIME_OPTION_CODE : - - if( ucLength == sizeof( xDHCPData.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. */ - xDHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter ); - - /* Divide the lease time by two to ensure a - renew request is sent before the lease actually - expires. */ - xDHCPData.ulLeaseTime >>= 1UL; - - /* Multiply with configTICK_RATE_HZ to get clock - ticks. */ - xDHCPData.ulLeaseTime = configTICK_RATE_HZ * xDHCPData.ulLeaseTime; - } - break; - - default : - - /* Not interested in this field. */ - - break; - } - - /* Jump over the data to find the next option code. */ - if( ucLength == 0u ) - { - break; - } - else - { - pucByte += ucLength; - } - } - - /* Were all the mandatory options received? */ - if( ulProcessed >= ulMandatoryOptions ) - { - /* HT:endian: used to be network order */ - xDHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; - FreeRTOS_printf( ( "vDHCPProcess: offer %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); - xReturn = pdPASS; - } - } - } - - FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload ); - } - - return xReturn; -} -/*-----------------------------------------------------------*/ - -static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize ) -{ -DHCPMessage_t *pxDHCPMessage; -size_t xRequiredBufferSize = sizeof( DHCPMessage_t ) + *pxOptionsArraySize; -uint8_t *pucUDPPayloadBuffer; - -#if( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) - const char *pucHostName = pcApplicationHostnameHook (); - size_t xNameLength = strlen( pucHostName ); - uint8_t *pucPtr; - - xRequiredBufferSize += ( 2 + xNameLength ); -#endif - - /* Get a buffer. This uses a maximum delay, but the delay will be capped - to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value still needs to - be test. */ - do - { - } while( ( pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xRequiredBufferSize, portMAX_DELAY ) ) == NULL ); - - pxDHCPMessage = ( DHCPMessage_t * ) pucUDPPayloadBuffer; - - /* Most fields need to be zero. */ - memset( ( void * ) pxDHCPMessage, 0x00, sizeof( DHCPMessage_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( xDHCPData.ulTransactionId ); - pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE; - if( xDHCPData.xUseBroadcast != pdFALSE ) - { - pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST; - } - else - { - pxDHCPMessage->usFlags = 0u; - } - - memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); - - /* Copy in the const part of the options options. */ - memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) 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 - 1 ) ] ); - pucPtr[ 0 ] = dhcpDNS_HOSTNAME_OPTIONS_CODE; - pucPtr[ 1 ] = ( uint8_t ) xNameLength; - memcpy( ( void *) ( pucPtr + 2 ), pucHostName, xNameLength ); - pucPtr[ 2 + xNameLength ] = dhcpOPTION_END_BYTE; - *pxOptionsArraySize += ( 2 + xNameLength ); - } - #endif - - /* Map in the client identifier. */ - memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), - ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); - - /* Set the addressing. */ - pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS; - pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT; - - return pucUDPPayloadBuffer; -} -/*-----------------------------------------------------------*/ - -static void prvSendDHCPRequest( void ) -{ -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. */ - dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */ - dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */ - dhcpREQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ - dhcpSERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */ - dhcpOPTION_END_BYTE -}; -size_t xOptionsLength = sizeof( ucDHCPRequestOptions ); - - pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPRequestOptions, &xOptionsLength ); - - /* Copy in the IP address being requested. */ - memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ), - ( void * ) &( xDHCPData.ulOfferedIPAddress ), sizeof( xDHCPData.ulOfferedIPAddress ) ); - - /* Copy in the address of the DHCP server being used. */ - memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ), - ( void * ) &( xDHCPData.ulDHCPServerAddress ), sizeof( xDHCPData.ulDHCPServerAddress ) ); - - FreeRTOS_debug_printf( ( "vDHCPProcess: reply %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); - iptraceSENDING_DHCP_REQUEST(); - - /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */ - if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 ) - { - /* The packet was not successfully queued for sending and must be - returned to the stack. */ - FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); - } -} -/*-----------------------------------------------------------*/ - -static void prvSendDHCPDiscover( void ) -{ -uint8_t *pucUDPPayloadBuffer; -struct freertos_sockaddr xAddress; -static const uint8_t ucDHCPDiscoverOptions[] = -{ - /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */ - dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */ - dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */ - dhcpPARAMETER_REQUEST_OPTION_CODE, 3, dhcpSUBNET_MASK_OPTION_CODE, dhcpGATEWAY_OPTION_CODE, dhcpDNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ - dhcpOPTION_END_BYTE -}; -size_t xOptionsLength = sizeof( ucDHCPDiscoverOptions ); - - pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, &xOptionsLength ); - - FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) ); - iptraceSENDING_DHCP_DISCOVER(); - - /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */ - if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 ) - { - /* The packet was not successfully queued for sending and must be - returned to the stack. */ - FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); - } -} -/*-----------------------------------------------------------*/ - -#if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) - - static void prvPrepareLinkLayerIPLookUp( void ) - { - 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. */ - xDHCPData.xDHCPTxTime = xTaskGetTickCount(); - - xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); - xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) ); - ucLinkLayerIPAddress[ 0 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 0 ] % 0xFDu ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */ - ucLinkLayerIPAddress[ 1 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 1 ] % 0xFDu ); /* get value 1..254 for IP-address 4th byte of IP address to try. */ - - xNetworkAddressing.ulGatewayAddress = FreeRTOS_htonl( 0xA9FE0203 ); - - /* prepare xDHCPData with data to test. */ - xDHCPData.ulOfferedIPAddress = - FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] ); - - xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */ - - xNetworkAddressing.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. */ - *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress; - - /* Setting the 'local' broadcast address, something like 192.168.1.255' */ - xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; - - /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */ - if( xDHCPData.xDHCPSocket != NULL ) - { - /* Close socket to ensure packets don't queue on it. */ - vSocketClose( xDHCPData.xDHCPSocket ); - xDHCPData.xDHCPSocket = NULL; - } - - xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); - xDHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000ul + ( ulNumbers[ 0 ] & 0x3ffuL ) ); /* 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 */ - - +/* + * FreeRTOS+TCP V2.2.0 + * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 + */ + +/* Standard includes. */ +#include + +/* 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_TCP_IP.h" +#include "FreeRTOS_DHCP.h" +#include "FreeRTOS_ARP.h" +#include "NetworkInterface.h" +#include "NetworkBufferManagement.h" + +/* Exclude the entire file if DHCP is not enabled. */ +#if( ipconfigUSE_DHCP != 0 ) + +#if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586u ) + /* DHCP must be able to receive an options field of 312 bytes, the fixed + part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */ + #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP +#endif + +/* Parameter widths in the DHCP packet. */ +#define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH 16 +#define dhcpSERVER_HOST_NAME_LENGTH 64 +#define dhcpBOOT_FILE_NAME_LENGTH 128 + +/* Timer parameters */ +#ifndef dhcpINITIAL_DHCP_TX_PERIOD + #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250 ) ) + #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000 ) ) +#endif + +/* Codes of interest found in the DHCP options field. */ +#define dhcpZERO_PAD_OPTION_CODE ( 0u ) +#define dhcpSUBNET_MASK_OPTION_CODE ( 1u ) +#define dhcpGATEWAY_OPTION_CODE ( 3u ) +#define dhcpDNS_SERVER_OPTIONS_CODE ( 6u ) +#define dhcpDNS_HOSTNAME_OPTIONS_CODE ( 12u ) +#define dhcpREQUEST_IP_ADDRESS_OPTION_CODE ( 50u ) +#define dhcpLEASE_TIME_OPTION_CODE ( 51u ) +#define dhcpMESSAGE_TYPE_OPTION_CODE ( 53u ) +#define dhcpSERVER_IP_ADDRESS_OPTION_CODE ( 54u ) +#define dhcpPARAMETER_REQUEST_OPTION_CODE ( 55u ) +#define dhcpCLIENT_IDENTIFIER_OPTION_CODE ( 61u ) + +/* The four DHCP message types of interest. */ +#define dhcpMESSAGE_TYPE_DISCOVER ( 1 ) +#define dhcpMESSAGE_TYPE_OFFER ( 2 ) +#define dhcpMESSAGE_TYPE_REQUEST ( 3 ) +#define dhcpMESSAGE_TYPE_ACK ( 5 ) +#define dhcpMESSAGE_TYPE_NACK ( 6 ) + +/* Offsets into the transmitted DHCP options fields at which various parameters +are located. */ +#define dhcpCLIENT_IDENTIFIER_OFFSET ( 5 ) +#define dhcpREQUESTED_IP_ADDRESS_OFFSET ( 13 ) +#define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ( 19 ) + +/* Values used in the DHCP packets. */ +#define dhcpREQUEST_OPCODE ( 1 ) +#define dhcpREPLY_OPCODE ( 2 ) +#define dhcpADDRESS_TYPE_ETHERNET ( 1 ) +#define dhcpETHERNET_ADDRESS_LENGTH ( 6 ) + +/* If a lease time is not received, use the default of two days. */ +/* 48 hours in ticks. Can not use pdMS_TO_TICKS() as integer overflow can occur. */ +#define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL ) * configTICK_RATE_HZ ) + +/* Don't allow the lease time to be too short. */ +#define dhcpMINIMUM_LEASE_TIME ( pdMS_TO_TICKS( 60000UL ) ) /* 60 seconds in ticks. */ + +/* Marks the end of the variable length options field in the DHCP packet. */ +#define dhcpOPTION_END_BYTE 0xffu + +/* Offset into a DHCP message at which the first byte of the options is +located. */ +#define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0 ) + +/* Standard DHCP port numbers and magic cookie value. */ +#if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN ) + #define dhcpCLIENT_PORT 0x4400u + #define dhcpSERVER_PORT 0x4300u + #define dhcpCOOKIE 0x63538263ul + #define dhcpBROADCAST 0x0080u +#else + #define dhcpCLIENT_PORT 0x0044u + #define dhcpSERVER_PORT 0x0043u + #define dhcpCOOKIE 0x63825363ul + #define dhcpBROADCAST 0x8000u +#endif /* ipconfigBYTE_ORDER */ + +#include "pack_struct_start.h" +struct xDHCPMessage +{ + uint8_t ucOpcode; + uint8_t ucAddressType; + uint8_t ucAddressLength; + uint8_t ucHops; + uint32_t ulTransactionID; + uint16_t usElapsedTime; + uint16_t usFlags; + uint32_t ulClientIPAddress_ciaddr; + uint32_t ulYourIPAddress_yiaddr; + uint32_t ulServerIPAddress_siaddr; + uint32_t ulRelayAgentIPAddress_giaddr; + uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ]; + uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ]; + uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ]; + uint32_t ulDHCPCookie; + uint8_t ucFirstOptionByte; +} +#include "pack_struct_end.h" +typedef struct xDHCPMessage DHCPMessage_t; + +/* DHCP state machine states. */ +typedef enum +{ + eWaitingSendFirstDiscover = 0, /* Initial state. Send a discover the first time it is called, and reset all timers. */ + eWaitingOffer, /* Either resend the discover, or, if the offer is forthcoming, send a request. */ + eWaitingAcknowledge, /* Either resend the request. */ + #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + eGetLinkLayerAddress, /* When DHCP didn't respond, try to obtain a LinkLayer address 168.254.x.x. */ + #endif + eLeasedAddress, /* Resend the request at the appropriate time to renew the lease. */ + eNotUsingLeasedAddress /* DHCP failed, and a default IP address is being used. */ +} eDHCPState_t; + +/* Hold information in between steps in the DHCP state machine. */ +struct xDHCP_DATA +{ + uint32_t ulTransactionId; + uint32_t ulOfferedIPAddress; + uint32_t ulDHCPServerAddress; + uint32_t ulLeaseTime; + /* Hold information on the current timer state. */ + TickType_t xDHCPTxTime; + TickType_t xDHCPTxPeriod; + /* Try both without and with the broadcast flag */ + BaseType_t xUseBroadcast; + /* Maintains the DHCP state machine state. */ + eDHCPState_t eDHCPState; + /* The UDP socket used for all incoming and outgoing DHCP traffic. */ + Socket_t xDHCPSocket; +}; + +typedef struct xDHCP_DATA DHCPData_t; + +#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 + + +/* + * Generate a DHCP discover message and send it on the DHCP socket. + */ +static void prvSendDHCPDiscover( void ); + +/* + * Interpret message received on the DHCP socket. + */ +static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ); + +/* + * Generate a DHCP request packet, and send it on the DHCP socket. + */ +static void prvSendDHCPRequest( void ); + +/* + * Prepare to start a DHCP transaction. This initialises some state variables + * and creates the DHCP socket if necessary. + */ +static void prvInitialiseDHCP( void ); + +/* + * 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 ); + +/* + * Create the DHCP socket, if it has not been created already. + */ +static void prvCreateDHCPSocket( void ); + +/* + * After DHCP has failed to answer, prepare everything to start searching + * for (trying-out) LinkLayer IP-addresses, using the random method: Send + * a gratuitous ARP request and wait if another device responds to it. + */ +#if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + static void prvPrepareLinkLayerIPLookUp( void ); +#endif + +/*-----------------------------------------------------------*/ + +/* The next DHCP transaction Id to be used. */ +static DHCPData_t xDHCPData; + +/*-----------------------------------------------------------*/ + +BaseType_t xIsDHCPSocket( Socket_t xSocket ) +{ +BaseType_t xReturn; + + if( xDHCPData.xDHCPSocket == xSocket ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vDHCPProcess( BaseType_t xReset ) +{ +BaseType_t xGivingUp = pdFALSE; +#if( ipconfigUSE_DHCP_HOOK != 0 ) + eDHCPCallbackAnswer_t eAnswer; +#endif /* ipconfigUSE_DHCP_HOOK */ + + /* Is DHCP starting over? */ + if( xReset != pdFALSE ) + { + xDHCPData.eDHCPState = eWaitingSendFirstDiscover; + } + + switch( xDHCPData.eDHCPState ) + { + case eWaitingSendFirstDiscover : + /* Ask the user if a DHCP discovery is required. */ + #if( ipconfigUSE_DHCP_HOOK != 0 ) + eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress ); + if( eAnswer == eDHCPContinue ) + #endif /* ipconfigUSE_DHCP_HOOK */ + { + /* Initial state. Create the DHCP socket, timer, etc. if they + have not already been created. */ + prvInitialiseDHCP(); + + /* See if prvInitialiseDHCP() has creates a socket. */ + if( xDHCPData.xDHCPSocket == NULL ) + { + xGivingUp = pdTRUE; + break; + } + + *ipLOCAL_IP_ADDRESS_POINTER = 0UL; + + /* Send the first discover request. */ + if( xDHCPData.xDHCPSocket != NULL ) + { + xDHCPData.xDHCPTxTime = xTaskGetTickCount(); + prvSendDHCPDiscover( ); + 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; + + case eWaitingOffer : + + xGivingUp = pdFALSE; + + /* Look for offers coming in. */ + if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS ) + { + #if( ipconfigUSE_DHCP_HOOK != 0 ) + /* Ask the user if a DHCP request is required. */ + eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, xDHCPData.ulOfferedIPAddress ); + + if( eAnswer == eDHCPContinue ) + #endif /* ipconfigUSE_DHCP_HOOK */ + { + /* An offer has been made, the user wants to continue, + generate the request. */ + xDHCPData.xDHCPTxTime = xTaskGetTickCount(); + xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + prvSendDHCPRequest( ); + xDHCPData.eDHCPState = eWaitingAcknowledge; + break; + } + + #if( ipconfigUSE_DHCP_HOOK != 0 ) + if( eAnswer == eDHCPUseDefaults ) + { + memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) ); + } + + /* The user indicates that the DHCP process does not continue. */ + xGivingUp = pdTRUE; + #endif /* ipconfigUSE_DHCP_HOOK */ + } + else if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.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. */ + xDHCPData.xDHCPTxPeriod <<= 1; + + if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) + { + if( xApplicationGetRandomNumber( &( xDHCPData.ulTransactionId ) ) != pdFALSE ) + { + xDHCPData.xDHCPTxTime = xTaskGetTickCount( ); + xDHCPData.xUseBroadcast = !xDHCPData.xUseBroadcast; + prvSendDHCPDiscover( ); + FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", xDHCPData.xDHCPTxPeriod ) ); + } + else + { + FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) ); + } + } + else + { + FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", 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. */ + xDHCPData.eDHCPState = eGetLinkLayerAddress; + } + #else + { + xGivingUp = pdTRUE; + } + #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ + } + } + break; + + case eWaitingAcknowledge : + + /* Look for acks coming in. */ + if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS ) + { + FreeRTOS_debug_printf( ( "vDHCPProcess: acked %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); + + /* DHCP completed. The IP address can now be used, and the + timer set to the lease timeout time. */ + *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress; + + /* Setting the 'local' broadcast address, something like + '192.168.1.255'. */ + xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; + xDHCPData.eDHCPState = eLeasedAddress; + + iptraceDHCP_SUCCEDEED( xDHCPData.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( ); + + /* Close socket to ensure packets don't queue on it. */ + vSocketClose( xDHCPData.xDHCPSocket ); + xDHCPData.xDHCPSocket = NULL; + + if( xDHCPData.ulLeaseTime == 0UL ) + { + xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; + } + else if( xDHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) + { + xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME; + } + else + { + /* The lease time is already valid. */ + } + + /* Check for clashes. */ + vARPSendGratuitous(); + vIPReloadDHCPTimer( xDHCPData.ulLeaseTime ); + } + else + { + /* Is it time to send another Discover? */ + if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod ) + { + /* Increase the time period, and if it has not got to the + point of giving up - send another request. */ + xDHCPData.xDHCPTxPeriod <<= 1; + + if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) + { + xDHCPData.xDHCPTxTime = xTaskGetTickCount(); + prvSendDHCPRequest( ); + } + else + { + /* Give up, start again. */ + xDHCPData.eDHCPState = eWaitingSendFirstDiscover; + } + } + } + break; + + #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + case eGetLinkLayerAddress: + if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod ) + { + if( xARPHadIPClash == pdFALSE ) + { + /* ARP OK. proceed. */ + iptraceDHCP_SUCCEDEED( xDHCPData.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( ); + xDHCPData.eDHCPState = eNotUsingLeasedAddress; + } + else + { + /* ARP clashed - try another IP address. */ + prvPrepareLinkLayerIPLookUp(); + + /* Setting an IP address manually so set to not using leased + address mode. */ + xDHCPData.eDHCPState = eGetLinkLayerAddress; + } + } + break; + #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */ + + case eLeasedAddress : + + /* Resend the request at the appropriate time to renew the lease. */ + prvCreateDHCPSocket(); + + if( xDHCPData.xDHCPSocket != NULL ) + { + xDHCPData.xDHCPTxTime = xTaskGetTickCount(); + xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + prvSendDHCPRequest( ); + xDHCPData.eDHCPState = eWaitingAcknowledge; + + /* From now on, we should be called more often */ + vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD ); + } + break; + + case eNotUsingLeasedAddress: + + vIPSetDHCPTimerEnableState( pdFALSE ); + break; + + default: + break; + } + + 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 canceled from here. */ + + /* Revert to static IP address. */ + taskENTER_CRITICAL(); + { + *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress; + iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress ); + } + taskEXIT_CRITICAL(); + + xDHCPData.eDHCPState = eNotUsingLeasedAddress; + vIPSetDHCPTimerEnableState( 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( ); + + /* Test if socket was indeed created. */ + if( xDHCPData.xDHCPSocket != NULL ) + { + /* Close socket to ensure packets don't queue on it. */ + vSocketClose( xDHCPData.xDHCPSocket ); + xDHCPData.xDHCPSocket = NULL; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvCreateDHCPSocket( void ) +{ +struct freertos_sockaddr xAddress; +BaseType_t xReturn; +TickType_t xTimeoutTime = ( TickType_t ) 0; + + /* Create the socket, if it has not already been created. */ + if( xDHCPData.xDHCPSocket == NULL ) + { + xDHCPData.xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); + if( xDHCPData.xDHCPSocket != FREERTOS_INVALID_SOCKET ) + { + + /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the + context of the IP task. */ + FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); + FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); + + /* Bind to the standard DHCP client port. */ + xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT; + xReturn = vSocketBind( xDHCPData.xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE ); + if( xReturn != 0 ) + { + /* Binding failed, close the socket again. */ + vSocketClose( xDHCPData.xDHCPSocket ); + xDHCPData.xDHCPSocket = NULL; + } + } + else + { + /* Change to NULL for easier testing. */ + xDHCPData.xDHCPSocket = NULL; + } + } +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseDHCP( void ) +{ + /* 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( &( xDHCPData.ulTransactionId ) ) != pdFALSE ) + { + xDHCPData.xUseBroadcast = 0; + xDHCPData.ulOfferedIPAddress = 0UL; + xDHCPData.ulDHCPServerAddress = 0UL; + xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; + + /* Create the DHCP socket if it has not already been created. */ + prvCreateDHCPSocket(); + FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) ); + vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD ); + } + else + { + /* There was a problem with the randomiser. */ + } +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType ) +{ +uint8_t *pucUDPPayload, *pucLastByte; +struct freertos_sockaddr xClient; +uint32_t xClientLength = sizeof( xClient ); +int32_t lBytes; +DHCPMessage_t *pxDHCPMessage; +uint8_t *pucByte, ucOptionCode, ucLength; +uint32_t ulProcessed, ulParameter; +BaseType_t xReturn = pdFALSE; +const uint32_t ulMandatoryOptions = 2ul; /* DHCP server address, and the correct DHCP message type must be present in the options. */ + + lBytes = FreeRTOS_recvfrom( xDHCPData.xDHCPSocket, ( void * ) &pucUDPPayload, 0ul, FREERTOS_ZERO_COPY, &xClient, &xClientLength ); + + if( lBytes > 0 ) + { + /* Map a DHCP structure onto the received data. */ + pxDHCPMessage = ( DHCPMessage_t * ) ( pucUDPPayload ); + + /* Sanity check. */ + if( ( lBytes >= sizeof( DHCPMessage_t ) ) && + ( pxDHCPMessage->ulDHCPCookie == ( uint32_t ) dhcpCOOKIE ) && + ( pxDHCPMessage->ucOpcode == ( uint8_t ) dhcpREPLY_OPCODE ) && + ( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( xDHCPData.ulTransactionId ) ) ) + { + if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ), + ( void * ) ipLOCAL_MAC_ADDRESS, + sizeof( MACAddress_t ) ) == 0 ) + { + /* None of the essential options have been processed yet. */ + ulProcessed = 0ul; + + /* Walk through the options until the dhcpOPTION_END_BYTE byte + is found, taking care not to walk off the end of the options. */ + pucByte = &( pxDHCPMessage->ucFirstOptionByte ); + /* Maintain a pointer to the last valid byte (i.e. not the first + invalid byte). */ + pucLastByte = pucUDPPayload + lBytes - 1; + + while( pucByte <= pucLastByte ) + { + ucOptionCode = pucByte[ 0 ]; + if( ucOptionCode == dhcpOPTION_END_BYTE ) + { + /* Ready, the last byte has been seen. */ + break; + } + if( ucOptionCode == dhcpZERO_PAD_OPTION_CODE ) + { + /* The value zero is used as a pad byte, + it is not followed by a length byte. */ + pucByte += 1; + continue; + } + + /* Stop if the response is malformed. */ + if( pucByte < pucLastByte ) + { + /* There are at least two bytes left. */ + ucLength = pucByte[ 1 ]; + pucByte += 2; + + if( pucByte + ucLength > pucLastByte ) + { + break; + } + } + else + { + break; + } + + /* In most cases, a 4-byte network-endian parameter follows, + just get it once here and use later. */ + if( ucLength >= sizeof( ulParameter ) ) + { + memcpy( ( void * ) &( ulParameter ), + ( void * ) pucByte, + ( size_t ) sizeof( ulParameter ) ); + } + else + { + ulParameter = 0; + } + + /* Option-specific handling. */ + switch( ucOptionCode ) + { + case dhcpMESSAGE_TYPE_OPTION_CODE : + + if( *pucByte == ( uint8_t ) xExpectedMessageType ) + { + /* The message type is the message type the + state machine is expecting. */ + ulProcessed++; + } + else if( *pucByte == ( uint8_t ) dhcpMESSAGE_TYPE_NACK ) + { + if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK ) + { + /* Start again. */ + xDHCPData.eDHCPState = eWaitingSendFirstDiscover; + } + } + else + { + /* Don't process other message types. */ + } + break; + + case dhcpSUBNET_MASK_OPTION_CODE : + + if( ucLength == sizeof( uint32_t ) ) + { + xNetworkAddressing.ulNetMask = ulParameter; + } + break; + + case dhcpGATEWAY_OPTION_CODE : + + if( ucLength == sizeof( uint32_t ) ) + { + /* ulProcessed is not incremented in this case + because the gateway is not essential. */ + xNetworkAddressing.ulGatewayAddress = ulParameter; + } + break; + + case dhcpDNS_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. */ + xNetworkAddressing.ulDNSServerAddress = ulParameter; + break; + + case dhcpSERVER_IP_ADDRESS_OPTION_CODE : + + if( ucLength == sizeof( uint32_t ) ) + { + if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER ) + { + /* Offers state the replying server. */ + ulProcessed++; + xDHCPData.ulDHCPServerAddress = ulParameter; + } + else + { + /* The ack must come from the expected server. */ + if( xDHCPData.ulDHCPServerAddress == ulParameter ) + { + ulProcessed++; + } + } + } + break; + + case dhcpLEASE_TIME_OPTION_CODE : + + if( ucLength == sizeof( xDHCPData.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. */ + xDHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter ); + + /* Divide the lease time by two to ensure a + renew request is sent before the lease actually + expires. */ + xDHCPData.ulLeaseTime >>= 1UL; + + /* Multiply with configTICK_RATE_HZ to get clock + ticks. */ + xDHCPData.ulLeaseTime = configTICK_RATE_HZ * xDHCPData.ulLeaseTime; + } + break; + + default : + + /* Not interested in this field. */ + + break; + } + + /* Jump over the data to find the next option code. */ + if( ucLength == 0u ) + { + break; + } + else + { + pucByte += ucLength; + } + } + + /* Were all the mandatory options received? */ + if( ulProcessed >= ulMandatoryOptions ) + { + /* HT:endian: used to be network order */ + xDHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; + FreeRTOS_printf( ( "vDHCPProcess: offer %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); + xReturn = pdPASS; + } + } + } + + FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload ); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize ) +{ +DHCPMessage_t *pxDHCPMessage; +size_t xRequiredBufferSize = sizeof( DHCPMessage_t ) + *pxOptionsArraySize; +uint8_t *pucUDPPayloadBuffer; + +#if( ipconfigDHCP_REGISTER_HOSTNAME == 1 ) + const char *pucHostName = pcApplicationHostnameHook (); + size_t xNameLength = strlen( pucHostName ); + uint8_t *pucPtr; + + xRequiredBufferSize += ( 2 + xNameLength ); +#endif + + /* Get a buffer. This uses a maximum delay, but the delay will be capped + to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value still needs to + be test. */ + do + { + } while( ( pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xRequiredBufferSize, portMAX_DELAY ) ) == NULL ); + + pxDHCPMessage = ( DHCPMessage_t * ) pucUDPPayloadBuffer; + + /* Most fields need to be zero. */ + memset( ( void * ) pxDHCPMessage, 0x00, sizeof( DHCPMessage_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( xDHCPData.ulTransactionId ); + pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE; + if( xDHCPData.xUseBroadcast != pdFALSE ) + { + pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST; + } + else + { + pxDHCPMessage->usFlags = 0u; + } + + memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); + + /* Copy in the const part of the options options. */ + memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) 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 - 1 ) ] ); + pucPtr[ 0 ] = dhcpDNS_HOSTNAME_OPTIONS_CODE; + pucPtr[ 1 ] = ( uint8_t ) xNameLength; + memcpy( ( void *) ( pucPtr + 2 ), pucHostName, xNameLength ); + pucPtr[ 2 + xNameLength ] = dhcpOPTION_END_BYTE; + *pxOptionsArraySize += ( 2 + xNameLength ); + } + #endif + + /* Map in the client identifier. */ + memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), + ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ); + + /* Set the addressing. */ + pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS; + pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT; + + return pucUDPPayloadBuffer; +} +/*-----------------------------------------------------------*/ + +static void prvSendDHCPRequest( void ) +{ +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. */ + dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */ + dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */ + dhcpREQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */ + dhcpSERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */ + dhcpOPTION_END_BYTE +}; +size_t xOptionsLength = sizeof( ucDHCPRequestOptions ); + + pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPRequestOptions, &xOptionsLength ); + + /* Copy in the IP address being requested. */ + memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ), + ( void * ) &( xDHCPData.ulOfferedIPAddress ), sizeof( xDHCPData.ulOfferedIPAddress ) ); + + /* Copy in the address of the DHCP server being used. */ + memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ), + ( void * ) &( xDHCPData.ulDHCPServerAddress ), sizeof( xDHCPData.ulDHCPServerAddress ) ); + + FreeRTOS_debug_printf( ( "vDHCPProcess: reply %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) ); + iptraceSENDING_DHCP_REQUEST(); + + /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */ + if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 ) + { + /* The packet was not successfully queued for sending and must be + returned to the stack. */ + FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); + } +} +/*-----------------------------------------------------------*/ + +static void prvSendDHCPDiscover( void ) +{ +uint8_t *pucUDPPayloadBuffer; +struct freertos_sockaddr xAddress; +static const uint8_t ucDHCPDiscoverOptions[] = +{ + /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */ + dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */ + dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */ + dhcpPARAMETER_REQUEST_OPTION_CODE, 3, dhcpSUBNET_MASK_OPTION_CODE, dhcpGATEWAY_OPTION_CODE, dhcpDNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ + dhcpOPTION_END_BYTE +}; +size_t xOptionsLength = sizeof( ucDHCPDiscoverOptions ); + + pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, &xOptionsLength ); + + FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) ); + iptraceSENDING_DHCP_DISCOVER(); + + /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */ + if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 ) + { + /* The packet was not successfully queued for sending and must be + returned to the stack. */ + FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer ); + } +} +/*-----------------------------------------------------------*/ + +#if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 ) + + static void prvPrepareLinkLayerIPLookUp( void ) + { + 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. */ + xDHCPData.xDHCPTxTime = xTaskGetTickCount(); + + xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); + xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) ); + ucLinkLayerIPAddress[ 0 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 0 ] % 0xFDu ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */ + ucLinkLayerIPAddress[ 1 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 1 ] % 0xFDu ); /* get value 1..254 for IP-address 4th byte of IP address to try. */ + + xNetworkAddressing.ulGatewayAddress = FreeRTOS_htonl( 0xA9FE0203 ); + + /* prepare xDHCPData with data to test. */ + xDHCPData.ulOfferedIPAddress = + FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] ); + + xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */ + + xNetworkAddressing.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. */ + *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress; + + /* Setting the 'local' broadcast address, something like 192.168.1.255' */ + xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask; + + /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */ + if( xDHCPData.xDHCPSocket != NULL ) + { + /* Close socket to ensure packets don't queue on it. */ + vSocketClose( xDHCPData.xDHCPSocket ); + xDHCPData.xDHCPSocket = NULL; + } + + xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) ); + xDHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000ul + ( ulNumbers[ 0 ] & 0x3ffuL ) ); /* 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 */ + +