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