3 * Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 * http://aws.amazon.com/freertos
23 * http://www.FreeRTOS.org
26 /* Standard includes. */
29 /* FreeRTOS includes. */
34 /* FreeRTOS+TCP includes. */
35 #include "FreeRTOS_IP.h"
36 #include "FreeRTOS_Sockets.h"
37 #include "FreeRTOS_IP_Private.h"
38 #include "FreeRTOS_UDP_IP.h"
39 #include "FreeRTOS_TCP_IP.h"
40 #include "FreeRTOS_DHCP.h"
41 #include "FreeRTOS_ARP.h"
42 #include "NetworkInterface.h"
43 #include "NetworkBufferManagement.h"
45 /* Exclude the entire file if DHCP is not enabled. */
46 #if( ipconfigUSE_DHCP != 0 )
48 #if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586u )
49 /* DHCP must be able to receive an options field of 312 bytes, the fixed
50 part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */
51 #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP
54 /* Parameter widths in the DHCP packet. */
55 #define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH 16
56 #define dhcpSERVER_HOST_NAME_LENGTH 64
57 #define dhcpBOOT_FILE_NAME_LENGTH 128
59 /* Timer parameters */
60 #ifndef dhcpINITIAL_DHCP_TX_PERIOD
61 #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250 ) )
62 #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000 ) )
65 /* Codes of interest found in the DHCP options field. */
66 #define dhcpZERO_PAD_OPTION_CODE ( 0u )
67 #define dhcpSUBNET_MASK_OPTION_CODE ( 1u )
68 #define dhcpGATEWAY_OPTION_CODE ( 3u )
69 #define dhcpDNS_SERVER_OPTIONS_CODE ( 6u )
70 #define dhcpDNS_HOSTNAME_OPTIONS_CODE ( 12u )
71 #define dhcpREQUEST_IP_ADDRESS_OPTION_CODE ( 50u )
72 #define dhcpLEASE_TIME_OPTION_CODE ( 51u )
73 #define dhcpMESSAGE_TYPE_OPTION_CODE ( 53u )
74 #define dhcpSERVER_IP_ADDRESS_OPTION_CODE ( 54u )
75 #define dhcpPARAMETER_REQUEST_OPTION_CODE ( 55u )
76 #define dhcpCLIENT_IDENTIFIER_OPTION_CODE ( 61u )
78 /* The four DHCP message types of interest. */
79 #define dhcpMESSAGE_TYPE_DISCOVER ( 1 )
80 #define dhcpMESSAGE_TYPE_OFFER ( 2 )
81 #define dhcpMESSAGE_TYPE_REQUEST ( 3 )
82 #define dhcpMESSAGE_TYPE_ACK ( 5 )
83 #define dhcpMESSAGE_TYPE_NACK ( 6 )
85 /* Offsets into the transmitted DHCP options fields at which various parameters
87 #define dhcpCLIENT_IDENTIFIER_OFFSET ( 5 )
88 #define dhcpREQUESTED_IP_ADDRESS_OFFSET ( 13 )
89 #define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ( 19 )
91 /* Values used in the DHCP packets. */
92 #define dhcpREQUEST_OPCODE ( 1 )
93 #define dhcpREPLY_OPCODE ( 2 )
94 #define dhcpADDRESS_TYPE_ETHERNET ( 1 )
95 #define dhcpETHERNET_ADDRESS_LENGTH ( 6 )
97 /* If a lease time is not received, use the default of two days. */
98 /* 48 hours in ticks. Can not use pdMS_TO_TICKS() as integer overflow can occur. */
99 #define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL ) * configTICK_RATE_HZ )
101 /* Don't allow the lease time to be too short. */
102 #define dhcpMINIMUM_LEASE_TIME ( pdMS_TO_TICKS( 60000UL ) ) /* 60 seconds in ticks. */
104 /* Marks the end of the variable length options field in the DHCP packet. */
105 #define dhcpOPTION_END_BYTE 0xffu
107 /* Offset into a DHCP message at which the first byte of the options is
109 #define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0 )
111 /* Standard DHCP port numbers and magic cookie value. */
112 #if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN )
113 #define dhcpCLIENT_PORT 0x4400u
114 #define dhcpSERVER_PORT 0x4300u
115 #define dhcpCOOKIE 0x63538263ul
116 #define dhcpBROADCAST 0x0080u
118 #define dhcpCLIENT_PORT 0x0044u
119 #define dhcpSERVER_PORT 0x0043u
120 #define dhcpCOOKIE 0x63825363ul
121 #define dhcpBROADCAST 0x8000u
122 #endif /* ipconfigBYTE_ORDER */
124 #include "pack_struct_start.h"
128 uint8_t ucAddressType;
129 uint8_t ucAddressLength;
131 uint32_t ulTransactionID;
132 uint16_t usElapsedTime;
134 uint32_t ulClientIPAddress_ciaddr;
135 uint32_t ulYourIPAddress_yiaddr;
136 uint32_t ulServerIPAddress_siaddr;
137 uint32_t ulRelayAgentIPAddress_giaddr;
138 uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ];
139 uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ];
140 uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ];
141 uint32_t ulDHCPCookie;
142 uint8_t ucFirstOptionByte;
144 #include "pack_struct_end.h"
145 typedef struct xDHCPMessage DHCPMessage_t;
147 /* DHCP state machine states. */
150 eWaitingSendFirstDiscover = 0, /* Initial state. Send a discover the first time it is called, and reset all timers. */
151 eWaitingOffer, /* Either resend the discover, or, if the offer is forthcoming, send a request. */
152 eWaitingAcknowledge, /* Either resend the request. */
153 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
154 eGetLinkLayerAddress, /* When DHCP didn't respond, try to obtain a LinkLayer address 168.254.x.x. */
156 eLeasedAddress, /* Resend the request at the appropriate time to renew the lease. */
157 eNotUsingLeasedAddress /* DHCP failed, and a default IP address is being used. */
160 /* Hold information in between steps in the DHCP state machine. */
163 uint32_t ulTransactionId;
164 uint32_t ulOfferedIPAddress;
165 uint32_t ulDHCPServerAddress;
166 uint32_t ulLeaseTime;
167 /* Hold information on the current timer state. */
168 TickType_t xDHCPTxTime;
169 TickType_t xDHCPTxPeriod;
170 /* Try both without and with the broadcast flag */
171 BaseType_t xUseBroadcast;
172 /* Maintains the DHCP state machine state. */
173 eDHCPState_t eDHCPState;
174 /* The UDP socket used for all incoming and outgoing DHCP traffic. */
175 Socket_t xDHCPSocket;
178 typedef struct xDHCP_DATA DHCPData_t;
180 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
181 /* Define the Link Layer IP address: 169.254.x.x */
182 #define LINK_LAYER_ADDRESS_0 169
183 #define LINK_LAYER_ADDRESS_1 254
185 /* Define the netmask used: 255.255.0.0 */
186 #define LINK_LAYER_NETMASK_0 255
187 #define LINK_LAYER_NETMASK_1 255
188 #define LINK_LAYER_NETMASK_2 0
189 #define LINK_LAYER_NETMASK_3 0
194 * Generate a DHCP discover message and send it on the DHCP socket.
196 static void prvSendDHCPDiscover( void );
199 * Interpret message received on the DHCP socket.
201 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType );
204 * Generate a DHCP request packet, and send it on the DHCP socket.
206 static void prvSendDHCPRequest( void );
209 * Prepare to start a DHCP transaction. This initialises some state variables
210 * and creates the DHCP socket if necessary.
212 static void prvInitialiseDHCP( void );
215 * Creates the part of outgoing DHCP messages that are common to all outgoing
218 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize );
221 * Create the DHCP socket, if it has not been created already.
223 static void prvCreateDHCPSocket( void );
226 * After DHCP has failed to answer, prepare everything to start searching
227 * for (trying-out) LinkLayer IP-addresses, using the random method: Send
228 * a gratuitous ARP request and wait if another device responds to it.
230 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
231 static void prvPrepareLinkLayerIPLookUp( void );
234 /*-----------------------------------------------------------*/
236 /* The next DHCP transaction Id to be used. */
237 static DHCPData_t xDHCPData;
239 /*-----------------------------------------------------------*/
241 BaseType_t xIsDHCPSocket( Socket_t xSocket )
245 if( xDHCPData.xDHCPSocket == xSocket )
256 /*-----------------------------------------------------------*/
258 void vDHCPProcess( BaseType_t xReset )
260 BaseType_t xGivingUp = pdFALSE;
261 #if( ipconfigUSE_DHCP_HOOK != 0 )
262 eDHCPCallbackAnswer_t eAnswer;
263 #endif /* ipconfigUSE_DHCP_HOOK */
265 /* Is DHCP starting over? */
266 if( xReset != pdFALSE )
268 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
271 switch( xDHCPData.eDHCPState )
273 case eWaitingSendFirstDiscover :
274 /* Ask the user if a DHCP discovery is required. */
275 #if( ipconfigUSE_DHCP_HOOK != 0 )
276 eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress );
277 if( eAnswer == eDHCPContinue )
278 #endif /* ipconfigUSE_DHCP_HOOK */
280 /* Initial state. Create the DHCP socket, timer, etc. if they
281 have not already been created. */
284 /* See if prvInitialiseDHCP() has creates a socket. */
285 if( xDHCPData.xDHCPSocket == NULL )
291 *ipLOCAL_IP_ADDRESS_POINTER = 0UL;
293 /* Send the first discover request. */
294 if( xDHCPData.xDHCPSocket != NULL )
296 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
297 prvSendDHCPDiscover( );
298 xDHCPData.eDHCPState = eWaitingOffer;
301 #if( ipconfigUSE_DHCP_HOOK != 0 )
304 if( eAnswer == eDHCPUseDefaults )
306 memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );
309 /* The user indicates that the DHCP process does not continue. */
312 #endif /* ipconfigUSE_DHCP_HOOK */
319 /* Look for offers coming in. */
320 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS )
322 #if( ipconfigUSE_DHCP_HOOK != 0 )
323 /* Ask the user if a DHCP request is required. */
324 eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, xDHCPData.ulOfferedIPAddress );
326 if( eAnswer == eDHCPContinue )
327 #endif /* ipconfigUSE_DHCP_HOOK */
329 /* An offer has been made, the user wants to continue,
330 generate the request. */
331 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
332 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
333 prvSendDHCPRequest( );
334 xDHCPData.eDHCPState = eWaitingAcknowledge;
338 #if( ipconfigUSE_DHCP_HOOK != 0 )
339 if( eAnswer == eDHCPUseDefaults )
341 memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );
344 /* The user indicates that the DHCP process does not continue. */
346 #endif /* ipconfigUSE_DHCP_HOOK */
348 else if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
350 /* It is time to send another Discover. Increase the time
351 period, and if it has not got to the point of giving up - send
352 another discovery. */
353 xDHCPData.xDHCPTxPeriod <<= 1;
355 if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
357 if( xApplicationGetRandomNumber( &( xDHCPData.ulTransactionId ) ) != pdFALSE )
359 xDHCPData.xDHCPTxTime = xTaskGetTickCount( );
360 xDHCPData.xUseBroadcast = !xDHCPData.xUseBroadcast;
361 prvSendDHCPDiscover( );
362 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", xDHCPData.xDHCPTxPeriod ) );
366 FreeRTOS_debug_printf( ( "vDHCPProcess: failed to generate a random Transaction ID\n" ) );
371 FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", xDHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) );
373 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
375 /* Only use a fake Ack if the default IP address == 0x00
376 and the link local addressing is used. Start searching
377 a free LinkLayer IP-address. Next state will be
378 'eGetLinkLayerAddress'. */
379 prvPrepareLinkLayerIPLookUp();
381 /* Setting an IP address manually so set to not using
382 leased address mode. */
383 xDHCPData.eDHCPState = eGetLinkLayerAddress;
389 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
394 case eWaitingAcknowledge :
396 /* Look for acks coming in. */
397 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS )
399 FreeRTOS_debug_printf( ( "vDHCPProcess: acked %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
401 /* DHCP completed. The IP address can now be used, and the
402 timer set to the lease timeout time. */
403 *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress;
405 /* Setting the 'local' broadcast address, something like
407 xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask;
408 xDHCPData.eDHCPState = eLeasedAddress;
410 iptraceDHCP_SUCCEDEED( xDHCPData.ulOfferedIPAddress );
412 /* DHCP failed, the default configured IP-address will be used
413 Now call vIPNetworkUpCalls() to send the network-up event and
414 start the ARP timer. */
415 vIPNetworkUpCalls( );
417 /* Close socket to ensure packets don't queue on it. */
418 vSocketClose( xDHCPData.xDHCPSocket );
419 xDHCPData.xDHCPSocket = NULL;
421 if( xDHCPData.ulLeaseTime == 0UL )
423 xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME;
425 else if( xDHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME )
427 xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME;
431 /* The lease time is already valid. */
434 /* Check for clashes. */
435 vARPSendGratuitous();
436 vIPReloadDHCPTimer( xDHCPData.ulLeaseTime );
440 /* Is it time to send another Discover? */
441 if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
443 /* Increase the time period, and if it has not got to the
444 point of giving up - send another request. */
445 xDHCPData.xDHCPTxPeriod <<= 1;
447 if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
449 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
450 prvSendDHCPRequest( );
454 /* Give up, start again. */
455 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
461 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
462 case eGetLinkLayerAddress:
463 if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
465 if( xARPHadIPClash == pdFALSE )
467 /* ARP OK. proceed. */
468 iptraceDHCP_SUCCEDEED( xDHCPData.ulOfferedIPAddress );
470 /* Auto-IP succeeded, the default configured IP-address will
471 be used. Now call vIPNetworkUpCalls() to send the
472 network-up event and start the ARP timer. */
473 vIPNetworkUpCalls( );
474 xDHCPData.eDHCPState = eNotUsingLeasedAddress;
478 /* ARP clashed - try another IP address. */
479 prvPrepareLinkLayerIPLookUp();
481 /* Setting an IP address manually so set to not using leased
483 xDHCPData.eDHCPState = eGetLinkLayerAddress;
487 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
489 case eLeasedAddress :
491 /* Resend the request at the appropriate time to renew the lease. */
492 prvCreateDHCPSocket();
494 if( xDHCPData.xDHCPSocket != NULL )
496 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
497 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
498 prvSendDHCPRequest( );
499 xDHCPData.eDHCPState = eWaitingAcknowledge;
501 /* From now on, we should be called more often */
502 vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD );
506 case eNotUsingLeasedAddress:
508 vIPSetDHCPTimerEnableState( pdFALSE );
515 if( xGivingUp != pdFALSE )
517 /* xGivingUp became true either because of a time-out, or because
518 xApplicationDHCPHook() returned another value than 'eDHCPContinue',
519 meaning that the conversion is canceled from here. */
521 /* Revert to static IP address. */
522 taskENTER_CRITICAL();
524 *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress;
525 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress );
529 xDHCPData.eDHCPState = eNotUsingLeasedAddress;
530 vIPSetDHCPTimerEnableState( pdFALSE );
532 /* DHCP failed, the default configured IP-address will be used. Now
533 call vIPNetworkUpCalls() to send the network-up event and start the ARP
535 vIPNetworkUpCalls( );
537 /* Test if socket was indeed created. */
538 if( xDHCPData.xDHCPSocket != NULL )
540 /* Close socket to ensure packets don't queue on it. */
541 vSocketClose( xDHCPData.xDHCPSocket );
542 xDHCPData.xDHCPSocket = NULL;
546 /*-----------------------------------------------------------*/
548 static void prvCreateDHCPSocket( void )
550 struct freertos_sockaddr xAddress;
552 TickType_t xTimeoutTime = ( TickType_t ) 0;
554 /* Create the socket, if it has not already been created. */
555 if( xDHCPData.xDHCPSocket == NULL )
557 xDHCPData.xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
558 if( xDHCPData.xDHCPSocket != FREERTOS_INVALID_SOCKET )
561 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the
562 context of the IP task. */
563 FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
564 FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
566 /* Bind to the standard DHCP client port. */
567 xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT;
568 xReturn = vSocketBind( xDHCPData.xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE );
571 /* Binding failed, close the socket again. */
572 vSocketClose( xDHCPData.xDHCPSocket );
573 xDHCPData.xDHCPSocket = NULL;
578 /* Change to NULL for easier testing. */
579 xDHCPData.xDHCPSocket = NULL;
583 /*-----------------------------------------------------------*/
585 static void prvInitialiseDHCP( void )
587 /* Initialise the parameters that will be set by the DHCP process. Per
588 https://www.ietf.org/rfc/rfc2131.txt, Transaction ID should be a random
589 value chosen by the client. */
591 /* Check for random number generator API failure. */
592 if( xApplicationGetRandomNumber( &( xDHCPData.ulTransactionId ) ) != pdFALSE )
594 xDHCPData.xUseBroadcast = 0;
595 xDHCPData.ulOfferedIPAddress = 0UL;
596 xDHCPData.ulDHCPServerAddress = 0UL;
597 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
599 /* Create the DHCP socket if it has not already been created. */
600 prvCreateDHCPSocket();
601 FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) );
602 vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD );
606 /* There was a problem with the randomiser. */
609 /*-----------------------------------------------------------*/
611 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType )
613 uint8_t *pucUDPPayload, *pucLastByte;
614 struct freertos_sockaddr xClient;
615 uint32_t xClientLength = sizeof( xClient );
617 DHCPMessage_t *pxDHCPMessage;
618 uint8_t *pucByte, ucOptionCode, ucLength;
619 uint32_t ulProcessed, ulParameter;
620 BaseType_t xReturn = pdFALSE;
621 const uint32_t ulMandatoryOptions = 2ul; /* DHCP server address, and the correct DHCP message type must be present in the options. */
623 lBytes = FreeRTOS_recvfrom( xDHCPData.xDHCPSocket, ( void * ) &pucUDPPayload, 0ul, FREERTOS_ZERO_COPY, &xClient, &xClientLength );
627 /* Map a DHCP structure onto the received data. */
628 pxDHCPMessage = ( DHCPMessage_t * ) ( pucUDPPayload );
631 if( ( lBytes >= sizeof( DHCPMessage_t ) ) &&
632 ( pxDHCPMessage->ulDHCPCookie == ( uint32_t ) dhcpCOOKIE ) &&
633 ( pxDHCPMessage->ucOpcode == ( uint8_t ) dhcpREPLY_OPCODE ) &&
634 ( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( xDHCPData.ulTransactionId ) ) )
636 if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ),
637 ( void * ) ipLOCAL_MAC_ADDRESS,
638 sizeof( MACAddress_t ) ) == 0 )
640 /* None of the essential options have been processed yet. */
643 /* Walk through the options until the dhcpOPTION_END_BYTE byte
644 is found, taking care not to walk off the end of the options. */
645 pucByte = &( pxDHCPMessage->ucFirstOptionByte );
646 /* Maintain a pointer to the last valid byte (i.e. not the first
648 pucLastByte = pucUDPPayload + lBytes - 1;
650 while( pucByte <= pucLastByte )
652 ucOptionCode = pucByte[ 0 ];
653 if( ucOptionCode == dhcpOPTION_END_BYTE )
655 /* Ready, the last byte has been seen. */
658 if( ucOptionCode == dhcpZERO_PAD_OPTION_CODE )
660 /* The value zero is used as a pad byte,
661 it is not followed by a length byte. */
666 /* Stop if the response is malformed. */
667 if( pucByte < pucLastByte )
669 /* There are at least two bytes left. */
670 ucLength = pucByte[ 1 ];
673 if( pucByte + ucLength > pucLastByte )
683 /* In most cases, a 4-byte network-endian parameter follows,
684 just get it once here and use later. */
685 if( ucLength >= sizeof( ulParameter ) )
687 memcpy( ( void * ) &( ulParameter ),
689 ( size_t ) sizeof( ulParameter ) );
696 /* Option-specific handling. */
697 switch( ucOptionCode )
699 case dhcpMESSAGE_TYPE_OPTION_CODE :
701 if( *pucByte == ( uint8_t ) xExpectedMessageType )
703 /* The message type is the message type the
704 state machine is expecting. */
707 else if( *pucByte == ( uint8_t ) dhcpMESSAGE_TYPE_NACK )
709 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK )
712 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
717 /* Don't process other message types. */
721 case dhcpSUBNET_MASK_OPTION_CODE :
723 if( ucLength == sizeof( uint32_t ) )
725 xNetworkAddressing.ulNetMask = ulParameter;
729 case dhcpGATEWAY_OPTION_CODE :
731 if( ucLength == sizeof( uint32_t ) )
733 /* ulProcessed is not incremented in this case
734 because the gateway is not essential. */
735 xNetworkAddressing.ulGatewayAddress = ulParameter;
739 case dhcpDNS_SERVER_OPTIONS_CODE :
741 /* ulProcessed is not incremented in this case
742 because the DNS server is not essential. Only the
743 first DNS server address is taken. */
744 xNetworkAddressing.ulDNSServerAddress = ulParameter;
747 case dhcpSERVER_IP_ADDRESS_OPTION_CODE :
749 if( ucLength == sizeof( uint32_t ) )
751 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER )
753 /* Offers state the replying server. */
755 xDHCPData.ulDHCPServerAddress = ulParameter;
759 /* The ack must come from the expected server. */
760 if( xDHCPData.ulDHCPServerAddress == ulParameter )
768 case dhcpLEASE_TIME_OPTION_CODE :
770 if( ucLength == sizeof( xDHCPData.ulLeaseTime ) )
772 /* ulProcessed is not incremented in this case
773 because the lease time is not essential. */
774 /* The DHCP parameter is in seconds, convert
775 to host-endian format. */
776 xDHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter );
778 /* Divide the lease time by two to ensure a
779 renew request is sent before the lease actually
781 xDHCPData.ulLeaseTime >>= 1UL;
783 /* Multiply with configTICK_RATE_HZ to get clock
785 xDHCPData.ulLeaseTime = configTICK_RATE_HZ * xDHCPData.ulLeaseTime;
791 /* Not interested in this field. */
796 /* Jump over the data to find the next option code. */
807 /* Were all the mandatory options received? */
808 if( ulProcessed >= ulMandatoryOptions )
810 /* HT:endian: used to be network order */
811 xDHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr;
812 FreeRTOS_printf( ( "vDHCPProcess: offer %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
818 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload );
823 /*-----------------------------------------------------------*/
825 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize )
827 DHCPMessage_t *pxDHCPMessage;
828 size_t xRequiredBufferSize = sizeof( DHCPMessage_t ) + *pxOptionsArraySize;
829 uint8_t *pucUDPPayloadBuffer;
831 #if( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
832 const char *pucHostName = pcApplicationHostnameHook ();
833 size_t xNameLength = strlen( pucHostName );
836 xRequiredBufferSize += ( 2 + xNameLength );
839 /* Get a buffer. This uses a maximum delay, but the delay will be capped
840 to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value still needs to
844 } while( ( pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xRequiredBufferSize, portMAX_DELAY ) ) == NULL );
846 pxDHCPMessage = ( DHCPMessage_t * ) pucUDPPayloadBuffer;
848 /* Most fields need to be zero. */
849 memset( ( void * ) pxDHCPMessage, 0x00, sizeof( DHCPMessage_t ) );
851 /* Create the message. */
852 pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode;
853 pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
854 pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
855 pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( xDHCPData.ulTransactionId );
856 pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE;
857 if( xDHCPData.xUseBroadcast != pdFALSE )
859 pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST;
863 pxDHCPMessage->usFlags = 0u;
866 memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
868 /* Copy in the const part of the options options. */
869 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) pucOptionsArray, *pxOptionsArraySize );
871 #if( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
873 /* With this option, the hostname can be registered as well which makes
874 it easier to lookup a device in a router's list of DHCP clients. */
876 /* Point to where the OPTION_END was stored to add data. */
877 pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1 ) ] );
878 pucPtr[ 0 ] = dhcpDNS_HOSTNAME_OPTIONS_CODE;
879 pucPtr[ 1 ] = ( uint8_t ) xNameLength;
880 memcpy( ( void *) ( pucPtr + 2 ), pucHostName, xNameLength );
881 pucPtr[ 2 + xNameLength ] = dhcpOPTION_END_BYTE;
882 *pxOptionsArraySize += ( 2 + xNameLength );
886 /* Map in the client identifier. */
887 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ),
888 ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
890 /* Set the addressing. */
891 pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS;
892 pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT;
894 return pucUDPPayloadBuffer;
896 /*-----------------------------------------------------------*/
898 static void prvSendDHCPRequest( void )
900 uint8_t *pucUDPPayloadBuffer;
901 struct freertos_sockaddr xAddress;
902 static const uint8_t ucDHCPRequestOptions[] =
904 /* Do not change the ordering without also changing
905 dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and
906 dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */
907 dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */
908 dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */
909 dhcpREQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */
910 dhcpSERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */
913 size_t xOptionsLength = sizeof( ucDHCPRequestOptions );
915 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPRequestOptions, &xOptionsLength );
917 /* Copy in the IP address being requested. */
918 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ),
919 ( void * ) &( xDHCPData.ulOfferedIPAddress ), sizeof( xDHCPData.ulOfferedIPAddress ) );
921 /* Copy in the address of the DHCP server being used. */
922 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ),
923 ( void * ) &( xDHCPData.ulDHCPServerAddress ), sizeof( xDHCPData.ulDHCPServerAddress ) );
925 FreeRTOS_debug_printf( ( "vDHCPProcess: reply %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
926 iptraceSENDING_DHCP_REQUEST();
928 /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */
929 if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )
931 /* The packet was not successfully queued for sending and must be
932 returned to the stack. */
933 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
936 /*-----------------------------------------------------------*/
938 static void prvSendDHCPDiscover( void )
940 uint8_t *pucUDPPayloadBuffer;
941 struct freertos_sockaddr xAddress;
942 static const uint8_t ucDHCPDiscoverOptions[] =
944 /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */
945 dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */
946 dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */
947 dhcpPARAMETER_REQUEST_OPTION_CODE, 3, dhcpSUBNET_MASK_OPTION_CODE, dhcpGATEWAY_OPTION_CODE, dhcpDNS_SERVER_OPTIONS_CODE, /* Parameter request option. */
950 size_t xOptionsLength = sizeof( ucDHCPDiscoverOptions );
952 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, &xOptionsLength );
954 FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) );
955 iptraceSENDING_DHCP_DISCOVER();
957 /* 'ucFirstOptionByte' is part of DHCP message struct, so subtract one byte. */
958 if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength - 1 ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )
960 /* The packet was not successfully queued for sending and must be
961 returned to the stack. */
962 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
965 /*-----------------------------------------------------------*/
967 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
969 static void prvPrepareLinkLayerIPLookUp( void )
971 uint8_t ucLinkLayerIPAddress[ 2 ];
972 uint32_t ulNumbers[ 2 ];
974 /* After DHCP has failed to answer, prepare everything to start
975 trying-out LinkLayer IP-addresses, using the random method. */
976 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
978 xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) );
979 xApplicationGetRandomNumber( &( ulNumbers[ 1 ] ) );
980 ucLinkLayerIPAddress[ 0 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 0 ] % 0xFDu ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */
981 ucLinkLayerIPAddress[ 1 ] = ( uint8_t )1 + ( uint8_t )( ulNumbers[ 1 ] % 0xFDu ); /* get value 1..254 for IP-address 4th byte of IP address to try. */
983 xNetworkAddressing.ulGatewayAddress = FreeRTOS_htonl( 0xA9FE0203 );
985 /* prepare xDHCPData with data to test. */
986 xDHCPData.ulOfferedIPAddress =
987 FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] );
989 xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */
991 xNetworkAddressing.ulNetMask =
992 FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 );
994 /* DHCP completed. The IP address can now be used, and the
995 timer set to the lease timeout time. */
996 *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress;
998 /* Setting the 'local' broadcast address, something like 192.168.1.255' */
999 xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask;
1001 /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */
1002 if( xDHCPData.xDHCPSocket != NULL )
1004 /* Close socket to ensure packets don't queue on it. */
1005 vSocketClose( xDHCPData.xDHCPSocket );
1006 xDHCPData.xDHCPSocket = NULL;
1009 xApplicationGetRandomNumber( &( ulNumbers[ 0 ] ) );
1010 xDHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000ul + ( ulNumbers[ 0 ] & 0x3ffuL ) ); /* do ARP test every (3 + 0-1024mS) seconds. */
1012 xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */
1013 vARPSendGratuitous();
1016 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
1017 /*-----------------------------------------------------------*/
1019 #endif /* ipconfigUSE_DHCP != 0 */