]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-Plus-UDP/FreeRTOS_DHCP.c
e9fe777b592b2d51c4ca293b4cc88ca7321d1f51
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-Plus-UDP / FreeRTOS_DHCP.c
1 /*\r
2  * FreeRTOS+UDP V1.0.4 (C) 2014 Real Time Engineers ltd.\r
3  * All rights reserved\r
4  *\r
5  * This file is part of the FreeRTOS+UDP distribution.  The FreeRTOS+UDP license\r
6  * terms are different to the FreeRTOS license terms.\r
7  *\r
8  * FreeRTOS+UDP uses a dual license model that allows the software to be used\r
9  * under a standard GPL open source license, or a commercial license.  The\r
10  * standard GPL license (unlike the modified GPL license under which FreeRTOS\r
11  * itself is distributed) requires that all software statically linked with\r
12  * FreeRTOS+UDP is also distributed under the same GPL V2 license terms.\r
13  * Details of both license options follow:\r
14  *\r
15  * - Open source licensing -\r
16  * FreeRTOS+UDP is a free download and may be used, modified, evaluated and\r
17  * distributed without charge provided the user adheres to version two of the\r
18  * GNU General Public License (GPL) and does not remove the copyright notice or\r
19  * this text.  The GPL V2 text is available on the gnu.org web site, and on the\r
20  * following URL: http://www.FreeRTOS.org/gpl-2.0.txt.\r
21  *\r
22  * - Commercial licensing -\r
23  * Businesses and individuals that for commercial or other reasons cannot comply\r
24  * with the terms of the GPL V2 license must obtain a commercial license before\r
25  * incorporating FreeRTOS+UDP into proprietary software for distribution in any\r
26  * form.  Commercial licenses can be purchased from http://shop.freertos.org/udp\r
27  * and do not require any source files to be changed.\r
28  *\r
29  * FreeRTOS+UDP is distributed in the hope that it will be useful.  You cannot\r
30  * use FreeRTOS+UDP unless you agree that you use the software 'as is'.\r
31  * FreeRTOS+UDP is provided WITHOUT ANY WARRANTY; without even the implied\r
32  * warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
33  * PURPOSE. Real Time Engineers Ltd. disclaims all conditions and terms, be they\r
34  * implied, expressed, or statutory.\r
35  *\r
36  * 1 tab == 4 spaces!\r
37  *\r
38  * http://www.FreeRTOS.org\r
39  * http://www.FreeRTOS.org/udp\r
40  *\r
41  */\r
42 \r
43 /* Standard includes. */\r
44 #include <stdint.h>\r
45 \r
46 /* FreeRTOS includes. */\r
47 #include "FreeRTOS.h"\r
48 #include "task.h"\r
49 #include "queue.h"\r
50 #include "timers.h"\r
51 \r
52 /* FreeRTOS+UDP includes. */\r
53 #include "FreeRTOS_UDP_IP.h"\r
54 #include "FreeRTOS_IP_Private.h"\r
55 #include "FreeRTOS_DHCP.h"\r
56 #include "FreeRTOS_Sockets.h"\r
57 #include "NetworkInterface.h"\r
58 #include "IPTraceMacroDefaults.h"\r
59 \r
60 /* Exclude the entire file if DHCP is not enabled. */\r
61 #if ipconfigUSE_DHCP != 0\r
62 \r
63 #if ( ipconfigUSE_DHCP != 0 ) && ( ipconfigNETWORK_MTU < 586 )\r
64         /* DHCP must be able to receive an options field of 312 bytes, the fixed\r
65         part of the DHCP packet is 240 bytes, and the IP/UDP headers take 28 bytes. */\r
66         #error ipconfigNETWORK_MTU needs to be at least 586 to use DHCP (588 if ipconfigCAN_FRAGMENT_OUTGOING_PACKETS is set to 1)\r
67 #endif\r
68 \r
69 /* Parameter widths in the DHCP packet. */\r
70 #define dhcpCLIENT_HARDWARE_ADDRESS_LENGTH              16\r
71 #define dhcpSERVER_HOST_NAME_LENGTH                             64\r
72 #define dhcpBOOT_FILE_NAME_LENGTH                               128\r
73 \r
74 /* Timer parameters.  Windows simulator times are much shorter because simulated\r
75 time is not real time. */\r
76 #ifdef _WINDOWS_\r
77         #define dhcpINITIAL_DHCP_TX_PERIOD                      ( 100 / portTICK_RATE_MS )\r
78         #define dhcpINITIAL_TIMER_PERIOD                        ( 10 / portTICK_RATE_MS )\r
79         #define dhcpMAX_TIME_TO_WAIT_FOR_ACK            ( 200 / portTICK_RATE_MS )\r
80 #else\r
81         #define dhcpINITIAL_DHCP_TX_PERIOD                      ( 5000 / portTICK_RATE_MS )\r
82         #define dhcpINITIAL_TIMER_PERIOD                        ( 250 / portTICK_RATE_MS )\r
83         #define dhcpMAX_TIME_TO_WAIT_FOR_ACK            ( 5000 / portTICK_RATE_MS )\r
84 #endif /* _WINDOWS_ */\r
85 \r
86 /* Codes of interest found in the DHCP options field. */\r
87 #define dhcpSUBNET_MASK_OPTION_CODE                             ( 1 )\r
88 #define dhcpGATEWAY_OPTION_CODE                                 ( 3 )\r
89 #define hdcpDNS_SERVER_OPTIONS_CODE                             ( 6 )\r
90 #define dhcpMESSAGE_TYPE_OPTION_CODE                    ( 53 )\r
91 #define dhcpLEASE_TIME_OPTION_CODE                              ( 51 )\r
92 #define dhcpCLIENT_IDENTIFIER_OPTION_CODE               ( 61 )\r
93 #define dhcpPARAMETER_REQUEST_OPTION_CODE               ( 55 )\r
94 #define dhcpREQUEST_IP_ADDRESS_OPTION_CODE              ( 50 )\r
95 #define dhcpSERVER_IP_ADDRESS_OPTION_CODE               ( 54 )\r
96 \r
97 /* The four DHCP message types of interest. */\r
98 #define dhcpMESSAGE_TYPE_DISCOVER                               ( 1 )\r
99 #define dhcpMESSAGE_TYPE_OFFER                                  ( 2 )\r
100 #define dhcpMESSAGE_TYPE_REQUEST                                ( 3 )\r
101 #define dhcpMESSAGE_TYPE_ACK                                    ( 5 )\r
102 #define dhcpMESSAGE_TYPE_NACK                                   ( 6 )\r
103 \r
104 /* Offsets into the transmitted DHCP options fields at which various parameters\r
105 are located. */\r
106 #define dhcpCLIENT_IDENTIFIER_OFFSET                    ( 5 )\r
107 #define dhcpREQUESTED_IP_ADDRESS_OFFSET                 ( 13 )\r
108 #define dhcpDHCP_SERVER_IP_ADDRESS_OFFSET               ( 19 )\r
109 \r
110 /* Values used in the DHCP packets. */\r
111 #define dhcpREQUEST_OPCODE                                              ( 1 )\r
112 #define dhcpREPLY_OPCODE                                                ( 2 )\r
113 #define dhcpADDRESS_TYPE_ETHERNET                               ( 1 )\r
114 #define dhcpETHERNET_ADDRESS_LENGTH                             ( 6 )\r
115 \r
116 /* If a lease time is not received, use the default of two days. */\r
117 #define dhcpDEFAULT_LEASE_TIME                                  ( ( 48UL * 60UL * 60UL * 1000UL ) / portTICK_RATE_MS ) /* 48 hours in ticks. */\r
118 \r
119 /* Don't allow the lease time to be too short. */\r
120 #define dhcpMINIMUM_LEASE_TIME                                  ( 60000UL / portTICK_RATE_MS )  /* 60 seconds in ticks. */\r
121 \r
122 /* Marks the end of the variable length options field in the DHCP packet. */\r
123 #define dhcpOPTION_END_BYTE 0xff\r
124 \r
125 /* Offset into a DHCP message at which the first byte of the options is\r
126 located. */\r
127 #define dhcpFIRST_OPTION_BYTE_OFFSET                    ( 0xf0 )\r
128 \r
129 /* When walking the variable length options field, the following value is used\r
130 to ensure the walk has not gone past the end of the valid options.  2 bytes is\r
131 made up of the length byte, and minimum one byte value. */\r
132 #define dhcpMAX_OPTION_LENGTH_OF_INTEREST               ( 2L )\r
133 \r
134 /* Standard DHCP port numbers and magic cookie value. */\r
135 #if( ipconfigBYTE_ORDER == FREERTOS_LITTLE_ENDIAN )\r
136         #define dhcpCLIENT_PORT 0x4400\r
137         #define dhcpSERVER_PORT 0x4300\r
138         #define dhcpCOOKIE              0x63538263\r
139         #define dhcpBROADCAST   0x0080\r
140 #else\r
141         #define dhcpCLIENT_PORT 0x0044\r
142         #define dhcpSERVER_PORT 0x0043\r
143         #define dhcpCOOKIE              0x63825363\r
144         #define dhcpBROADCAST   0x8000\r
145 #endif /* ipconfigBYTE_ORDER */\r
146 \r
147 #include "pack_struct_start.h"\r
148 struct xDHCPMessage\r
149 {\r
150         uint8_t ucOpcode;\r
151         uint8_t ucAddressType;\r
152         uint8_t ucAddressLength;\r
153         uint8_t ucHops;\r
154         uint32_t ulTransactionID;\r
155         uint16_t usElapsedTime;\r
156         uint16_t usFlags;\r
157         uint32_t ulClientIPAddress_ciaddr;\r
158         uint32_t ulYourIPAddress_yiaddr;\r
159         uint32_t ulServerIPAddress_siaddr;\r
160         uint32_t ulRelayAgentIPAddress_giaddr;\r
161         uint8_t ucClientHardwareAddress[ dhcpCLIENT_HARDWARE_ADDRESS_LENGTH ];\r
162         uint8_t ucServerHostName[ dhcpSERVER_HOST_NAME_LENGTH ];\r
163         uint8_t ucBootFileName[ dhcpBOOT_FILE_NAME_LENGTH ];\r
164         uint32_t ulDHCPCookie;\r
165         uint8_t ucFirstOptionByte;\r
166 }\r
167 #include "pack_struct_end.h"\r
168 typedef struct xDHCPMessage xDHCPMessage_t;\r
169 \r
170 /* DHCP state machine states. */\r
171 typedef enum\r
172 {\r
173         eWaitingSendFirstDiscover = 0,  /* Initial state.  Send a discover the first time it is called, and reset all timers. */\r
174         eWaitingOffer,                                  /* Either resend the discover, or, if the offer is forthcoming, send a request. */\r
175         eWaitingAcknowledge,                    /* Either resend the request. */\r
176         eLeasedAddress,                                 /* Resend the request at the appropriate time to renew the lease. */\r
177         eNotUsingLeasedAddress                  /* DHCP failed, and a default IP address is being used. */\r
178 } eDHCPState_t;\r
179 \r
180 /*\r
181  * Generate a DHCP discover message and send it on the DHCP socket.\r
182  */\r
183 static void prvSendDHCPDiscover( xMACAddress_t *pxMACAddress );\r
184 \r
185 /*\r
186  * Interpret message received on the DHCP socket.\r
187  */\r
188 static BaseType_t prvProcessDHCPReplies( uint8_t ucExpectedMessageType, xMACAddress_t *pxMACAddress, xNetworkAddressingParameters_t *pxNetworkAddressing );\r
189 \r
190 /*\r
191  * Generate a DHCP request packet, and send it on the DHCP socket.\r
192  */\r
193 static void prvSendDHCPRequest( xMACAddress_t *pxMACAddress );\r
194 \r
195 /*\r
196  * Prepare to start a DHCP transaction.  This initialises some state variables\r
197  * and creates the DHCP socket if necessary.\r
198  */\r
199 static void prvInitialiseDHCP( void );\r
200 \r
201 /*\r
202  * Creates the part of outgoing DHCP messages that are common to all outgoing\r
203  * DHCP messages.\r
204  */\r
205 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, xMACAddress_t *pxMACAddress, uint8_t ucOpcode, const uint8_t * const pucOptionsArray, size_t xOptionsArraySize );\r
206 \r
207 /*\r
208  * Create the DHCP socket, if it has not been created already.\r
209  */\r
210 static void prvCreateDHCPSocket( void );\r
211 \r
212 /*-----------------------------------------------------------*/\r
213 \r
214 /* The timer used to drive the DHCP state machine. */\r
215 static xTimerHandle xDHCPTimer = NULL;\r
216 \r
217 /* The UDP socket used for all incoming and outgoing DHCP traffic. */\r
218 static xSocket_t xDHCPSocket = NULL;\r
219 \r
220 /* Hold information in between steps in the DHCP state machine. */\r
221 static uint32_t ulTransactionId = 0UL, ulOfferedIPAddress = 0UL, ulDHCPServerAddress = 0UL, ulLeaseTime = 0;\r
222 \r
223 /* Hold information on the current timer state. */\r
224 static TickType_t xDHCPTxTime = 0x00, xDHCPTxPeriod = 0x00;\r
225 \r
226 /* Maintains the DHCP state machine state. */\r
227 static eDHCPState_t eDHCPState = eWaitingSendFirstDiscover;\r
228 \r
229 /*-----------------------------------------------------------*/\r
230 \r
231 void vDHCPProcess( BaseType_t xReset, xMACAddress_t *pxMACAddress, uint32_t *pulIPAddress, xNetworkAddressingParameters_t *pxNetworkAddressing )\r
232 {\r
233         if( xReset != pdFALSE )\r
234         {\r
235                 eDHCPState = eWaitingSendFirstDiscover;\r
236         }\r
237 \r
238         switch( eDHCPState )\r
239         {\r
240                 case eWaitingSendFirstDiscover :\r
241 \r
242                         /* Initial state.  Create the DHCP socket, timer, etc. if they\r
243                         have not already been created. */\r
244                         prvInitialiseDHCP();\r
245                         *pulIPAddress = 0UL;\r
246 \r
247                         /* Send the first discover request. */\r
248                         if( xDHCPSocket != NULL )\r
249                         {\r
250                                 xDHCPTxTime = xTaskGetTickCount();\r
251                                 prvSendDHCPDiscover( pxMACAddress );\r
252                                 eDHCPState = eWaitingOffer;\r
253                         }\r
254                         break;\r
255 \r
256                 case eWaitingOffer :\r
257 \r
258                         /* Look for offers coming in. */\r
259                         if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_OFFER, pxMACAddress, pxNetworkAddressing ) == pdPASS )\r
260                         {\r
261                                 /* An offer has been made, generate the request. */\r
262                                 xDHCPTxTime = xTaskGetTickCount();\r
263                                 xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;\r
264                                 prvSendDHCPRequest( pxMACAddress );\r
265                                 eDHCPState = eWaitingAcknowledge;\r
266                         }\r
267                         else\r
268                         {\r
269                                 /* Is it time to send another Discover? */\r
270                                 if( ( xTaskGetTickCount() - xDHCPTxTime ) > xDHCPTxPeriod )\r
271                                 {\r
272                                         /* Increase the time period, and if it has not got to the\r
273                                         point of giving up - send another discovery. */\r
274                                         xDHCPTxPeriod <<= 1;\r
275                                         if( xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )\r
276                                         {\r
277                                                 ulTransactionId++;\r
278                                                 xDHCPTxTime = xTaskGetTickCount();\r
279                                                 prvSendDHCPDiscover( pxMACAddress );\r
280                                         }\r
281                                         else\r
282                                         {\r
283                                                 /* Revert to static IP address. */\r
284                                                 taskENTER_CRITICAL();\r
285                                                 {\r
286                                                         *pulIPAddress = pxNetworkAddressing->ulDefaultIPAddress;\r
287                                                         iptraceDHCP_REQUESTS_FAILED_USING_DEFAULT_IP_ADDRESS( pxNetworkAddressing->ulDefaultIPAddress );\r
288                                                 }\r
289                                                 taskEXIT_CRITICAL();\r
290                                                 eDHCPState = eNotUsingLeasedAddress;\r
291                                                 xTimerStop( xDHCPTimer, ( TickType_t ) 0 );\r
292 \r
293                                                 #if ipconfigUSE_NETWORK_EVENT_HOOK == 1\r
294                                                 {\r
295                                                         vApplicationIPNetworkEventHook( eNetworkUp );\r
296                                                 }\r
297                                                 #endif\r
298 \r
299                                                 /* Static configuration is being used, so the network is now up. */\r
300                                                 #if ipconfigFREERTOS_PLUS_NABTO == 1\r
301                                                 {\r
302                                                         /* Return value is used in configASSERT() inside the\r
303                                                         function. */\r
304                                                         ( void ) xStartNabtoTask();\r
305                                                 }\r
306                                                 #endif /* ipconfigFREERTOS_PLUS_NABTO */\r
307 \r
308                                                 /* Close socket to ensure packets don't queue on it. */\r
309                                                 FreeRTOS_closesocket( xDHCPSocket );\r
310                                                 xDHCPSocket = NULL;\r
311                                         }\r
312                                 }\r
313                         }\r
314                         break;\r
315 \r
316                 case eWaitingAcknowledge :\r
317 \r
318                         /* Look for acks coming in. */\r
319                         if( prvProcessDHCPReplies( dhcpMESSAGE_TYPE_ACK, pxMACAddress, pxNetworkAddressing ) == pdPASS )\r
320                         {\r
321                                 /* DHCP completed.  The IP address can now be used, and the\r
322                                 timer set to the lease timeout time. */\r
323                                 *pulIPAddress = ulOfferedIPAddress;\r
324                                 eDHCPState = eLeasedAddress;\r
325 \r
326                                 #if ipconfigUSE_NETWORK_EVENT_HOOK == 1\r
327                                 {\r
328                                         vApplicationIPNetworkEventHook( eNetworkUp );\r
329                                 }\r
330                                 #endif\r
331 \r
332                                 /* Static configuration is being used, so the network is now\r
333                                 up. */\r
334                                 #if ipconfigFREERTOS_PLUS_NABTO == 1\r
335                                 {\r
336                                         /* Return value is used in configASSERT() inside the\r
337                                         function. */\r
338                                         ( void ) xStartNabtoTask();\r
339                                 }\r
340                                 #endif /* ipconfigFREERTOS_PLUS_NABTO */\r
341 \r
342                                 /* Close socket to ensure packets don't queue on it. */\r
343                                 FreeRTOS_closesocket( xDHCPSocket );\r
344                                 xDHCPSocket = NULL;\r
345 \r
346                                 if( ulLeaseTime == 0UL )\r
347                                 {\r
348                                         ulLeaseTime = dhcpDEFAULT_LEASE_TIME;\r
349                                 }\r
350                                 else if( ulLeaseTime < dhcpMINIMUM_LEASE_TIME )\r
351                                 {\r
352                                         ulLeaseTime = dhcpMINIMUM_LEASE_TIME;\r
353                                 }\r
354                                 else\r
355                                 {\r
356                                         /* The lease time is already valid. */\r
357                                 }\r
358 \r
359                                 xTimerChangePeriod( xDHCPTimer, ulLeaseTime, portMAX_DELAY );\r
360                         }\r
361                         else\r
362                         {\r
363                                 /* Is it time to send another Discover? */\r
364                                 if( ( xTaskGetTickCount() - xDHCPTxTime ) > xDHCPTxPeriod )\r
365                                 {\r
366                                         /* Increase the time period, and if it has not got to the\r
367                                         point of giving up - send another request. */\r
368                                         xDHCPTxPeriod <<= 1;\r
369                                         if( xDHCPTxPeriod <= ipconfigMAXIMUM_DISCOVER_TX_PERIOD )\r
370                                         {\r
371                                                 xDHCPTxTime = xTaskGetTickCount();\r
372                                                 prvSendDHCPRequest( pxMACAddress );\r
373                                         }\r
374                                         else\r
375                                         {\r
376                                                 /* Give up, start again. */\r
377                                                 eDHCPState = eWaitingSendFirstDiscover;\r
378                                         }\r
379                                 }\r
380                         }\r
381                         break;\r
382 \r
383                 case eLeasedAddress :\r
384 \r
385                         /* Resend the request at the appropriate time to renew the lease. */\r
386                         prvCreateDHCPSocket();\r
387                         if( xDHCPSocket != NULL )\r
388                         {\r
389                                 xDHCPTxTime = xTaskGetTickCount();\r
390                                 xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;\r
391                                 prvSendDHCPRequest( pxMACAddress );\r
392                                 eDHCPState = eWaitingAcknowledge;\r
393                         }\r
394                         xTimerChangePeriod( xDHCPTimer, dhcpINITIAL_TIMER_PERIOD, portMAX_DELAY );\r
395                         break;\r
396 \r
397                 case eNotUsingLeasedAddress:\r
398                         xTimerStop( xDHCPTimer, ( TickType_t ) 0 );\r
399                         break;\r
400         }\r
401 }\r
402 /*-----------------------------------------------------------*/\r
403 \r
404 static void prvCreateDHCPSocket( void )\r
405 {\r
406 struct freertos_sockaddr xAddress;\r
407 BaseType_t xReturn;\r
408 TickType_t xTimeoutTime = 0;\r
409 \r
410         /* Create the socket, if it has not already been created. */\r
411         if( xDHCPSocket == NULL )\r
412         {\r
413                 xDHCPSocket = FreeRTOS_socket( FREERTOS_AF_INET, FREERTOS_SOCK_DGRAM, FREERTOS_IPPROTO_UDP );\r
414                 configASSERT( ( xDHCPSocket != FREERTOS_INVALID_SOCKET ) );\r
415 \r
416                 /* Ensure the Rx and Tx timeouts are zero as the DHCP executes in the\r
417                 context of the IP task. */\r
418                 FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_RCVTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );\r
419                 FreeRTOS_setsockopt( xDHCPSocket, 0, FREERTOS_SO_SNDTIMEO, ( void * ) &xTimeoutTime, sizeof( TickType_t ) );\r
420 \r
421                 /* Bind to the standard DHCP client port. */\r
422                 xAddress.sin_port = dhcpCLIENT_PORT;\r
423                 xReturn = FreeRTOS_bind( xDHCPSocket, &xAddress, sizeof( xAddress ) );\r
424                 configASSERT( xReturn == 0 );\r
425 \r
426                 /* Remove compiler warnings if configASSERT() is not defined. */\r
427                 ( void ) xReturn;\r
428         }\r
429 }\r
430 /*-----------------------------------------------------------*/\r
431 \r
432 static void prvInitialiseDHCP( void )\r
433 {\r
434 extern void vIPFunctionsTimerCallback( xTimerHandle xTimer );\r
435 \r
436         /* Initialise the parameters that will be set by the DHCP process. */\r
437         if( ulTransactionId == 0 )\r
438         {\r
439                 ulTransactionId = ipconfigRAND32();\r
440         }\r
441         else\r
442         {\r
443                 ulTransactionId++;\r
444         }\r
445         ulOfferedIPAddress = 0UL;\r
446         ulDHCPServerAddress = 0UL;\r
447         xDHCPTxPeriod = dhcpINITIAL_DHCP_TX_PERIOD;\r
448 \r
449         /* Create the DHCP socket if it has not already been created. */\r
450         prvCreateDHCPSocket();\r
451 \r
452         if( xDHCPTimer == NULL )\r
453         {\r
454                 xDHCPTimer = xTimerCreate( "DHCP", dhcpINITIAL_TIMER_PERIOD, pdTRUE, ( void * ) eDHCPEvent, vIPFunctionsTimerCallback );\r
455                 configASSERT( xDHCPTimer );\r
456                 xTimerStart( xDHCPTimer, portMAX_DELAY );\r
457         }\r
458         else\r
459         {\r
460                 xTimerChangePeriod( xDHCPTimer, dhcpINITIAL_TIMER_PERIOD, portMAX_DELAY );\r
461         }\r
462 }\r
463 /*-----------------------------------------------------------*/\r
464 \r
465 static BaseType_t prvProcessDHCPReplies( uint8_t ucExpectedMessageType, xMACAddress_t *pxMACAddress, xNetworkAddressingParameters_t *pxNetworkAddressing )\r
466 {\r
467 uint8_t *pucUDPPayload, *pucLastByte;\r
468 struct freertos_sockaddr xClient;\r
469 uint32_t xClientLength = sizeof( xClient );\r
470 int32_t lBytes;\r
471 xDHCPMessage_t *pxDHCPMessage;\r
472 uint8_t *pucByte, ucOptionCode, ucLength;\r
473 uint32_t ulProcessed;\r
474 BaseType_t xReturn = pdFALSE;\r
475 const uint32_t ulMandatoryOptions = 2; /* DHCP server address, and the correct DHCP message type must be present in the options. */\r
476 \r
477         lBytes = FreeRTOS_recvfrom( xDHCPSocket, ( void * ) &pucUDPPayload, 0, FREERTOS_ZERO_COPY, &xClient, &xClientLength );\r
478 \r
479         if( lBytes > 0 )\r
480         {\r
481                 /* Map a DHCP structure onto the received data. */\r
482                 pxDHCPMessage = ( xDHCPMessage_t * ) ( pucUDPPayload );\r
483 \r
484                 /* Sanity check. */\r
485                 if( ( pxDHCPMessage->ulDHCPCookie == dhcpCOOKIE ) && ( pxDHCPMessage->ucOpcode == dhcpREPLY_OPCODE ) && ( pxDHCPMessage->ulTransactionID == ulTransactionId ) )\r
486                 {\r
487                         if( memcmp( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) ) == 0 )\r
488                         {\r
489                                 /* None of the essential options have been processed yet. */\r
490                                 ulProcessed = 0;\r
491 \r
492                                 /* Walk through the options until the dhcpOPTION_END_BYTE byte\r
493                                 is found, taking care not to walk off the end of the options. */\r
494                                 pucByte = &( pxDHCPMessage->ucFirstOptionByte );\r
495                                 pucLastByte = &( pucUDPPayload[ lBytes - dhcpMAX_OPTION_LENGTH_OF_INTEREST ] );\r
496                                 while( ( *pucByte != dhcpOPTION_END_BYTE ) && ( pucByte < pucLastByte ) )\r
497                                 {\r
498                                         ucOptionCode = *pucByte;\r
499                                         pucByte++;\r
500                                         ucLength = *pucByte;\r
501                                         pucByte++;\r
502 \r
503                                         switch( ucOptionCode )\r
504                                         {\r
505                                                 case dhcpMESSAGE_TYPE_OPTION_CODE       :\r
506 \r
507                                                         if( *pucByte == ucExpectedMessageType )\r
508                                                         {\r
509                                                                 /* The message type is the message type the\r
510                                                                 state machine is expecting. */\r
511                                                                 ulProcessed++;\r
512                                                         }\r
513                                                         else if( *pucByte == dhcpMESSAGE_TYPE_NACK )\r
514                                                         {\r
515                                                                 if( ucExpectedMessageType == dhcpMESSAGE_TYPE_ACK )\r
516                                                                 {\r
517                                                                         /* Start again. */\r
518                                                                         eDHCPState = eWaitingSendFirstDiscover;\r
519                                                                 }\r
520                                                         }\r
521                                                         else\r
522                                                         {\r
523                                                                 /* Don't process other message types. */\r
524                                                         }\r
525                                                         break;\r
526 \r
527                                                 case dhcpSUBNET_MASK_OPTION_CODE :\r
528 \r
529                                                         if( ucLength == sizeof( uint32_t ) )\r
530                                                         {\r
531                                                                 memcpy( ( void * ) &( pxNetworkAddressing->ulNetMask ), ( void * ) pucByte, ( size_t ) ucLength );\r
532                                                         }\r
533                                                         break;\r
534 \r
535                                                 case dhcpGATEWAY_OPTION_CODE :\r
536 \r
537                                                         if( ucLength == sizeof( uint32_t ) )\r
538                                                         {\r
539                                                                 /* ulProcessed is not incremented in this case\r
540                                                                 because the gateway is not essential. */\r
541                                                                 memcpy( ( void * ) &( pxNetworkAddressing->ulGatewayAddress ), ( void * ) pucByte, ( size_t ) ucLength );\r
542                                                         }\r
543                                                         break;\r
544 \r
545                                                 case hdcpDNS_SERVER_OPTIONS_CODE :\r
546 \r
547                                                         /* ulProcessed is not incremented in this case\r
548                                                         because the DNS server is not essential.  Only the\r
549                                                         first DNS server address is taken. */\r
550                                                         memcpy( ( void * ) &( pxNetworkAddressing->ulDNSServerAddress ), ( void * ) pucByte, sizeof( uint32_t ) );\r
551                                                         break;\r
552 \r
553                                                 case dhcpSERVER_IP_ADDRESS_OPTION_CODE :\r
554 \r
555                                                         if( ucLength == sizeof( uint32_t ) )\r
556                                                         {\r
557                                                                 if( ucExpectedMessageType == dhcpMESSAGE_TYPE_OFFER )\r
558                                                                 {\r
559                                                                         /* Offers state the replying server. */\r
560                                                                         ulProcessed++;\r
561                                                                         memcpy( ( void * ) &ulDHCPServerAddress, ( void * ) pucByte, ( size_t ) ucLength );\r
562                                                                 }\r
563                                                                 else\r
564                                                                 {\r
565                                                                         /* The ack must come from the expected server. */\r
566                                                                         if( memcmp( ( void * ) &ulDHCPServerAddress, ( void * ) pucByte, ( size_t ) ucLength ) == 0 )\r
567                                                                         {\r
568                                                                                 ulProcessed++;\r
569                                                                         }\r
570                                                                 }\r
571                                                         }\r
572                                                         break;\r
573 \r
574                                                 case dhcpLEASE_TIME_OPTION_CODE :\r
575 \r
576                                                         if( ucLength == sizeof( &ulLeaseTime ) )\r
577                                                         {\r
578                                                                 /* ulProcessed is not incremented in this case\r
579                                                                 because the lease time is not essential. */\r
580                                                                 memcpy( ( void * ) &ulLeaseTime, ( void * ) pucByte, ( size_t ) ucLength );\r
581                                                                 ulLeaseTime = FreeRTOS_ntohl( ulLeaseTime );\r
582 \r
583                                                                 /* Convert the lease time to milliseconds\r
584                                                                 (*1000) then ticks (/portTICK_RATE_MS). */\r
585                                                                 ulLeaseTime *= ( 1000UL / portTICK_RATE_MS );\r
586 \r
587                                                                 /* Divide the lease time to ensure a renew\r
588                                                                 request is sent before the lease actually\r
589                                                                 expires. */\r
590                                                                 ulLeaseTime >>= 1UL;\r
591                                                         }\r
592                                                         break;\r
593 \r
594                                                 default :\r
595 \r
596                                                         /* Not interested in this field. */\r
597 \r
598                                                         break;\r
599                                         }\r
600 \r
601                                         /* Jump over the data to find the next option code. */\r
602                                         if( ucLength == 0 )\r
603                                         {\r
604                                                 break;\r
605                                         }\r
606                                         else\r
607                                         {\r
608                                                 pucByte += ucLength;\r
609                                         }\r
610                                 }\r
611 \r
612                                 /* Were all the mandatory options received? */\r
613                                 if( ulProcessed == ulMandatoryOptions )\r
614                                 {\r
615                                         ulOfferedIPAddress = pxDHCPMessage->ulYourIPAddress_yiaddr;\r
616                                         xReturn = pdPASS;\r
617                                 }\r
618                         }\r
619                 }\r
620 \r
621                 FreeRTOS_ReleaseUDPPayloadBuffer( ( void * ) pucUDPPayload );\r
622         }\r
623 \r
624         return xReturn;\r
625 }\r
626 /*-----------------------------------------------------------*/\r
627 \r
628 static uint8_t *prvCreatePartDHCPMessage( struct freertos_sockaddr *pxAddress, xMACAddress_t *pxMACAddress, uint8_t ucOpcode, const uint8_t * const pucOptionsArray, size_t xOptionsArraySize )\r
629 {\r
630 xDHCPMessage_t *pxDHCPMessage;\r
631 const size_t xRequiredBufferSize = sizeof( xDHCPMessage_t ) + xOptionsArraySize;\r
632 uint8_t *pucUDPPayloadBuffer;\r
633 \r
634         /* Get a buffer.  This uses a maximum delay, but the delay will be capped\r
635         to ipconfigMAX_SEND_BLOCK_TIME_TICKS so the return value still needs to be\r
636         test. */\r
637         do\r
638         {\r
639         }while( ( pucUDPPayloadBuffer = ( uint8_t * ) FreeRTOS_GetUDPPayloadBuffer( xRequiredBufferSize, portMAX_DELAY ) ) == NULL );\r
640 \r
641         pxDHCPMessage = ( xDHCPMessage_t * ) pucUDPPayloadBuffer;\r
642 \r
643         /* Most fields need to be zero. */\r
644         memset( ( void * ) pxDHCPMessage, 0x00, sizeof( xDHCPMessage_t ) );\r
645 \r
646         /* Create the message. */\r
647         pxDHCPMessage->ucOpcode = ucOpcode;\r
648         pxDHCPMessage->ucAddressType = dhcpADDRESS_TYPE_ETHERNET;\r
649         pxDHCPMessage->ucAddressLength = dhcpETHERNET_ADDRESS_LENGTH;\r
650         pxDHCPMessage->ulTransactionID = ulTransactionId;\r
651         pxDHCPMessage->ulDHCPCookie = dhcpCOOKIE;\r
652         pxDHCPMessage->usFlags = dhcpBROADCAST;\r
653         memcpy( ( void * ) &( pxDHCPMessage->ucClientHardwareAddress[ 0 ] ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) );\r
654 \r
655         /* Copy in the const part of the options options. */\r
656         memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET ] ), ( void * ) pucOptionsArray, xOptionsArraySize );\r
657 \r
658         /* Map in the client identifier. */\r
659         memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpCLIENT_IDENTIFIER_OFFSET ] ), ( void * ) pxMACAddress, sizeof( xMACAddress_t ) );\r
660 \r
661         /* Set the addressing. */\r
662         pxAddress->sin_addr = ipBROADCAST_IP_ADDRESS;\r
663         pxAddress->sin_port = ( uint16_t ) dhcpSERVER_PORT;\r
664 \r
665         return pucUDPPayloadBuffer;\r
666 }\r
667 /*-----------------------------------------------------------*/\r
668 \r
669 static void prvSendDHCPRequest( xMACAddress_t *pxMACAddress )\r
670 {\r
671 uint8_t *pucUDPPayloadBuffer;\r
672 struct freertos_sockaddr xAddress;\r
673 static const uint8_t ucDHCPRequestOptions[] =\r
674 {\r
675         /* Do not change the ordering without also changing\r
676         dhcpCLIENT_IDENTIFIER_OFFSET, dhcpREQUESTED_IP_ADDRESS_OFFSET and\r
677         dhcpDHCP_SERVER_IP_ADDRESS_OFFSET. */\r
678         dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_REQUEST,              /* Message type option. */\r
679         dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0,                 /* Client identifier. */\r
680         dhcpREQUEST_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0,                              /* The IP address being requested. */\r
681         dhcpSERVER_IP_ADDRESS_OPTION_CODE, 4, 0, 0, 0, 0,                               /* The IP address of the DHCP server. */\r
682         dhcpOPTION_END_BYTE\r
683 };\r
684 \r
685         pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, pxMACAddress, dhcpREQUEST_OPCODE, ucDHCPRequestOptions, sizeof( ucDHCPRequestOptions ) );\r
686 \r
687         /* Copy in the IP address being requested. */\r
688         memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpREQUESTED_IP_ADDRESS_OFFSET ] ), ( void * ) &ulOfferedIPAddress, sizeof( ulOfferedIPAddress ) );\r
689 \r
690         /* Copy in the address of the DHCP server being used. */\r
691         memcpy( ( void * ) &( pucUDPPayloadBuffer[ dhcpFIRST_OPTION_BYTE_OFFSET + dhcpDHCP_SERVER_IP_ADDRESS_OFFSET ] ), ( void * ) &ulDHCPServerAddress, sizeof( ulDHCPServerAddress ) );\r
692 \r
693         iptraceSENDING_DHCP_REQUEST();\r
694         if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( xDHCPMessage_t ) + sizeof( ucDHCPRequestOptions ) ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )\r
695         {\r
696                 /* The packet was not successfully queued for sending and must be\r
697                 returned to the stack. */\r
698                 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );\r
699         }\r
700 }\r
701 /*-----------------------------------------------------------*/\r
702 \r
703 static void prvSendDHCPDiscover( xMACAddress_t *pxMACAddress )\r
704 {\r
705 uint8_t *pucUDPPayloadBuffer;\r
706 struct freertos_sockaddr xAddress;\r
707 static const uint8_t ucDHCPDiscoverOptions[] =\r
708 {\r
709         /* Do not change the ordering without also changing dhcpCLIENT_IDENTIFIER_OFFSET. */\r
710         dhcpMESSAGE_TYPE_OPTION_CODE, 1, dhcpMESSAGE_TYPE_DISCOVER,                                     /* Message type option. */\r
711         dhcpCLIENT_IDENTIFIER_OPTION_CODE, 6, 0, 0, 0, 0, 0, 0,                                         /* Client identifier. */\r
712         dhcpPARAMETER_REQUEST_OPTION_CODE, 3, dhcpSUBNET_MASK_OPTION_CODE, dhcpGATEWAY_OPTION_CODE, hdcpDNS_SERVER_OPTIONS_CODE,        /* Parameter request option. */\r
713         dhcpOPTION_END_BYTE\r
714 };\r
715 \r
716         pucUDPPayloadBuffer = prvCreatePartDHCPMessage( &xAddress, pxMACAddress, dhcpREQUEST_OPCODE, ucDHCPDiscoverOptions, sizeof( ucDHCPDiscoverOptions ) );\r
717 \r
718         iptraceSENDING_DHCP_DISCOVER();\r
719         if( FreeRTOS_sendto( xDHCPSocket, pucUDPPayloadBuffer, ( sizeof( xDHCPMessage_t ) + sizeof( ucDHCPDiscoverOptions ) ), FREERTOS_ZERO_COPY, &xAddress, sizeof( xAddress ) ) == 0 )\r
720         {\r
721                 /* The packet was not successfully queued for sending and must be\r
722                 returned to the stack. */\r
723                 FreeRTOS_ReleaseUDPPayloadBuffer( pucUDPPayloadBuffer );\r
724         }\r
725 }\r
726 /*-----------------------------------------------------------*/\r
727 \r
728 #endif /* ipconfigUSE_DHCP != 0 */\r
729 \r
730 \r