You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
729 lines
25 KiB
C
729 lines
25 KiB
C
/*
|
|
* 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 */
|
|
|
|
|