/* | |
* FreeRTOS+UDP V1.0.4 | |
* 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://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* Standard includes. */ | |
#include <stdint.h> | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "queue.h" | |
#include "timers.h" | |
/* FreeRTOS+UDP includes. */ | |
#include "FreeRTOS_UDP_IP.h" | |
#include "FreeRTOS_IP_Private.h" | |
#include "FreeRTOS_DHCP.h" | |
#include "FreeRTOS_Sockets.h" | |
#include "NetworkInterface.h" | |
#include "IPTraceMacroDefaults.h" | |
/* Exclude the entire file if DHCP is not enabled. */ | |
#if ipconfigUSE_DHCP != 0 | |
#if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586 ) | |
/* 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 (588 if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS is set to 1) | |
#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. Windows simulator times are much shorter because simulated | |
time is not real time. */ | |
#ifdef _WINDOWS_ | |
#define dhcpINITIAL_DHCP_TX_PERIOD ( 100 / portTICK_RATE_MS ) | |
#define dhcpINITIAL_TIMER_PERIOD ( 10 / portTICK_RATE_MS ) | |
#define dhcpMAX_TIME_TO_WAIT_FOR_ACK ( 200 / portTICK_RATE_MS ) | |
#else | |
#define dhcpINITIAL_DHCP_TX_PERIOD ( 5000 / portTICK_RATE_MS ) | |
#define dhcpINITIAL_TIMER_PERIOD ( 250 / portTICK_RATE_MS ) | |
#define dhcpMAX_TIME_TO_WAIT_FOR_ACK ( 5000 / portTICK_RATE_MS ) | |
#endif /* _WINDOWS_ */ | |
/* Codes of interest found in the DHCP options field. */ | |
#define dhcpSUBNET_MASK_OPTION_CODE ( 1 ) | |
#define dhcpGATEWAY_OPTION_CODE ( 3 ) | |
#define hdcpDNS_SERVER_OPTIONS_CODE ( 6 ) | |
#define dhcpMESSAGE_TYPE_OPTION_CODE ( 53 ) | |
#define dhcpLEASE_TIME_OPTION_CODE ( 51 ) | |
#define dhcpCLIENT_IDENTIFIER_OPTION_CODE ( 61 ) | |
#define dhcpPARAMETER_REQUEST_OPTION_CODE ( 55 ) | |
#define dhcpREQUEST_IP_ADDRESS_OPTION_CODE ( 50 ) | |
#define dhcpSERVER_IP_ADDRESS_OPTION_CODE ( 54 ) | |
/* 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. */ | |
#define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL * 1000UL ) / portTICK_RATE_MS ) /* 48 hours in ticks. */ | |
/* Don't allow the lease time to be too short. */ | |
#define dhcpMINIMUM_LEASE_TIME ( 60000UL / portTICK_RATE_MS ) /* 60 seconds in ticks. */ | |
/* Marks the end of the variable length options field in the DHCP packet. */ | |
#define dhcpOPTION_END_BYTE 0xff | |
/* Offset into a DHCP message at which the first byte of the options is | |
located. */ | |
#define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0 ) | |
/* When walking the variable length options field, the following value is used | |
to ensure the walk has not gone past the end of the valid options. 2 bytes is | |
made up of the length byte, and minimum one byte value. */ | |
#define dhcpMAX_OPTION_LENGTH_OF_INTEREST ( 2L ) | |
/* Standard DHCP port numbers and magic cookie value. */ | |
#if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN ) | |
#define dhcpCLIENT_PORT 0x4400 | |
#define dhcpSERVER_PORT 0x4300 | |
#define dhcpCOOKIE 0x63538263 | |
#define dhcpBROADCAST 0x0080 | |
#else | |
#define dhcpCLIENT_PORT 0x0044 | |
#define dhcpSERVER_PORT 0x0043 | |
#define dhcpCOOKIE 0x63825363 | |
#define dhcpBROADCAST 0x8000 | |
#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 xDHCPMessage_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. */ | |
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; | |
/* | |
* Generate a DHCP discover message and send it on the DHCP socket. | |
*/ | |
static void prvSendDHCPDiscover( xMACAddress_t *pxMACAddress ); | |
/* | |
* Interpret message received on the DHCP socket. | |
*/ | |
static BaseType_t prvProcessDHCPReplies( uint8_t ucExpectedMessageType, xMACAddress_t *pxMACAddress, xNetworkAddressingParameters_t *pxNetworkAddressing ); | |
/* | |
* Generate a DHCP request packet, and send it on the DHCP socket. | |
*/ | |
static void prvSendDHCPRequest( xMACAddress_t *pxMACAddress ); | |
/* | |
* 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, xMACAddress_t *pxMACAddress, uint8_t ucOpcode, const uint8_t * const pucOptionsArray, size_t xOptionsArraySize ); | |
/* | |
* Create the DHCP socket, if it has not been created already. | |
*/ | |
static void prvCreateDHCPSocket( void ); | |
/*-----------------------------------------------------------*/ | |
/* The timer used to drive the DHCP state machine. */ | |
static xTimerHandle xDHCPTimer = NULL; | |
/* The UDP socket used for all incoming and outgoing DHCP traffic. */ | |
static xSocket_t xDHCPSocket = NULL; | |
/* Hold information in between steps in the DHCP state machine. */ | |
static uint32_t ulTransactionId = 0UL, ulOfferedIPAddress = 0UL, ulDHCPServerAddress = 0UL, ulLeaseTime = 0; | |
/* Hold information on the current timer state. */ | |
static TickType_t xDHCPTxTime = 0x00, xDHCPTxPeriod = 0x00; | |
/* Maintains the DHCP state machine state. */ | |
static eDHCPState_t eDHCPState = eWaitingSendFirstDiscover; | |
/*-----------------------------------------------------------*/ | |
void vDHCPProcess( BaseType_t xReset, xMACAddress_t *pxMACAddress, uint32_t *pulIPAddress, xNetworkAddressingParameters_t *pxNetworkAddressing ) | |
{ | |
if( xReset != pdFALSE ) | |
{ | |
eDHCPState = eWaitingSendFirstDiscover; | |
} | |
switch( eDHCPState ) | |
{ | |
case eWaitingSendFirstDiscover : | |
/* Initial state. Create the DHCP socket, timer, etc. if they | |
have not already been created. */ | |
prvInitialiseDHCP(); | |
*pulIPAddress = 0UL; | |
/* Send the first discover request. */ | |
if( xDHCPSocket != NULL ) | |
{ | |
xDHCPTxTime = xTaskGetTickCount(); | |
prvSendDHCPDiscover( pxMACAddress ); | |
eDHCPState = eWaitingOffer; | |
} | |
break; | |
case eWaitingOffer : | |
/* Look for offers coming in. */ | |
if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxMACAddress, pxNetworkAddressing ) == pdPASS ) | |
{ | |
/* An offer has been made, generate the request. */ | |
xDHCPTxTime = xTaskGetTickCount(); | |
xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; | |
prvSendDHCPRequest( pxMACAddress ); | |
eDHCPState = eWaitingAcknowledge; | |
} | |
else | |
{ | |
/* Is it time to send another Discover? */ | |
if( ( xTaskGetTickCount() - xDHCPTxTime ) > xDHCPTxPeriod ) | |
{ | |
/* Increase the time period, and if it has not got to the | |
point of giving up - send another discovery. */ | |
xDHCPTxPeriod <<= 1; | |
if( xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) | |
{ | |
ulTransactionId++; | |
xDHCPTxTime = xTaskGetTickCount(); | |
prvSendDHCPDiscover( pxMACAddress ); | |
} | |
else | |
{ | |
/* Revert to static IP address. */ | |
taskENTER_CRITICAL(); | |
{ | |
*pulIPAddress = pxNetworkAddressing->ulDefaultIPAddress; | |
iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( pxNetworkAddressing->ulDefaultIPAddress ); | |
} | |
taskEXIT_CRITICAL(); | |
eDHCPState = eNotUsingLeasedAddress; | |
xTimerStop( xDHCPTimer, ( TickType_t ) 0 ); | |
#if ipconfigUSE_NETWORK_EVENT_HOOK == 1 | |
{ | |
vApplicationIPNetworkEventHook( eNetworkUp ); | |
} | |
#endif | |
/* Static configuration is being used, so the network is now up. */ | |
#if ipconfigFREERTOS_PLUS_NABTO == 1 | |
{ | |
/* Return value is used in configASSERT() inside the | |
function. */ | |
( void ) xStartNabtoTask(); | |
} | |
#endif /* ipconfigFREERTOS_PLUS_NABTO */ | |
/* Close socket to ensure packets don't queue on it. */ | |
FreeRTOS_closesocket( xDHCPSocket ); | |
xDHCPSocket = NULL; | |
} | |
} | |
} | |
break; | |
case eWaitingAcknowledge : | |
/* Look for acks coming in. */ | |
if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK, pxMACAddress, pxNetworkAddressing ) == pdPASS ) | |
{ | |
/* DHCP completed. The IP address can now be used, and the | |
timer set to the lease timeout time. */ | |
*pulIPAddress = ulOfferedIPAddress; | |
eDHCPState = eLeasedAddress; | |
#if ipconfigUSE_NETWORK_EVENT_HOOK == 1 | |
{ | |
vApplicationIPNetworkEventHook( eNetworkUp ); | |
} | |
#endif | |
/* Static configuration is being used, so the network is now | |
up. */ | |
#if ipconfigFREERTOS_PLUS_NABTO == 1 | |
{ | |
/* Return value is used in configASSERT() inside the | |
function. */ | |
( void ) xStartNabtoTask(); | |
} | |
#endif /* ipconfigFREERTOS_PLUS_NABTO */ | |
/* Close socket to ensure packets don't queue on it. */ | |
FreeRTOS_closesocket( xDHCPSocket ); | |
xDHCPSocket = NULL; | |
if( ulLeaseTime == 0UL ) | |
{ | |
ulLeaseTime = dhcpDEFAULT_LEASE_TIME; | |
} | |
else if( ulLeaseTime < dhcpMINIMUM_LEASE_TIME ) | |
{ | |
ulLeaseTime = dhcpMINIMUM_LEASE_TIME; | |
} | |
else | |
{ | |
/* The lease time is already valid. */ | |
} | |
xTimerChangePeriod( xDHCPTimer, ulLeaseTime, portMAX_DELAY ); | |
} | |
else | |
{ | |
/* Is it time to send another Discover? */ | |
if( ( xTaskGetTickCount() - xDHCPTxTime ) > xDHCPTxPeriod ) | |
{ | |
/* Increase the time period, and if it has not got to the | |
point of giving up - send another request. */ | |
xDHCPTxPeriod <<= 1; | |
if( xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) | |
{ | |
xDHCPTxTime = xTaskGetTickCount(); | |
prvSendDHCPRequest( pxMACAddress ); | |
} | |
else | |
{ | |
/* Give up, start again. */ | |
eDHCPState = eWaitingSendFirstDiscover; | |
} | |
} | |
} | |
break; | |
case eLeasedAddress : | |
/* Resend the request at the appropriate time to renew the lease. */ | |
prvCreateDHCPSocket(); | |
if( xDHCPSocket != NULL ) | |
{ | |
xDHCPTxTime = xTaskGetTickCount(); | |
xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; | |
prvSendDHCPRequest( pxMACAddress ); | |
eDHCPState = eWaitingAcknowledge; | |
} | |
xTimerChangePeriod( xDHCPTimer, dhcpINITIAL_TIMER_PERIOD, portMAX_DELAY ); | |
break; | |
case eNotUsingLeasedAddress: | |
xTimerStop( xDHCPTimer, ( TickType_t ) 0 ); | |
break; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvCreateDHCPSocket( void ) | |
{ | |
struct freertos_sockaddr xAddress; | |
BaseType_t xReturn; | |
TickType_t xTimeoutTime = 0; | |
/* Create the socket, if it has not already been created. */ | |
if( xDHCPSocket == NULL ) | |
{ | |
xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP ); | |
configASSERT( ( 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( xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); | |
FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) ); | |
/* Bind to the standard DHCP client port. */ | |
xAddress.sin_port = dhcpCLIENT_PORT; | |
xReturn = FreeRTOS_bind( xDHCPSocket, &xAddress, sizeof( xAddress ) ); | |
configASSERT( xReturn == 0 ); | |
/* Remove compiler warnings if configASSERT() is not defined. */ | |
( void ) xReturn; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvInitialiseDHCP( void ) | |
{ | |
extern void vIPFunctionsTimerCallback( xTimerHandle xTimer ); | |
/* Initialise the parameters that will be set by the DHCP process. */ | |
if( ulTransactionId == 0 ) | |
{ | |
ulTransactionId = ipconfigRAND32(); | |
} | |
else | |
{ | |
ulTransactionId++; | |
} | |
ulOfferedIPAddress = 0UL; | |
ulDHCPServerAddress = 0UL; | |
xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD; | |
/* Create the DHCP socket if it has not already been created. */ | |
prvCreateDHCPSocket(); | |
if( xDHCPTimer == NULL ) | |
{ | |
xDHCPTimer = xTimerCreate( "DHCP", dhcpINITIAL_TIMER_PERIOD, pdTRUE, ( void * ) eDHCPEvent, vIPFunctionsTimerCallback ); | |
configASSERT( xDHCPTimer ); | |
xTimerStart( xDHCPTimer, portMAX_DELAY ); | |
} | |
else | |
{ | |
xTimerChangePeriod( xDHCPTimer, dhcpINITIAL_TIMER_PERIOD, portMAX_DELAY ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static BaseType_t prvProcessDHCPReplies( uint8_t ucExpectedMessageType, xMACAddress_t *pxMACAddress, xNetworkAddressingParameters_t *pxNetworkAddressing ) | |
{ | |
uint8_t *pucUDPPayload, *pucLastByte; | |
struct freertos_sockaddr xClient; | |
uint32_t xClientLength = sizeof( xClient ); | |
int32_t lBytes; | |
xDHCPMessage_t *pxDHCPMessage; | |
uint8_t *pucByte, ucOptionCode, ucLength; | |
uint32_t ulProcessed; | |
BaseType_t xReturn = pdFALSE; | |
const uint32_t ulMandatoryOptions = 2; /* DHCP server address, and the correct DHCP message type must be present in the options. */ | |
lBytes = FreeRTOS_recvfrom( xDHCPSocket, ( void * ) &pucUDPPayload, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength ); | |
if( lBytes > 0 ) | |
{ | |
/* Map a DHCP structure onto the received data. */ | |
pxDHCPMessage = ( xDHCPMessage_t * ) ( pucUDPPayload ); | |
/* Sanity check. */ | |
if( ( pxDHCPMessage->ulDHCPCookie == dhcpCOOKIE ) && ( pxDHCPMessage->ucOpcode == dhcpREPLY_OPCODE ) && ( pxDHCPMessage->ulTransactionID == ulTransactionId ) ) | |
{ | |
if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) ) == 0 ) | |
{ | |
/* None of the essential options have been processed yet. */ | |
ulProcessed = 0; | |
/* 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 ); | |
pucLastByte = &( pucUDPPayload[ lBytes - dhcpMAX_OPTION_LENGTH_OF_INTEREST ] ); | |
while( ( *pucByte != dhcpOPTION_END_BYTE ) && ( pucByte < pucLastByte ) ) | |
{ | |
ucOptionCode = *pucByte; | |
pucByte++; | |
ucLength = *pucByte; | |
pucByte++; | |
switch( ucOptionCode ) | |
{ | |
case dhcpMESSAGE_TYPE_OPTION_CODE : | |
if( *pucByte == ucExpectedMessageType ) | |
{ | |
/* The message type is the message type the | |
state machine is expecting. */ | |
ulProcessed++; | |
} | |
else if( *pucByte == dhcpMESSAGE_TYPE_NACK ) | |
{ | |
if( ucExpectedMessageType == dhcpMESSAGE_TYPE_ACK ) | |
{ | |
/* Start again. */ | |
eDHCPState = eWaitingSendFirstDiscover; | |
} | |
} | |
else | |
{ | |
/* Don't process other message types. */ | |
} | |
break; | |
case dhcpSUBNET_MASK_OPTION_CODE : | |
if( ucLength == sizeof( uint32_t ) ) | |
{ | |
memcpy( ( void * ) &( pxNetworkAddressing->ulNetMask ), ( void * ) pucByte, ( size_t ) ucLength ); | |
} | |
break; | |
case dhcpGATEWAY_OPTION_CODE : | |
if( ucLength == sizeof( uint32_t ) ) | |
{ | |
/* ulProcessed is not incremented in this case | |
because the gateway is not essential. */ | |
memcpy( ( void * ) &( pxNetworkAddressing->ulGatewayAddress ), ( void * ) pucByte, ( size_t ) ucLength ); | |
} | |
break; | |
case hdcpDNS_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. */ | |
memcpy( ( void * ) &( pxNetworkAddressing->ulDNSServerAddress ), ( void * ) pucByte, sizeof( uint32_t ) ); | |
break; | |
case dhcpSERVER_IP_ADDRESS_OPTION_CODE : | |
if( ucLength == sizeof( uint32_t ) ) | |
{ | |
if( ucExpectedMessageType == dhcpMESSAGE_TYPE_OFFER ) | |
{ | |
/* Offers state the replying server. */ | |
ulProcessed++; | |
memcpy( ( void * ) &ulDHCPServerAddress, ( void * ) pucByte, ( size_t ) ucLength ); | |
} | |
else | |
{ | |
/* The ack must come from the expected server. */ | |
if( memcmp( ( void * ) &ulDHCPServerAddress, ( void * ) pucByte, ( size_t ) ucLength ) == 0 ) | |
{ | |
ulProcessed++; | |
} | |
} | |
} | |
break; | |
case dhcpLEASE_TIME_OPTION_CODE : | |
if( ucLength == sizeof( &ulLeaseTime ) ) | |
{ | |
/* ulProcessed is not incremented in this case | |
because the lease time is not essential. */ | |
memcpy( ( void * ) &ulLeaseTime, ( void * ) pucByte, ( size_t ) ucLength ); | |
ulLeaseTime = FreeRTOS_ntohl( ulLeaseTime ); | |
/* Convert the lease time to milliseconds | |
(*1000) then ticks (/portTICK_RATE_MS). */ | |
ulLeaseTime *= ( 1000UL / portTICK_RATE_MS ); | |
/* Divide the lease time to ensure a renew | |
request is sent before the lease actually | |
expires. */ | |
ulLeaseTime >>= 1UL; | |
} | |
break; | |
default : | |
/* Not interested in this field. */ | |
break; | |
} | |
/* Jump over the data to find the next option code. */ | |
if( ucLength == 0 ) | |
{ | |
break; | |
} | |
else | |
{ | |
pucByte += ucLength; | |
} | |
} | |
/* Were all the mandatory options received? */ | |
if( ulProcessed == ulMandatoryOptions ) | |
{ | |
ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr; | |
xReturn = pdPASS; | |
} | |
} | |
} | |
FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload ); | |
} | |
return xReturn; | |
} | |
/*-----------------------------------------------------------*/ | |
static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, xMACAddress_t *pxMACAddress, uint8_t ucOpcode, const uint8_t * const pucOptionsArray, size_t xOptionsArraySize ) | |
{ | |
xDHCPMessage_t *pxDHCPMessage; | |
const size_t xRequiredBufferSize = sizeof( xDHCPMessage_t ) + xOptionsArraySize; | |
uint8_t *pucUDPPayloadBuffer; | |
static uint8_t ucUseBroadcastFlag = pdFALSE; | |
/* Get a buffer. This uses a maximum delay, but the delay will be capped | |
to ipconfigMAX_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 = ( xDHCPMessage_t * ) pucUDPPayloadBuffer; | |
/* Most fields need to be zero. */ | |
memset( ( void * ) pxDHCPMessage, 0x00, sizeof( xDHCPMessage_t ) ); | |
/* Create the message. */ | |
pxDHCPMessage->ucOpcode = ucOpcode; | |
pxDHCPMessage->ucAddressType = dhcpADDRESS_TYPE_ETHERNET; | |
pxDHCPMessage->ucAddressLength = dhcpETHERNET_ADDRESS_LENGTH; | |
pxDHCPMessage->ulTransactionID = ulTransactionId; | |
pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE; | |
/* For maximum possibility of success, alternate between broadcast and non | |
broadcast. */ | |
ucUseBroadcastFlag = !ucUseBroadcastFlag; | |
if( ucUseBroadcastFlag == pdTRUE ) | |
{ | |
pxDHCPMessage->usFlags = dhcpBROADCAST; | |
} | |
else | |
{ | |
pxDHCPMessage->usFlags = 0; | |
} | |
memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) ); | |
/* Copy in the const part of the options options. */ | |
memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) pucOptionsArray, xOptionsArraySize ); | |
/* Map in the client identifier. */ | |
memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) ); | |
/* Set the addressing. */ | |
pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS; | |
pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT; | |
return pucUDPPayloadBuffer; | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvSendDHCPRequest( xMACAddress_t *pxMACAddress ) | |
{ | |
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 | |
}; | |
pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, pxMACAddress, dhcpREQUEST_OPCODE, ucDHCPRequestOptions, sizeof( ucDHCPRequestOptions ) ); | |
/* Copy in the IP address being requested. */ | |
memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ), ( void * ) &ulOfferedIPAddress, sizeof( ulOfferedIPAddress ) ); | |
/* Copy in the address of the DHCP server being used. */ | |
memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ), ( void * ) &ulDHCPServerAddress, sizeof( ulDHCPServerAddress ) ); | |
iptraceSENDING_DHCP_REQUEST(); | |
if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( xDHCPMessage_t ) + sizeof( ucDHCPRequestOptions ) ), 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( xMACAddress_t *pxMACAddress ) | |
{ | |
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, hdcpDNS_SERVER_OPTIONS_CODE, /* Parameter request option. */ | |
dhcpOPTION_END_BYTE | |
}; | |
pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, pxMACAddress, dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, sizeof( ucDHCPDiscoverOptions ) ); | |
iptraceSENDING_DHCP_DISCOVER(); | |
if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( xDHCPMessage_t ) + sizeof( ucDHCPDiscoverOptions ) ), 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 ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
#endif /* ipconfigUSE_DHCP != 0 */ | |