2 * FreeRTOS+TCP Labs Build 160919 (C) 2016 Real Time Engineers ltd.
\r
3 * Authors include Hein Tibosch and Richard Barry
\r
5 *******************************************************************************
\r
6 ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***
\r
9 *** FREERTOS+TCP IS STILL IN THE LAB (mainly because the FTP and HTTP ***
\r
10 *** demos have a dependency on FreeRTOS+FAT, which is only in the Labs ***
\r
13 *** FreeRTOS+TCP is functional and has been used in commercial products ***
\r
14 *** for some time. Be aware however that we are still refining its ***
\r
15 *** design, the source code does not yet quite conform to the strict ***
\r
16 *** coding and style standards mandated by Real Time Engineers ltd., and ***
\r
17 *** the documentation and testing is not necessarily complete. ***
\r
19 *** PLEASE REPORT EXPERIENCES USING THE SUPPORT RESOURCES FOUND ON THE ***
\r
20 *** URL: http://www.FreeRTOS.org/contact Active early adopters may, at ***
\r
21 *** the sole discretion of Real Time Engineers Ltd., be offered versions ***
\r
22 *** under a license other than that described below. ***
\r
25 ***** NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ******* NOTE ***
\r
26 *******************************************************************************
\r
28 * FreeRTOS+TCP can be used under two different free open source licenses. The
\r
29 * license that applies is dependent on the processor on which FreeRTOS+TCP is
\r
30 * executed, as follows:
\r
32 * If FreeRTOS+TCP is executed on one of the processors listed under the Special
\r
33 * License Arrangements heading of the FreeRTOS+TCP license information web
\r
34 * page, then it can be used under the terms of the FreeRTOS Open Source
\r
35 * License. If FreeRTOS+TCP is used on any other processor, then it can be used
\r
36 * under the terms of the GNU General Public License V2. Links to the relevant
\r
39 * The FreeRTOS+TCP License Information Page: http://www.FreeRTOS.org/tcp_license
\r
40 * The FreeRTOS Open Source License: http://www.FreeRTOS.org/license
\r
41 * The GNU General Public License Version 2: http://www.FreeRTOS.org/gpl-2.0.txt
\r
43 * FreeRTOS+TCP is distributed in the hope that it will be useful. You cannot
\r
44 * use FreeRTOS+TCP unless you agree that you use the software 'as is'.
\r
45 * FreeRTOS+TCP is provided WITHOUT ANY WARRANTY; without even the implied
\r
46 * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
\r
47 * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they
\r
48 * implied, expressed, or statutory.
\r
50 * 1 tab == 4 spaces!
\r
52 * http://www.FreeRTOS.org
\r
53 * http://www.FreeRTOS.org/plus
\r
54 * http://www.FreeRTOS.org/labs
\r
58 /* Standard includes. */
\r
61 /* FreeRTOS includes. */
\r
62 #include "FreeRTOS.h"
\r
66 /* FreeRTOS+TCP includes. */
\r
67 #include "FreeRTOS_IP.h"
\r
68 #include "FreeRTOS_Sockets.h"
\r
69 #include "FreeRTOS_IP_Private.h"
\r
70 #include "FreeRTOS_UDP_IP.h"
\r
71 #include "FreeRTOS_TCP_IP.h"
\r
72 #include "FreeRTOS_DHCP.h"
\r
73 #include "FreeRTOS_ARP.h"
\r
74 #include "NetworkInterface.h"
\r
75 #include "NetworkBufferManagement.h"
\r
77 /* Exclude the entire file if DHCP is not enabled. */
\r
78 #if( ipconfigUSE_DHCP != 0 )
\r
80 #if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586u )
\r
81 /* DHCP must be able to receive an options field of 312 bytes, the fixed
\r
82 part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */
\r
83 #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP
\r
86 /* Parameter widths in the DHCP packet. */
\r
87 #define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH 16
\r
88 #define dhcpSERVER_HOST_NAME_LENGTH 64
\r
89 #define dhcpBOOT_FILE_NAME_LENGTH 128
\r
91 /* Timer parameters */
\r
92 #ifndef dhcpINITIAL_DHCP_TX_PERIOD
\r
93 #define dhcpINITIAL_TIMER_PERIOD ( pdMS_TO_TICKS( 250 ) )
\r
94 #define dhcpINITIAL_DHCP_TX_PERIOD ( pdMS_TO_TICKS( 5000 ) )
\r
97 /* Codes of interest found in the DHCP options field. */
\r
98 #define dhcpIPv4_ZERO_PAD_OPTION_CODE ( 0u )
\r
99 #define dhcpIPv4_SUBNET_MASK_OPTION_CODE ( 1u )
\r
100 #define dhcpIPv4_GATEWAY_OPTION_CODE ( 3u )
\r
101 #define dhcpIPv4_DNS_SERVER_OPTIONS_CODE ( 6u )
\r
102 #define dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE ( 12u )
\r
103 #define dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE ( 50u )
\r
104 #define dhcpIPv4_LEASE_TIME_OPTION_CODE ( 51u )
\r
105 #define dhcpIPv4_MESSAGE_TYPE_OPTION_CODE ( 53u )
\r
106 #define dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE ( 54u )
\r
107 #define dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE ( 55u )
\r
108 #define dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE ( 61u )
\r
110 /* The four DHCP message types of interest. */
\r
111 #define dhcpMESSAGE_TYPE_DISCOVER ( 1 )
\r
112 #define dhcpMESSAGE_TYPE_OFFER ( 2 )
\r
113 #define dhcpMESSAGE_TYPE_REQUEST ( 3 )
\r
114 #define dhcpMESSAGE_TYPE_ACK ( 5 )
\r
115 #define dhcpMESSAGE_TYPE_NACK ( 6 )
\r
117 /* Offsets into the transmitted DHCP options fields at which various parameters
\r
119 #define dhcpCLIENT_IDENTIFIER_OFFSET ( 5 )
\r
120 #define dhcpREQUESTED_IP_ADDRESS_OFFSET ( 13 )
\r
121 #define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ( 19 )
\r
123 /* Values used in the DHCP packets. */
\r
124 #define dhcpREQUEST_OPCODE ( 1 )
\r
125 #define dhcpREPLY_OPCODE ( 2 )
\r
126 #define dhcpADDRESS_TYPE_ETHERNET ( 1 )
\r
127 #define dhcpETHERNET_ADDRESS_LENGTH ( 6 )
\r
129 /* If a lease time is not received, use the default of two days. */
\r
130 /* 48 hours in ticks. Can not use pdMS_TO_TICKS() as integer overflow can occur. */
\r
131 #define dhcpDEFAULT_LEASE_TIME ( ( 48UL * 60UL * 60UL ) * configTICK_RATE_HZ )
\r
133 /* Don't allow the lease time to be too short. */
\r
134 #define dhcpMINIMUM_LEASE_TIME ( pdMS_TO_TICKS( 60000UL ) ) /* 60 seconds in ticks. */
\r
136 /* Marks the end of the variable length options field in the DHCP packet. */
\r
137 #define dhcpOPTION_END_BYTE 0xffu
\r
139 /* Offset into a DHCP message at which the first byte of the options is
\r
141 #define dhcpFIRST_OPTION_BYTE_OFFSET ( 0xf0 )
\r
143 /* When walking the variable length options field, the following value is used
\r
144 to ensure the walk has not gone past the end of the valid options. 2 bytes is
\r
145 made up of the length byte, and minimum one byte value. */
\r
146 #define dhcpMAX_OPTION_LENGTH_OF_INTEREST ( 2L )
\r
148 /* Standard DHCP port numbers and magic cookie value.
\r
149 DHCPv4 uses UDP port number 68 for clients and port number 67 for servers.
\r
151 #if( ipconfigBYTE_ORDER == pdFREERTOS_LITTLE_ENDIAN )
\r
152 #define dhcpCLIENT_PORT 0x4400u
\r
153 #define dhcpSERVER_PORT 0x4300u
\r
154 #define dhcpCOOKIE 0x63538263ul
\r
155 #define dhcpBROADCAST 0x0080u
\r
157 #define dhcpCLIENT_PORT 0x0044u
\r
158 #define dhcpSERVER_PORT 0x0043u
\r
159 #define dhcpCOOKIE 0x63825363ul
\r
160 #define dhcpBROADCAST 0x8000u
\r
161 #endif /* ipconfigBYTE_ORDER */
\r
163 #include "pack_struct_start.h"
\r
164 struct xDHCPMessage
\r
167 uint8_t ucAddressType;
\r
168 uint8_t ucAddressLength;
\r
170 uint32_t ulTransactionID;
\r
171 uint16_t usElapsedTime;
\r
173 uint32_t ulClientIPAddress_ciaddr;
\r
174 uint32_t ulYourIPAddress_yiaddr;
\r
175 uint32_t ulServerIPAddress_siaddr;
\r
176 uint32_t ulRelayAgentIPAddress_giaddr;
\r
177 uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ];
\r
178 uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ];
\r
179 uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ];
\r
180 uint32_t ulDHCPCookie;
\r
181 uint8_t ucFirstOptionByte;
\r
183 #include "pack_struct_end.h"
\r
184 typedef struct xDHCPMessage DHCPMessage_t;
\r
186 /* DHCP state machine states. */
\r
189 eWaitingSendFirstDiscover = 0, /* Initial state. Send a discover the first time it is called, and reset all timers. */
\r
190 eWaitingOffer, /* Either resend the discover, or, if the offer is forthcoming, send a request. */
\r
191 eWaitingAcknowledge, /* Either resend the request. */
\r
192 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
193 eGetLinkLayerAddress, /* When DHCP didn't respond, try to obtain a LinkLayer address 168.254.x.x. */
\r
195 eLeasedAddress, /* Resend the request at the appropriate time to renew the lease. */
\r
196 eNotUsingLeasedAddress /* DHCP failed, and a default IP address is being used. */
\r
199 /* Hold information in between steps in the DHCP state machine. */
\r
202 uint32_t ulTransactionId;
\r
203 uint32_t ulOfferedIPAddress;
\r
204 uint32_t ulDHCPServerAddress;
\r
205 uint32_t ulLeaseTime;
\r
206 /* Hold information on the current timer state. */
\r
207 TickType_t xDHCPTxTime;
\r
208 TickType_t xDHCPTxPeriod;
\r
209 /* Try both without and with the broadcast flag */
\r
210 BaseType_t xUseBroadcast;
\r
211 /* Maintains the DHCP state machine state. */
\r
212 eDHCPState_t eDHCPState;
\r
213 /* The UDP socket used for all incoming and outgoing DHCP traffic. */
\r
214 Socket_t xDHCPSocket;
\r
217 typedef struct xDHCP_DATA DHCPData_t;
\r
219 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
220 /* Define the Link Layer IP address: 169.254.x.x */
\r
221 #define LINK_LAYER_ADDRESS_0 169
\r
222 #define LINK_LAYER_ADDRESS_1 254
\r
224 /* Define the netmask used: 255.255.0.0 */
\r
225 #define LINK_LAYER_NETMASK_0 255
\r
226 #define LINK_LAYER_NETMASK_1 255
\r
227 #define LINK_LAYER_NETMASK_2 0
\r
228 #define LINK_LAYER_NETMASK_3 0
\r
233 * Generate a DHCP discover message and send it on the DHCP socket.
\r
235 static void prvSendDHCPDiscover( void );
\r
238 * Interpret message received on the DHCP socket.
\r
240 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType );
\r
243 * Generate a DHCP request packet, and send it on the DHCP socket.
\r
245 static void prvSendDHCPRequest( void );
\r
248 * Prepare to start a DHCP transaction. This initialises some state variables
\r
249 * and creates the DHCP socket if necessary.
\r
251 static void prvInitialiseDHCP( void );
\r
254 * Creates the part of outgoing DHCP messages that are common to all outgoing
\r
257 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize );
\r
260 * Create the DHCP socket, if it has not been created already.
\r
262 static void prvCreateDHCPSocket( void );
\r
265 * After DHCP has failed to answer, prepare everything to start searching
\r
266 * for (trying-out) LinkLayer IP-addresses, using the random method: Send
\r
267 * a gratuitous ARP request and wait if another device responds to it.
\r
269 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
270 static void prvPrepareLinkLayerIPLookUp( void );
\r
273 /*-----------------------------------------------------------*/
\r
275 /* The next DHCP transaction Id to be used. */
\r
276 static DHCPData_t xDHCPData;
\r
278 /*-----------------------------------------------------------*/
\r
280 BaseType_t xIsDHCPSocket( Socket_t xSocket )
\r
282 BaseType_t xReturn;
\r
284 if( xDHCPData.xDHCPSocket == xSocket )
\r
295 /*-----------------------------------------------------------*/
\r
297 void vDHCPProcess( BaseType_t xReset )
\r
299 BaseType_t xGivingUp = pdFALSE;
\r
300 #if( ipconfigUSE_DHCP_HOOK != 0 )
\r
301 eDHCPCallbackAnswer_t eAnswer;
\r
302 #endif /* ipconfigUSE_DHCP_HOOK */
\r
304 /* Is DHCP starting over? */
\r
305 if( xReset != pdFALSE )
\r
307 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
\r
310 switch( xDHCPData.eDHCPState )
\r
312 case eWaitingSendFirstDiscover :
\r
313 /* Ask the user if a DHCP discovery is required. */
\r
314 #if( ipconfigUSE_DHCP_HOOK != 0 )
\r
315 eAnswer = xApplicationDHCPHook( eDHCPPhasePreDiscover, xNetworkAddressing.ulDefaultIPAddress );
\r
316 if( eAnswer == eDHCPContinue )
\r
317 #endif /* ipconfigUSE_DHCP_HOOK */
\r
319 /* Initial state. Create the DHCP socket, timer, etc. if they
\r
320 have not already been created. */
\r
321 prvInitialiseDHCP();
\r
323 /* See if prvInitialiseDHCP() has creates a socket. */
\r
324 if( xDHCPData.xDHCPSocket == NULL )
\r
326 xGivingUp = pdTRUE;
\r
330 *ipLOCAL_IP_ADDRESS_POINTER = 0UL;
\r
332 /* Send the first discover request. */
\r
333 if( xDHCPData.xDHCPSocket != NULL )
\r
335 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
336 prvSendDHCPDiscover( );
\r
337 xDHCPData.eDHCPState = eWaitingOffer;
\r
340 #if( ipconfigUSE_DHCP_HOOK != 0 )
\r
343 if( eAnswer == eDHCPUseDefaults )
\r
345 memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );
\r
348 /* The user indicates that the DHCP process does not continue. */
\r
349 xGivingUp = pdTRUE;
\r
351 #endif /* ipconfigUSE_DHCP_HOOK */
\r
354 case eWaitingOffer :
\r
356 xGivingUp = pdFALSE;
\r
358 /* Look for offers coming in. */
\r
359 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER ) == pdPASS )
\r
361 #if( ipconfigUSE_DHCP_HOOK != 0 )
\r
362 /* Ask the user if a DHCP request is required. */
\r
363 eAnswer = xApplicationDHCPHook( eDHCPPhasePreRequest, xDHCPData.ulOfferedIPAddress );
\r
365 if( eAnswer == eDHCPContinue )
\r
366 #endif /* ipconfigUSE_DHCP_HOOK */
\r
368 /* An offer has been made, the user wants to continue,
\r
369 generate the request. */
\r
370 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
371 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
\r
372 prvSendDHCPRequest( );
\r
373 xDHCPData.eDHCPState = eWaitingAcknowledge;
\r
377 #if( ipconfigUSE_DHCP_HOOK != 0 )
\r
378 if( eAnswer == eDHCPUseDefaults )
\r
380 memcpy( &xNetworkAddressing, &xDefaultAddressing, sizeof( xNetworkAddressing ) );
\r
383 /* The user indicates that the DHCP process does not continue. */
\r
384 xGivingUp = pdTRUE;
\r
385 #endif /* ipconfigUSE_DHCP_HOOK */
\r
387 else if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
\r
389 /* It is time to send another Discover. Increase the time
\r
390 period, and if it has not got to the point of giving up - send
\r
391 another discovery. */
\r
392 xDHCPData.xDHCPTxPeriod <<= 1;
\r
394 if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
\r
396 xDHCPData.ulTransactionId++;
\r
397 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
398 xDHCPData.xUseBroadcast = !xDHCPData.xUseBroadcast;
\r
399 prvSendDHCPDiscover( );
\r
400 FreeRTOS_debug_printf( ( "vDHCPProcess: timeout %lu ticks\n", xDHCPData.xDHCPTxPeriod ) );
\r
404 FreeRTOS_debug_printf( ( "vDHCPProcess: giving up %lu > %lu ticks\n", xDHCPData.xDHCPTxPeriod, ipconfigMAXIMUM_DISCOVER_TX_PERIOD ) );
\r
406 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
408 /* Only use a fake Ack if the default IP address == 0x00
\r
409 and the link local addressing is used. Start searching
\r
410 a free LinkLayer IP-address. Next state will be
\r
411 'eGetLinkLayerAddress'. */
\r
412 prvPrepareLinkLayerIPLookUp();
\r
414 /* Setting an IP address manually so set to not using
\r
415 leased address mode. */
\r
416 xDHCPData.eDHCPState = eGetLinkLayerAddress;
\r
420 xGivingUp = pdTRUE;
\r
422 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
\r
427 case eWaitingAcknowledge :
\r
429 /* Look for acks coming in. */
\r
430 if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK ) == pdPASS )
\r
432 FreeRTOS_debug_printf( ( "vDHCPProcess: acked %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
\r
434 /* DHCP completed. The IP address can now be used, and the
\r
435 timer set to the lease timeout time. */
\r
436 *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress;
\r
438 /* Setting the 'local' broadcast address, something like
\r
439 '192.168.1.255'. */
\r
440 xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask;
\r
441 xDHCPData.eDHCPState = eLeasedAddress;
\r
443 iptraceDHCP_SUCCEDEED( xDHCPData.ulOfferedIPAddress );
\r
445 /* DHCP failed, the default configured IP-address will be used
\r
446 Now call vIPNetworkUpCalls() to send the network-up event and
\r
447 start the ARP timer. */
\r
448 vIPNetworkUpCalls( );
\r
450 /* Close socket to ensure packets don't queue on it. */
\r
451 vSocketClose( xDHCPData.xDHCPSocket );
\r
452 xDHCPData.xDHCPSocket = NULL;
\r
454 if( xDHCPData.ulLeaseTime == 0UL )
\r
456 xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME;
\r
458 else if( xDHCPData.ulLeaseTime < dhcpMINIMUM_LEASE_TIME )
\r
460 xDHCPData.ulLeaseTime = dhcpMINIMUM_LEASE_TIME;
\r
464 /* The lease time is already valid. */
\r
467 /* Check for clashes. */
\r
468 vARPSendGratuitous();
\r
469 vIPReloadDHCPTimer( xDHCPData.ulLeaseTime );
\r
473 /* Is it time to send another Discover? */
\r
474 if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
\r
476 /* Increase the time period, and if it has not got to the
\r
477 point of giving up - send another request. */
\r
478 xDHCPData.xDHCPTxPeriod <<= 1;
\r
480 if( xDHCPData.xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )
\r
482 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
483 prvSendDHCPRequest( );
\r
487 /* Give up, start again. */
\r
488 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
\r
494 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
495 case eGetLinkLayerAddress:
\r
496 if( ( xTaskGetTickCount() - xDHCPData.xDHCPTxTime ) > xDHCPData.xDHCPTxPeriod )
\r
498 if( xARPHadIPClash == pdFALSE )
\r
500 /* ARP OK. proceed. */
\r
501 iptraceDHCP_SUCCEDEED( xDHCPData.ulOfferedIPAddress );
\r
503 /* Auto-IP succeeded, the default configured IP-address will
\r
504 be used. Now call vIPNetworkUpCalls() to send the
\r
505 network-up event and start the ARP timer. */
\r
506 vIPNetworkUpCalls( );
\r
507 xDHCPData.eDHCPState = eNotUsingLeasedAddress;
\r
511 /* ARP clashed - try another IP address. */
\r
512 prvPrepareLinkLayerIPLookUp();
\r
514 /* Setting an IP address manually so set to not using leased
\r
516 xDHCPData.eDHCPState = eGetLinkLayerAddress;
\r
520 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
\r
522 case eLeasedAddress :
\r
524 /* Resend the request at the appropriate time to renew the lease. */
\r
525 prvCreateDHCPSocket();
\r
527 if( xDHCPData.xDHCPSocket != NULL )
\r
529 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
530 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
\r
531 prvSendDHCPRequest( );
\r
532 xDHCPData.eDHCPState = eWaitingAcknowledge;
\r
534 /* From now on, we should be called more often */
\r
535 vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD );
\r
539 case eNotUsingLeasedAddress:
\r
541 vIPSetDHCPTimerEnableState( pdFALSE );
\r
548 if( xGivingUp != pdFALSE )
\r
550 /* xGivingUp became true either because of a time-out, or because
\r
551 xApplicationDHCPHook() returned another value than 'eDHCPContinue',
\r
552 meaning that the conversion is cancelled from here. */
\r
554 /* Revert to static IP address. */
\r
555 taskENTER_CRITICAL();
\r
557 *ipLOCAL_IP_ADDRESS_POINTER = xNetworkAddressing.ulDefaultIPAddress;
\r
558 iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( xNetworkAddressing.ulDefaultIPAddress );
\r
560 taskEXIT_CRITICAL();
\r
562 xDHCPData.eDHCPState = eNotUsingLeasedAddress;
\r
563 vIPSetDHCPTimerEnableState( pdFALSE );
\r
565 /* DHCP failed, the default configured IP-address will be used. Now
\r
566 call vIPNetworkUpCalls() to send the network-up event and start the ARP
\r
568 vIPNetworkUpCalls( );
\r
570 /* Test if socket was indeed created. */
\r
571 if( xDHCPData.xDHCPSocket != NULL )
\r
573 /* Close socket to ensure packets don't queue on it. */
\r
574 vSocketClose( xDHCPData.xDHCPSocket );
\r
575 xDHCPData.xDHCPSocket = NULL;
\r
579 /*-----------------------------------------------------------*/
\r
581 static void prvCreateDHCPSocket( void )
\r
583 struct freertos_sockaddr xAddress;
\r
584 BaseType_t xReturn;
\r
585 TickType_t xTimeoutTime = ( TickType_t ) 0;
\r
587 /* Create the socket, if it has not already been created. */
\r
588 if( xDHCPData.xDHCPSocket == NULL )
\r
590 xDHCPData.xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );
\r
591 if( xDHCPData.xDHCPSocket != FREERTOS_INVALID_SOCKET )
\r
594 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the
\r
595 context of the IP task. */
\r
596 FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
597 FreeRTOS_setsockopt( xDHCPData.xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );
\r
599 /* Bind to the standard DHCP client port. */
\r
600 xAddress.sin_port = ( uint16_t ) dhcpCLIENT_PORT;
\r
601 xReturn = vSocketBind( xDHCPData.xDHCPSocket, &xAddress, sizeof( xAddress ), pdFALSE );
\r
604 /* Binding failed, close the socket again. */
\r
605 vSocketClose( xDHCPData.xDHCPSocket );
\r
606 xDHCPData.xDHCPSocket = NULL;
\r
611 /* Change to NULL for easier testing. */
\r
612 xDHCPData.xDHCPSocket = NULL;
\r
616 /*-----------------------------------------------------------*/
\r
618 static void prvInitialiseDHCP( void )
\r
620 /* Initialise the parameters that will be set by the DHCP process. */
\r
621 if( xDHCPData.ulTransactionId == 0ul )
\r
623 xDHCPData.ulTransactionId = ipconfigRAND32();
\r
627 xDHCPData.ulTransactionId++;
\r
630 xDHCPData.xUseBroadcast = 0;
\r
631 xDHCPData.ulOfferedIPAddress = 0UL;
\r
632 xDHCPData.ulDHCPServerAddress = 0UL;
\r
633 xDHCPData.xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;
\r
635 /* Create the DHCP socket if it has not already been created. */
\r
636 prvCreateDHCPSocket();
\r
637 FreeRTOS_debug_printf( ( "prvInitialiseDHCP: start after %lu ticks\n", dhcpINITIAL_TIMER_PERIOD ) );
\r
638 vIPReloadDHCPTimer( dhcpINITIAL_TIMER_PERIOD );
\r
640 /*-----------------------------------------------------------*/
\r
642 static BaseType_t prvProcessDHCPReplies( BaseType_t xExpectedMessageType )
\r
644 uint8_t *pucUDPPayload, *pucLastByte;
\r
645 struct freertos_sockaddr xClient;
\r
646 uint32_t xClientLength = sizeof( xClient );
\r
648 DHCPMessage_t *pxDHCPMessage;
\r
649 uint8_t *pucByte, ucOptionCode, ucLength;
\r
650 uint32_t ulProcessed, ulParameter;
\r
651 BaseType_t xReturn = pdFALSE;
\r
652 const uint32_t ulMandatoryOptions = 2ul; /* DHCP server address, and the correct DHCP message type must be present in the options. */
\r
654 lBytes = FreeRTOS_recvfrom( xDHCPData.xDHCPSocket, ( void * ) &pucUDPPayload, 0ul, FREERTOS_ZERO_COPY, &xClient, &xClientLength );
\r
658 /* Map a DHCP structure onto the received data. */
\r
659 pxDHCPMessage = ( DHCPMessage_t * ) ( pucUDPPayload );
\r
661 /* Sanity check. */
\r
662 if( ( pxDHCPMessage->ulDHCPCookie == ( uint32_t ) dhcpCOOKIE ) &&
\r
663 ( pxDHCPMessage->ucOpcode == ( uint8_t ) dhcpREPLY_OPCODE ) &&
\r
664 ( pxDHCPMessage->ulTransactionID == FreeRTOS_htonl( xDHCPData.ulTransactionId ) ) )
\r
666 if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) ) == 0 )
\r
668 /* None of the essential options have been processed yet. */
\r
671 /* Walk through the options until the dhcpOPTION_END_BYTE byte
\r
672 is found, taking care not to walk off the end of the options. */
\r
673 pucByte = &( pxDHCPMessage->ucFirstOptionByte );
\r
674 pucLastByte = &( pucUDPPayload[ lBytes - dhcpMAX_OPTION_LENGTH_OF_INTEREST ] );
\r
676 while( pucByte < pucLastByte )
\r
678 ucOptionCode = pucByte[ 0 ];
\r
679 if( ucOptionCode == ( uint8_t ) dhcpOPTION_END_BYTE )
\r
681 /* Ready, the last byte has been seen. */
\r
684 if( ucOptionCode == ( uint8_t ) dhcpIPv4_ZERO_PAD_OPTION_CODE )
\r
686 /* The value zero is used as a pad byte,
\r
687 it is not followed by a length byte. */
\r
691 ucLength = pucByte[ 1 ];
\r
694 /* In most cases, a 4-byte network-endian parameter follows,
\r
695 just get it once here and use later */
\r
696 memcpy( ( void * ) &( ulParameter ), ( void * ) pucByte, ( size_t ) sizeof( ulParameter ) );
\r
698 switch( ucOptionCode )
\r
700 case dhcpIPv4_MESSAGE_TYPE_OPTION_CODE :
\r
702 if( *pucByte == ( uint8_t ) xExpectedMessageType )
\r
704 /* The message type is the message type the
\r
705 state machine is expecting. */
\r
710 if( *pucByte == ( uint8_t ) dhcpMESSAGE_TYPE_NACK )
\r
712 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_ACK )
\r
715 xDHCPData.eDHCPState = eWaitingSendFirstDiscover;
\r
718 /* Stop processing further options. */
\r
723 case dhcpIPv4_SUBNET_MASK_OPTION_CODE :
\r
725 if( ucLength == sizeof( uint32_t ) )
\r
727 xNetworkAddressing.ulNetMask = ulParameter;
\r
731 case dhcpIPv4_GATEWAY_OPTION_CODE :
\r
732 /* The DHCP server may send more than 1 gateway addresses. */
\r
733 if( ucLength >= sizeof( uint32_t ) )
\r
735 /* ulProcessed is not incremented in this case
\r
736 because the gateway is not essential. */
\r
737 xNetworkAddressing.ulGatewayAddress = ulParameter;
\r
741 case dhcpIPv4_DNS_SERVER_OPTIONS_CODE :
\r
743 /* ulProcessed is not incremented in this case
\r
744 because the DNS server is not essential. Only the
\r
745 first DNS server address is taken. */
\r
746 xNetworkAddressing.ulDNSServerAddress = ulParameter;
\r
749 case dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE :
\r
751 if( ucLength == sizeof( uint32_t ) )
\r
753 if( xExpectedMessageType == ( BaseType_t ) dhcpMESSAGE_TYPE_OFFER )
\r
755 /* Offers state the replying server. */
\r
757 xDHCPData.ulDHCPServerAddress = ulParameter;
\r
761 /* The ack must come from the expected server. */
\r
762 if( xDHCPData.ulDHCPServerAddress == ulParameter )
\r
770 case dhcpIPv4_LEASE_TIME_OPTION_CODE :
\r
772 if( ucLength == sizeof( xDHCPData.ulLeaseTime ) )
\r
774 /* ulProcessed is not incremented in this case
\r
775 because the lease time is not essential. */
\r
776 /* The DHCP parameter is in seconds, convert
\r
777 to host-endian format. */
\r
778 xDHCPData.ulLeaseTime = FreeRTOS_ntohl( ulParameter );
\r
780 /* Divide the lease time by two to ensure a
\r
781 renew request is sent before the lease actually
\r
783 xDHCPData.ulLeaseTime >>= 1UL;
\r
785 /* Multiply with configTICK_RATE_HZ to get clock
\r
787 xDHCPData.ulLeaseTime = configTICK_RATE_HZ * xDHCPData.ulLeaseTime;
\r
793 /* Not interested in this field. */
\r
798 /* Jump over the data to find the next option code. */
\r
799 if( ucLength == 0u )
\r
805 pucByte += ucLength;
\r
809 /* Were all the mandatory options received? */
\r
810 if( ulProcessed >= ulMandatoryOptions )
\r
812 /* HT:endian: used to be network order */
\r
813 xDHCPData.ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr;
\r
814 FreeRTOS_printf( ( "vDHCPProcess: offer %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
\r
820 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload );
\r
821 } /* if( lBytes > 0 ) */
\r
825 /*-----------------------------------------------------------*/
\r
827 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, BaseType_t xOpcode, const uint8_t * const pucOptionsArray, size_t *pxOptionsArraySize )
\r
829 DHCPMessage_t *pxDHCPMessage;
\r
830 size_t xRequiredBufferSize = sizeof( DHCPMessage_t ) + *pxOptionsArraySize;
\r
831 uint8_t *pucUDPPayloadBuffer;
\r
833 #if( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
\r
834 const char *pucHostName = pcApplicationHostnameHook ();
\r
835 size_t xNameLength = strlen( pucHostName );
\r
838 xRequiredBufferSize += ( 2 + xNameLength );
\r
841 /* Get a buffer. This uses a maximum delay, but the delay will be capped
\r
842 to ipconfigUDP_MAX_SEND_BLOCK_TIME_TICKS so the return value still needs to
\r
846 } while( ( pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xRequiredBufferSize, portMAX_DELAY ) ) == NULL );
\r
848 pxDHCPMessage = ( DHCPMessage_t * ) pucUDPPayloadBuffer;
\r
850 /* Most fields need to be zero. */
\r
851 memset( ( void * ) pxDHCPMessage, 0x00, sizeof( DHCPMessage_t ) );
\r
853 /* Create the message. */
\r
854 pxDHCPMessage->ucOpcode = ( uint8_t ) xOpcode;
\r
855 pxDHCPMessage->ucAddressType = ( uint8_t ) dhcpADDRESS_TYPE_ETHERNET;
\r
856 pxDHCPMessage->ucAddressLength = ( uint8_t ) dhcpETHERNET_ADDRESS_LENGTH;
\r
858 /* ulTransactionID doesn't really need a htonl() translation, but when DHCP
\r
859 times out, it is nicer to see an increasing number in this ID field */
\r
860 pxDHCPMessage->ulTransactionID = FreeRTOS_htonl( xDHCPData.ulTransactionId );
\r
861 pxDHCPMessage->ulDHCPCookie = ( uint32_t ) dhcpCOOKIE;
\r
862 if( xDHCPData.xUseBroadcast != pdFALSE )
\r
864 pxDHCPMessage->usFlags = ( uint16_t ) dhcpBROADCAST;
\r
868 pxDHCPMessage->usFlags = 0u;
\r
871 memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
\r
873 /* Copy in the const part of the options options. */
\r
874 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) pucOptionsArray, *pxOptionsArraySize );
\r
876 #if( ipconfigDHCP_REGISTER_HOSTNAME == 1 )
\r
878 /* With this option, the hostname can be registered as well which makes
\r
879 it easier to lookup a device in a router's list of DHCP clients. */
\r
881 /* Point to where the OPTION_END was stored to add data. */
\r
882 pucPtr = &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + ( *pxOptionsArraySize - 1 ) ] );
\r
883 pucPtr[ 0 ] = dhcpIPv4_DNS_HOSTNAME_OPTIONS_CODE;
\r
884 pucPtr[ 1 ] = ( uint8_t ) xNameLength;
\r
885 memcpy( ( void *) ( pucPtr + 2 ), pucHostName, xNameLength );
\r
886 pucPtr[ 2 + xNameLength ] = dhcpOPTION_END_BYTE;
\r
887 *pxOptionsArraySize += ( 2 + xNameLength );
\r
891 /* Map in the client identifier. */
\r
892 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ),
\r
893 ( void * ) ipLOCAL_MAC_ADDRESS, sizeof( MACAddress_t ) );
\r
895 /* Set the addressing. */
\r
896 pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS;
\r
897 pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT;
\r
899 return pucUDPPayloadBuffer;
\r
901 /*-----------------------------------------------------------*/
\r
903 static void prvSendDHCPRequest( void )
\r
905 uint8_t *pucUDPPayloadBuffer;
\r
906 struct freertos_sockaddr xAddress;
\r
907 static const uint8_t ucDHCPRequestOptions[] =
\r
909 /* Do not change the ordering without also changing
\r
910 dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and
\r
911 dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */
\r
912 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST, /* Message type option. */
\r
913 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */
\r
914 dhcpIPv4_REQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address being requested. */
\r
915 dhcpIPv4_SERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0, /* The IP address of the DHCP server. */
\r
916 dhcpOPTION_END_BYTE
\r
918 size_t xOptionsLength = sizeof( ucDHCPRequestOptions );
\r
920 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, ( uint8_t ) dhcpREQUEST_OPCODE, ucDHCPRequestOptions, &xOptionsLength );
\r
922 /* Copy in the IP address being requested. */
\r
923 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ),
\r
924 ( void * ) &( xDHCPData.ulOfferedIPAddress ), sizeof( xDHCPData.ulOfferedIPAddress ) );
\r
926 /* Copy in the address of the DHCP server being used. */
\r
927 memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ),
\r
928 ( void * ) &( xDHCPData.ulDHCPServerAddress ), sizeof( xDHCPData.ulDHCPServerAddress ) );
\r
930 FreeRTOS_debug_printf( ( "vDHCPProcess: reply %lxip\n", FreeRTOS_ntohl( xDHCPData.ulOfferedIPAddress ) ) );
\r
931 iptraceSENDING_DHCP_REQUEST();
\r
933 if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )
\r
935 /* The packet was not successfully queued for sending and must be
\r
936 returned to the stack. */
\r
937 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
\r
940 /*-----------------------------------------------------------*/
\r
942 static void prvSendDHCPDiscover( void )
\r
944 uint8_t *pucUDPPayloadBuffer;
\r
945 struct freertos_sockaddr xAddress;
\r
946 static const uint8_t ucDHCPDiscoverOptions[] =
\r
948 /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */
\r
949 dhcpIPv4_MESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER, /* Message type option. */
\r
950 dhcpIPv4_CLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0, /* Client identifier. */
\r
951 dhcpIPv4_PARAMETER_REQUEST_OPTION_CODE, 3, dhcpIPv4_SUBNET_MASK_OPTION_CODE, dhcpIPv4_GATEWAY_OPTION_CODE, dhcpIPv4_DNS_SERVER_OPTIONS_CODE, /* Parameter request option. */
\r
952 dhcpOPTION_END_BYTE
\r
954 size_t xOptionsLength = sizeof( ucDHCPDiscoverOptions );
\r
956 pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, ( uint8_t ) dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, &xOptionsLength );
\r
958 FreeRTOS_debug_printf( ( "vDHCPProcess: discover\n" ) );
\r
959 iptraceSENDING_DHCP_DISCOVER();
\r
961 if( FreeRTOS_sendto( xDHCPData.xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( DHCPMessage_t ) + xOptionsLength ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )
\r
963 /* The packet was not successfully queued for sending and must be
\r
964 returned to the stack. */
\r
965 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );
\r
968 /*-----------------------------------------------------------*/
\r
970 #if( ipconfigDHCP_FALL_BACK_AUTO_IP != 0 )
\r
972 static void prvPrepareLinkLayerIPLookUp( void )
\r
974 uint8_t ucLinkLayerIPAddress[ 2 ];
\r
976 /* After DHCP has failed to answer, prepare everything to start
\r
977 trying-out LinkLayer IP-addresses, using the random method. */
\r
978 xDHCPData.xDHCPTxTime = xTaskGetTickCount();
\r
980 ucLinkLayerIPAddress[ 0 ] = ( uint8_t )1 + ( uint8_t )( ipconfigRAND32() % 0xFDu ); /* get value 1..254 for IP-address 3rd byte of IP address to try. */
\r
981 ucLinkLayerIPAddress[ 1 ] = ( uint8_t )1 + ( uint8_t )( ipconfigRAND32() % 0xFDu ); /* get value 1..254 for IP-address 4th byte of IP address to try. */
\r
983 xNetworkAddressing.ulGatewayAddress = FreeRTOS_htonl( 0xA9FE0203 );
\r
985 /* prepare xDHCPData with data to test. */
\r
986 xDHCPData.ulOfferedIPAddress =
\r
987 FreeRTOS_inet_addr_quick( LINK_LAYER_ADDRESS_0, LINK_LAYER_ADDRESS_1, ucLinkLayerIPAddress[ 0 ], ucLinkLayerIPAddress[ 1 ] );
\r
989 xDHCPData.ulLeaseTime = dhcpDEFAULT_LEASE_TIME; /* don't care about lease time. just put anything. */
\r
991 xNetworkAddressing.ulNetMask =
\r
992 FreeRTOS_inet_addr_quick( LINK_LAYER_NETMASK_0, LINK_LAYER_NETMASK_1, LINK_LAYER_NETMASK_2, LINK_LAYER_NETMASK_3 );
\r
994 /* DHCP completed. The IP address can now be used, and the
\r
995 timer set to the lease timeout time. */
\r
996 *ipLOCAL_IP_ADDRESS_POINTER = xDHCPData.ulOfferedIPAddress;
\r
998 /* Setting the 'local' broadcast address, something like 192.168.1.255' */
\r
999 xNetworkAddressing.ulBroadcastAddress = ( xDHCPData.ulOfferedIPAddress & xNetworkAddressing.ulNetMask ) | ~xNetworkAddressing.ulNetMask;
\r
1001 /* Close socket to ensure packets don't queue on it. not needed anymore as DHCP failed. but still need timer for ARP testing. */
\r
1002 vSocketClose( xDHCPData.xDHCPSocket );
\r
1003 xDHCPData.xDHCPSocket = NULL;
\r
1004 xDHCPData.xDHCPTxPeriod = pdMS_TO_TICKS( 3000ul + ( ipconfigRAND32() & 0x3fful ) ); /* do ARP test every (3 + 0-1024mS) seconds. */
\r
1006 xARPHadIPClash = pdFALSE; /* reset flag that shows if have ARP clash. */
\r
1007 vARPSendGratuitous();
\r
1010 #endif /* ipconfigDHCP_FALL_BACK_AUTO_IP */
\r
1011 /*-----------------------------------------------------------*/
\r
1013 #endif /* ipconfigUSE_DHCP != 0 */
\r